Skip to content

Android Declare and Library Enhancements

There have been several enhancements to Declare and Kotlin Library support in Xojo 2024 Release 3 which, along with previous improvements, should allow you to interface your Android projects with more from the native ecosystem.

This post describes the things you can do with Declares and Libraries with Android projects.

Declare Integer Types

The size-specific Integer types are now precise when used with Declares. The mappings are as follows:

Xojo TypeKotlin Type
Int8, ByteByte
UInt8UByte
Int16Short
UInt16UShort
Int32Int
UInt32UInt
Integer, Int64Long
UInteger, UInt64ULong

When mapping to Android API functions or Library functions, be sure to use the correct Xojo type that matches what is expected on Android. By far you will use Int32 and Integer.

If you previously created Declares, you will likely need to change usage of Integer to Int32 as the mapping for that have changed compared to prior versions.

Nullable Types

Xojo object variables (also properties, parameters and return values) can be Nil. With Kotlin, the default is that these things cannot be null (the equivalent of Nil). To ensure that you don’t get compile errors with your parameters, declare them as nullable in Kotlin by appending the “?” to the end of the type name.

Application Context

Many Android APIs require the application context. This is not available in a Library so it has to be supplied by the app itself. There is a property for this:

MobileApplication.AndroidContextHandle As Ptr

You can pass this value to a function on the Library that then either saves it or uses it directly.

Using Ptr

Xojo uses the Ptr type to interface with Declares, however Kotlin does not have a Ptr type. Instead the Any type is used with Kotlin. This means that if you are passing something that is a Ptr to Kotlin (such as the app context above), your library function declaration should use “Any?” as the type of the parameter.

In your function, cast the parameter to the actual type you want. The same rule applies to return values. If the return value in the Declare is a Ptr, then in Kotlin the function declaration should be “Any?”.

You can also use Ptr with API calls to save object references as described below.

Library Permissions

Depending on the API you are using, you may need to add permissions to the app manifest.xml for the Library. These permissions also need to be applied to the main app. This can be done using the Advanced tab of the Android Build Settings. There you’ll find the Permissions property in the Inspector where you can add one Permission per line.

Previous versions let you separate permissions by spaces or commas. Going forward, each permission must be on its own line. Empty lines are ignored.

There you can add the permission constants that are required by your Library. In addition, you can follow the constant with additional attributes and they will also be applied to the app’s manifest file.

For example, to include the android:maxSdkVersion attribute with the BLUETOOTH permission:

android.permission.BLUETOOTH android:maxSdkVersion="30"

Library Dependencies

Similarly to permissions, your Library may make use of additional dependencies, which could even be other Libraries. These are added to the build.gradle file for the Library, but also need to be included in the main app.

You can add these dependencies using the Dependencies property on the Advanced Android Build Settings.

Creating Objects

Previous versions of Xojo only let you call Companion (essentially shared) methods on Library classes. Now you can also work with class instances and call instance methods. This gives you even better control and ability to interface with Android APIs.

To create an object, your Library class should have a factory method on the Companion that returns a new instance. It is clearest to use create() as this method name.

In Xojo you create a Declare to this create() method and call it, saving the result in a Ptr.

Declare Function create Lib "com.example.utility.counter" () As Ptr
Var counter As Ptr = create

Instead of using a shared factory method, you can also call the Class constructor using this syntax:

Declare Function counter Lib "com.example.utility.counter" () As Ptr

Because the function name has the same name as the class itself, this will call the constructor of the class.

When you want to call a method on the instance, you pass the instance as a Ptr. First, Declare to the method adding “.instance” to the library location to indicate you are calling a class instance.

Declare Function increment Lib "com.example.utility.counter.instance" (ref As Ptr) As Integer

Then you can call the method and save its value.

Var count As Integer = increment(counter)

The reference as the Ptr must be the first parameter passed in the Declare. Follow it with other parameters as usual.

You can also call instance methods directly in the Android API in this manner.

Method Callbacks

There are Android APIs that use callbacks to methods that you provide. In Xojo these methods are Delegates and are methods that you supply using AddressOf. It is very important that the signature of this method exactly matches what the callback expects which means you’ll be limited to just the Boolean and Ptr types.

If you want to use an API that uses its own callbacks, you’ll be better served by creating a Library and having the API do the callback to your own Library methods, which you can then call back to your Xojo methods.

You can pass the reference to the Xojo method using AddressOf like this:

Var cb As Ptr = AddressOf TestCallback
Declare Sub xojocallback Lib "com.example.utility.callback" (cb As Ptr)
xojocallback(cb)

The Xojo method can only use parameter types of Boolean and Ptr. It looks like this:

Public Sub TestCallback(b As Boolean, i As Ptr, s As Ptr)

In the Kotlin library you have to cast the incoming parameter to a function reference. This is a two-step process where you verify the function has the correct number of parameters and then you cast it to the specific parameter types. Sample code:

if (cb is Function3<*, *, *, *>) {
    try {
         (cb as Function3<Boolean, Long, String, Unit>).invoke(true, 42, "Hello")
         println("Sent callback to Xojo")
    } catch (e: ClassCastException) {
         println("Casting failed: ${e.message}")
    }
}

The “Function3” indicates a function with 3 parameters (the last “*” indicates the return type), but you can change that as needed using Function1, Function2, etc. You can pass anything back through a Ptr type, but the Xojo code has to convert them using Ptr methods so you should not change the types of the values you send back to be different than what your Xojo code expects.

Object Declares

Object Declares are not new, but for completeness are described here. Essentially an Object Declare uses special syntax to Declare to a UI control.

The typical way to use an Object Declare is by adding an Extends method to a Module. For example, an extension method to allow MobileButton to change its colors could look like this:

SetBackColor(Extends ctrl As MobileButton, c As Color)

The Object Declare looks like this:

Declare Sub setBackgroundColor Lib "Object:ctrl:MobileButton" (myColor As Int32)
setBackgroundColor(c.ToInteger)

Breaking this down, we’re calling the setBackgroundColor function. The Lib denotes this new type of Object declare: an Object, separated by a colon, the Xojo name of the object (in this case, our “ctrl” parameter), followed by another colon, and the Xojo type of the object.

Kotlin Declares

You can use the Android-specific “Kotlin” Declare designation to indicates that what is specified in the Alias section of the Declare should be used as is. This is particularly helpful when you want to cast Ptr values to a specific type for use with OS APIs.

This code gets a ColorStateList object for a Xojo Color value:

Var c As Color = Color.Blue

Declare Function valueOf Lib "android.content.res.ColorStateList:Kotlin" Alias _
"android.content.res.ColorStateList.valueOf(android.graphics.Color.argb(alpha.toInt(), r.toInt(), g.toInt(), b.toInt()))" _
(alpha As Int32, r As Int32, g As Int32, b As Int32) As Ptr

Var csl As Ptr = valueOf(255 - c.Alpha, c.Red, c.Green, c.Blue)

This code can then be used to call setStrokeColor on a MobileButton:

Declare Sub import Lib "android.content.res.ColorStateList:Import" // Imports the ColorStateList class

Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias "setStrokeColor(strokecolor as ColorStateList)" (strokeColor As Ptr)
setStrokeColor(csl)

As you can see, in the Alias is the Kotlin method call that casts the incoming Ptr value to a ColorStateList.

Import

Note the “import” declare in the code snippet above. This is also specific to Android. This Declare indicates that a specific class should be imported so that it can be used for casting purposes. If you left off that import, then the cast to ColorStateList would cause a compile error because ColorStateList would not be known.

You only need to import a specific class once. After you’ve done so it can be referenced by any other Declare, even ones in different methods.

The use of an import Declare is optional and is meant to simply the Kotlin method call code in the Alias. Instead of using import you can specify the full class path in the cast like this:

Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias "setStrokeColor(strokecolor as android.content.res.ColorStateList)" (strokeColor As Ptr)
setStrokeColor(csl)

Handles

Many classes in the framework now have Handle properties (or methods) which you can use with various Declare techniques described above. These include: FolderItem, Font, GraphicsPath, Locale, TimeZone, Graphics, Picture. In addition, the mobile controls have Handle properties that can serve as an alternative for an Object Declare.

Sample Project

You can download the sample project that demonstrates a library and some of the concepts described above. You can use this as a template or starting point for creating your own libraries or Declares.

Sample Utility Library Project

You might also want to take a look at the Android Design Extensions open source project, which has hundreds of Declares that can enhance your Android apps.

References

Paul learned to program in BASIC at age 13 and has programmed in more languages than he remembers, with Xojo being an obvious favorite. When not working on Xojo, you can find him talking about retrocomputing at Goto 10 and on Mastodon @lefebvre@hachyderm.io.