1. Gluon Attach

1.1. Overview

Attach addresses the integration with low-level platform APIs. Using Attach, you write code that accesses device and hardware features using a uniform, platform-independent API.

At runtime, the appropriate implementation (desktop, android, ios) makes sure the platform specific code is used to deliver the functionality.

The following sections provide a high-level overview of the current Attach features.

More information can be found in the Gluon website and you can find different samples using different Attach services.

This library is being actively extended, so if you think an important feature is missing, feel free to log an issue or contribute a pull request at our Attach repository.

1.2. General

The Attach project is split up into different services. Each service takes care of implementing a certain hardware or device feature, like access to general device information, information about the display, outputs of different sensors that are found on the device, etc.

The full list of services can be found here.

1.3. Using Attach

1.3.1. Prerequisites

Attach prerequisites are in line with those required by Gluon Client.

1.3.2. Adding Attach to the project

Configuring your project to include an Attach service is done via adding the dependency to the project and the name of the service to attachList configuration.

When creating a project with the Gluon IDE plugin, the attachList is already available and pre-configured with the following four services: display, lifecycle, statusbar and storage.

The list of services can be extended with any of the available services.

For instance, the Position service, that allows accessing the device’s GPS, can be added as follows:

<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>position</artifactId>
    <version>${attach.version}</version>
</dependency>
...
<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>client-maven-plugin</artifactId>
    <version>${client.maven.plugin.version}</version>
    <configuration>
        <target>${client.target}</target>
        <attachList>
            ...
            <list>position</list>
        </attachList>
        <mainClass>${main.class}</mainClass>
    </configuration>
</plugin>

1.3.3. Using Services

Each Attach service provides the functionality to access a given feature in every platform where it is supported.

Each service can be accessed via its create() method, which returns an Optional which contains an instance of the requested service.

The returned optional object might be empty if the runtime platform has no available implementation for that specific service.

PositionService positionService = PositionService.create().orElseThrow(() -> new RuntimeException("PositionService not available."));
positionService.positionProperty().addListener((obs, ov, nv) -> {
    System.out.println("Latest known GPS coordinates from device: " + nv.getLatitude() + ", " + nv.getLongitude());
});
positionService.start();

Another useful class is the enum com.gluonhq.attach.util.Platform, which provides an easy way to detect what platform the application is currently running on: ANDROID, IOS or DESKTOP.

1.4. Services Overview

For an overview of all the available services, we refer you to the services package overview page in the Attach javadoc:

1.4.1. Service Requirements

Each service has certain requirements that need to be addressed before deploying the mobile applications, either on Android, or on iOS, or both.

Attach javadoc provides detailed information on these cases.

Android

For instance, the Dialer service requires a CALL_PHONE permission included in the Android manifest file:

<manifest ...>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    ...
</manifest>

The AndroidManifest.xml file is generated for the project with the client:package goal, and it is available at target/client/aarch64-android/gensrc/android.

The Client plugin takes care of most of these modifications, and usually, there is no need to modify this manifest.

Only in case you need to make any change, copy this file to src/android and make any modification that might be needed. For every new run of client:package, the manifest found at src/android will be used.

iOS

For instance, the Position service requires a few keys included in the Default-Info.plist file:

<plist ...>
<dict>
    ...
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>This app needs location services</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>This app needs location services</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>This app needs bluetooth services</string>
</dict>
</plist>

The Default-Info.plist file is generated for the project with the client:link goal, and it is available at target/client/arm64-ios/gensrc/ios.

In case you need to make any change, copy this file to src/ios and make any modification that might be needed. For every new run of client:link, the plist found at src/ios will be used.

1.5. Attach source code

Attach is open source, and it is freely licensed under the GPL license. Its source code can be found under the Gluon Attach repository.

1.6. Building Attach

Note

The following sections are intended only for developers who want to create a native service or understand how the Attach native services are created.

If you want to build Attach from code, you need JDK 11+, Xcode 11+, the Android SDK 29 and the Android NDK.

Clone the repository, and from Mac OS X, run:

export ANDROID_SDK=$ANDROID_HOME
export ANDROID_NDK=$ANDROID_HOME/ndk-bundle
./gradlew clean build publishToMavenLocal

It will build all services for all possible platforms (desktop, iOS and Android), and it will install them to your .m2 local repository.

You can try to build from Linux or Windows, however the iOS implementation won’t be built.

Single service builds can be done as well. For instance, to build and install locally the Display service, run:

./gradlew :display:publishToMavenLocal

1.6.1. Platform builds

The tasks under mavenPublish.gradle and native-build.gradle perform the native build for desktop, iOS and Android.

The goal is to produce an artifact that can be used as dependency by the Client plugin when creating a native image that will be deployed to the target platform.

Such artifact (a jar file), bundles Java classes, configuration files and native libraries.

The builds are done with the nativeBuild task. For instance, to build the native libraries for the three platforms, run:

./gradlew :display:nativeBuild

or in case you want to build a given platform, run:

./gradlew :display:iosBuild
Desktop

For a given service, its Java code (GraalVM) is compiled against JDK 11+, and with the desktopJar task it is added to the ${service}-${version}-desktop.jar file.

Reflection and jni configuration json files are added under META-INF/substrate/config (see config files).

Native code, if present, will be compiled and the native library will be added to the jar to a native folder.

iOS

For a given service, its Java code (GraalVM) is compiled against JDK 11+, and with the iosJar task it is added to the ${service}-${version}-ios.jar file.

Reflection and jni configuration json files are added under META-INF/substrate/config (see config files).

Native code using Objective-C and found for each service under:

def osSources = []
osSources = "$projectDir/src/main/native/ios/${name}.m"

is compiled and linked, using the iOS SDK with certain flags and iOS frameworks, to create a native library under:

def linkerOutput = "$buildDir/native/ios/$arch/lib${name}.a"

The native library is then added to the jar to a native folder.

Android

For a given service, its Java code (GraalVM) is compiled against JDK 11+, and with the androidJar task it is added to the ${service}-${version}-android.jar file.

Reflection and jni configuration json files are added under META-INF/substrate/config (see config files).

Native code using C and found for each service under:

def nativeSourcesDir = []
nativeSourcesDir = "$projectDir/src/main/native/android/c/*.c"

is compiled and linked, using the Android SDK with certain flags, to create a native library under:

def linkerOutput = "$buildDir/native/android/lib${name}.a"

The native library is then added to the jar to a native folder.

Finally, Java code for Android (Dalvik) is compiled against JDK 1.7, using the Android SDK. An Android library project is generated and bundled under META-INF/substrate/dalvik/${name}.aar. It will contain mainly a jar with classes and an AndroidManifest.xml file with the service requirements (like permissions or activities).

1.6.2. Attach with Client plugin

An Attach service is added to a project as regular dependency but it has also to be included in the attachList:

<!-- dependencies -->
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>display</artifactId>
    <version>4.0.9</version>
</dependency>

<!-- plugin -->
<configuration>
    <attachList>
        <list>display</list>
    </attachList>
</configuration>

If needed, the platform classifier can be used to run the project on HotSpot with the JavaFX Maven plugin:

<!-- dependencies -->
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>display</artifactId>
    <version>4.0.9</version>
</dependency>
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>display</artifactId>
    <version>4.0.9</version>
    <classifier>desktop</classifier>
    <scope>runtime</scope>
</dependency>

This can be done with the Maven profiles, so these platform dependencies are only activated for the correct profile.

When running the Client plugin goals, for each Attach service, the plugin will apply the correct classifier based on the target and resolve the artifact.

Then, for every ${service}-${version}-${platform}.jar file found in the classpath, the plugin will:

  • add the Java classes (GraalVM) to the native-image classpath to be compiled with client:compile,

  • merge the configuration json files with others found in the classpath,

  • extract the native libraries into target/client/$arch-$os/gvm/lib/lib${name}.a, so they can be linked with client:link,

  • and when targetting Android, extract the Android libraries (.aar) into target/client/$arch-$os/gvm/android_project/libs. These libraries can be added as regular dependencies for the Android project when creating the apk in the client:package goal.

2. Gluon Attach JavaDoc

The Gluon Attach JavaDoc can be found here.