Try Java 9 with Gluon VM
You can now try deploying Java 9 applications by leveraging Gluon VM. Read more about how to get started on the javafxmobile plugin repository on GitHub.

1. Introduction

Gluon provides Java Enterprise capabilities for Java Client development. With Gluon, Java Client developers use an API in a familiar language (Java) to perform familiar Enterprise operations.

Gluon consists of [A] a client component named Gluon Mobile and [B] a server component named Gluon CloudLink.

gluon arch1

Gluon Mobile [A] is the client-side library and development tool that

  1. provides UI controls for the client application

  2. handles communication with the server-side Gluon CloudLink

  3. handles communication with other 3rd party services

  4. abstracts (parts of) the platform specific APIs.

Gluon CloudLink [B] is the bridge between your application running on devices (desktop, laptop, mobile or embedded) and optional enterprise systems at your backend (5). See the Gluon CloudLink documentation for more information.

1.1. Gluon Mobile

Charm

Gluon Mobile provides:

  1. Tooling to build and deploy your Java app to desktop, Android and iOS platforms

  2. Glisten, the UI component that offers JavaFX Controls that have a cross platform behavior, but a platform specific look and feel. They make your application look like a native application.

  3. Connect, the API for communicating to Gluon CloudLink or other 3rd party webservices.

  4. Down, which makes device APIs available in a device-independent fashion, like the storage systems, accelerometers, screen details, GPS devices, and more.

2. Getting started

JavaFX applications using Gluon Mobile can target three different platforms: Android, iOS and the regular desktop. Creating a project that is able to build the packages for those platforms and get them deployed on your devices is not an easy task. However, by using one of our IDE plugins (see below), all of this should be a breeze.

2.1. Prerequisites

2.1.1. General

  1. The latest version of JDK 8, available from http://www.oracle.com/technetwork/java/javase/downloads/index.html

  2. Gradle 2.2 or higher is required to build applications with the jfxmobile plugin

2.1.2. iOS

  1. A Mac with MacOS X 10.11.5 or superior

  2. Xcode 8.x or superior, available from the Mac App Store

2.1.3. Android

  1. The Android SDK, available here.

  2. From the Android SDK Manager, install at least version 23.0.1 of the Build-tools, the SDK Platform for API 21 and from Extras the Android Support Repository. Getting started with the Android SDK Manager is explained here.

  3. Finally, you have to let the jfxmobile plugin know where to look for the Android SDK installation directory using one of the following ways:

    • an androidSdk property defined under jfxmobile.android in build.gradle

    • a gradle property with the name ANDROID_HOME: defined in ~/.gradle/gradle.properties or by passing -PANDROID_HOME to the gradle build

    • a system environment variable with the name ANDROID_HOME

The jfxmobile plugin will first try to look if an androidSdk property has been set. If not, it will look for the ANDROID_HOME gradle property and finally resort to the ANDROID_HOME system environment variable.

2.1.4. Embedded

  1. The latest version of JDK 8 installed on your embedded device, available from http://www.oracle.com/technetwork/java/javase/downloads/index.html

  2. The latest version of the JavaFX Embedded SDK port, available from http://gluonhq.com/open-source/javafxports/downloads. Unzip the downloaded JavaFX Embedded SDK and copy the following files into the JDK 8 installation directory:

armv6hf-sdk/rt/lib/ext/jfxrt.jar

jre/lib/ext/

armv6hf-sdk/rt/lib/arm/*

jre/lib/arm/

armv6hf-sdk/rt/lib/javafx.platform.properties

jre/lib/

armv6hf-sdk/rt/lib/javafx.properties

jre/lib/

armv6hf-sdk/rt/lib/jfxswt.jar

jre/lib/

2.2. Project setup

Once you have checked the list of prerequisites, there are two ways to get your project up and running:

  1. Use the Gluon IDE plugins

  2. Download a sample application

At Gluon we are introducing IDE plugins to support the community in making use of our technology. The plugin aids in creating a Gluon application project inside your IDE. At present we support NetBeans, IntelliJ IDEA and Eclipse.

2.3. The Gluon Plugin for NetBeans

In this section, we’ll explain briefly how to install the plugin on NetBeans and how to use it to create a sample application that can be deployed on desktop, Android and iOS devices. Before you start, be sure that you have checked the list of prerequisites.

2.3.1. Plugin Installation

You can get it from the NetBeans plugin portal, or you can directly install it from NetBeans: click Tools→Plugins. Now select Available Plugins…​ and find Gluon Plugin.

Plugins Window

Select the plugin, click Install and follow the steps:

Install plugin

Accept the license and click Install:

Plugin license

Click Continue when prompted:

Plugin warning

Wait until the Gluon Plugin is installed. And click Finish to restart the IDE.

Plugin installed

You will find the plugin under the Installed tab:

Gluon Plugin installed

2.3.2. Creating a new Gluon project

Now that we have the plugin installed, we are going to use it to create a sample application.

In NetBeans, click File→New→Project…​ and select Gluon on the left, and one of the available projects on the right. For instance, Gluon Mobile - Single View Project. Press Next.

New Gluon Project

The first time you use the plugin, you will be asked to enter your email address.

Gluon Settings

If you already have a Gluon Mobile/Desktop license key for your projects, you can insert them here as well, so they will be added by default to your new projects. If you don’t, you will be using the free (trial) version. Please find more about Gluon Mobile licenses or Gluon Desktop licenses.

Gluon Settings

Once you have completed this first-time-only form, click Next.

Type the name of the project, find a proper location, add the package name and change the main class name if required. Note that you can select what platforms the project will be deployed to.

Project Settings

Press Finish and the project will be created and opened.

NetBeans Project

You will notice that a multi-project has been created, with a root project and the Gluon Mobile sub-project.

------------------------------------------------------------
Root project
------------------------------------------------------------

Root project 'GluonApplication'
+--- Project ':GluonApplicationApp'
The Gluon Mobile project

You will see different folders were created:

  • main for the code that is common to all platforms

  • desktop for the code that is specific to the desktop

  • android for the code that is specific to the Android platform

  • ios for the code that is specific to the iOS platform

The plugin already adds some default code in the main folders, so we’ll be able to run it without adding a single line of code.

Update build

Note that it is really convenient to set the ANDROID_HOME property in the .gradle/gradle.properties file in your user home directory. It should point to an Android SDK folder on your file system.

2.3.3. Deploying the project

The plugin includes a series of tasks, and to access them we just need to right click on the root of the project and select Tasks. A menu will appear, showing all the available gradle tasks.

Before deploying on mobile it’s easier to run the application on desktop first and verify that there are no errors.

To do that, select run from the task list. Verify that all the tasks are executed without errors, and the project runs fine on your desktop.

Task run

Let’s make a slight change to the code. In the BasicView class, update the text of the label on line 21 to the following:

button.setOnAction(e -> label.setText("Hello from NetBeans!"));

and run it again to see that the new message shows up on your desktop when you click the button.

Now you are ready for deploying on an Android or iOS device.

For Android, for instance, select android→android from the tasks list to create the apk or android→androidInstall if you have an Android device connected to your PC to create and deploy the apk on it.

After the build completes, the application should be installed on your device. Otherwise, check the output console for errors.

Task Android

When the installation ends successfully, find the application on your device and open it up:

Android app
Gluon Mobile Settings

The Gluon Mobile project makes use of the Charm Down library.

Displaying the context menu available for the project or sub-project, and selecting the option Gluon→Gluon Mobile Settings:

Gluon Mobile Settings

a list is presented, with the currently used services in the project. It can be easily extended or modified, by selecting any of the available services to the left.

Charm Down services

After making changes in this list, note that you should reload the gradle project to refresh the project dependencies.

If the Gluon Mobile project is connected to a Gluon CloudLink application, displaying the context menu available for the project or sub-project, and selecting the option Gluon→Gluon CloudLink Settings:

Gluon CloudLink Settings

you can easily retrieve the configuration file that binds the client app with the CloudLink application.

The first time you will be prompted to sign in into your CloudLink account with your credentials.

Gluon CloudLink  account sign in

You can find more about CloudLink here if you don’t have an account yet.

In the next dialog, you will get a list of your existing CloudLink applications.

Gluon CloudLink  application secrets

Selecting one of the available applications will download the configuration file to your Gluon Mobile project:

Gluon CloudLink config file
Adding a Gluon Function sub-project

If the Gluon Mobile project is connected to a CloudLink application, you can create a Gluon Function project, displaying the context menu available for the project or sub-project, and selecting the option Gluon→Add Gluon Function project:

Gluon Function

Give a valid name to your function, and a package name:

Gluon Function name

and a new sub-project will be created.

Gluon Function sub-project

Now you can add the required code for the function.

Don’t forget to upload the project to CloudLink after making changes to this function, displaying the context menu available for the Gluon Function sub-project, and selecting the option Gluon→Upload Gluon Function:

Gluon Function upload
Using functions in Gluon Mobile

Once the Gluon Function is created and uploaded to CloudLink, you can include it in your Gluon Mobile project. From Source→Insert Code…​, select Gluon Function…​:

Insert Gluon Function

and select the Function from the dropdown list. You can provide a name for that function, the result type (you can select it from the drop menu button), and the return type.

Define function name

A new method will be added to your code:

Gluon CloudLink config file

Running the application, when calling the remote function, a call will be done to the CloudLink to run the function and return the result to your mobile app:

Gluon CloudLink config file

2.3.4. General Gluon Settings

You can change your Gluon settings at any time. For that, on Windows or Linux go to NetBeans→Tools→Options→Miscellaneous, or on Mac, go to NetBeans→Preferences→Miscellaneous, and select Gluon tab. You will see your currrent settings and you will be able to modify them if needed.

Gluon Settings

2.3.5. Conclusion

If you have made it here without problems, congratulations!

If you have found any difficulties along the way, please review the lists of prerequisites and try again, and if the problems still persist, you can visit our Forums, and ask any question there if you don’t find a suitable solution.

We encourage you to start developing new projects that can be deployed on desktop, Android and iOS devices using the Gluon plugin on your favorite IDE.

2.4. The Gluon Plugin for IntelliJ IDEA

In this section, we’ll explain briefly how to install the plugin on IntelliJ IDEA and how to use it to create a sample application that can be deployed on desktop, Android and iOS devices. Before you start, be sure that you have checked list of prerequisites.

2.4.1. Plugin Installation

You can get it from here, or you can directly install it from IntelliJ IDEA: click File→Settings and select Plugins on the left. You will see the installed plugins on your system.

Settings Window

Now click Browse Repositories…​, find Gluon Plugin, click Install plugin, and confirm installation:

Plugin file

The Gluon Plugin will be downloaded and installed. You may need to restart the IDE.

Gluon Plugin installed

2.4.2. Creating a new Gluon project

Now that we have the plugin installed, we are going to use it to create a sample application.

In IntelliJ IDEA, click File→New→Project…​ and select Gluon on the left, and one of the available projects on the right. For instance, Gluon Mobile - Single View Project. Press Next.

New Gluon Project

The first time you use the plugin, you will be asked to enter your email address.

Gluon Settings

If you already have a Gluon Mobile/Desktop license key for your projects, you can insert them here as well, so they will be added by default to your new projects. If you don’t, you will be using the free (trial) version. Please find more about Gluon Mobile licenses or Gluon Desktop licenses.

Gluon Settings

Once you have completed this first-time-only form, click Next.

Type the package name and the main class name. Note that you can select what platforms the project will be deployed to. Press Next.

Package name

Select a valid Java JDK and press Next.

Java JDK

Now add a name and a location for the project and press Finish.

Project name

IDEA detects we are trying to import a Gradle project, and asks us for some options. We can select the default ones:

Import Project

Note that it is really convenient to set the ANDROID_HOME property in the .gradle/gradle.properties file in your user home directory. It should point to an Android SDK folder on your file system.

Press OK and the project will be imported and opened.

IDEA Project

You will see the different folders were created:

  • main for the code that is common to all platforms

  • desktop for the code that is specific to the desktop

  • android for the code that is specific to the Android platform

  • ios for the code that is specific to the iOS platform

There’s some default code in our main folders, so we’ll be able to run it without adding a single line of code.

Update build

2.4.3. Deploying the project

The plugin includes a series of tasks, and to access them we just need to click View→Tool Windows→Gradle and a panel will show up on the right side revealing a list of all the available gradle tasks.

Before deploying on mobile it’s easier to run the application on desktop first and verify that there are no errors.

To do that, select application→run from the task list. Verify that all the tasks are executed without errors, and the project runs fine on your desktop.

Task run

Let’s make a slight change to the code. In the BasicView class, update the text of the label on line 21 to the following:

button.setOnAction(e -> label.setText("Hello from IntelliJ IDEA!"));

and run it again to see that the new message shows up on your desktop.

Now you are ready for deploying on an Android or iOS device.

For Android, for instance, select other→android from the tasks list to create the apk or other→androidInstall if you have an Android device connected to your PC to create and deploy the apk on it.

After the build completes, the application should be installed on your device. Otherwise, check the output console for errors.

Task Android

Find the application and open it up:

Android app

2.4.4. Changing Gluon Settings

You can change at any time your Gluon settings. For that, on Windows or Linux go to IntelliJ-IDEA→File→Settings→Other Settings, or on Mac, go to IntelliJ-IDEA→Preferences→Other Settings, and select Gluon. You will see your currrent settings and you will be able to modify them if needed.

Gluon Settings

2.4.5. Conclusion

If you have made it here without problems, congratulations!

If you have found any difficulties along the way, please review the lists of prerequisites and try again, and if the problems still persist, you can visit our Forums, and ask any question there if you don’t find a suitable solution.

We encourage you to start developing new projects that can be deployed on desktop, Android and iOS devices using the Gluon plugin on your favorite IDE.

2.5. The Gluon Plugin for Eclipse

In this section, we’ll explain briefly how to install the plugin on Eclipse and how to use it to create a sample application that can be deployed on desktop, Android and iOS devices. Before you start, be sure that you have checked list of prerequisites.

2.5.1. Plugin Installation

Eclipse Marketplace

The easiest way to install the Gluon Plugin can be done through the Eclipse Marketplace. Use this link or go to the Eclipse Marketplace and type gluon in the search field and press Go to find the plugin.

Eclipse Marketplace

Drag the Install button to Eclipse, and a window will show up with several features:

Eclipse Marketplace

Select the Gluon package and optionally the e(fx)clipse package, in case it is not installed yet. e(fx)clipse is highly recommended as it adds a lot of functionality for JavaFX applications development.

Press Confirm and accept the terms of the license agreement.

Accept License Agreements

Press Finish to begin the installation of the plugin. The Gluon Plugin will be downloaded and installed. During the installation process, Eclipse will ask you to trust the Gluon certificate.

Trust the Gluon certificate

Select the Gluon certificate and click OK to continue the installation. When the installation is completed, Eclipse will ask you to restart your IDE for the changes to take effect.

You can now continue to Creating a new Gluon Project.

Update Site

Alternatively, you can install the Gluon Plugin by providing an update site.

Open Eclipse and choose Help→Install New Software…. Paste the following Update Site URL in the Work with: text field: http://download.gluonhq.com/tools/eclipse/release.

Select the Gluon package and optionally the e(fx)clipse package. e(fx)clipse is highly recommended as it adds a lot of functionality for JavaFX applications development.

Install new software

Press Next and review the items that are going to be installed.

Review the items to be installed

Press Next again and accept the terms of the license agreement.

Accept License Agreements

Press Finish to begin the installation of the plugin. The Gluon Plugin will be downloaded and installed. During the installation process, Eclipse will ask you to trust the Gluon certificate.

Trust the Gluon certificate

Select the Gluon certificate and click OK to continue the installation. When the installation is completed, Eclipse will ask you to restart your IDE for the changes to take effect.

2.5.2. Creating a new Gluon project

Now that the plugin is installed, we can use it to create a sample application.

In Eclipse, click File→New→Project… and select and one of the available projects under the Gluon category, for instance, Gluon Mobile - Single View Project and click Next.

New Gluon Project

The first time you use the plugin, you will be asked to enter your email address.

Gluon Settings

If you already have a Gluon Mobile/Desktop license key for your projects, you can insert them here as well, so they will be added by default to your new projects. If you don’t, you will be using the free (trial) version. Please find more about Gluon Mobile licenses or Gluon Desktop licenses.

Gluon Settings

Once you have completed this first-time-only form, click Next.

Provide a name for the project, optionally choose a custom location to store the project and click Next.

Provide project name and location

The plugin will generate a JavaFX application class. In the following step you can configure the name of the package and the name of the class that will be generated. You can select what platforms the project will be deployed to, and the plugin will prepare the directory structure and necessary files for every selected platform. Click Finish to complete the creation of the Gluon project.

Provide package and class name and select platforms

Please note, creating a Gluon project for the first time, might take a while to complete. This is because the gradle build process will initially download all the dependencies of the project.

Eclipse Basic Gluon project

You will see different folders were created:

  • main for code that is common to all platforms

  • desktop for code that should only run on a desktop

  • android for Android specific code

  • ios for iOS specific code

There’s also the default JavaFX application class, so we’ll be able to run it without adding a single line of code.

2.5.3. Deploying the application

The project that is created by the plugin is a Gradle project, which already has the jfxmobile-plugin applied. The jfxmobile-plugin provides a series of gradle tasks that can be used to run your application on any of the supported platforms.

Update build

To get an overview of the different tasks, open the Gradle Tasks view by selecting Window→Show View→Other…. You can find the view under the Gradle (STS) category.

Show the Gradle Tasks view

In the Gradle Tasks view, select the project you just created in the Project combobox and it will load all the available gradle tasks.

Load project into Gradle Tasks view

Before deploying on mobile it’s easier to run the application on desktop first to verify there are no errors. Double click the run task from the list of tasks to launch the application on your desktop.

If all went fine we can make a small change to the code. In the BasicView class, update the text of the label on line 21 to the following:

button.setOnAction(e -> label.setText("Hello from Eclipse!"));

Run the application again to verify that the text inside the label has changed. As a final task, we can try and install the application on a mobile device. Again, make sure that you have followed the prerequisites of the jfxmobile plugin for the platform that you are going to install the application to. When you are ready and your mobile device is plugged in, choose the task androidInstall for Android, or launchIOSDevice for iOS. Please note, that any of these tasks might take a while to complete. Especially for iOS, because when the task is run for the first time, it has to convert the entire RoboVM and JavaFX runtime to iOS native code.

Task Android

If everything went successful, you should see something on your device like the screenshot below:

Android app

2.5.4. Changing Gluon Settings

You can change at any time your Gluon settings. For that, on Windows or Linux go to Eclipse→Windows→Preferences→Gluon, or on Mac, go to Eclipse→Preferences→Gluon. You will see your currrent settings and you will be able to modify them if needed.

Gluon Settings

2.5.5. Conclusion

If you have made it this far without problems, congratulations!

If you have found any difficulties along the way, please review the prerequisites and try again. If the problems persist, you can visit our Support Forums and ask any question there if you don’t find a suitable solution.

We encourage you to start developing new applications that can be deployed on desktop, Android and iOS devices using the Gluon Plugin on your favorite IDE.

2.5.6. Gluon Application

After having installed the plugin into your IDE, you can create a new project.

NetBeans

Choose File→New→Project…​ from the menu and select an appropriate Gluon application template from the Gluon category

IntelliJ

Choose File→New→Project…​ from the menu and select an appropriate Gluon application template from the Gluon category

Eclipse

Choose File→New→Project…​ from the menu and select an appropriate Gluon application template from the Gluon category

2.5.7. Sample Applications

Even if you don’t want to use one of the IDE plugins, it’s easy to get started. You can clone our Gluon Samples repository at GitHub: https://github.com/gluonhq/gluon-samples and open the helloworld sample with your IDE or text editor to get you started. All of our samples are gradle projects, so you can run gradle tasks to build the application and run it on any of the three platforms.

  • ./gradlew android will create an Android package signed with a debug keystore

  • ./gradlew androidRelease will create an Android package signed with the configured signingConfig

  • ./gradlew androidInstall installs your Android application on an Android device that is connected to your development system via a USB cable

  • ./gradlew launchIOSDevice launches your application on an iOS device that is connected to your development system

  • ./gradlew launchIPadSimulator launches your application in an iPad simulator

  • ./gradlew launchIPhoneSimulator launches your application in an iPhone simulator

  • ./gradlew createIpa creates an iOS IPA package

  • ./gradlew run will launch your application on your development system

  • ./gradlew runEmbedded will launch your application on an embedded device

2.5.8. Adding Gluon Mobile to an existing project

If you already have an existing project using gradle, it’s very easy to include Gluon Mobile features.

Open up the build.gradle file (which in NetBeans is located under Build Scripts/Project) and add the Gluon Nexus repository http://nexus.gluonhq.com/nexus/content/repositories/releases/ to the repositories section. To be able to make use of Gluon Charm, you also need to add its dependencies. Your updated build.gradle file will now look something like this:

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases/'
    }
}

dependencies {
    compile 'com.gluonhq:charm:4.4.1'
}

Clean and build the project and check that those dependencies are downloaded (only the first time).

Now that everything is in place, you can start including Charm features in your project.

2.6. Adding your license

Gluon Mobile requires developers to buy a license if they don’t want the nag screen popup when starting their application.

For all information about Gluon Mobile licenses see this link.

After you have created the project or downloaded an existing one, there are 2 ways of adding your license:

1. Using an annotation:

Add your valid license key to your main application class using the @License annotation like this:

import com.gluonhq.charm.glisten.license.License;

@License(key="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
public class HelloWorld extends MobileApplication {
    ...
}

2. Using a license file:

Add a file called gluonmobile.license to your resources and add your valid license key as the content. This method allows you to keep your license private by excluding it from your source version control system.

src/main/resources/gluonmobile.license

XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Licenses are validated online once per application install. If for some reason the license service can’t be contacted, your end-users won’t be annoyed by the popup, but the license check will be retried each time the application starts until successful.

2.7. IDE Live templates

A live template specifies an abbreviation that you may type into your IDE that auto-completes a large chunk of code for you. We have defined live templates for IntelliJ IDEA to create JavaFX properties, including getters, setters, and the property method, and we have done this with support for all property types, and both read-only and read/write properties.

You can download them from here. Once you have the file on your machine, simply do the following to import them into your IntelliJ IDEA install:

  • Unzip the zip file. This will result in there being a settings.jar file as the only file.

  • In IntelliJ, click on the ‘File’ menu

  • Click on ‘Import Settings…’

  • Find the settings.jar file on your file system

  • Restart IntelliJ when asked

Once you have done this, from within your editor, you may simply start to type ‘fxprop‘, and if everything goes to plan you should see a popup that lets you choose the type of property you want, and once you press tab (or enter), the code will be generated inside your editor. You can then immediately start typing the property name, and this will automatically update all the method names. Once you’ve done this, all you need to do is import the relevant classes.

3. Building and deploying

By default, Gluon Mobile applications make use of a gradle build. See the section about IDE plugins to find out how to run specific tasks in your IDE.

To summarize, these are the most important tasks added by the jfx-mobile plugin:

android

Generates a debug Android apk containing the JavaFX application.

androidInstall

Launches the application on a connected android device.

androidRelease

Generates a release Android apk containing the JavaFX application.

launchIOSDevice

Launches the application on a connected ios device.

launchIPadSimulator

Launches the application on an iPad simulator.

launchIPhoneSimulator

Launches the application on an iPhone simulator.

runEmbedded

Launches the application on a remote embedded platform.

The plugin has several configuration options which are described below.

3.1. Dependency Configurations

3.1.1. Platform Dependencies

Apart from the default dependency configurations compile and runtime, the jfxmobile plugin adds extra configurations for each supported platform. This allows developers to add dependencies that should only be applied to a specific platform. The names of the platform specific configurations are platformCompile and platformRuntime where platform can be one of the following: android, ios, desktop or embedded. The platform specific compile and runtime configurations extend from the default compile and runtime configuration respectively.

For instance, if your android code wants to do some json processing, you configure the dependencies as follows:

dependencies {
    androidCompile 'org.glassfish:javax.json:1.0.4'
}

The dependency declared above will make sure the json library is only included when targetting the Android platform, e.g. when creating an APK or when installing it on an Android device. In other words, it won’t be part of an iOS build or when running the application directly on your desktop.

3.1.2. Retrolambda Dependencies

Retrolambda is a tool that converts classes that were compiled with Java 8 as the target into bytecode that can be run on a Java 6 runtime. Retrolambda is required for any Java code that needs to run on an Android device. That is because Android platforms before API 19 only support Java 6, while Java 7 is supported by Android platforms starting from API 19. More information about retrolambda can be read here.

When creating the Android APK of your application, the jfxmobile plugin will apply retrolambda to all compiled classes and to all classes from the dependencies that are found in the androidRuntime configuration. In short this applies to all libraries that are declared in either the compile, runtime, androidCompile or androidRuntime configurations. However, in some cases, applying retrolambda to a library that for instance has already been processed with retrolambda itself, can result in the following exception when running the application on an Android device.

Caused by: java.lang.IncompatibleClassChangeError: The method 'java.lang.Object com.airhacks.afterburner.views.FXMLView.lambda$loadSynchronously$2(java.lang.Class)' *was expected to be of type direct but instead was found to be of type virtual* (declaration of 'com.airhacks.afterburner.views.FXMLView' appears in /data/app/com.gluonhq.otn-1/base.apk)

In that case, you can use the compileNoRetrolambda or runtimeNoRetrolambda configurations to exclude the library from the retrolambda process. For instance, if you don’t want to apply retrolambda to the afterburner library, you can configure it with the following code:

dependencies {
    compile 'com.gluonhq:charm:4.3.5'
    compileNoRetrolambda 'com.airhacks:afterburner.mfx:1.6.2'
    androidRuntime 'com.gluonhq:charm-down-core-android:3.5.0'
}

In this configuration, all three dependencies will be included in the android application, but retrolambda will only be applied to the two gluon libraries, not to the afterburner library. Note that the afterburner version 1.6.3 can be used now with compile, as it doesn’t apply retrolambda anymore.

3.2. General

Use the jfxmobile gradle extension to further configure JavaFXPorts:

jfxmobile {
    javafxportsVersion = '8.60.9'
}

3.2.1. Properties

property

description

default value

javafxportsVersion

Specifies which version of javafxports to use.

8.60.9

javacEncoding

The character encoding that is used when compiling java sources.

utf-8

3.2.2. Extensions

downConfig

The downConfig extension is a convenience class for configuring the dependencies to Charm Down plugins that are being used in a jfxmobile project. It will take care of adding the correct compile and runtime dependencies for each platform. The following example adds the dependency configuration for two Charm Down plugins, storage and lifecycle, using version 3.5.0:

jfxmobile {
    downConfig {
        version '3.5.0'
        plugins 'storage', 'lifecycle'
    }
}

The above code is the equivalent of adding the dependencies to the two plugins manually in the dependencies section of the build.gradle file, like shown below. Note that it’s still perfectly valid to define those dependencies manually. In case duplicate dependencies were created, the one with the highest version will be the final dependency being used.

dependencies {
    compile 'com.gluonhq:charm-down-core:3.5.0'
    compile 'com.gluonhq:charm-down-plugins-storage:3.5.0'
    compile 'com.gluonhq:charm-down-plugins-lifecycle:3.5.0'

    androidRuntime 'com.gluonhq:charm-down-core-android:3.5.0'
    androidRuntime 'com.gluonhq:charm-down-plugins-storage-android:3.5.0'
    androidRuntime 'com.gluonhq:charm-down-plugins-lifecycle-android:3.5.0'

    iosRuntime 'com.gluonhq:charm-down-core-ios:3.5.0'
    iosRuntime 'com.gluonhq:charm-down-plugins-storage-ios:3.5.0'
    iosRuntime 'com.gluonhq:charm-down-plugins-lifecycle-ios:3.5.0'

    desktopRuntime 'com.gluonhq:charm-down-plugins-storage-desktop:3.5.0'
    desktopRuntime 'com.gluonhq:charm-down-plugins-lifecycle-desktop:3.5.0'
}
Properties

property

description

default value

version

The version to use when configuring the Charm Down dependencies.

3.5.0

Methods

method

description

plugins(String…​plugins)

A list of Charm Down plugins to create the dependencies for.

3.3. Android

Android specific configuration can be done in the android gradle extension within the jfxmobile configuration:

jfxmobile {
    javafxportsVersion = '8.60.9'
    android {
        manifest = 'src/android/AndroidManifest.xml'
        dexOptions {
            javaMaxHeapSize '3g'
        }
        packagingOptions {
            exclude 'META-INF/LICENSE.txt'
            exclude 'META-INF/NOTICE.txt'
        }
    }
}

3.3.1. Properties

property

description

default value

androidSdk

The directory containing the Android SDK.

The ANDROID_HOME gradle property or the system environment variable with the same name.

dalvikSdk

The directory containing the JavaFX port for dalvik.

It will be downloaded from maven central when not specified.

buildToolsVersion

The version of the Android build-tools that should be used.

The plugin will try to look for the highest non-preview version available in the Android SDK.

applicationPackage

The name of the Android package, that uniquely identifies your application.

The package name of your main class name.

manifest

The location to a custom AndroidManifest.xml file to use for the application.

The plugin will generate a default AndroidManifest.xml file. A copy can be found in build/javafxports/tmp/android.

compileSdkVersion

The API version of the Android platform to compile against.

21

minSdkVersion

The minimum API version where the application can run on.

4

targetSdkVersion

The API version that the application targets.

compileSdkVersion

assetsDirectory

The directory that contains the Android assets.

src/android/assets

resDirectory

The directory containing the Android resources.

src/android/res

nativeDirectory

The directory containing the native Android libraries.

src/android/jniLibs

3.3.2. Extensions

dexOptions

The dexOptions extension adds options to configure Android dex processing. It is an extension of the DexOptions class found in the Android build tools.

Properties

property

description

default value

additionalParameters

List of additional parameters to be passed to dex.

[]

javaMaxHeapSize

Specifies the -Xmx value when calling dex. Example value is "2048m".

2g

jumboMode

Enable jumbo mode in dex (--force-jumbo).

false

keepRuntimeAnnotatedClasses

Keep all classes with runtime annotations in the main dex in legacy multidex. Disable this for apps that do not use reflection and need more space in their main dex.

true

packagingOptions

The packagingOptions extension adds options that decide what to do when encountering duplicate files during APK generation. It is based entirely on the PackagingOptions class found in the Android build tools.

Properties

property

description

default value

excludes

The list of excluded paths.

[]

pickFirsts

The list of paths where the first occurrence is packaged in the APK.

[]

Methods

method

description

exclude(String path)

Adds an excluded path.

pickFirst(String path)

Adds a firstPick path. First pick paths do get packaged in the APK, but only the first occurrence gets packaged.

signingConfig

The signingConfig extension is used for signing configuration for the release build. It is based entirely on the SigningConfig class found in the Android build tools.

See Signing Your Applications at the Android developer guide for more information. The only difference with the Android gradle plugin is, that you directly set the configuration instead of a reference to a signing configuration that was defined elsewhere.

The android task will use a debug certificate generated by the plugin. The debug keystore and certificate are stored in $HOME/.android/debug.keystore. The androidRelease tasks requires a signingConfig to be configured.

Properties

property

description

keyAlias

Key alias used when signing.

keyPassword

Key password used when signing.

storeFile

Store file used when signing.

storePassword

Store password used when signing.

storeType

Store type used when signing.

3.4. iOS

iOS specific configuration can be done in the ios gradle extension within the jfxmobile configuration:

jfxmobile {
    javafxportsVersion = '8.60.9'
    ios {
        forceLinkClasses = [ 'ensemble.**.*' ]
        infoPList = file('src/ios/Info.plist')
    }
}

property

description

default

iosSdk

The directory containing the JavaFX port for ios.

It will be downloaded from maven central when not specified.

robovmVersion

The version of the RoboVM compiler to use.

2.3.1

forceLinkClasses

A list of string patterns for classes and/or complete packages that should be linked when starting the RoboVM compiler.

[]

frameworks

A list of string for iOS frameworks that should be added to the list of frameworks that are already included by default (which are: UIKit, OpenGLES, QuartzCore, CoreGraphics, CoreText, ImageIO, MobileCoreServices, CoreBluetooth, CoreLocation, CoreMedia, CoreMotion, AVFoundation, AudioToolbox, MediaPlayer, UserNotifications, AVKit and StoreKit).

[]

infoPList

A File that specifies a custom Info.plist file to use for the application.

The plugin will generate a default Default-Info.plist file. A copy can be found in build/javafxports/tmp/ios.

propertiesFile

The path to a RoboVM properties file which contains info for the application.

robovm.properties and robovm.local.properties

configFile

The path to a RoboVM configuration file which configures the RoboVM compiler.

robovm.xml

os

The operating system to use when running the application. Can be any of the following values: ios, macosx, linux.

ios

arch

The architecture to use when running the application. Can be any of the following values: x86, x86_64, thumbv7, arm64.

arm64

iosSkipSigning

A boolean specifying whether signing of the application should be skipped or not.

false

iosSignIdentity

The name of the identity to sign with when building an iOS bundle for the application.

Default is to look for an identity starting with ‘iPhone Developer’ or ‘iOS Development’.

iosProvisioningProfile

The name of the provisioning profile to use when building for device.

debug

A boolean specifying whether the application should be launched in debug mode or not. The application will suspend before the main method is called and will wait for a debugger to connect.

false

debugPort

An integer specifying the port to listen for debugger connections on when launching in debug mode.

If not set a default port will be used. The port that is actually used, will be written to the console before the app is launched.

ipaArchs

A list of architectures to include in the IPA. Either thumbv7 or arm64 or both.

thumbv7, arm64

apsEnvironment

The value for the aps-environment key in the Entitlements.plist file. Valid values are development and production.

assetsDirectory

The directory that contains the iOS resource files.

src/ios/assets

nativeDirectory

The directory containing the native iOS libraries.

src/ios/jniLibs

A lot of the configuration properties for iOS can be directly overruled using gradle properties. For instance, instead of specifying the debug in the build.gradle file, you can run gradle as follows to enable debugging: gradle -Probovm.debug=true -Probovm.debugPort=9000 launchIPhoneSimulator. Below follows a list of properties that can be overruled by which gradle property:

configuration property

gradle property

os

robovm.os

arch

robovm.arch

iosSkipSigning

robovm.iosSkipSigning

iosSignIdentity

robovm.iosSignIdentity

iosProvisioingProfile

robovm.iosProvisioningProfile

debug

robovm.debug

debugPort

robovm.debugPort

ipaArchs

robovm.ipaArchs

apsEnvironment

jfxmobile.ios.apsEnvironment

Finally, two more gradle properties can be provided when launching the iOS simulator to specify which device name and sdk version the simulator should run.

gradle property

description

robovm.device.name

The name of the device to use in the iOS simulator. For instance: iPhone-6s, iPhone-7 or iPad-2.

robovm.sdk.version

The iOS sdk version to use in the iOS simulator. 8.4, 9.3 or 10.3.

To get a list of possible device names and matching sdk versions, you can open Xcode, Window→Devices, and see a list of possible simulators on the left, or click on the plus icon to add a new simulator, and see the list of available devices and simulator SDKs (already available or to be installed).

3.5. Embedded

If you want to run your application on an embedded device, you need to configure a remotePlatform in the embedded extension within the jfxmobile extension:

jfxmobile {
    javafxportsVersion = '8.60.9'
    embedded {
        remotePlatforms {
            raspberry {
                host = '192.168.1.10'
                username = 'pi'
                password = 'raspberry'
                workingDir = '/home/pi/sample'
                jreLocation = '/opt/java'
                execPrefix = 'sudo'
            }
        }
    }
}

property

description

default

host

The ip address of the embedded device. It must be reachable from your development environment.

port

The port that is being used by the ssh server on the embedded device.

22

username

The username to use when connecting to the embedded device.

password

The password of the user that is used when connecting to the embedded device.

keyfile

A keyfile that is being used for connecting to the embedded device. This property will be ignored when a password has been provided.

passphrase

The passphrase for the configured keyfile. This property will be ignored when a password has been provided.

jreLocation

The installation location of JDK 8 for the embedded device.

execPrefix

This string will be prepended to the java command, e.g. sudo

4. Charm Glisten

4.1. Glisten APIs

4.1.1. MobileApplication

The MobileApplication class extends from Application, hence for Glisten-based applications it will be considered the base class.

Let’s have a look at the hierarchy of components added to generate the user interface.

The GlassPane

The GlassPane is the root node added automatically by Glisten to the primary scene.

It can be seen as the invisible container for all the nodes that will be added: views, layers, dialogs or toolbars.

Usually, there won’t be any need to access this container directly. But it can be retrieved with MobileApplication.getInstance().getGlassPane(), if needed.

The first children of the GlassPane container will always be a View and the AppBar.

MobileApp

The nodes on top will be layers (like a side menu), popups or dialogs.

In between, there is a semi-transparent node that will ensure that the node on top appears distinct from the content beneath it, whenever this is shown. Otherwise it won’t be visible.

By calling setBackgroundFade(double) the developer will be able to fade this layer to a darker color, obscuring the main application and drawing focus to the popup.

MobileAppLayer
The AppBar

The AppBar is a special node acting as a toolbar for branding, navigation, search and other different actions.

It is placed at the top of the layout and is generally made up of some buttons (nav icon and action items), a title and a menu.

The content (in terms of nodes and their respective actions) will be managed directly by each of the different views, so the AppBar will be like a toolbar placeholder for each one of them.

Typically, the developer sets up the AppBar for a given View by overriding its updateAppBar method:

class HomeView extends View {

    public HomeView(String name) {
        super(name);
        getLayers().add(new FloatingActionButton());
    }

    @Override
    protected void updateAppBar(AppBar appBar) {
        appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("nav icon")));
        appBar.setTitleText("The AppBar");
        appBar.getActionItems().addAll(
            MaterialDesignIcon.SEARCH.button(e -> System.out.println("search")),
            MaterialDesignIcon.FAVORITE.button(e -> System.out.println("fav")));
        appBar.getMenuItems().addAll(new MenuItem("Settings"));
    }
}
TheAppBar

If the developer doesn’t require the AppBar for a given view, it can be hidden by setting its visibility to false: appBar.setVisible(false).

Another way to update the AppBar will be adding a listener to each view’s showingProperty:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            View view = new View(new Label("Hello Glisten!"));
            view.showingProperty().addListener((obs, oldValue, newValue) -> {
                if (newValue) {
                    AppBar appBar = MobileApplication.getInstance().getAppBar();
                    appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("nav icon")));
                }
            });
            return view;
        });
    }
}
TheAppBar2
The Views

Glisten user interfaces are built using views, which are extensions of MobileLayoutPane. A View is a Glisten container that will allow adding nodes to its top, center and bottom.

Usually a View instance is created by providing a name for the view and the node for its content, this instance is added to a factory of views, so the Glisten UI can load and unload them on demand.

The Home View

By default, the home view will be the first view displayed when the stage is shown.

It has no predefined content, so this view has to be designed by the developer, but its name is already assigned: MobileApplication.HOME_VIEW.

This short snippet will create a very simple mobile application with a single view:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(new Label("Hello Glisten!")));
    }

}
FirstView

Note that if the View is constructed without a name it will take the one given to the factory.

4.1.2. How it works

In order to understand how Glisten works, let’s have a look at the initial JavaFX application lifecycle. Whenever an application is launched, the JavaFX runtime:

  • Constructs an instance of the specified Application class

  • Calls the init() method

  • Calls the start(javafx.stage.Stage) method

Typically, the developer of a Glisten application will only override the init() method, in order to add one or more View objects, and provide them as factories that can be called on-demand. The MobileApplication implementation will take care of the rest: when start() is called, it will create a Scene, adding a root node to it, and finally it will put the scene in the primary stage and show it.

When the runtime calls init(), we just provide a Supplier<View> for HOME_VIEW, but the view is not instantiated at this point yet.

Then start(Stage) is called. In this moment, MobileApplication creates an instance of Scene, sets as root an empty instance of a GlassPane and adds the scene to the primary stage.

After some internal settings, like adding the default Swatch, it calls switchView("HOME_VIEW"). This is the moment when an instance of the home view is created and added to the pane.

Finally the stage is shown.

Changing the default Swatch

What if we want to modify some scene settings before it is shown?

Before showing the stage, there’s a call to the postInit(Scene) method, that can be used by the developer for one time initialization.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(CheckBox("I like Glisten")));
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.GREEN.assignTo(scene);
    }

}
FirstViewGreen

In case the developer wants to change the swatch for each view, this can be done by listening to the changes in the viewProperty of a MobileApplication instance, and assigning the corresponding Swatch:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(new Label("Hello Glisten!")) {
            {
                getLayers().add(new FloatingActionButton(MaterialDesignIcon.ADD_BOX.text, e -> switchView(OTHER_VIEW)));
            }
        });
        addViewFactory(OTHER_VIEW, () -> new View(new CheckBox("I like Glisten")) {
            {
                getLayers().add(new FloatingActionButton(MaterialDesignIcon.ADD_CIRCLE_OUTLINE.text, e -> switchView(HOME_VIEW)));
            }
        });

        viewProperty().addListener((obs, oldView, newView) -> {
            switch(newView.getName()) {
                case HOME_VIEW:
                    Swatch.INDIGO.assignTo(newView.getScene());
                    break;
                case OTHER_VIEW:
                    Swatch.GREEN.assignTo(newView.getScene());
                    break;
                default:
                    Swatch.getDefault().assignTo(newView.getScene());
            }
        });
    }
}

Another way to accomplish the same will be by adding a listener to each view’s showingProperty:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            View view = new View(new RadioButton("Glisten style"));
            view.getLayers().add(new FloatingActionButton(MaterialDesignIcon.ADD_BOX.text, e -> switchView(OTHER_VIEW)));
            view.showingProperty().addListener((obs, oldValue, newValue) -> {
                if (newValue) {
                    Swatch.RED.assignTo(view.getScene());
                }
            });
            return view;
        });
        addViewFactory(OTHER_VIEW, () -> {
            View view = new View(new CheckBox("I like Glisten"));
            view.getLayers().add(new FloatingActionButton(MaterialDesignIcon.ADD_CIRCLE_OUTLINE.text, e -> switchView(HOME_VIEW)));
            view.showingProperty().addListener((obs, oldValue, newValue) -> {
                if (newValue) {
                    Swatch.TEAL.assignTo(view.getScene());
                }
            });
            return view;
        });
    }
}
FirstViewRed
Creating a View

As we have already seen in the previous code snippets, there are several possible ways to create a View.

Extending View

A custom view can be created by extending View. This allows for more complex views, for adding a view transition, customizing the AppBar…​

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new HomeView(HOME_VIEW));
        addViewFactory(OTHER_VIEW, () -> new View(new CheckBox("I like Glisten")));
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.LIGHT_GREEN.assignTo(scene);
    }

    class HomeView extends View {

        public HomeView(String name) {
            super(name);

            setCenter(new Label("Hello Glisten!"));
        }

        @Override
        protected void updateAppBar(AppBar appBar) {
            appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> switchView(OTHER_VIEW)));
            appBar.setTitleText("Home View");
        }
    }
}
HomeView

Adding content

A View can be created by using one of its constructors, where the content is set. Any Node will work as content for the View.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(new Home()) {
            @Override protected void updateAppBar(AppBar appBar) {
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> switchView(OTHER_VIEW)));
                appBar.setTitleText("Home View");
            }
        });
    }

    class Home extends VBox {

        public Home() {
            setSpacing(30);
            setAlignment(Pos.CENTER);
            getChildren().addAll(new Label("Hello Glisten!"), new Button("Click me"));
        }

    }
}
VBoxView

Creating Views with FXML

As well, View can be used within FXML, by adding the proper import.

<?xml version="1.0" encoding="UTF-8"?>

<?import com.gluonhq.charm.glisten.mvc.View?>
<?import javafx.scene.control.Button?>

<View fx:id="home" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <Button fx:id="button" onAction="#onClick" text="Click me!" />
   </center>
</View>

And then loading the fxml file with FXMLLoader. Assuming the home.fxml file is under the same package as the application, but in the Resources folder:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            try {
                return (View)FXMLLoader.load(getClass().getResource("views/home_1.fxml"));
            } catch (Exception ex) { }
            return null;
        });
        addViewFactory(OTHER_VIEW, () -> new View(new CheckBox("I like Glisten")));
        viewProperty().addListener((obs, ov, nv) -> {
            AppBar appBar = MobileApplication.getInstance().getAppBar();
            switch(nv.getName()) {
                case HOME_VIEW:
                    appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> switchView(OTHER_VIEW)));
                    appBar.setTitleText("Home View");
                    Swatch.TEAL.assignTo(appBar.getScene());
                    break;
                case OTHER_VIEW:
                    appBar.setVisible(false);
                    break;
            }
        });
    }
}
FXMLView

Using Gluon’s Scene Builder

In order to open home.fmxl with Scene Builder from your IDE, or to be able to add a View from Scene Builder, the file charm-common-4.4.1.jar should be imported.

ImportJar

Then, View will be available on the Custom titled pane on the top left of Scene Builder.

SceneBuilder

Afterburner framework

Adding the dependency to the build.gradle script, allows using this MVP framework from Adam Bien.

dependencies {
    compile 'com.airhacks:afterburner.mfx:1.6.2'
}

For every view, these files are required:

In the code folder:

  • The view. I.e, HomeView. An empty class that just extends FXMLView.

  • The presenter. I.e. HomePresenter.

  • A service (optional), i.e. HomeService.

And in the resources folder:

  • The fxml, i.e. home.fxml

  • The css (optional), i.e. home.css.

In the presenter, layers can be added to the view, like a FloatingActionButton to trigger some action when the user clicks on it.

A transition can be set for switching views from a large list of available transitions (see the Charm JavaDoc). In case no transition is desired, the developer can select NoTransition().

public class HomePresenter implements Initializable {

    @FXML
    private View home;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        FloatingActionButton fab = new FloatingActionButton();
        fab.setOnAction(e -> {
            MobileApplication.getInstance().switchView(OTHER_VIEW);
        });
        home.getLayers().add(fab);

        home.setShowTransitionFactory(v -> new FadeInLeftBigTransition(v));

        home.showingProperty().addListener((obs, ov, nv) -> {
            if (nv) {
                final AppBar appBar = MobileApplication.getInstance().getAppBar();
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("menu")));
                appBar.setTitleText("The Home View");
                appBar.getActionItems().addAll(
                        MaterialDesignIcon.SEARCH.button(),
                        MaterialDesignIcon.FAVORITE.button());
                appBar.getMenuItems().addAll(new MenuItem("Settings"));

                Swatch.PURPLE.assignTo(home.getScene());
            }
        });
    }

    @FXML
    public void onClick() {
       System.out.println("click");
    }
}

Then, a view can be created and loaded:

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            final HomeView homeView = new HomeView();
            return (View) homeView.getView();
        });
    }
}
Afterburner

In order to perform some actions while the transition is running or when it finishes, a custom ActionEvent can be provided to the end of the transition.

This can be done as well based on the LifecycleEvent events. The following code snippet will set the view transparent to mouse clicks just during the time it is added to the scene and the transition ends.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            final HomeView homeView = new HomeView();
            final View view = (View) homeView.getView();
            view.addEventHandler(LifecycleEvent.SHOWING, e ->
                view.setMouseTransparent(true));
            view.addEventHandler(LifecycleEvent.SHOWN, e ->
                view.setMouseTransparent(false));
            return view;
        });
    }
}

4.1.3. Layers

A Layer is an overlay that can be shown above any MobileLayoutPane object, like the views, or the GlassPane itself.

Layers can be provided as factories, to be shown on demand. In this case they will be added and installed to the GlassPane with MobileLayoutPane.getInstance().addLayerFactory().

Also, they can be added to any of the views (view.getLayers()), just by adding the layer into that list.

Showing a Layer is then achieved either by calling , or by using API available on MobileApplication.

If the Layer is installed in the GlassPane, it can be shown at any time by calling showLayer(layerName), regardless of the current View.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(new Label("Hello Glisten!")) {
            @Override
            protected void updateAppBar(AppBar appBar) {
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> showLayer("New Layer")));
            }
        });

        addLayerFactory("New Layer", () -> {
            final StackPane stackPane = new StackPane(new Button("Click"));
            stackPane.setStyle("-fx-background-color: white; -fx-padding: 10;");
            SidePopupView sidePopupView = new SidePopupView(stackPane);
            return sidePopupView;
        });
    }
}
Layer1

But if the layer is installed in a specific MobileLayoutPane, only if that pane is currently showing, calling view.getLayers().get(index).show() will show the layer at the given index, on top of the pane.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            Button button = new Button("Show layer");
            View view = new View(button);
            button.setOnAction(e -> view.getLayers().get(0).show());
            final StackPane stackPane = new StackPane(new Button("Click"));
            stackPane.setStyle("-fx-background-color: white; -fx-padding: 10;");
            view.getLayers().add(new SidePopupView(stackPane));
            return view;
        });
    }
}
Creating a Layer

Developers can create custom Layer nodes. This is a minimal implementation, including a faded background when the layer is shown.

public class MyApp extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new View(new Label("Hello Glisten!")) {
            @Override
            protected void updateAppBar(AppBar appBar) {
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> showLayer("New Layer")));
            }
        });
        addLayerFactory("New Layer", () -> new Layer() {
            private final Node root;
            private final double size = 150;

            {
                root = new StackPane(new Button("A custom layer"));
                root.setStyle("-fx-background-color: white;");
                getChildren().add(root);
                getGlassPane().getLayers().add(this);
            }

            @Override
            public void show() {
                setBackgroundFade(GlassPane.DEFAULT_BACKGROUND_FADE_LEVEL);
                super.show();
            }

            @Override
            public void hide() {
                setBackgroundFade(0.0);
                super.hide();
            }

            @Override
            public void layoutChildren() {
                root.setVisible(isShowing());
                if (!isShowing()) {
                    return;
                }
                root.resize(size, size);
                resizeRelocate((getGlassPane().getWidth() - size)/2, (getGlassPane().getHeight()- size)/2, size, size);
            }
        });
    }
}
CustomLayer

However, Glisten already provides a series of built-in layers, so this won’t be necessary in most of the occasions.

Using built-in layers

Layers that are installed in the GlassPane are: SidePopupView, and its two subclasses MenuSidePopupView and SnackbarPopupView, and MenuPopupView.

Layers installed only in the View: FloatingActionButton.

For a description of each layer, check the API documentation: Glisten layers.

4.1.4. UI Controls

In addition to the controls illustrated in the Layers section, Glisten comes with a collection of cross platform UI controls based on the Material Design Specification: Glisten Controls.

4.2. Glisten CSS Overview

An overview of the CSS style classes and properties that are available to users of the Gluon Charm Glisten library. Users of Glisten CSS should be familiar with the JavaFX CSS functionality.

4.2.1. Glisten Themes

Glisten ships with two CSS themes: light and dark. Despite the connotation, the amount of CSS required to specify these themes is incredibly minimal - and as a courtesy is reproduced here:

Light Theme
.root{
  -fx-background: white;
  -fx-text-fill: -text-light;
  -fx-prompt-text-fill: rgba(0,0,0,.3);

  -text-disabled: -text-disabled-light;
  -background-disabled:rgba(0,0,0,.12);
  -background-fill: -text-light;

  -flat-button-hover-background: rgba(#999999,.2);
  -flat-button-pressed-background: rgba(#999999,.4);
  -flat-button-disabled-fill: rgba(#000000,.26);
}
Dark Theme
.root{
  -fx-background: #333333;
  -fx-text-fill: -text-dark;
  -fx-prompt-text-fill: rgba(255,255,255,.3);

  -text-disabled: -text-disabled-dark;
  -background-disabled:rgba(255,255,255,.12);
  -background-fill: -text-dark;

  -flat-button-hover-background: rgba(#CCCCCC,.15);
  -flat-button-pressed-background: rgba(#CCCCCC,.25);
  -flat-button-disabled-fill: rgba(#FFFFFF,.3);
}

4.2.2. Glisten CSS Properties

There are a number of global properties that can be used by any application that uses Glisten. These properties allow for custom controls to be styled in a way that should relatively closely match up with the Glisten-styled controls. What follows is a list of such properties.

Swatches

Perhaps most importantly, Glisten supports swatches, where each swatches populates a series of pre-specified CSS properties which can be used within your own CSS. The properties that are populated for each swatch are the following:

-primary-swatch-50
-primary-swatch-100
-primary-swatch-200
-primary-swatch-300
-primary-swatch-400
-primary-swatch-500
-primary-swatch-600
-primary-swatch-700
-primary-swatch-800
-primary-swatch-900
-alternate-swatch-100
-alternate-swatch-200
-alternate-swatch-400
-alternate-swatch-700

Each of these properties is simply a color along a certain color spectrum. All swatches provided by Glisten are based on the Material Design color style guide. The Glisten CSS files do not typically make use of all swatch colors, so it is also advised that any use of these colors be restrained, outside of the -primary-swatch-500, and possibly -primary-swatch-200, -primary-swatch-600, and -primary-swatch-700.

An example of how such a property could be used is shown in the sample code below:

.button {
  -fx-background-color: -primary-swatch-500;
}

.button:hover {
  -fx-background-color:-primary-swatch-600;
}
Other Properties

There are a number of other properties that are available to use. Some of these are custom Glisten properties, but many are standard JavaFX CSS properties that Glisten simply modifies to the appropriate value. Some notable properties include the following (refer to the JavaFX CSS Reference Guide for more details of any that start with -fx-) :

  • -text-light: This is a light-colored text, best used on a dark background.

  • -text-dark: This is a dark-colored text, best used on a light background.

  • -fx-text-fill: This is populated based on the theme that is currently set (i.e. light or dark).

  • -text-disabled: This is used in controls where the control is disabled.

Text Fill

It is important to understand how text fill works in Glisten, as the underlying JavaFX CSS engine is significantly more powerful than normal CSS engines. In particular, when it comes to deciding what color text fill to use, you can use JavaFX CSS laddering to decide. In other words, it is never a good idea to use -fx-text-fill directly, as -fx-text-fill does not take into account the background color behind the text (so it is possible that the text is unreadable).

In cases where a custom text fill is desired, it is much better to do something such as the following:

.button {
  -fx-background-color: -primary-swatch-500;
  -fx-text-fill: ladder(-primary-swatch-500, -text-dark 49%, -text-light 50%)
}

This code states to use -text-dark or -text-light, depending on the darkness of the first value, in this case -primary-swatch-500. The result of this is that when -primary-swatch-500 is a very light color (such as a bright yellow), the -fx-text-fill will be -text-light (and therefore appear as a dark color). When -primary-swatch-500 is a very dark color, -text-dark is used, and is therefore a light color that is readable on a dark background.

4.2.3. Glisten Style Classes

By default, when a UI control is instantiated in Glisten, the control does not receive any additional style classes over what is provided by the underlying JavaFX technology. However, depending on the use case for each individual control, it can make sense to apply additional style classes to have Glisten apply additional styling to these controls.

There exists a class called GlistenStyleClasses that is located in the com.gluonhq.glisten.visual package. This class provides a number of predefined style classes that can be applied to a control, via one of two methods:

1. JavaFX StyleClass List API:
final ToggleButton toggleVisibilityButton = new ToggleButton("Toggle Visibility");

/*
 * TOGGLE_BUTTON_SWITCH is a static import from GlistenStyleClasses.
 * It is simply a String consisting of 'switch'.
 */
toggleVisibilityButton.getStyleClass.add(TOGGLE_BUTTON_SWITCH);
2. GlistenStyleClasses Convenience API:
final ToggleButton toggleVisibilityButton = new ToggleButton("Toggle Visibility");

/*
 * Both applyStyleClass and TOGGLE_BUTTON_SWITCH are static
 * imports from GlistenStyleClasses
 */
applyStyleClass(toggleVisibilityButton, TOGGLE_BUTTON_SWITCH);

Both approaches are more or less equivalent, but the second approach is recommended.

Once these additional style classes have been applied, both Glisten CSS and your own CSS can be applied as applicable. For example, a button that has been styled to be flat (using GlistenStyleClasses.BUTTON_FLAT) will be able to receive alternate styling via css such as the following:

.button.flat {
  ...
}

Of course, as noted, any styling of UI controls based on style classes from the GlistenStyleClasses class will be styled differently by Glisten CSS, so there is no need (unless desired) to apply additional styling.

What follows is a list of additional CSS style classes that are available to some UI controls:

Button

Buttons have the CSS style class .button. There are the following additional style classes that are available in Glisten:

Style Class

GlistenStyleClasses Property

Description

flat

BUTTON_FLAT

The flat style class results in a button that is represented with a visually 'flat' style

round

BUTTON_ROUND

The round style class causes the button to be rounded (for example, think of the commonly-used 'floating action button' that is present in the Material Design documentation).

Toggle Button

Toggle buttons have the CSS style class .toggle-button.

Style Class

GlistenStyleClasses Property

Description

switch

TOGGLE_BUTTON_SWITCH

The switch style class results in toggle button that matches the Material Design Switch representation.

4.2.4. Other Questions

If this document does not answer all your questions regarding Glisten CSS functionality, please reach out to Gluon staff and ask any questions. They will help us to improve future versions of this document.

5. Charm Down

Charm Down is the library that addresses the integration with low-level platform APIs. Using Charm Down, 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 Charm Down features. More information can be found in the JavaDoc and samples. Also, this library is being actively extended, so if you think an important feature is missing, feel free to contact us.

5.1. General

The Charm Down 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, …​

5.2. Project Configuration

Configuring your project to include a Charm Down service is done via the downConfig extension in the build.gradle file. When creating a project with the Gluon IDE plugin, the downConfig section 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 services listed in the overview below. For instance, adding the position service is done like this:

jfxmobile {
    downConfig {
        plugins 'display', 'lifecycle', 'statusbar', 'storage', 'position'
    }
    android {
        // android specific configuration follows
    }
    ios {
        // ios specific configuration follows
    }
}

5.2.1. Prerequisites

Charm Down prerequisites are in line with those required by Gluon Mobile.

In addition to the Android Support repository, the Google repository needs to be installed from the Android SDK manager as well. From the SDK Tools section, select and install the latest version of Google Repository.

5.2.2. Using Services

Each Charm Down service provides the functionality to access the device’s feature. Each of these services are accessed through a single class called com.gluonhq.charm.down.Services. To get a service, you use the Services.get() method, which returns an Optional that 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 = Services.get(PositionService.class).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()));

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

The Gluon samples contain many different use cases of different Charm Down services.

5.2.3. Services Overview

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

5.2.4. 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.

For instance, the Dialer service requires some modifications in the Android manifest file:

<manifest ...>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    ...
   <activity android:name="com.gluonhq.impl.charm.down.plugins.android.PermissionRequestActivity" />
</manifest>

The service javadoc provides detailed info in each case.

5.2.5. Service debugging

During development, setting the system property "com.gluonhq.charm.down.debug" to true, before initializing the service, will enable a more verbose console output.

System.setProperty("com.gluonhq.charm.down.debug", "true");
Services.get(VideoService.class).ifPresent(video -> video.play());

Don’t forget to remove it before the app is released.

5.3. Charm Down source code

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

6. Gluon Connect

Gluon Connect is a client-side library that simplifies binding your data from any source and format to your JavaFX UI controls. It works by retrieving data from a data source and converting that data from a specific format into JavaFX observable lists and observable objects that can be used directly in JavaFX UI controls. It is designed to allow developers to easily add support for custom data sources and data formats.

Learn more about Gluon Connect: Gluon Connect Documentation or Gluon Mobile JavaDoc

7. Samples

7.1. Samples

Our samples page contains a lot of samples for a number of different use cases. Make sure to check out their sources and documentation.

7.2. The 'Comments App' Example

This section uses the Comments App to help you get started with Charm. You can fork or clone the Gluon Samples repository here. The sample for the Comments App is located in the comments directory.

7.2.1. The build script

This is the full build.gradle file:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.3.8'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases/'
    }
}

mainClassName = 'com.gluonhq.demo.comments.CommentsFX'

dependencies {
    compile "com.gluonhq:charm:4.4.1"
    compile 'com.airhacks:afterburner.mfx:1.6.3'
}

jfxmobile {
    downConfig {
        version = '3.6.0'
        plugins 'display', 'lifecycle', 'statusbar', 'storage'
    }
    android {
        manifest = 'src/android/AndroidManifest.xml'
    }
    ios {
        infoPList = file('src/ios/Default-Info.plist')
        forceLinkClasses = ['com.gluonhq.**.*', \
                            'com.airhacks.afterburner.**.*', \
                            'org.glassfish.json.**.*']
    }
}

If you clean and build the project, the dependencies will be downloaded the first time. On NetBeans, if you right-click on the project’s root and select Reload Project, the dependencies will show up under the Dependencies folder of the project. On IntelliJ, click on the refresh button on the Gradle Tool Window (View→Tool Windows→Gradle) to refresh the Gradle projects and you will see them under External Libraries in the Project Tool Window.

Project folders

7.2.2. The MobileApplication class

Have a look at the documentation for MobileApplication. This class should be considered as the base class for any Charm project, in a similar fashion that the Application class is for JavaFX applications.

In fact, MobileApplication extends from Application, and our class will extend it, so there is no need for a start method. We’ll add View instances on its constructor or by overriding the init method.

There is a postInit method that can be used for one time initialization, because it’s called once there is a valid scene instance. In this sample, we will install an indigo Swatch.

@License(key="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
public class CommentsFX extends MobileApplication {

    @Override
    public void init() {

        /**
        * Create default home view
        */
        addViewFactory(HOME_VIEW,()->{
            HomeView homeView = new HomeView();
            return (View)homeView.getView();
        });
    }

    @Override
    public void postInit(Scene scene){
        Swatch.INDIGO.assignTo(scene);
    }
}

If you have a valid license key for Gluon Charm, add it to your application with the @License annotation, and you will avoid the initial nag popup dialog.

7.2.3. Adding Views

Views (check the docs) can be added easily, just by placing JavaFX nodes on the top, center or bottom of the View, or by using FXML files created with Scene Builder. In this sample, we’ll use the mobile-enabled version of the Afterburner MVP framework.

Adding View on Scene Builder

You can check the FXML file code. Besides the View pane, an Icon is used, selected from the MaterialDesignIcon list of icons, based on the Material Design style guide.

<View fx:id="homeView" prefHeight="600.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gluonhq.demo.comments.views.home.HomePresenter">
    <top>
        <ToolBar fx:id="topHome" prefHeight="40.0" prefWidth="200.0">
            <items>
                <Button styleClass="icon-toggle">
                    <graphic>
                        <Icon content="COMMENT"/>
                    </graphic>
                </Button>
                <Label text="The Gluon Comments App"/>
            </items>
        </ToolBar>
    </top>
    <center>
        <ListView fx:id="comments" />
    </center>
</View>

This sample uses Gluon CloudLink to store and synchronize data. The Data Storage section in the Gluon CloudLink documentation contains detailed information for the following steps.

Gluon CloudLink Portal

Before you can start using Gluon CloudLink, you will need to register your application. Go to the Gluon CloudLink Product page and click on "Sign up" under "Free".

Select Sign up and register for a new account (or login if you already have one). You will be able to see the dashboard with your existing applications and their key and secret tokens. As a word of advice, keep them safe and don’t send them to anyone.

The DataClientProvider class

We can now build a new DataClient, which is the access point to the Data Storage service of Gluon CloudLink.

DataClient dataClient = DataClientBuilder.create()
        .build();

The CommentsService class

Once you have a DataClient reference, you can retrieve data from and store data to Gluon CloudLink. In this case, the data will be a list with comments. The way we can get this list from this service is:

GluonObservableList<Comment> comments =
    DataProvider.retrieveList(dataClient.createListDataReader("comments",
        Comment.class, SyncFlag.LIST_WRITE_THROUGH, SyncFlag.LIST_READ_THROUGH));

Finally, we wrap this observable list into an ObjectProperty in order to expose it to the view and bind it with the ListView content.

ListView with comments

You can find out how the rest of the views are added to this application by following this link.

8. Migration Guide to Gluon Mobile 4

8.1. Project configuration

8.1.1. jfxmobile plugin

The version of the jfxmobile plugin has been updated to 1.1.0. Change the version at the top of the build.gradle file.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.1.0'
    }
}

8.1.2. dependencies

Gluon Mobile 4 requires only one dependency declaration instead of the four declarations that were needed previously. That will include the dependencies to Charm Glisten and Gluon Connect.

dependencies {
    compile 'com.gluonhq:charm:4.0.0'
}

8.1.3. Charm Down

The dependency to Charm Down has been removed from the dependency defined above. This is because Charm Down has been refactored a bit. Previously, the Charm Down project bundled all the features in one jar dependency for the core classes and one jar dependency for each of the implemented platforms: android, ios and desktop. The disadvantage of this approach is that you can’t exclude certain features from your application. Since Gluon Mobile 4, Charm Down features have been divided into separate plugins, that allows a developer to choose which Charm Down feature he likes to include in his Gluon Mobile project. While doing that, we also increased the number of features from 10 to 21.

The current way of selecting a Charm Down plugin is done in the build.gradle file, using the new downConfig configuration. When using Gluon Mobile, the following four plugins are mandatory in the configuration: display, lifecycle, statusbar and storage.

jfxmobile {
    downConfig {
        version '3.0.0'
        plugins 'display', 'lifecycle', 'statusbar', 'storage'
    }
}

This is the complete list of available plugins: accelerometer, barcode-scan, battery, ble, browser, cache, compass, connectivity, device, dialer, display, lifecycle, local-notifications, magnetometer, orientation, pictures, position, settings, statusbar, storage and vibration.

8.1.4. Retrolambda

With jfxmobile plugin 1.1.0, building for android will apply retrolambda to all dependencies of the androidRuntime gradle configuration, while in previous versions, retrolambda was only applied to the compiled sources of the project. This means that all dependencies that are declared in compile, runtime, androidCompile and androidRuntime will be processed by retrolambda before handing it over to the android build tools for generating the android application bundle. Applying retrolambda to a dependency on which retrolambda was already applied will cause build failures. Examples of such dependencies are com.airhacks:afterburner.mfx:1.6.2 and de.jensd:fontawesomefx:8.8.

Two new configurations have been added to be able to exclude the dependency from the retrolambda process: compileNoRetrolambda and runtimeNoRetrolambda. It can be used in conjunction with the other configurations when declaring your dependencies:

dependencies {
    compile 'com.gluonhq:charm:4.0.0'
    compileNoRetrolambda 'com.airhacks:afterburner.mfx:1.6.2'
    compileNoRetrolambda 'de.jensd:fontawesomefx:8.8'
}

8.2. Code Changes

8.2.1. Charm Down

In Gluon Mobile 3.x, accessing a Charm Down feature was done through the com.gluonhq.charm.down.common.PlatformFactory class. With the refactoring of Charm Down into separate plugins, the common package, along with its classes, have been removed. Starting from Gluon Mobile 4, accessing a feature is done through the com.gluonhq.charm.down.Services class by using the method named get(). This returns an instance of java.util.Optional containing the service implementation for the platform where the application is currently running on. The Optional will be empty if the service has no implementation for the specific platform, so be sure to always check if the returned Optional isn’t empty.

Services.get(SettingsService.class).ifPresent(service -> {
    service.store("aKey", "aValue");
});

Another useful class is com.gluonhq.charm.down.Platform. Using this enum you can easily determine what platform the application is currently running on. For instance, you can execute code that should only be run on a desktop with the following code:

// only add a quit item to the drawer menu when running on desktop
if (Platform.isDesktop()) {
    final Item quitItem = new Item("Quit", MaterialDesignIcon.EXIT_TO_APP.graphic());
    quitItem.selectedProperty().addListener((obs, ov, nv) -> {
            if (nv) {
                Services.get(LifecycleService.class).ifPresent(LifecycleService::shutdown);
            }
    });
    navigationDrawer.getItems().add(quitItem);
}

9. Gluon Mobile JavaDoc

The Gluon Mobile JavaDoc can be found here.