Developer Guide
Developer Guide
Codename One Documentation Team; Shai Almog; Steve Hannah; Chen Fishbein
While this guide focuses on tutorial and conceptual material, the complete API reference remains
available in the Codename One JavaDoc [[Link] The source code for
the framework and this manual lives alongside each other in Git, so improvements to
documentation and code can evolve together through the same contribution workflow.
1
Authors
This document includes content from multiple authors and community wiki edits. If you edit pages
within the guide feel free to add your name here alphabetized by surname:
• Matt [[Link]
2
Rights & Licensing
You may copy/redistribute/print this document without prior permission from Codename One.
However, you may not charge for the document itself although charging for costs such as printing
is permitted.
Notice that while you can print and reproduce sections arbitrarily such changes must explicitly and
clearly state that this is a modified copy and link to the original source at
[Link] in a clear way!
3
Conventions
This guide uses some notations to provide tips and further guidance.
In case of further information that breaks from the current tutorial flow we use a sidebar as such:
Sidebar
Dig deeper into some details that don’t quite fit into the current flow. We use sidebars for
things that are an important detour, you can skip them while reading but you might want to
come back and read them later.
This convention is used when we refer to a button or a widget to press in the UI. E.g. press the
button labeled Press Here .
This is a quote
— By Author
Bold is used for light emphasis on a specific word or two within a sentence.
4
Chapter 1. Maven Project Workflow
Codename One uses Maven as the standard way to create, run, and maintain applications. This
chapter consolidates the workflow guidance that used to live in the standalone Maven manual so
the developer guide provides a single source of truth for building apps, updating projects, and
managing add-ons.
1.1. Introduction
Codename One uses Maven as its primary build tool. This guide aims to be the definitive source of
information for this project structure.
1.1.1. Conventions
The instructions throughout this chapter provide parallel guidance for the supported development
environments. Each subsection heading clearly identifies the target tooling so you can follow the
directions that match your workflow:
• Command Line (CLI) – Focused on running Maven from a terminal. The commands use a Unix-
style shell syntax; adapt the examples for Windows when necessary.
When you encounter environment-specific sections later in the guide, simply follow the subsection
corresponding to your preferred tools.
The easiest way to create a new project is to use the Codename One initializr
[[Link]
5
This tool will allow you to choose from a growing selection of project templates, and download a
starter project that you can open in your preferred IDE (IntelliJ IDEA, NetBeans, etc..), or build
directly on the command-line using Maven.
The starter projects are based on the Section A.1, “Codename One Application Project Archetype
(cn1app-archetype)”.
The following tutorials provide step-by-step instructions for getting started with
bare-bones app templates. Those tutorials are a better starting place for Codename
One development than this manual, as they are written in tutorial form.
See Getting Started with the Bare-bones Java App Template [[Link]
cn1-maven-archetypes/cn1app-archetype-tutorial/[Link]].
6
Generating a New Project from the Command-line
If you prefer to generate your projects directly on the command-line, you can use the Section A.1,
“Codename One Application Project Archetype (cn1app-archetype)” to generate the project directly
on the command-line.
mvn archetype:generate \
-DarchetypeGroupId=[Link] \
-DarchetypeArtifactId=cn1app-archetype \
-DarchetypeVersion=LATEST \
-DgroupId=YOUR_GROUP_ID \
-DartifactId=YOUR_ARTIFACT_ID \
-Dversion=1.0-SNAPSHOT \
-DmainName=YOUR_MAIN_NAME \
-DinteractiveMode=false
This will generate a project in the current directory. The project’s directory will have the same
name as the artifact ID you specified here. E.g. If your command had -DartifactId=myapp, then the
project will be located in a newly created directory named "myapp".
If you haven’t used Maven archetypes before, this snippet may be confusing. See
Introduction to Maven Archetypes [[Link]
[Link]] to get up to speed.
This command uses the Section A.1, “Codename One Application Project Archetype (cn1app-
archetype)” which has the following Maven coordinates:
<dependency>
<groupId>[Link]</groupId>
<artifactId>cn1app-archetype</artifactId>
<version>LATEST</version>
<type>maven-archetype</type>
</dependency>
This archetype generates a bare-bones Java project (the same one described in Getting Started with
the Bare-bones Java App Template [[Link]
tutorial/[Link]]).
You can learn more about using the archetype in the appendix.
Project Templates
7
[Link]] is an alternative starter project that uses Kotlin as the primary language instead
of Java. It is built on the cn1app-archetype at its core, but it includes some additional configuration
settings and sources to modify the template. You can use such templates as starter projects by using
the generate-app-project goal of the Codename One Maven plugin.
Here is an example which generates a project based on the bare-bones kotlin template:
mvn [Link]:codenameone-maven-plugin:7.0.210:generate-app-project \
-DarchetypeGroupId=$archetypeGroupId \
-DarchetypeArtifactId=$archetypeArtifactId \
-DarchetypeVersion=$archetypeVersion \
-DartifactId=$artifactId \
-DgroupId=$groupId \
-Dversion=$version \
-DmainName=$mainName \
-DinteractiveMode=false \
-DsourceProject=/path/to/kotlin-example-app
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
Like the archetype:generate goal, this will create the project in a directory named after your
specified artifact ID. E.g. If your command included -DartifactId=myapp, then the project would be
in a newly-created directory named "myapp".
3. The groupId, artifactId, and version work the same as for the archetype:generate goal. That is,
that they specify the coordinates for your newly created project.
4. The mainName specifies the Main class name for your app. This is just the class name, and should
not include the full package. E.g. "MyApp", not "[Link]"
5. The sourceProject property is the path to the "template" project. In this case, we’ll assume that
you have cloned the bare-bones kotlin project template repository [[Link]
cn1app-archetype-kotlin-template] at /path/to/kotlin-example-app.
8
A project template is not much different than a regular project. The template can
be either a legacy Ant project, or a new Maven project. In fact, this goal is the same
one you would use to migrate a legacy Ant project to use the new Maven project
structure.
See Section 1.4, “Creating Project Templates” for instructions on building your own
project templates.
If you have an existing Codename One application project that uses the old Ant project structure,
you can use the generate-app-project goal to migrate the project over to maven. This goal doesn’t
make any changes to the Ant project. It creates a new Maven project and copies over all of the
project sources and libraries, reorganized to fit the new project structure.
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
This will generate the new project in the current directory inside a folder named after the
artifactId parameter.
After building the project, try running it to make sure that the migration worked. E.g. Assuming that
your artifactId was myapp:
Command Line
cd myapp
./[Link]
9
If All goes well, your app should open in the Codename One simulator.
IntelliJ IDEA
Open the myapp folder in IntelliJ. Then press the "Run" button in the upper right of the toolbar.
If All goes well, your app should open in the Codename One simulator.
NetBeans
Before opening the project in NetBeans, be sure to copy the files in the
tools/netbeans directory into the root directory. These are necessary for NetBeans
to properly run, build, and debug the project.
Open the myapp folder as a project in NetBeans. Then press the "Run" button on the toolbar.
Eclipse IDE
In the Import dialog, expand Maven, select Existing Maven Projects, and press Next.
10
In the next panel, press the Browse button, and, in the file dialog, select the "myapp" directory, and
press Next.
11
The next panel should look similar to the one below. Make sure all of the projects are "checked",
and press Finish.
12
Almost there, but not quite…
Next we need to import the Eclipse launch configurations located inside the tools/eclipse directory.
Select File > Import… again, but this time, in the Import dialog, select Run/Debug > Launch
Configurations and click Next.
13
In the next panel, press Browse… then select the tools/eclipse directory.
14
Then check the eclipse option, and press Finish
15
The "Run" button menu should now include options for all of the major build targets. You can see
them by pressing on the Run button in the toolbar:
16
Select the MyApp - Run Simulator option from this menu.
Let’s consider a concrete example, now. Download the KitchenSink Ant project from here
[[Link] and extract it.
The following is a bash script that uses curl to download this project as a zip file, and then converts
it to a fully-functional Maven project.
CN1_VERSION=7.0.210
curl -L [Link] > [Link]
unzip [Link]
rm [Link]
mvn [Link]:codenameone-maven-plugin:${CN1_VERSION}:generate-app-project \
-DarchetypeGroupId=com.codename1 \
-DarchetypeArtifactId=cn1app-archetype \
-DarchetypeVersion=${CN1_VERSION} \
-DartifactId=kitchensink \
-DgroupId=[Link] \
-Dversion=1.0-SNAPSHOT \
-DinteractiveMode=false \
-DsourceProject=KitchenSink-1.0-cn7.0.11
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
17
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
This will generate the maven project in a directory named "kitchensink" in the current working
directory because of the -DartifactId=kitchensink directory.
One of the reasons to use Maven as the build tool is because it makes the management of project
dependencies almost trivial. If the library you want to add is on Maven central, then you can just
copy and paste its <dependency> snippet into your [Link] file and you’re good to go. Maven does
the rest.
With Codename One projects, there are a few caveats (see Section [Link], “The Compliance Check”),
and a few added nicities that make it easier to find and install add-on libraries in your project (see
Section [Link], “Managing Add-Ons in Control Center”).
Let’s assume that you have a Maven <dependency> snippet that you’ve copied from Maven central,
and it’s burning a hole in your clipboard while you’re trying to figure out where to paste it into
your project. Codename One application projects, being multi-module projects, have more than one
[Link] file; One per module.
The "common" module is where nearly all of your Codename One application resides. It houses
your Java and Kotlin files, your CSS files, your GUI builder files, your Codename One configuration
files (i.e. codenameone_settings.properties). Pretty much everything. The only things you’d place in
the other modules (e.g. javase, ios, etc…) are your platform-specific native interface
implementations; And in many applications you won’t need any of that.
Therefore, when adding dependencies into your app, you’ll almost always place them inside the
[Link] file for the "common" module.
You can add dependencies without needing to modify XML configuration files
using the Control Center. See Section [Link], “Managing Add-Ons in Control
18
Center”.
The instructions say that we almost always add dependencies in the common/[Link] file.
So what are the other modules' [Link] files for, and when do we need to modify them, or
add dependencies to them?
Here’s an overview:
%PROJECT_ROOT%/[Link]
The root [Link] file is the parent module of all of other modules. Anything you add here
will be inherited by all of the modules. It can be helpful to use <dependencyManagement> and
<pluginManagement> sections in this file to consolidate versions for dependencies and
plugins project-wide. This is also a good place to add project meta-data like <developers>,
<scm>.
javase/[Link]
Any dependencies that are only required for native implementations on the JavaSE
platform can be added here. Dependencies added to this project are not subject to the
compliance check.
Additionally, this module handles the build toolchain for the JavaSE platform. This
includes Mac and Windows Desktop builds, as well as JavaSE desktop builds. If you want
to customize the build workflow for any of these targets, you would do so by adding plugin
executions in this [Link] file.
<dependency>
<groupId>[Link]</groupId>
<artifactId>googlemaps-lib</artifactId>
<version>1.0.1</version>
<type>pom</type>
</dependency>
19
You should, however, look on Maven central [
[Link] to see what the latest version number is,
and substitute that version into the <version> tag of the snippet.
Copy and paste this snippet into the <dependencies> section of your common/[Link] file. And save
it.
The common/[Link] file has a lot of existing configuration in it, and it may not
be clear, on first glance, where the <dependencies> tag is located. A simple "find" for
<dependencies> may deliver you a red herring also, since there are a few <profile>
tags which also include <dependencies> sections.
The correct <dependencies> section, is located near the top of the file. You can
identify it because it will include the following comment:
This is a special marker that is used by some of the Codename One tooling to help
it locate the optimal place to inject dependencies.
You can paste any Maven dependency snippet you like into your project, but libraries that haven’t
been specifically developed for Codename One might not be compatible. See Appendix C, API. If you
are unsure whether a library is compatible, you could just add the dependency and try to use it in
your app. If it isn’t compatible, it will fail when you try to build the app, during the compliance
check.
The easiest way to find compatible libraries is to use the extensions section of the Control Center.
Libraries listed in this section have been build specifically for Codename One and are guaranteed to
be compatible.
All application code in the common module of your Codename One project must be compatible
with Codename One. This includes all dependencies. When you build your project, it will perform a
compliance check to ensure that no code uses unsupported APIs. (See Appendix C, API).
If the compliance check fails (i.e. the app uses unsupported APIs), the build will fail. The error log
should provide some clues as to where the offending code resides.
As I mention throughout this guide, the best place to find and install add-ons for your project is in
the Codename One Control Center (aka Codename One Preferences. aka Codename One Settings).
See Appendix D, Codename One Settings.
20
From the dashboard, select "Advanced Settings" > "Extensions" in the navigation menu on the left as
shown below:
This will bring up a list of available Codename One extensions as shown below:
Type in "Maps" into the search box, and it should narrow the options down to three libraries as
shown below:
21
The one in the middle "Codename One Google Native", is the Google maps lib that we want.
Many of the extensions listed in the control center are deployed as cn1lib bundles. Others are
deployed on Maven central and could simply be installed by adding a snippet into the
[Link] file (as described in Section [Link], “Example: Adding Google Maps Dependency via
Maven Central”).
The control center UI shields you from the details of how it installs the extensions into your
app. For extensions that are deployed on Maven central, it will simply add the Maven
dependency for the library directly into your project’s common/[Link] file. For extensions
that are distributed as cn1lib bundles, it uses the install-cn1lib Maven goal to install it into
your project.
You shouldn’t need to worry about this, as it happens seamlessly. If you are curious, you can
look at the <dependencies> section of your common/[Link] file to see the added <dependency>
tag after you install an extension.
The recommended approach for installing add-ons to your project is to use the control center, or by
adding the maven dependency to your common/[Link] file. However, in some situations you may
not be able to use those methods. E.g. If you have a legacy cnlib file that you need to use in your
22
app, and it isn’t available on Maven central or the control center.
In cases like this you can use the install-cn1lib Maven goal to install it as follows:
You can use the update goal to update both the Codename One libraries, and the Codename One
dependencies in your project.
e.g.
mvn cn:update
CLI
Alternatively you can use the [Link]/[Link] script to run this goal as follows:
./[Link] update
IntelliJ
Alternatively you can click on the "Configuration" menu, and select "Tools" > "Update Codename
One" as shown here:
NetBeans
Alternatively you can right click on the project in the project inspector, and select "Run Maven" >
23
"Update Codename One" as shown here:
You can also update your Codename One dependencies manually by modifying the [Link] and
[Link] properties defined in your project’s [Link] file.
E.g. Open the [Link] file, and look for the following:
<[Link]>7.0.210</[Link]>
<[Link]>7.0.210</[Link]>
Change these values to reflect the latest version of the codenameone-maven-plugin found here
[[Link]
24
templates directly in Maven.
If you have an existing maven Codename One application project, you can convert it into a project
template by adding a file named [Link] in the root directory of the project.
[Link]=$YOUR_PROJECT_MAIN_NAME
[Link]=$YOUR_PROJECT_PACKAGE_NAME
[dependencies]
====
... YOUR PROJECT MAVEN DEPENDENCIES ...
====
[parentDependencies]
====
... YOUR PARENT PROJECT MAVEN DEPENDENCIES ...
====
$YOUR_PROJECT_MAIN_NAME
This should be the value of the [Link] property in the project’s
codenameone_settings.properties file.
$YOUR_PROJECT_PACKAGE_NAME
This should be the value of the [Link] property in the project’s
codenameone_settings.properties file.
See Section B.5.4.2, “Sample [Link] file” for a more concrete example of the
[Link].
25
1.4.2. Test your Project Template
You can test your project template by using it as the sourceProject parameter for the generate-app-
project goal. See Section B.5, “Generate App Project (generate-app-project)”.
If you have a project template that you want to share with the community, please file an issue in the
Codename One issue tracker [[Link] with a link to a Github
Repository of your project template, and request to have it added Codename One initializr
[[Link]
3. Build hints, which will affect how projects will be built that include this library. These can
contain things like gradle dependencies on Android, cocopods dependencies on iOS, and other
hints to affect the build-server process.
4. CSS files.
.cn1lib vs .jar
You may be wondering why the .cn1lib format is even necessary. Why not just distribute
libraries as .jar files? The .cn1lib format offers several advantages over the plain .jar format:
1. cn1libs can contain platform-specific native sources that make use of native APIs on
the various platforms. E.g. they can contain Objective-C code which will be compiled on
the build server when deploying on iOS.
2. Codename One library projects perform a compliance check at the time that the
library is compiled to ensure it only uses supported Codename One APIs. This provides a
sort of "certification" that the library will be compatible with Codename One application
projects.
3. Codename One libraries can include CSS files and build hints which will be appended
to the build hints of application projects when they are built.
All that said, you can still distribute libraries as plain old jars and include them in your
Maven Codename One projects as jar dependencies. Codename One application projects will
perform an additional compliance check to ensure that the jar is compatible, and the build
26
will fail if it uses APIs that are not available in Codename One.
Use the cn1lib-archetype for generating a new Codename One library project as follows:
Command Line
mvn archetype:generate \
-DarchetypeArtifactId=cn1lib-archetype \
-DarchetypeGroupId=[Link] \
-DarchetypeVersion=LATEST \
-DgroupId=[Link] \
-DartifactId=mylib \
-Dversion=1.0-SNAPSHOT \
-DinteractiveMode=false
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
In the above snippet you would change the groupId, artifactId, and version properties to reflect
your project settings.
You can run the archetype:generate goal with as many or few properties as you
like, and it will prompt you to enter any properties that are required. E.g. You
could just enter:
mvn archetype:generate
27
Mac). It will work on Windows also if you use bash. If you are on
Windows and are using PowerShell or the regular command
prompt, then you’ll need to modiy the command slightly. In
particular, the entire command would need to be on a single line.
(Remove the '\' at the end of each line, and merge lines together,
with space between the command-line flags)
And follow the prompts. This will, result in fewer prompts because you have
already specified the archetype to use.
This will create a new project for you in the current directory, in a newly created directory named
after the artifactId that you entered.
IntelliJ IDEA
3.
Check the "Create from Archetype" checkbox. . This should allow
you to choose from of archetypes that are already known to IntelliJ.
4. If you don’t see an option for "[Link]:cn1lib-archetype", then IntelliJ doesn’t know
about it yet. If, however you do see this option, you can skip to the next step. Press the "Add
28
Archetype…" button. This will display a dialog for you to enter the details of the archetype.
6. This will display a form where you can enter the details of your project such as its location
(where you want to create the project folder), the name, the artifact ID, and the groupID. Fill in
this form as you see fit.
29
Then click "Next"
7. The final form in this wizard summarizes the project details and gives you an opportunity to
add additional properties to pass to the archetype:generate goal. In our case we don’t need to
add any additional properties. If the information looks correct, you can just press "Next".
30
NetBeans
2. In the "New Project" dialog, select "Java with Maven" in the left panel, and "Project from
Archetype" in the right panel, as shown below.
3. This will bring you to the "Maven Archetype" dialog as shown below:
31
Enter "[Link]" or "cn1lib-archetype" into the search field. Then select "cn1lib-
archetype" in the "Known archetypes:" panel. This will prefill the Group ID, Artifact ID and
Version fields for you. You may want to change Version to LATEST to ensure that it tries to use
the latest available version of the archetype.
4. This will bring you to the "Name and Location" panel of the wizard.
32
Enter in the project name (which you’ll be forced to use as the artifact ID also), project location,
groupId, version, and package. The "Package" is unimportant here as it isn’t used anywhere in
the project.
Once you have entered the information to your liking press the "Finish" button.
This will create a new libary project for you at the location you specified.
Eclipse IDE
2. In the New Project dialog, expand the Maven item, and select Maven Project
33
Then press "Next"
3. The next panel will look like the below image. The default settings on this panel should be fine.
Press Next
34
4. In the next panel, enter "cn1lib" in the Filter field. After a moment the cn1lib-archetype should
appear in the area below as shown here:
35
Select that option, and press Next
5. The next panel, allows you to enter your project details, such as group ID, and artifact ID. Your
project information here and then press Finish.
36
This will create a new libary project for you at the location you specified.
Project Structure
Let’s take a look at the project that was created. It is a multi-module Maven project with the
following modules:
common
The module where you’ll add all of your cross-platform code and CSS, and build hint
configuration. This module is in the "common" directory of the main project.
javase
The module where you can implement native interfaces for the JavaSE platform. This module is
in the "javase" directory of the main project.
ios
The module where you can implement native interfaces for the iOS platform. This module is in
the "ios" directory of the main project.
android
The module where you can implement native interfaces for the Android platform. This module
is in the "android" directory of the main project.
37
javascript
The module where you can implement native interfaces for the Javascript platform. This module
is in the "javascript" directory of the main project.
lib
The library module which includes all of the other modules as dependencies, and can be used as
a pom dependency in Codename One application projects that wish to use this library. This
module is in the "lib" directory of the main project.
tests
An application project for writing unit tests against your library. This module is in the "tests"
directory of the main project.
This top-level view of the module structure may seem daunting. Most of your development will
occur inside the "common" module. If we expand that module it will look more familiar to
developers who have used the old Ant project structure:
38
Your cross-platform Java source would go in the common/src/main/java directory. Your CSS files go in
the common/src/main/css directory.
NetBeans
This top-level view of the modules doesn’t provide a clear view of the project landscape, however,
since 99% of your development will occur inside the common submodule. Let’s open that "common"
sub-module project as well and take a peek.
Right click on the "Common" sub-module, and select "Open Project" as shown below:
With the common subproject open, the project inspector will look like:
39
In this screenshot I have expanded "Source Packages" and "Other Sources/css" to highlight where
your Java source files and CSS source files will be located.
The project inspector hides a few important files, however, so here is a screenshot of the File
inspector for the common project:
40
In this screenshot, I have expanded the common/src/main/css and common/src/main/java directories
as this is where most of your module source will go.
Command Line
41
./tests/common/src/test/java
./tests/common/src/test/java/com
./tests/common/src/test/java/com/example
./tests/common/src/test/java/com/example/myfirstlib
./tests/common/src/test/java/com/example/myfirstlib/[Link]
./tests/common/src/main
./tests/common/src/main/css
./tests/common/src/main/css/[Link]
./tests/common/src/main/java
./tests/common/src/main/java/com
./tests/common/src/main/java/com/example
./tests/common/src/main/java/com/example/myfirstlib
./tests/common/src/main/java/com/example/myfirstlib/[Link]
./tests/cn1libs
./tests/.mvn
./tests/.mvn/[Link]
./[Link]
./javase
./javase/[Link]
./javase/src
./javase/src/main
./javase/src/main/java
./javase/src/main/java/com
./javase/src/main/java/com/example
./javase/src/main/java/com/example/myfirstlib
./ios
./ios/[Link]
./ios/src
./ios/src/main
./ios/src/main/objectivec
./common
./common/codenameone_library_required.properties
./common/[Link]
./common/codenameone_library_appended.properties
./common/src
./common/src/test
./common/src/test/java
./common/src/test/java/com
./common/src/test/java/com/example
./common/src/test/java/com/example/myfirstlib
./common/src/test/java/com/example/myfirstlib/[Link]
./common/src/main
./common/src/main/css
./common/src/main/css/[Link]
./common/src/main/java
./common/src/main/java/com
./common/src/main/java/com/example
./common/src/main/java/com/example/myfirstlib
./common/src/main/java/com/example/myfirstlib/[Link]
./android
./android/[Link]
42
./android/src
./android/src/main
./android/src/main/java
./android/src/main/java/com
./android/src/main/java/com/example
./android/src/main/java/com/example/myfirstlib
./lib
./lib/[Link]
./[Link]
./javascript
./javascript/[Link]
./javascript/src
./javascript/src/main
./javascript/src/main/javascript
./.idea
./.idea/[Link]
./.idea/[Link]
./.idea/.gitignore
./.idea/[Link]
./.idea/[Link]
./.idea/[Link]
This may seem daunting at first, but it is important to realize that 99% of the time, you’ll be working
in the "common" module - most of the other stuff is boilerplate.
Important Files
There are a few key files in this project that you’ll be using more than the others.
[Link]
The maven configuration file of the root module is where you will set project-wide properties
such as the [Link] property, which specifies the version of the Codename One libraries that
the module should be compiled against. Periodically, you’ll want to update the [Link]
property to point to the latest version.
When/if you decide to deploy your module to Maven central, you’ll need to add additional
deployment-related settings in this file.
common/[Link]
The maven configuration file for the "common" module, which will contain most of your cn1lib’s
soure code, CSS files, and properties files. If your library depends on other libraries or jar files,
you’ll usually be adding them as dependencies in this file, and not the root [Link] file.
common/codenameone_library_appended.properties
This file is where you can specify properties that should be merged with the
codenameone_settings.properties of application projects that include this library as a
dependency. This is where you would add, for example, gradle dependencies required for the
Android builds, or cocoapods dependencies that are required for iOS builds.
43
common/codenameone_library_required.properties
This file allows you to specific build hints that must be present in application projects that
include this library. If this libary requires a particular android build tools version, or a specific
Java version, then those requirements should be specified in this file.
Important Directories
As mentioned previously, 99% of all of your development will likely occur inside the "common"
module. The other modules are mostly for native implementations of Native interfaces.
common/src/main/java
This is where your cross-platform Java source files will be placed.
common/src/main/css
If your library uses CSS, this is where all CSS-related files will be placed.
common/src/main/resources
Other non-java resources that you want to have included in the classpath.
Command Line
To build the library, simply run the "install" goal on the root module as follows:
mvn install
NetBeans
Right click on the "root" module in the project explorer and select "Build".
You must build the root module and not one of the submodules.
Alternatively you could have just selected the "root" module in the project explorer and pressed the
44
Eclipse IDE IDE
Right click on the "root" module in the project explorer and select Run as > Maven Install
If the build fails for any reason, check to make sure that your project is using the
latest version of the Codename One plugin. You can do this by opening the [Link]
file, and changing the [Link] and [Link] properties to reference
the latest version. Check for the latest version here [[Link]
[Link]/codenameone].
When using the Maven build tool, we no longer require the .cn1lib file at all. Your library projects
can be handled entirely via Maven’s dependency mechanism. The preferred way to distribute your
libraries is on Maven central, and the preferred way to add a library to an application is via a
Maven "pom" dependency.
That being said, you may still want to distribute your library as a .cn1lib file for the sake of users
who are still using Ant as their build tool. For that reason, when you bulid a library project, the
45
cn1lib is automatically built as well. After running a build, you can look in the common/target
directory and find your .cn1lib file ready to be distributed.
In order to get acquainted with our project, let’s add a "Hello World" java class that we want to
make available as part of our cn1lib.
Add a new class inside the "common/src/main/java" directory with package "[Link]", and
name "HelloWorld". Enter the following contents into the class:
package [Link];
Now build the library again. (See Section [Link], “Building the Library”).
Now that we’ve built our library and added a Java class, let’s try adding it as a dependency in an
application project. If you haven’t yet created an application project, do that now. See Section 1.2.1,
“Creating a New Project” for instructions on creating a new application project.
Make sure you’re editing the common/[Link] file of the application project and
not the library project.
This file may look a little hairy as there’s a lot of configuration in there. We’ll be looking for the
<dependencies> section.
The common/[Link] file will have more than one <dependencies> tag, as it includes some profiles
handling things like kotlin support. There will be one particular <dependencies> tag that includes a
comment like
For the sake of this example, suppose our library was set up with the following coordinates:
groupId: [Link]
artifactId: mylib
46
version: 1.0-SNAPSHOT
In this case we would add the following XML snippet to the <dependencies> section of our
application’s common/[Link] file:
<dependency>
<groupId>[Link]</groupId>
<artifactId>mylib-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
Notice that we appended "-lib" to the artifactId. This is because we are including
the "lib" module of our library project as the dependency, and not the root module.
Also the <type>pom</type> is important as it indicates that this is a pom dependency
- not a regular jar dependency.
Now let’s try it out. Try adding the following code to your application project’s main class (or
anywhere in the application project, for that matter):
[Link]();
And build the project. The project should build OK, and if you run it, you should see that the
helloWorld() method works as designed.
The recommended way to distribute your library is on Maven central. That way users will be able
to install your library by copying and pasting a familiar <dependency> snippet into their [Link]
file.
47
48
Chapter 2. Introduction
Codename One is a Write Once Run Anywhere mobile development platform for Java/Kotlin
developers. It fits naturally into modern Maven-capable IDEs such as IntelliJ IDEA, NetBeans, VS
Code and Eclipse, and it can also be driven entirely from the command line.
Unify the complex and fragmented task of mobile device programming into
a single set of tools, APIs and services. As a result create a more manageable
approach to mobile application development without sacrificing the
power/control given to developers.
This effectively means bringing that old "Write Once Run Anywhere" (WORA) Java mantra to
mobile devices without "dumbing it down" to the lowest common denominator.
The things that make Codename One stand out from other tools in this field are:
• Write Once Run Anywhere support with no special hardware requirements and 100% code
reuse
• Compiles Java/Kotlin into native code for iOS, UWP (Universal Windows Platform), Android and
even JavaScript/PWA
• Easy to use with 100% portable Drag and Drop GUI builder
• Full access to underlying native OS capabilities using the native OS programming language (e.g.
Objective-C) without compromising portability
• Lets you use native widgets (views) and mix them with Codename One components within the
same hierarchy (heavyweight/lightweight mixing)
Codename One can trace its roots to the open source LWUIT project started at Sun Microsystem in
2007 by Chen Fishbein (co-founder of Codename One). It’s a huge project that’s been under constant
development for over a decade!
When we develop an app in Codename One we use the builtin simulator when running and
49
debugging. When we want to build a native app we can use the build cloud where Macs create the
native iOS apps and Windows machines create the native Windows apps. This works seamlessly
and makes Codename One apps native as they are literally compiled by the native platform. E.g. for
iOS builds the build cloud uses Macs running xcode (the native Apple tool) to build the app.
Codename One doesn’t send source code to the build cloud, only compiled
bytecode!
The build servers allow building native iOS Apps without a Mac and native Windows apps without
a Windows machine. They remove the need to install/update complex toolchains and simplify the
process of building a native app to a right click.
Even though the build servers streamline delivery, Codename One also supports fully local builds.
You can install the toolchain on your own hardware and follow the workflows in Chapter 1, Maven
Project Workflow and [working-with-codename-one-sources] to compile, package, and test apps
without leaving your desktop environment.
E.g.: Since building native iOS applications requires a Mac OS X machine with a recent version of
xcode Codename One maintains such machines in the cloud. When developers send an iOS build
such a Mac will be used to generate C source code using ParparVM [[Link]
CodenameOne/tree/master/vm] and it will then compile the C source code using xcode & sign the
resulting binary using xcode. You can install the binary to your device or build a distribution binary
for the appstore. Since C code is generated it also means that your app will be "future proof" in a
case of changes from Apple. You can also inject Objective-C native code into the app while keeping
it 100% portable thanks to the "native interfaces" capability of Codename One.
Subscribers can receive the C source code back using the include sources feature of Codename One
and use those sources for benchmarking, debugging on devices etc.
The same is true for most other platforms. For the Android, J2ME & Blackberry the standard Java
code is executed as is.
Codename One uses a SaaS based approach so the information in this appendix might (and
probably will) change in the future to accommodate improved architectures. I included this
information for reference only, you don’t need to understand this in order to follow the content of
the book…
Since Android is already based on Java, Codename One is already native to Android and “just
50
works” with the Android VM (ART/Dalvik).
On iOS, Codename One built and open sourced ParparVM, which is a very conservative VM.
ParparVM features a concurrent (non-blocking) GC and it’s written entirely in Java/C. ParparVM
generates C source code matching the given Java bytecode. This effectively means that an xcode
project is generated and compiled on the build servers. It’s as if you handcoded a native app and is
thus “future proof” for changes that Apple might introduce. E.g. Apple migrated to 64bit and later
introduced bitcode support to iOS. ParparVM needed no modifications to comply with those
changes.
For Windows 10 desktop and Mobile support, Codename One uses iKVM to target UWP (Universal
Windows Platform) and has open sourced the changes to the original iKVM code.
JavaScript build targets use TeaVM to do the translation statically. TeaVM provides support for
threading using JavaScript by breaking the app down in a rather elaborate way. To support the
complex UI Codename One uses the HTML5 Canvas API which allows absolute flexibility for
building applications.
For desktop builds Codename One uses javapackager, since both Macs and Windows machines are
available in the cloud the platform specific nature of javapackager is not a problem.
Lightweight Architecture
What makes Codename One stand out is the approach it takes to UI: “lightweight architecture”.
Lightweight architecture is the “not so secrete sauce” to Codename One’s portability. Essentially it
means all the components/widgets in Codename One are written in Java. Thus their behavior is
consistent across all platforms and they are fully customizable from the developer code as they
don’t rely on OS internal semantics. This allows developers to preview the application accurately in
the simulators and GUI builders.
One of the big accomplishments in Codename One is its unique ability to embed “heavyweight”
widgets into place among the “lightweights”. This is crucial for apps such as Uber where the cars
and widgets on top are implemented as Codename One components yet below them we have the
native map component.
Codename One achieves fast performance by drawing using the native gaming API’s of most
platforms e.g. OpenGL ES on iOS. The core technologies behind Codename One are all open source
including most of the stuff developed by Codename One itself, e.g. ParparVM but also the full
library, platform ports, designer tool, device skins etc.
Lightweight components date back to Smalltalk frameworks, this notion was popularized in
the Java world by Swing. Swing was the main source of inspiration to Codename One’s
predecessor LWUIT. Many frameworks took this approach over the years including JavaFX
51
and most recently Ionic in the JavaScript world.
Why ParparVM
• Truly Native — since code is translated to C rather than directly to ARM or LLVM code the app
is "more native". It uses the official tools and approaches from Apple and can benefit from their
advancements e.g. latest bitcode changes or profiling capabilities.
• Smaller Class Library — ParparVM includes a very small segment of the full JavaAPI’s
resulting in final binaries that are smaller than the alternatives by orders of magnitude. This
maps directly to performance and memory overhead.
• Simple and Extensible — to work with ParparVM you need a basic understanding of C. This is
crucial for the fast moving world of mobile development, as Apple changes things left and right
we need a more agile VM.
Windows Phone/UWP
In the past Codename One had 2 major Windows VM port rewrites and 3 or 4 rendering pipelines
within those ports (depends on how you would define a "rewrite").
The old Windows Phone port was deprecated and is no longer supported, the UWP
port is the only supported Windows mobile target
Codename One now targets UWP by leveraging a modified version of iKVM [[Link]
shannah/cn1-ikvm-uwp] to build native Windows Universal Applications.
iKVM uses a bytecode to CLR translation process that effectively converts Java bytecode directly to
the .net equivalent. This is paired with a port of the Codename One API’s that was built for the UWP
environment. The UWP port generates native Windows 10 applications that can support ARM
Windows devices natively as well as desktops etc. These binaries can be uploaded directly to
Microsofts online store without special processing.
52
JavaScript Port
The JavaScript port of Codename One is based on the amazing work of the TeaVM project
[[Link] The team behind TeaVM effectively built a JVM that translates Java bytecode into
JavaScript source code while maintaining threading semantics using a very imaginative approach.
The JavaScript port allows unmodified Codename One applications to run within a desktop or
mobile browser. The port itself is based on the HTML5 Canvas API, this provides a pixel perfect
implementation of the Codename One API.
The JavaScript port is only available for Enterprise grade subscribers of Codename
One
The other ports of Codename One use the VM’s available on the host machines/environments to
execute the runtime. Retrolambda [[Link] is used to provide Java 8
language features in a portable way.
The Android port uses the native Android tools including the gradle build environment in the latest
versions.
The desktop port creates a standard JavaSE application which is packaged with the JRE and an
installer.
The Desktop port is only available to pro grade subscribers of Codename One
One of the confusing things about Codename One is the versions. Since Codename One is a SaaS
product versioning isn’t as simple as a 2.x or 3.x moniker. However, to conform to this convention
Codename One does make versioned releases which contribute to the general confusion.
When a version of Codename One is released the version number refers to the libraries at the time
of the release. These libraries are then frozen and are made available to developers who use the
Versioned Builds [[Link]
[Link]] feature. The plugin, which includes the designer
as well as all development that is unrelated to versioned builds continues with its regular updates
immediately after release. The same is true for the build servers that move directly to their
standard update cycle.
2.2. History
53
Figure 1. LWUIT App Screenshot circa 2007
Codename One was started by Chen Fishbein and Shai Almog who authored the Open Source
LWUIT project at Sun Microsystems (circa 2007). The LWUIT project aimed to solve the
fragmentation within J2ME/Blackberry devices by creating a higher standard of user interface than
the common baseline at the time. LWUIT received critical acclaim and traction within multiple
industries but was limited by the declining feature phone market. It was forked by several
companies including Nokia. It was used as the base standard for DTV in Brazil. Another fork has
brought a LWUIT into high end cars from Toyota and other companies. This fork later adapted
Codename One as well.
In 2012 Shai and Chen formed Codename One as they left Oracle. The project has taken many of the
basic concepts developed within the LWUIT project and adapted them to the smartphone world
which is still experiencing similar issues to the device fragmentation of the old J2ME phones.
You can skip this section if you feel you are familiar enough with the core problems/issues in
mobile app development.
2.3.1. Density
Density is also known as DPI (Dots Per Inch) or PPI (pixels or points per inch). Density is confusing,
unintuitive and might collide with common sense. E.g. an iPhone 7 plus has a resolution of
1080x1920 pixels and a PPI of 401 for a 5 inch screen. On the other hand an iPad 4 has 1536x2048
pixels with a PPI of 264 on a 9.7 inch screen… Smaller devices can have higher resolutions!
As the following figure shows, if a Pixel 2 XL had pixels the size of an iPad it would have been twice
the size of that iPad. While in reality it’s nearly half the height of the iPad!
54
Figure 2. Device Density vs. Resolution
Differences in density can be extreme. A second generation iPad has 132 PPI, where modern
phones have PPI that crosses the 600 mark. Low resolution images on high PPI devices will look
either small or pixelated. High resolution images on low PPI devices will look huge, overscaled
(artifacts) and will consume too much memory.
55
Figure 3. How the Same Image Looks in Different Devices
The exact same image will look different on each device, sometimes to a comical effect. One of the
solutions for this problem is multi-images. All OS’s support the ability to define different images for
various densities. I will discuss multi-images later in Chapter 2.
This also highlights the need for working with measurements other than pixels. Codename One
supports millimeters (or dips) as a unit of measurement. This is highly convenient and is a better
representation of size when dealing with mobile devices.
But there is a bigger conceptual issue involved. We need to build a UI that adapts to the wide
differences in form factors. We might have fewer pixels on an iPad but because of its physical size
we would expect the app to cram more information into that space so the app won’t feel like a
blown up phone application. There are multiple strategies to address that but one of the first steps
is in the layout managers.
I’ll discuss the layout managers in depth in Chapter 2 but the core concept is that they decide where
a UI element is placed based on generic logic. That way the user interface can adapt automatically
to the huge variance in display size and density.
The fact that mobile devices use a touch interface today isn’t news… But the implications of that
aren’t immediately obvious to some developers.
UI elements need to be finger sized and heavily spaced. Otherwise we risk the “fat finger” effect.
56
That means spacing should be in millimeters and not in pixels due to device density.
Scrolling poses another challenge in touch based interfaces. In desktop applications it’s very
common to nest scrollable items. However, in touch interfaces the scrolling gesture doesn’t allow
such nuance. Furthermore, scrolling on both the horizontal and vertical axis (side scrolling) can be
very inconvenient in touch based interfaces.
Some developers single out this wide range of resolutions and densities as “device fragmentation”.
While it does contribute to development complexity for the most part it isn’t a difficult problem to
overcome.
Densities aren’t the cause of device fragmentation. Device fragmentation is caused by multiple OS
versions with different behaviors. This is very obvious on Android and for the most part relates to
the slow rollout of Android vendor versions compared to Googles rollout. E.g. 7 months after the
Android 8 (Oreo) release in 2018 it was still available on 1.1% of the devices. The damning statistic
is that 12% of the devices in mid 2018 run Android 4.4 Kitkat released in 2013!
This makes QA difficult as the disparity between these versions is pretty big. These numbers will be
out of date by the time you read this but the core problem remains. It’s hard to get all device
manufacturers on the same page so this problem will probably remain in the foreseeable future
despite everything.
2.3.4. Performance
Besides the obvious need for performance and smooth animation within a mobile app there are a
couple of performance related issues that might not be intuitive to new developers: size and power.
App Size
Apps are installed and managed via stores. This poses some restrictions about what an app can do.
But it also creates a huge opportunity. Stores manage automatic update and to some degree the
marketing/monetization of the app.
A good mobile app is updated once a month and sometimes even once a week. Since the app
downloads automatically from the store this can be a huge benefit:
• Existing users are reminded of the app and get new features instantly
If an app is big it might not update over a cellular network connection. Google and Apple have
restrictions on automatic updates over cellular networks to preserve battery life and data plans. A
large app might negatively impact users perception of the app and trigger uninstalls e.g. when a
phone is low on available space.
Power Drain
Desktop developers rarely think about power usage within their apps. In mobile development this
is a crucial concept. Modern device OS’s have tools that highlight misbehaving applications and this
57
can lead to bad reviews.
Code that loops forever while waiting for input will block the CPU from sleeping and slowly drain
the battery.
Worse. Mobile OS’s kill applications that drain the battery. If the app is draining the battery and is
minimized (e.g. during an incoming call) the app could be killed. This will impact app performance
and usability.
Apps installed on the device are "sandboxed" to a specific area so they won’t harm the device or its
functionality. The filesystem of mobile applications is restricted so one application can’t access the
files of another application. Things that most developers take for granted on the desktop such as a
"file picker" or accessing the image folder don’t work on devices!
This means that when your application works on a file it belongs only to your application. In order
to share the file with a different application you need to ask the operating system to do that for you.
Furthermore, some features require a "permission" prompt and in some cases require special flags
in system files. Apps need to request permission to use sensitive capabilities e.g. Camera, Contacts
etc.
Historically Android developers just declared required permissions for an app and the user was
prompted with permissions during install. Android 6 adopted the approach used by iOS of
prompting the user for permission when accessing a feature.
This means that in runtime a user might revoke a permission. A good example in the case of an
Uber app is the location permission. If a user revokes that permission the app might lose its
location.
Codename One projects are built with Maven. Typical Maven targets such as package, clean and
install work out of the box, but the Codename One integrations that ship with each IDE provide
dedicated Run and Build actions for a smoother workflow.
To create a new Codename One project visit [Link] and generate a starter
project, or run the Codename One Application Project Archetype (cn1app-archetype) directly on the
command line:
mvn archetype:generate \
-DarchetypeGroupId=[Link] \
-DarchetypeArtifactId=cn1app-archetype \
-DarchetypeVersion=LATEST \
-DgroupId=YOUR_GROUP_ID \
-DartifactId=YOUR_ARTIFACT_ID \
58
-Dversion=1.0-SNAPSHOT \
-DmainName=YOUR_MAIN_NAME \
-DinteractiveMode=false
This command generates a project in the current directory. The folder name matches the artifactId
value. For example, specifying -DartifactId=myapp produces a project inside a new myapp directory.
Import the generated Maven project into your preferred IDE and use the Codename One Run in
Simulator task from the IDE toolbar or Run/Debug buttons:
• IntelliJ IDEA – use File > Open on the project directory, then choose the Codename One Run in
Simulator action from the toolbar or standard Run/Debug controls.
• NetBeans – use File > Open Project, select the generated Maven project, and rely on the
Codename One toolbar actions to run and debug the simulator.
• VS Code – install the Java and Codename One extensions, open the folder, and trigger the Run
in Simulator task from the command palette or the Run/Debug buttons.
• Eclipse – use File > Import > Existing Maven Projects, then use the Codename One launch
shortcuts provided by the plugin for simulator and build tasks.
• Command line – invoke Maven goals directly whenever you need to integrate with CI/CD
pipelines or scripting.
For deeper coverage of the Maven goals, project structure, and automation tasks, continue with
Chapter 1, Maven Project Workflow.
Arbitrary Maven dependencies probably won’t work for Codename One. Many
dependencies assume a full JDK which Codename One can’t provide and they often
assume functionality that might not be available e.g. reflection, Spring, etc.
Legacy Ant-based project instructions remain available for teams maintaining older
codebases. New projects should follow the Maven workflows described in this guide.
Before we get to the code there are few important things we need to understand about Codename
One applications.
• App Name - This is the name of the app and the main class, it’s important to get this right as it’s
hard to change this value later
• Package Name - It’s crucial you get this value right. Besides the difficulty of changing this after
the fact, once an app is submitted to iTunes/Google Play with a specific package name this can’t
be changed! See the sidebar "Picking a Package Name".
• Theme - There are various types of builtin themes in Codename One, for simplicity we
recommend Native as it’s a clean slate starting point
59
Picking a Package Name
Apple, Google and Microsoft identify applications based on their package names. If you use a
domain that you don’t own it’s possible that someone else will use that domain and collide
with you. In fact some developers left the default [Link] domain in place all the way
into production in some cases.
This can cause difficulties when submitting to Apple, Google or Microsoft. Submitting to one
of them is no guarantee of success when submitting to another.
To come up with the right package name use a reverse domain notation. So if my website is
[Link] my package name should start with [Link]. I highly recommend the
following guidelines for package names:
• Lower Case - some OS’s are case sensitive and handling a mistake in case is painful. The
Java convention is lower case and I would recommend sticking to that although it isn’t a
requirement
• Avoid Dash and Underscore - You can’t use a dash character (-) for a package name in
Java. Underscore (_) doesn’t work for iOS. If you want more than one word just use a
deeper package e.g.: [Link]
• Obey Java Rules - A package name can’t start with a number so you can’t use
[Link].1sler. You should avoid using Java keywords like this, if etc.
• Avoid Top Level - instead of using [Link] use [Link]. That would
allow you to have more than one app on a domain
2.4.2. Runtime
Once Maven is set up we can run the HelloWorld application by selecting the Codename One Run in
Simulator task from the IDE run menu. The Codename One simulator launches and you can use its
menus to control and inspect details related to the device. You can rotate it, determine its location
in the world, monitor networking calls etc.
With the Skins menu you can download device skins to see how your app will look on different
devices.
Some skins are bigger than the screen size, uncheck the Scrollable flag in the
Simulator menu to handle them more effectively
Use your IDE’s Debug button with the Run in Simulator task to launch the simulator under the
debugger.
Codename One ships with a simulator similarly to the iOS toolchain which also has a
simulator. Android ships with an emulator. Emulators go the extra mile. They create a virtual
machine that’s compatible with the device CPU and then boot the full mobile OS within that
60
environment. This provides an accurate runtime environment but is painfully slow.
Simulators rely on the fact that OS’s are similar and so they leave the low level details in
place and just map the API behavior. Since Codename One relies on Java it can start
simulating on top of the virtual machine on the desktop. That provides several advantages
including fast development cycles and full support for all the development tools/debuggers
you can use on the desktop.
Emulators make sense for developers who want to build OS level services e.g. screensavers or
low level services. Standard applications are better served by simulators.
After clicking finish in the new project wizard we have a HelloWorld project with a few default
settings. I’ll break the class down to small pieces and explain each piece starting with the enclosing
class:
① This is the main class, it’s the entry point to the app, notice it doesn’t have a main method but
rather callback which we will discuss soon
② Forms are the “top level” UI element in Codename One. Only one Form is shown at a time and
everything you see on the screen is a child of that Form
③ Every app has a theme, it determines how everything within the application looks e.g. colors,
fonts etc.
Next let’s discuss the first lifecycle method init(Object). I discuss the lifecycle in depth in the
Application Lifecycle Sidebar.
61
[Link]("Connection Error", ⑩
"There was a networking error in the connection to " +
[Link]().getUrl(), "OK", null);
});
}
① init is the first of the four lifecycle methods. It’s responsible for initialization of variables and
values
② By default Codename One has one thread that performs all the networking, we set the default to
two which gives better performance
③ The theme determines the appearance of the application. We’ll discuss this in the next chapter
④ This enables the Toolbar API by default, it allows finer control over the title bar area
⑤ Crash protection automatically sends device crash logs through the cloud
⑥ In case of a network error the code in this block would run, you can customize it to handle
networking errors effectively
⑦ consume() swallows the event so it doesn’t trigger other alerts, it generally means “we got this”
⑧ Not all errors include an exception, if we have an exception we can log it with this code
⑨ This will email the log from the device to you if you have a pro subscription
⑩ This shows an error dialog to the user, in production you might want to remove that code
init(Object) works as a constructor to some degree. We recommend avoiding the constructor for
the main class and placing logic in the init method instead. This isn’t crucial but we recommend it
since the constructor might happen too early in the application lifecycle.
In a cold start init(Object) is invoked followed by the start() method. However, start() can be
invoked more than once if an app is minimized and restored, see the sidebar Application Lifecycle:
① If the app was minimized we usually don’t want to do much, just show the last Form of the
application
② current is a Form which is the top most visual element. We can only have one Form showing and
we enforce that by using the show() method
③ We create a new simple Form instance. It has the title “Hello World” and arranges elements
vertically (on the Y axis)
62
④ We add another Label below the title, see figure [TitleAndLabelImage]. We will discuss
component hierarchy later
⑤ The show() method places the Form on the screen. Only one Form can be shown at a time
There are some complex ideas within this short snippet which I’ll address later in this chapter
when talking about layout. The gist of it is that we create and show a Form. Form is the top level UI
element, it takes over the whole screen. We can add UI elements to that Form object, in this case the
Label. We use the BoxLayout to arrange the elements within the Form from top to the bottom
vertically.
Application Lifecycle
A few years ago Romain Guy (a senior Google Android engineer) was on stage at the Google
IO conference. He asked for a show of hands of people who understand the Activity lifecycle
(Activity is similar to a Codename One main class). He then proceeded to jokingly call the
audience members who lifted their hands “liars” claiming that after all his years in Google he
still doesn’t understand it…
Lifecycle seems simple on the surface but hides a lot of nuance. Android’s lifecycle is
ridiculously complex. Codename One tries to simplify this and also make it portable.
Sometimes complexity leaks out and the nuances can be difficult to deal with.
• Foreground - it’s running and in the foreground which means the user can physically
interact with the app
• Suspended - the app isn’t in the foreground, it’s either paused or has a background
process running
• Not Running - the app was never launched, was killed or crashed
The lifecycle is the process of transitioning between these 3 states and the callbacks invoked
when such a transition occurs. The first time we launch the app we start from a “Cold Start”
(Not Running State) but on subsequent launches the app is usually started from the "Warm
Start" (Suspended State).
63
Figure 5. Codename One Application Lifecycle
Codename One has four standard callback methods in the lifecycle API:
• init(Object) - is invoked when the app is first launched from a Not Running state.
• start() - is invoked for two separate cases. After start() is finished the app transitions to
the Foreground state.
◦ Following init(Object) in case of a cold start. Cold start refers to starting the app from
a Not Running state.
◦ When the app is restored from Suspended state. In this case init(Object) isn’t invoked
• stop() - is invoked when the app is minimized e.g. when switching to a different app.
After stop() is finished the app transitions to the Suspended state.
• destroy() - is invoked when the app is destroyed e.g. killed by a user in the task manager.
After destroy() is finished the app is no longer running hence it’s in the Not Running state.
Now that we have a general sense of the lifecycle lets look at the last two lifecycle methods:
64
}
② As the app is stopped we save the current Form so we can restore it back in start() if the app is
restored
③ Dialog is a bit of a special case restoring a Dialog might block the proper flow of application
execution so we dispose them and then get the parent Form
④ destroy() is a very special case. Under normal circumstances you shouldn’t write code in
destroy(). stop() should work for most cases
That’s it. Hopefully you have a general sense of the code. It’s time to run on the device.
You can use the Control Center to configure almost anything. Specifically, the application title,
application version, application icon etc. are all found in the Codename One Settings maven target.
There are many options within this UI that control almost every aspect of the application from
signing to basic settings.
Your device builds using the Codename One Cloud can also be found right here as well as
subscription information.
65
Figure 7. Device Builds in Logged out State
Signing/Certificates
All of the modern mobile platforms require signed applications but they all take radically different
approaches when implementing it.
Signing is a process that marks your final application for the device with a special value. This value
(signature) is a value that only you can generate based on the content of the application and your
certificate. Effectively it guarantees the app came from you. This blocks a 3rd party from signing
their apps and posing as you to the appstore or to the user. It’s a crucial security layer.
A certificate is the tool we use for signing. Think of it as a mathematical rubber stamp that
generates a different value each time. Unlike a rubber stamp a signature can’t be forged!
Signing on Android
Android uses a self signed certificate approach. You can just generate a certificate by describing
who you are and picking a password!
If this wasn’t the case someone else could potentially push an “upgrade” to your app. Once an app is
submitted with a certificate to Google Play this app can’t be updated with any other certificate.
66
Figure 8. Process of Certificate Generation for Android
Your certificate will generate into the file [Link] in your home directory
Make sure to back that up and the password as losing these can have dire
consequences
In theory yes. In practice it’s a pain… Keeping multiple certificates and managing them is a
pain so we often just use one.
The drawback of this approach occurs when you are building an app for someone else or
want to sell the app. Giving away your certificate is akin to giving away your house keys. So it
makes sense to have separate certificates for each app.
Code signing for iOS relies on Apple as the certificate authority. This is something that doesn’t exist
on Android. iOS also requires provisioning as part of the certificate process and completely
separates the process for development/release.
• Losing an iOS certificate is no big deal - in fact we revoke them often with no impact on
shipping apps
• Codename One has a wizard that hides most of the pain related to iOS signing
67
In iOS Apple issues the certificates for your applications. That way the certificate is trusted by Apple
and is assigned to your Apple iOS developer account. There is one important caveat: You need an
iOS Developer Account and Apple charges a 99USD Annual fee for that.
The 99USD price and requirement have been around since the introduction of the
iOS developer program for roughly 10 years at the time of this writing. It might
change at some point though
Apple also requires a “provisioning profile” which is a special file bound to your certificate and
app. This file describes some details about the app to the iOS installation process. One of the details
it includes during development is the list of permitted devices.
Figure 9. The Four Files Required for iOS Signing and Provisioning
We need 4 files for signing. Two certificates and two provisioning profiles:
1. Production — The production certificate/provisioning pair is used for builds that are uploaded
68
to iTunes
The certificate wizard automatically creates these 4 files and configures them for you.
69
If you have more than one project you should use the same iOS P12 certificate files
in all the projects and just regenerate the provisioning. In this situation the
certificate wizard asks you if you want to revoke the existing certificate which you
shouldn’t revoke in such a case. You can update the provisioning profile in Apple’s
iOS developer website.
One important aspect of provisioning on iOS is the device list in the provisioning step. Apple only
allows you to install the app on 100 devices during development. This blocks developers from
skipping the appstore altogether. It’s important you list the correct UDID for the device in the list
otherwise install will fail.
There are several apps and tools that offer the UDID of the device, they aren’t
necessarily reliable and might give a fake number!
You can right click the UDID and select copy to copy it
The simplest and most reliable process for getting a UDID is via iTunes. I’ve used other approaches
in the past that worked but this approach is guaranteed.
Ad hoc provisioning allows 1000 beta testers for your application but it’s a more
complex process that we won’t discuss here although it’s supported by Codename
One
70
Build and Install
Now that we have certificates the process of device builds is literally a right click away for both
OS’s. We can right click the project and select Codename One → Send iOS Debug Build or Codename
One → Send Android Build .
Figure 14. Right click menu options for sending device builds
The first time you send a build you will be prompted for the email and password
you provided when signing up for Codename One
Once you send a build you should see the results in the build server page:
On iOS make sure you use Safari when installing, as 3rd party browsers might
have issues
Once you go through those steps you should have the HelloWorld app running on your device. This
process is non-trivial when starting so if you run into difficulties don’t despair and seek help at the
discussion forum ([Link] or stack overflow
71
([Link] Once you go through signing and installation, it
becomes easier.
You can also install the application either by emailing the install link to your
account (using the e-mail Link button)
You can also download the binaries in order to upload them to the appstores.
2.5. Kotlin
Codename One started before Kotlin became public. Kotlin has since shown itself as an interesting
option for developers especially within the Android community. With that in mind we decided to
integrate support for Kotlin into Codename One.
To use Kotlin with Codename One you can create a kotlin directory next to the java directory under
the common/src/main directory. Kotlin code that resides there can work as usual and interact with the
Java code.
• Don’t use the project conversion tools or accept the warning that the project isn’t a Kotlin
project. We do our own build process
• Warnings and errors aren’t listed correctly and builds that claim to have errors might pass
Due to the way Kotlin works you can just create a regular Java project and convert sources to
Kotlin. You can mix Java and Kotlin code without a problem and Codename One would "just work".
The hello world Java source file looks like this (removed some comments and whitespace):
72
}
When you select that file and select the menu option Code → Convert Java file to Kotlin File you
should get this:
class MyApplication {
private var current: Form? = null
private var theme: Resources? = null
fun start() {
if (current != null) {
current!!.show()
return
}
val hi = Form("Hi World", BoxLayout.y())
[Link](Label("Hi World"))
[Link]()
}
fun stop() {
current = getCurrentForm()
if (current is Dialog) {
(current as Dialog).dispose()
current = getCurrentForm()
}
}
fun destroy() {
}
}
73
That’s pretty familiar. The problem is that there are two bugs in the automatic conversion… That is
the code for Kotlin behaves differently from standard Java.
The first problem is that Kotlin classes are final unless declared otherwise so we need to add the
open keyword before the class declaration as such:
This is essential as the build server will fail with weird errors related to instanceof.
This only applies to the main class of the project, other classes in Codename One
can remain final
The second problem is that arguments are non-null by default. The init method might have a null
argument. So this fails with an exception. The solution is to add a question mark to the end of the
call: fun init(context: Any?).
fun start() {
if (current != null) {
current!!.show()
return
}
val hi = Form("Hi World", BoxLayout.y())
[Link](Label("Hi World"))
[Link]()
}
fun stop() {
current = getCurrentForm()
if (current is Dialog) {
(current as Dialog).dispose()
current = getCurrentForm()
}
}
fun destroy() {
}
74
}
Once all of that is in place Kotlin should just work. This should be possible for additional JVM
languages in the future.
75
76
Chapter 3. Basics: Themes, Styles,
Components and Layouts
Let’s start with a brief overview of the ideas within Codename One. We’ll dig deeper into these
ideas as we move forward.
3.1. Components
Every button, label or element you see on the screen in a Codename One application is a
Component [[Link] This is a highly
simplified version of this class hierarchy:
77
A Codename One application is effectively a series of forms, only one Form can be shown at a time.
The Form includes everything we see on the screen. Under the hood the Form is comprised of a few
separate pieces:
• Content Pane - this is literally the body of the Form. When we add a Component into the Form it goes
into the content pane. Notice that Content Pane is scrollable by default on the Y axis!
• Title Area - we can’t add directly into this area. The title area is managed by the Toolbar class.
Toolbar is a special component that resides in the top portion of the form and abstracts the title
design. The title area is broken down into two parts:
◦ Title of the Form and its commands (the buttons on the right/left of the title)
◦ Status Bar - on iOS the area on the top includes a special space so the notch, battery, clock
etc. can fit. Without this the battery indicator/clock or notch would be on top of the title
Now that we understand this let’s look at the new project we created and open the Java file
[Link]. In it we should see the lines that setup the UI in the start() method:
A layout manager is an algorithm that decides the size and location of the components within a
Container. Every Container has a layout manager associated with it. The default layout manager is
FlowLayout.
To understand layouts we need to understand a basic concept about Component. Each component has
a “preferred size”. This is the size in which a component “wants” to appear. E.g. for a Label the
preferred size will be the exact size that fits the label text, icon and padding of the component.
78
By default Codename One invokes the getPreferredSize() method and not
calcPreferredSize() directly.
getPreferredSize() invokes calcPreferredSize() and caches the value
The preferred size is decided by the component based on internal constraints such as the font
size, border sizes, padding etc.
When a layout manager positions and sizes the component, it MIGHT take the preferred size
into account. Notice that it MIGHT ignore it entirely!
You can define a group of components to have the same preferred width or height by using
the setSameWidth and setSameHeight methods e.g.:
Listing 5. setSameWidth/Height
Codename One has a setPreferredSize method that allows developers to explicitly request the
size of the component. However, this caused quite a lot of problems. E.g. the preferred size
should change with device orientation or similar operations. The API also triggered frequent
inadvertent hardcoding of UI values such as forcing pixel sizes for components. As a result
the method was deprecated.
A layout manager places a component based on its own logic and the preferred size (sometimes
referred to as “natural size”). A FlowLayout will just traverse the components based on the order
they were added and size/place them one after the other. When it reaches the end of the row it will
go to the new row.
79
Figure 19. Layout Manager Primer Part I
Scrolling doesn’t work well for all types of layouts as the positioning algorithm within the layout
might break. Scrolling on the Y axis works great for BoxLayout Y which is why I picked it for the
TodoForm:
Layout Scrollable
Only one element can be scrollable within the hierarchy, otherwise if you drag your finger
over the Form Codename One won’t know which element you are trying to scroll. By default
form’s content pane is scrollable on the Y axis unless you explicitly disable it (setting the
80
layout to BorderLayout implicitly disables scrolling).
It’s important to notice that it’s OK to have non-scrollable layouts, e.g. BorderLayout, as items
within a scrollable container type. E.g. in the TodoApp we added TodoItem which uses
BorderLayout into a scrollable BoxLayout Form.
• Constraint Based - BorderLayout (and a few others such as GridBagLayout, MigLayout and
TableLayout)
When we add a Component to a Container with a regular layout we do so with a simple add method:
This works great for regular layouts but might not for constraint based layouts. A constraint based
layout accepts another argument. E.g. BorderLayout needs a location for the Component:
This line assumes you have an import static [Link].*; in the top of the file. In
BorderLayout (which is a constraint based layout) placing an item in the NORTH places it in the top of
the Container.
The CN class is a class that contains multiple static helper methods and functions.
It’s specifically designed for static import in this way to help keep our code terse
That’s optional, if you don’t like static imports you can just write CN. for
every element
From that point on you can write code that looks like this:
81
callSerially(() -> runThisOnTheEDT());
Instead of:
addToQueue(myConnectionRequest);
Instead of:
[Link]().addToQueue(myConnectionRequest);
Some things were changed so we won’t have too many conflicts e.g. Log.p or Log.e would
have been problematic so we now have:
• Terse code
Some of our samples in this guide might rely on that static import being in place. This helps
us keep the code terse and readable in the code listings.
Terse Syntax
Almost every layout allows us to add a component using several variants of the add method:
82
[Link](new Label("Chaining")). ③
add(new Label("Value"));
① Regular add
③ add returns the parent Container instance so we can chain calls like that
In the race to make code “tighter” we can make this even shorter. Almost all layout managers have
their own custom terse syntax style e.g.:
② FlowLayout has variants that support aligning the components on various axis
To sum this up, we can use layout managers and nesting to create elaborate UI’s that implicitly
adapt to different screen sizes and device orientation.
Flow Layout
83
add(new Label("Second")).
add(new Label("Third")).
add(new Label("Fourth")).
add(new Label("Fifth"));
[Link]();
Flow layout can be aligned to the left (the default), to the center, or to the right. It can also be
vertically aligned to the top (the default), middle (center), or bottom.
84
Figure 24. Flow layout aligned to the center horizontally & the middle vertically
Components within the flow layout get their natural preferred size by default and are not stretched
in any axis.
The natural sizing behavior is often used to prevent other layout managers from
stretching components. E.g. if we have a border layout element in the south and
we want it to keep its natural size instead of adding the element to the south
directly we can wrap it using [Link]([Link],
[Link](dontGrowThisComponent)).
Box Layout
85
Figure 25. BoxLayout Y
Box layout also supports a shorter terse notation which we use here to demonstrate the X axis box.
The box layout keeps the preferred size of its destination orientation and scales elements on the
other axis. Specifically X_AXIS will keep the preferred width of the component while growing all the
components vertically to match in size. Its Y_AXIS counterpart keeps the preferred height while
growing the components horizontally.
This behavior is very useful since it allows elements to align as they would all have the same size.
In some cases the growing behavior in the X axis is undesired, for these cases we can use the
X_AXIS_NO_GROW variant.
86
Figure 27. BoxLayout X_AXIS_NO_GROW
Border Layout
You can use the static import of the CN class and then the syntax can be add(SOUTH,
new Label("South"))
The layout always stretches the NORTH/SOUTH components on the X-axis to completely fill the
container and the EAST/WEST components on the Y-axis. The center component is stretched to fill the
remaining area by default. However, the setCenterBehavior allows us to manipulate the behavior of
the center component so it is placed in the center without stretching.
E.g.:
87
Form hi = new Form("Border Layout", new BorderLayout());
((BorderLayout)[Link]()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER);
[Link]([Link], new Label("Center")).
add([Link], new Label("South")).
add([Link], new Label("North")).
add([Link], new Label("East")).
add([Link], new Label("West"));
[Link]();
Results in:
Because of its scaling behavior scrolling a border layout makes no sense. Container
implicitly blocks scrolling on a border layout, but it can scroll its parents/children
In the case of RTL the EAST and WEST values are implicitly reversed as shown in this image:
RTL (Right To Left) or Bidi (bi-directional) are common terms used for languages such as
Hebrew, Arabic etc. These languages are written from the right to left direction hence all the
UI needs to be “reversed”. Bidi denotes the fact that while the language is written from right
to left, the numbers are still written in the other direction hence two directions…
Grid Layout
88
dimensions of the largest components.
The main use case for this layout is a grid of icons e.g. like one would see in the
iPhone home screen
If the number of rows * columns is smaller than the number of components added a new row is
implicitly added to the grid. However, if the number of components is smaller than available cells
(won’t fill the last row) blank spaces will be left in place.
In this example we can see that a 2x2 grid is used to add 5 elements, this results in an additional
row that’s implicitly added turning the grid to a 3x2 grid implicitly and leaving one blank cell.
When we use a 2x4 size ratio we would see elements getting cropped as we do here. The grid layout
uses the grid size first and doesn’t pay too much attention to the preferred size of the components it
holds.
Grid also has an autoFit attribute that can be used to automatically calculate the column count
based on available space and preferred width. This is really useful for working with UI’s where the
89
device orientation might change.
There is also a terse syntax for working with a grid that has two versions, one that uses the "auto
fit" option and another that accepts the number of columns. Here’s a sample of the terse syntax
coupled with auto fit followed by screenshots of the same code in two orientations:
[Link](new Label("First"),
new Label("Second"),
new Label("Third"),
new Label("Fourth"),
new Label("Fifth")));
Despite being constraint based the TableLayout isn’t strict about constraints and will implicitly add a
constraint when one is missing. This is unlike the BorderLayout which will throw an exception in
this case.
Unlike GridLayout TableLayout won’t implicitly add a row if the row/column count
90
is incorrect
Figure 35. 2x2 TableLayout with 5 elements, notice that the last element is missing
TableLayout supports the ability to grow the last column which can be enabled using the
setGrowHorizontally method. You can also use a shortened terse syntax to construct a TableLayout
however since the TableLayout is a constraint based layout you won’t be able to utilize its full power
with this syntax.
The default usage of the encloseIn method below uses the setGrowHorizontally flag.
Figure 36. [Link]() with default behavior of growing the last column
TableLayout is a beast, to truly appreciate it we need to use the constraint syntax which allows us to
span, align and set width/height for the rows and columns.
91
[Link]] instance that can communicate our intentions into the layout manager.
Such constraints can include more than one attribute e.g. span and height.
The table layout constraint sample tries to demonstrate some of the unique things you can do with
constraints.
add([Link](). ④
horizontalSpan(2).
heightPercentage(80).
92
verticalAlign([Link]).
horizontalAlign([Link]),
new Label("Span H")).
add(new Label("BBB")).
add([Link]().
widthPercentage(60).
heightPercentage(20),
new Label("CCC")).
add([Link]().
widthPercentage(20),
new Label("DDD"));
① We need the TableLayout instance to create constraints. A constraint must be created for every
component and must be used with the same layout as the parent container
② To get the look in the screenshot we need to turn scrolling off so the height constraint doesn’t
take up available height. Otherwise it will miscalculate available height due to scrolling. You can
scroll a TableLayout but sizing will be different
③ We create the constraint and instantly apply width to it. This is a shorthand syntax for the code
block below
④ We can chain constraint creation using a call like this so multiple constraints apply to a single
cell. Notice that we don’t span and set width on the same axis (horizontal span + width), doing
something like that would create confusing behavior
[Link] cn = [Link]();
[Link](20);
[Link](cn, new Label("AAA")).
Figure 37. TableLayout constraints can be used to create very elaborate UI’s
TextMode Layout
TextModeLayout is a unique layout manager. It acts like TableLayout on Android and like
BoxLayout.Y_AXIS in other platforms. Internally it delegates to one of these two layout managers so
in a sense it doesn’t have as much functionality of its own.
93
Form f = new Form("Pixel Perfect", tl);
TextComponent title = new TextComponent().label("Title");
TextComponent price = new TextComponent().label("Price");
TextComponent location = new TextComponent().label("Location");
TextComponent description = new TextComponent().label("Description").multiline(true);
[Link]([Link]().horizontalSpan(2), title);
[Link]([Link]().widthPercentage(30), price);
[Link]([Link]().widthPercentage(70), location);
[Link]([Link]().horizontalSpan(2), description);
[Link]([Link]());
[Link]();
As you can see from the code and samples above there is a lot going on under the hood. On Android
we want a layout that’s similar to TableLayout so we can “pack” the entries. On iOS we want a box
layout Y type of layout but we also want the labels/text to align properly…
The TextModeLayout isn’t really a layout as much as it is a delegate. When running in the Android
mode (which we refer to as the “on top” mode) the layout is almost an exact synonym of
TableLayout and in fact delegates to an underlying TableLayout. In fact there is a public final table
instance within the layout that you can refer to directly…
There is one small difference between the TextModeLayout and the underlying TableLayout and that’s
our choice to default to align entries to TOP with this mode.
When working in the non-android environment we use a BoxLayout on the Y axis as the delegate.
There’s one thing we do here that’s different from a default box layout: grouping. Grouping allows
94
the labels to align by setting them to the same width, internally it invokes [Link]().
Since text components hide the labels there is a special group method there that can be used.
However, this is implicit with the TextModeLayout which is pretty cool.
TextModeLayout was created specifically for the TextComponent and InputComponent so check out the
section about them in the components chapter.
Figure 40. The X on this button was placed there using the layered layout code below
The code to generate this UI is slightly complex and contains very few relevant pieces. The only
truly relevant piece is this block:
[Link]([Link](settingsLabel,
[Link](close)));
2. We are creating a layered layout and placing two components within. This would be the
equivalent of just creating a LayeredLayout Container and invoking add twice
When used without constraints, the layered layout sizes all components to the
exact same size one on top of the other. It usually requires that we use another
container within; in order to position the components correctly
95
Button settingsLabel = new Button("");
Style settingsStyle = [Link]();
[Link](0xff);
[Link](null);
[Link](0xff00);
[Link](255);
[Link]([Link]().getFont().derive(w / 3, Font.STYLE_PLAIN));
[Link](settingsLabel, FontImage.MATERIAL_SETTINGS);
Button close = new Button("");
[Link]("Container");
[Link]().setFgColor(0xff0000);
[Link](close, FontImage.MATERIAL_CLOSE);
[Link]([Link](settingsLabel,
[Link](close)));
Forms have a built in layered layout that you can access via getLayeredPane(), this allows you to
overlay elements on top of the content pane.
Codename One also includes a GlassPane that resides on top of the layered pane.
Its useful if you just want to "draw" on top of elements but is harder to use than
layered pane
As an example, suppose you wanted to position a button in the lower right corner of its container.
This can be achieved with LayeredLayout [[Link]
layouts/[Link]] as follows:
96
The only thing new here is this line:
This is called after btn has already been added to the container. It says that we want its insets to be
"auto" on the top and left, and 0 on the right and bottom. This insets string follows the CSS notation
of top right bottom left (i.e. start on top and go clockwise), and the values of each inset may be
provided in pixels (px), millimetres (mm), percent (%), or the special "auto" value. Like CSS, you can
also specify the insets using a 1, 2, or 3 values. E.g.
2. "1mm 2mm" - Sets 1mm insets on top and bottom; 2mm on left and right.
3. "1mm 10% 2mm" - Sets 1mm on top, 10% on left and right, and 2mm on bottom.
4. "1mm 2mm 1px 50%" - Sets 1mm on top, 2mm on right, 1px on bottom, and 50% on left.
auto Insets
The special "auto" inset indicates that it is a flexible inset. If all insets are set to "auto", then the
component will be centered both horizontally and vertically inside its "bounding box".
The "inset bounding box" is the containing box from which a component’s insets
are measured. If the component’s insets are not linked to any other components,
then its inset bounding box will be the inner bounds (i.e. taking padding into
account) of the component’s parent container.
If one inset is fixed (i.e. defined in px, mm, or %), and the opposite inset is "auto", then the "auto"
inset will simply allow the component to be its preferred size. So if you want to position a
component to be centered vertically, and 5mm from the left edge, you could do:
Resulting in:
97
Figure 41. Button vertically centered 5mm from left edge
% Insets
Percent (%) insets are calculated with respect to the inset bounding box. A 50% inset is measured as
50% of the length of the bounding box on the inset’s axis. E.g. A 50% inset on top would be 50% of
the height of the inset bounding box. A 50% inset on the right would be 50% of the width of the
inset bounding box.
A component’s position in a layered layout is determined as follows: (Assume that cmp is the
component that we are positioning, and cnt is the container (In pseudo-code):
If no inset is specified, then it is assumed to be 0. This ensures compatibility with designs that were
created before layered layout supported insets.
If all you need to do is position a component relative to its parent container’s bounds, then mere
insets provide you with sufficient vocabulary to achieve this. But most UIs are more complex than
this and require another concept: reference components. In many cases you will want to position a
component relative to another child of the same container. This is also supported.
98
For example, suppose I want to place a text field in the center of the form (both horizontally and
vertically), and have a button placed beside it to the right. Positioning the text field is trivial
(setInset(textField, "auto")), but there is no inset that we can provide that would position the
button to the right of the text field. To accomplish our goal, we need to set the text field as a
reference component of the button’s left inset - so that the button’s left inset is "linked" to the text
field. Here is the syntax:
② Links btn’s left inset to `tf so that it is measured from the text field. The third parameter (
1.0) is the reference position. This will generally either be 0 (meaning the reference point is the
left edge of the text field), or 1 (meaning the reference point is the right edge of the text field). In
this case we set a reference position of 1.0 because we want the button to be aligned to the text
field’s right edge.
99
A reference position of 0 means that the inset is measured from the leading edge of
the reference component. A value of 1.0 means that the inset is measured from the
trailing edge of the reference component. A value of 0.5 means that the inset is
measured from the center of the reference component. Etc… Any floating point
value can be used, though the most common values are 0 and 1.
The definition above may make reference components and reference position seem more complex
than it is. Some examples:
a. referencePosition == 0 ⇒ the inset is measured from the top edge of the reference
component.
b. referencePosition == 1 ⇒ the inset is measured from the bottom edge of the reference
component.
a. referencePosition == 0 ⇒ the inset is measured from the bottom edge of the reference
component.
b. referencePosition == 1 ⇒ the inset is measured from the top edge of the reference
component.
a. referencePosition == 0 ⇒ the inset is measured from the left edge of the reference
component.
b. referencePosition == 1 ⇒ the inset is measured from the right edge of the reference
component.
a. referencePosition == 0 ⇒ the inset is measured from the right edge of the reference
component.
b. referencePosition == 1 ⇒ the inset is measured from the left edge of the reference
component.
Codename One allows placing components one on top of the other and we commonly use
layered layout to do that. The form class has a builtin Container that resides in a layer on top
of the content pane of the form.
When you add an element to a form it implicitly goes into the content pane. However, you
can use getLayeredPane() and add any Component there. Such a Component will appear above the
content pane. Notice that this layer resides below the title area (on the Y axis) and won’t draw
on top of that.
When Codename One introduced the layered pane it was instantly useful. However, its
popularity caused conflicts. Two separate pieces of code using the layered pane could easily
collide with one another. Codename One solved it with getLayeredPane(Class c, boolean top).
100
This method allocates a layer for a specific class within the layered pane. This way if two
different classes use this method instead of the getLayeredPane() method they won’t collide.
Each will get its own container in a layered layout within the layered pane seamlessly. The
top flag indicates whether we want the layer to be the top most or bottom most layer within
the layered pane (assuming it wasn’t created already). This allows you to place a layer that
can appear above or below the already installed layers.
We only make use of the layered pane in this book but there are two additional layers on top
of it. The form layered pane is identical to the layered pane but spans the entire height of the
Form (including the title area). As a result the form layered pane is slower as it needs to handle
some special cases to support this functionality.
The glass pane is the top most layer, unlike the layered pane it’s purely a graphical layer. You
can only draw on the glass pane with a Painter instance and a Graphics object. You can’t add
components into that layer.
Our recommendation is to use Table which is just as powerful but has better Codename One
integration.
To demonstrate GridBagLayout we ported the sample from the Java tutorial [[Link]
javase/tutorial/uiswing/layout/[Link]] to Codename One.
Button button;
[Link](new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
101
//natural height, maximum width
[Link] = [Link];
button = new Button("Button 1");
[Link] = 0.5;
[Link] = [Link];
[Link] = 0;
[Link] = 0;
[Link](c, button);
Notice that because of the way gridbag works we didn’t provide any terse syntax API for it although
it should be possible.
102
Figure 44. GridbagLayout sample from the Java tutorial running on Codename One
It was originally added during the LWUIT days as part of an internal attempt to port Matisse to
LWUIT. It’s still useful to this day as developers copy and paste Matisse code into Codename One
and produce very elaborate layouts with drag and drop.
Since the layout is based on an older version of GroupLayout some things need to be adapted in the
code or you should use the special "compatibility" library for Matisse to get better interaction. We
also recommend tweaking Matisse to use import statements instead of full package names, that way
if you use Label just changing the awt import to a Codename One import will make it use work for
Codenmae One’s Label.
Unlike any other layout manager GroupLayout adds the components into the container instead of the
standard API. This works nicely for GUI builder code but as you can see from this sample it doesn’t
make the code very readable:
[Link]("label1");
[Link]("label2");
[Link]("label3");
[Link]("label4");
[Link]("label5");
[Link]("label6");
[Link]("label7");
103
.add([Link]()
.addContainerGap()
.add([Link]([Link])
.add([Link]()
.add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap([Link])
.add([Link]([Link])
.add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
.add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add([Link]()
.add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap([Link])
.add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
.addContainerGap(296, Short.MAX_VALUE))
);
[Link](
[Link]([Link])
.add([Link]()
.addContainerGap()
.add([Link]([Link])
.add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addPreferredGap([Link])
.add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap([Link])
.add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap([Link])
.add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap([Link])
.add([Link]([Link])
.add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addContainerGap(150, Short.MAX_VALUE))
);
If you are porting newer Matisse code there are simple changes you can do:
104
3.1.6. Mig Layout
MigLayout [[Link] is a
popular cross platform layout manager that was ported to Codename One from Swing.
The best reference for MiG would probably be its quick start guide (PDF link)
[[Link] As a reference we ported one of the samples from that PDF to
Codename One:
[Link](new Label("First")).
add("span 2 2", new Label("Second")). // The component will span 2x2 cells.
add("wrap", new Label("Third")). // Wrap to next row
add(new Label("Forth")).
add("wrap", new Label("Fifth")). // Note that it "jumps over" the occupied cells.
add(new Label("Sixth")).
add(new Label("Seventh"));
[Link]();
It should be reasonably easy to port MiG code but you should notice the following:
• MiG handles a lot of the spacing/padding/margin issues that are missing in Swing/AWT. With
Codename One styles we have the padding and margin which are probably a better way to do a
lot of the things that MiG does
• The add method in Codename One can be changed as shown in the sample above.
• The constraint argument for Coedname One add calls appears before the Component instance.
Themes are very similar conceptually to CSS, in fact they can be created with CSS syntax as we’ll
105
discuss soon. The various Codename One ports ship with a native theme representing the
appearance of the native OS UI elements. Every Codename One application has its own theme that
derives the native theme and overrides behavior within it.
If the native theme has a button defined, we can override properties of that button in our theme.
This allows us to customize the look while retaining some native appearances. This works by
merging the themes to one big theme where our application theme overrides the definitions of the
native theme. This is pretty similar to the cascading aspect of CSS if you are familiar with that.
Themes consist of a set of UIID definitions. Every component in Codename One has a UIID
associated with it. UIID stands for User Interface Identifier. This UIID connects the theme to a
specific component. A UIID maps to CSS classes if you are familiar with that concept. However,
Codename One doesn’t support the complex CSS selector syntax options as those can impact
runtime performance.
[Link]("Label");
This is a text field component (user input field) but it will look like a Label.
Effectively we told the text field that it should use the UIID of Label when it’s drawing itself. It’s very
common to do tricks like that in Codename One. E.g. [Link]("Label") would make a button
appear like a label and allow us to track clicks on a “Label”.
The UIID’s translate the theme elements into a set of Style objects. These Style objects get their
initial values from the theme but can be further manipulated after the fact. So if I want to make the
text field’s foreground color red I could use this code:
[Link]().setFgColor(0xff0000);
The color is in hexadecimal RRGGBB format so 0xff00 would be green and 0xff0000 would be red.
Each component can have one of 4 states and each state has a Style object. This means we can have
4 style objects per Component:
• Unselected — used when a component isn’t touched and doesn’t have focus. You can get that
object with getUnselectedStyle().
• Selected — used when a component is touched or if focus is drawn for non-touch devices. You
can get that object with getSelectedStyle().
• Pressed — used when a component is pressed. Notice it’s only applicable to buttons and button
subclasses usually. You can get that object with getPressedStyle().
106
• Disabled — used when a component is disabled. You can get that object with
getDisabledStyle().
The getAllStyles() method returns a special case Style object that lets you set the values of all 4
styles from one class so the code before would be equivalent to invoking all 4 setFgColor methods.
However, getAllStyles() only works for setting properties not for getting them!
As you can see, it’s a bit of a hassle to change styles from code which is why the theme is so
appealing.
3.2.1. Theme
A theme allows you to define the styles externally via a set of UIID’s (User Interface ID’s). Themes
can be authored directly in CSS and then compiled into the Codename One resource file, which
keeps styling concerns separate from application logic.
We load the theme file using this line of code in the init(Object) method in the main class of the
application:
theme = [Link]("/theme");
In a CSS project this file is generated automatically from the stylesheet. Legacy applications that still
edit the resource file by hand can continue to do so, but new projects should prefer the CSS
workflow described below.
This code is shorthand for resource file loading and for the installation of theme. You could
technically have more than one theme in a resource file at which point you could use
initNamedTheme() instead. The resource file is a special file format that includes inside it several
features:
• Themes
• Images
• Localization Bundles
• Data files
107
3.2.2. Working with CSS Themes
Modern Codename One projects ship with a src/main/css/[Link] file (or an equivalent
stylesheet). Editing this file allows you to define UIIDs using standard CSS syntax together with
Codename One–specific extensions such as cn1-derive for inheritance and the #Constants block for
theme constants. Each time you build or run the project, the build tool compiles the CSS into the
[Link] resource file automatically. Saving the CSS while the simulator is running will also trigger
a refresh so you can iterate on styling quickly.
Because the CSS compiler produces the final resource file, you should treat the generated [Link]
as an output artifact and keep your changes in the CSS source. Images referenced from CSS rules
(e.g. background images or multi-images) should be placed alongside the stylesheet so that they are
picked up by the compiler. Additional details about the supported selectors and properties are
covered in the dedicated CSS chapter later in this guide.
Codename One originally shipped with a GUI builder that generated a Statemachine class and
stored its data directly inside the resource file. That legacy tool targeted very constrained
devices and has since been deprecated. Modern projects should rely on the standalone GUI
builder launched from the IDE, which is the focus of the remainder of this section. The
current builder includes an auto layout mode (introduced in version 3.7) that lets you freely
position and resize components on a canvas while using LayeredLayout behind the scenes.
Creating a hello world app in the new GUI builder is actually pretty trivial, you need to start with a
regular handcoded application. Not a GUI builder application as it refers to the old GUI builder!
Following are the instructions for creating a form and launching the GUI builder. While they are
similar there are minor IDE differences. Usage of the GUI builder is identical in all IDE’s as the GUI
builder is a separate application.
NetBeans
108
Figure 47. Right click the package select New → Other
Figure 48. In the Codename One section select the GUI builder form
Figure 49. Type in the name of the form and click finish, you can change the type to be a Container or Dialog
Figure 50. Launch the GUI builder thru the right click menu on the newly created file
IntelliJ/IDEA
109
Figure 51. Right click the package select New → Codename One AutoLayout Form (or Dialog/Container)
Figure 53. Launch the GUI builder thru the right click menu on the newly created file
Eclipse
110
Figure 55. In the Codename One section select the GUI builder option
Figure 56. Type in the name of the form and click finish, you can change the type to be a Container or Dialog
Figure 57. Launch the GUI builder thru the right click menu on the newly created file
Basic Usage
Notice that the UI of the new GUIBuilder might change in various ways but the basic concepts
should remain the same.
The GUI builder is controlled via it’s main toolbar, notice that your changes will only be applied
when you click the Save button on the right:
111
Figure 58. The features of the left toolbar
• Main Form — This is where we place the components of the UI we are building
• Component Tree — This is a logical representation of the component hierarchy within the Main
Form. It’s often easier to pick a component from the tree rather than the form itself
• Property Inspector — When we select an element in the tree or form we can see its details here.
112
We can then edit the various details of the component in this area
• Palette — Components can be dragged from the palette to the Main Form and placed in the UI
We’ll start by selecting the Component Palette and dragging a button into the UI:
Figure 61. You can drag any component you want from the palette to the main UI
By default the auto-layout mode of the GUI builder uses layered layout to position components.
Sides can be bound to a component or to the Form. We then use distance units to determine the
binding behavior. The GUI builder tries to be "smart" and guesses your intention as you drag the
components along.
When you select the component you placed you can edit the properties of that component:
113
There are five property sheets per component:
• Basic Settings — These include the basic configuration for a component e.g. name, icon, text etc.
• Advanced Settings — These include features that aren’t as common such as icon gap, mask etc.
• Events — By clicking a button in this tab a method will be added to the source file with a
callback matching your component name. This will let you bind an event to a button, text field
etc.
• Layout — You can determine the layout of the parent Container here. For auto-layout this should
stay as layered layout, however you can nest other layout types in here
• Style Customization — This isn’t a theme, if you want to customize the style of a specific
component you can do that through this UI. The theme works on a more global/reusable level
and this is designed for a specific component only
For things like setting the text on the component we can use a convenient "long click" on the
component to edit the text in place as such:
Figure 63. Use the long click to edit the text "in place"
Events
As of now, the events tab was completely disabled. See issue 3593 for more info.
[[Link]
When a component supports broadcasting events you can bind such events by selecting it, then
selecting the events tab and clicking the button matching the event type
114
Figure 64. The events tab is listed below supported event types can be bound above
Once an event is bound the IDE will open to the event code e.g.:
Some IDE’s only generate the project source code after you explicitly build the
project so if your code needs to access variables etc. try building first
Within the code you can access all the GUI components you defined with the gui_ prefix e.g.
Button_1 from the UI is represented as:
Underlying XML
Saving the project generates an XML file representing the UI into the res directory in the project,
the GUI file is created in a matching hierarchy in the project under the res/guibuilder directory:
115
Figure 65. The java and GUI files in the hierarchy
If you refactor (rename or move) the java file it’s connection with the GUI file will
break. You need to move/rename both
You can edit the GUI file directly but changes won’t map into the GUI builder unless you reopen it.
These files should be under version control as they are the main files that change. The GUI builder
file for the button and label code looks like this:
This format is relatively simple and is roughly the same format used by the old GUI builder which
makes the migration to the new GUI builder possible. This file triggers the following Java source
file:
package [Link];
/**
* GUI builder created Form
*
* @author shai
*/
public class MyForm extends [Link] {
public MyForm() {
this([Link]());
}
116
initGuiBuilderComponents(resourceObjectInstance);
}
public EventCallbackClass() {
}
if(sourceComponent == gui_Button_1) {
onButton_1ActionEvent(ev);
}
}
The GUI builder uses the "magic comments" approach where code is generated into those areas to
match the XML defined in the GUI builder. Various IDE’s generate that code at different times. Some
will generate it when you run the app while others will generate it as you save the GUI in the
builder.
117
You can write code freely within the class both by using the event mechanism, by writing code in
the constructors or thru overriding functionality in the base class.
As of version 3.7, new forms created with the GUI Builder will use auto-layout mode. In this mode
you can move and resize your components exactly as you see fit. You aren’t constrained to the
positions dictated by the form’s layout manager.
As an example, let’s drag a button onto a blank form and see what happens. The button will be
"selected" initially after adding it, so you’ll see its outline and resize handles for adjusting its size
and position. You’ll also see four floating labels (above, below, to the left, and to the right) that show
the corresponding side’s inset values and allow you to adjust them.
When a component is selected you can drag it to reposition it or use the resize handles to change its
size. The floating inset labels update as you drag so you can fine tune spacing without leaving the
canvas.
Press the mouse inside the bounds of the button and drag it around to reposition it. You will notice
that the inset labels change to reflect the new inset values. If you drag the button close to the edge
of the form, the corresponding inset value will change to millimetres. If you move farther away
from the edge, it will change to percentage values.
Let’s take a closer look at the inset control (the inset controls are the black buttons that appear to
the top, bottom, left, and right of the selected component).
Figure 66. The inset control allows you to change the inset size and units, toggle it between fixed and
flexible, and link it to another component.
1. The inset value drop-down menu. This shows the current value of the inset (e.g. 0mm, 25%,
auto, etc…). If you click on this, it will open a menu that will allow you to change the units. If the
inset is currently in millimetres, it will have options for pixels, and percent. If the inset is in
percent, it will have options for pixels and millimetres. Etc.. It also includes a text field to enter
a an inset value explicitly.
2.
The "Link" Button - If the inset is linked to a reference component, then this button will
be highlighted "blue", and hovering over it will highlight the reference component in the UI so
118
that you can clearly see which component it is linked to. Clicking on this button will open a
dialog that will allow you to "break" this link. You can drag this button over any component in
the form to "link".
3.
The "Lock" Button" - This button allows you to toggle the inset between "flexible" (i.e.
auto) and "fixed" (i.e. millimetres or percent).
Auto Snap
Notice the "auto-snap" checkbox that appears in the top-right corner of the GUI builder window.
Auto-snap does exactly what it sounds like: It automatically snaps two components together when
you drag them near each other. This is handy for linking components together without having to
explicitly link them (using the "link" button). This feature is turned on by default. If auto-snap is
turned off, you can still initiate a "snap" by holding down the ALT/Option key on your keyboard
during the drag.
Smart Insets
Smart Inset uses some heuristics during a drag to try to determine how the insets should be linked.
Currently the heuristics are quite basic (it tries to link to the nearest neighbor component in most
cases), but we will be working on improving this for future releases. This feature is turned off by
default while it is still being refined. The goal is to improve this to the point where it always makes
the correct link choices - at which time you will be able to use the GUI builder without having any
knowledge of insets or reference components.
When a component is selected, you should see a black floating panel appear in the lower right of
the screen.
This is the widget control pad, and it provides an alternative view of the component’s links. It also
provides a useful list of incoming links (i.e. components that "depend on" this component’s
positioning). In some cases, you may want to disconnect incoming links so that you can drag the
119
component without affecting the position of dependent components.
This control pad also includes game-pad-like controls (up, down, left, right), that allow you to "tab"
the component to the next guide in that direction. Tab positions exist at component edges in the
form. This is useful for aligning components with each other.
Keyboard Short-Cuts
1. Arrow Keys - Use the up/down/left/right arrow keys to nudge the currently selected component
a little bit at a time. This is a convenient way to move the component to a position that is more
precise than can easily be achieved with a mouse drag.
2. Arrow Keys + SHIFT - Hold down the SHIFT key while pressing an arrow key and it will "tab"
the component to the next tab marker. The form has implicit tab markers at the edge of each
component on the form.
3. ALT/Option Key + Click or Drag - Holding down the option/alt key while clicking or dragging a
component will resulting in "snapping" behaviour even if auto-snap is turned off.
Sub-Containers
In some cases, you may need to add sub-containers to your form to aid in grouping your
components together. You can drag a container onto your form using the "Container" palette item
(under "Core Components"). The default layout the subcontainer will be LayeredLayout so that you
are able to position components within the sub-container with precision, just like on the root
container.
You can also change the layout of subcontainers to another classical layout manager (e.g. grid
layout, box layout, etc..) and drag components directly into it just as you did with the old GUI
builder. This is very useful if parts of your form lend themselves. As an example, let’s drag a
container onto the canvas that uses BoxLayout Y. (You can find this under the "Containers" section
of the component palette).
Drag the button (that was previously on the form) over that container, and you should see a drop-
zone become highlighted.
You can drop the button directly there. You can As you drag more components into the sub-
container, you’ll see them automatically laid out vertically.
120
The Canvas Resize Tool
When designing a UI with the new GUI builder it is very important that you periodically test the
form’s "resizing" behavior so that you know how it will behave on different devices. Components
may appear to be positioned correctly when the canvas is one size, but become out of whack when
the container is resized. After nearly every manipulation you perform, it is good practice to drag
the canvas resize tool (the button in the lower right corner of the GUI builder) smaller and bigger so
you can see how the positions are changed. If things grow out of whack, you may need to toggle an
inset between fixed and auto, or add a link between some of the components so that the resizing
behavior matches your expectations.
121
122
Chapter 4. Theme Basics
This chapter covers the creation of a simple hello world style theme and its visual customization. It
uses the Codename One Designer tool to demonstrate basic concepts in theme creation such as 9-
piece borders, selectors and style types. We would recommend reviewing this even if you end up
using CSS.
By default, Codename One themes derive the native operating system themes, although this
behavior is entirely optional.
Codename One themes have some built-in defaults. E.g. borders for buttons and
padding/margin/opacity for various components. These are a set of “common sense” defaults that
can be overridden within the theme.
Codename One themes are effectively a set of UIID’s mapped to a Style [[Link]
javadoc/com/codename1/ui/plaf/[Link]] object. Codename One applications always have a theme, you
can modify it to suit your needs and you can add multiple themes within the main resource file.
You can also add multiple resource files to a project and work with them. In code a theme is
initialized using this code in your main class:
The initFirstTheme method is a helper method that hides some try/catch logic as well as some
verbosity. This could be expressed as:
try {
theme = [Link]("/theme");
[Link]().setThemeProps([Link]([Link]()[0]));
123
} catch(IOException e){
[Link]();
}
When you select the theme you will see the theme default view.
124
Figure 73. Theme default view
There are several interesting things to notice here the preview section allows us to instantly see the
changes we make to the theme data.
The theme state tabs and constant tabs allow us to pass between the various editing modes for the
theme and also add theme constants.
We discussed styles before, you can pick the right style mode through the tabs.
125
Figure 75. You can use these tabs to add the various types of styles and theme constants
The most important section is the style section. It allows us to add/edit/remove style UIID’s.
Notice the Default Style section, it allows us to customize global defaults for the styles. Use it with
caution as changes here can have wide implications.
Figure 76. The theme selection area allows us to add, edit and delete entries. Notice the default style entry
which is a unique special case
When we add an entry to the style we can just type the desired UIID into the box at the top of the
dialog. We can also pick a UIID from the combo box but that might not include all potential options.
You can use the Component Inspector tool in the simulator to locate a component
and its UIID in a specific Form [[Link]
[Link]]
Figure 77. When pressing the Add/Edit entry we can edit a specific style entry UIID
When we add/edit an entry an important piece of the puzzle is the Derive check box that appears
next to all of the UIID entries. All styles derive from the base style and usually from the native
126
theme defaults, so when this flag is checked the defaults will be used.
When you uncheck that checkbox the fields below it become editable and you can override the
default behavior. To restore the default just recheck that flag.
A common oddity for developers is that when they press Add and don’t derive any
entry nothing is actually added. The entries in the theme are essentially key/value
pairs so when you don’t add anything there are no keys so the entry doesn’t show
up
The Title is surrounded by a TitleArea container that encloses it, above the title you will also see
the StatusBar UIID that prevents the status details from drawing on top of the title text.
The StatusBar UIID is a special case that is only there on iOS. In iOS the application
needs to render the section under the status bar (which isn’t the case for other
OS’s) and the StatusBar UIID was added so developers can ignore that behavior.
A slightly confusing aspects of styles in Codename One is the priorities of backgrounds. When you
define a specific type of background it will override prior definitions, this even applies to
inheritance.
E.g. if the theme defined a border for the Button UIID (a very common case) if you will try to define
the background image or the background color of Button those will be ignored!
The solution is to derive the border and select the Empty border type
1. Border - if the component has a border it can override everything. Image borders always
override all background settings you might have.
127
4.3.2. The Background Behavior and Image
Lets start in the first page of the style entry, we’ll customize the background behavior for the Title
UIID and demonstrate/explain some of the behaviors.
The pictures below demonstrate the different types of background image behaviors.
Figure 79. IMAGE_SCALED scales the image without preserving aspect ratio to fit the exact size of the
component
Figure 80. IMAGE_SCALED_FILL scales the image while preserving aspect ratio so it fills the entire space of
the component
Aspect ratio is the ratio between the width and the height of the image. E.g. if the
image is 100x50 pixels and we want the width to be 200 pixels preserving the
aspect ratio will require the height to also double to 200x100.
128
We highly recommend preserving the aspect ratio to keep images more "natural".
Figure 81. IMAGE_SCALED_FIT scales the image while preserving aspect ratio so it fits within the
component
Figure 82. IMAGE_TILE_BOTH tiles the image on both axis of the component
129
Figure 83. IMAGE_TILE_VERTICAL_ALIGN_LEFT tiles the image on the left side of the component
Figure 84. IMAGE_TILE_VERTICAL_ALIGN_CENTER tiles the image in the middle of the component
130
Figure 85. IMAGE_TILE_VERTICAL_ALIGN_RIGHT tiles the image on the right side of the component
Figure 86. IMAGE_TILE_HORIZONTAL_ALIGN_TOP tiles the image on the top of the component
131
Figure 87. IMAGE_TILE_HORIZONTAL_ALIGN_CENTER tiles the image in the middle of the component
Figure 88. IMAGE_TILE_HORIZONTAL_ALIGN_BOTTOM tiles the image to the bottom of the component
132
Figure 89. IMAGE_ALIGNED_TOP places the image centered at the top part of the component
Figure 90. IMAGE_ALIGNED_BOTTOM places the image centered at the bottom part of the component
133
Figure 91. IMAGE_ALIGNED_LEFT places the image centered at the left part of the component
Figure 92. IMAGE_ALIGNED_RIGHT places the image centered at the right part of the component
134
Figure 93. IMAGE_ALIGNED_TOP_LEFT places the image at the top left corner
Figure 94. IMAGE_ALIGNED_TOP_RIGHT places the image at the top right corner
135
Figure 95. IMAGE_ALIGNED_BOTTOM_LEFT places the image at the bottom left corner
Figure 96. IMAGE_ALIGNED_BOTTOM_RIGHT places the image at the bottom right corner
136
Figure 97. IMAGE_ALIGNED_CENTER places the image in the middle of the component
The color settings are much simpler than the background behavior. As explained above the priority
for color is at the bottom so if you have a border, image or gradient defined the background color
settings will be ignored.
• Foreground color is the RRGGBB color that sets the style foreground color normally used to
draw the text of the component. You can use the color picker button on the side to pick a color
• Background same as foreground only determines the background color of the component
137
Setting the background will have no effect unless transparency is higher than 0. If
you don’t explicitly define this it might have a different value based on the native
theme
4.3.4. Alignment
Not all component types support alignment and even when they do they don’t support it for all
elements. E.g. a Label [[Link] and its
subclasses support alignment but will only apply it to the text and not the icon.
Aligning text components to anything other than the default alignment might be a
problem if they are editable. The native editing capabilities might collide with the
alignment behavior.
Bidi/RtL layout reverses the alignment value so left becomes right and visa versa
Padding and margin are concepts derived from the CSS box model. They are slightly different in
Codename One, where the border spacing is part of the padding, but other than that they are pretty
similar:
138
Figure 100. Padding and Margin/Box Model
In the diagram, we can see the component represented in yellow occupying its preferred size. The
padding portion in gray effectively increases the components size. The margin is the space between
components, it allows us to keep whitespace between multiple components. Margin is represented
in red in the diagram.
The theme allows us to customize the padding/margin, and specify them for all 4 sides of a
component. They can be specified in pixels, millimeters/dips, or screen percentage:
We recommend using millimeters for all spacing to make it look good for all device
densities. Percentages make sense only in very extreme cases
4.3.6. Borders
Borders are a big subject in their own right, the UI for their creation is also a bit confusing:
139
Figure 102. Border entry in the theme
A common border type is the 9-piece image border, to facilitate that border type we have a special
Image Border Wizard .
A 9 piece image border is a common convention in UI theming that divides a border into 9 pieces 4
representing corners, 4 representing the sides and one representing the middle.
Android uses a common variation on the 9-piece border: 9-patch. The main
difference between the 9-piece border and 9-patch is that 9-piece borders tile the
sides/center whereas 9-patch scales them
9-piece image borders work better than background images for many use cases where the
background needs to "grow/shrink" extensively and might need to change aspect ratio.
They don’t work well in cases where the image is asymmetric on both axis. E.g. a radial gradient
image. 9-piece images in general don’t work very well with complex gradients.
The image border wizard simplifies the process of generating a 9-piece image border using a 3 stage
process.
140
Figure 103. Stage 1: create or pick an image from an existing PNG file that we will convert to a 9-piece
image
For your convenience you can create a rudimentary image with the create image stage but for a
professional looking application you would usually want to use a design by a professional designer.
141
Figure 104. Stage 2: Cutting the image and adapting it to the DPI’s
The second stage is probably the hardest and most important one in this wizard!
You can change the values of the top/bottom/left/right spinners to move the position of the guide
lines that indicate the various 9 pieces. The image shows the correct cut for this image type with
special attention to the following:
• The left/right position is high enough to fit in the rounded corners in their entirety. Notice that
we didn’t just leave 1 pixel as that performs badly, we want to leave as much space as possible!
• The top and bottom lines have exactly one pixel between them. This is to avoid breaking the
gradient. E.g. if we set the lines further apart we will end up with this:
Figure 105. This is why it’s important to keep the lines close when a gradient is involved, notice the tiling
effect…
Figure 106. When the lines are close together the gradient effect grows more effectively
• The elements on the right hand side include the Generate Multi Image options. Here you can
indicate the density of the source image you are using (e.g. if its for iPhone 5 class device pick
Very High). You can then select in the checkboxes below the densities that should be generated
automatically for you. This allows fine detail on the border to be maintained in the various
high/low resolution devices.
We go into a lot of details about multi images in the advanced theming section.
142
Figure 107. Stage 3: Styles to which the border is applied
The last page indicates the styles to which the wizard will apply the border. Under normal usage
you don’t really need to touch this as its properly filled out.
You can define the same border for multiple UIIDs from here though.
A common oddity when using the image borders is the fact that even when padding is
removed the component might take a larger size than the height of the text within it.
The reason for that is the border. Because of the way borders are implemented they can’t be
drawn to be smaller than the sum of their corners. E.g. the minimum height of a border
would be the height of the bottom corner + the height of the top corner. The minimum width
would be the width of the left + right corners.
This is coded into the common preferred size methods in Codename One and components
generally don’t shrink below the size of the image border even if padding is 0.
Normally we can just use the 9-piece border wizard but we can also customize the border by
pressing the "…" button on the border section in the theme.
143
Figure 108. Press this to customize borders
The UI for the 9-piece border we created above looks like this.
You can pick the image represented by every section in the border from the combo boxes. They are
organized in the same way the border is with the 9-pieces placed in the same position they would
occupy when the border is rendered.
Notice that the other elements in the UI are disabled when the image border type
144
is selected.
3 Image Mode
The 9-piece border has a (rarely used) special case: 3 image mode. In this mode a developer
can specify the top left corner, the top image and the center image to produce a 9 piece
border. The corner and top piece are then rotated dynamically to produce a standard 9-piece
border on the device.
This is useful for reducing application code size but isn’t used often as it requires a more
symetric UI.
Don’t confuse the 3-image mode for the 9-piece border with the
horizontal/vertical image border below
The 9-piece border is the workhorse of borders in Codename One, however there are some edge
cases of UI elements that should grow on one axis and not on another. A perfect example of this is
the iOS 6 style back button. If we tried to cut it into a 9-piece border the arrow effect would be
broken.
Figure 110. Horizontal image border is commonly used for UI’s that can’t grow vertically e.g. the iOS 6 style
back button
The horizontal and vertical image borders accept 3 images of their respective AXIS and build the
145
border by placing one image on each side and tiling the center image between them. E.g. A
horizontal border will never grow vertically.
[1]
In RTL/Bidi modes the borders flip automatically to show the reverse direction.
An iOS style back button will point to the right in such languages.
Empty borders enforce the removal of a border. This is important if you would like to block a base
style from having a border.
E.g. Buttons have borders by default. If you would like to create a Button
[[Link] that is strictly of solid color you
could just define the border to be empty and then use the solid color as you see fit.
There is a null border which is often confused with an empty border. You should
use empty border and not null border
Circles and completely round border sides are problematic for multi-resolutions. You need to draw
them dynamically and can’t use image borders which can’t be tiled/cut to fit round designs (due to
physical constraints of the round shape).
Round Border is a bit confusing since we already support a rounded border type. The rounded
border type is a rectangle with rounded corners whereas the round border has completely round
sides or appears as a circle.
To make matters worse the round border has a ridiculous number of features/configurations that
would have made the already cluttered UI darn near impossible to navigate. To simplify this we
split the UI into 3 tabs for standard borders, image borders and round border.
146
Figure 111. Round Border
The RoundRectBorder was developed based on the RoundBorder and has similar features. It produces a
rounded rectangle UI.
Don’t confuse the Rounded Rectangle border with the deprecated Rounded border…
We generally recommend avoiding bevel/etched border types as they aren’t as efficient and look a
bit dated in todays applications. We cover them here mostly for completeness.
147
Figure 113. Bevel border
4.3.13. Derive
Derive allows us to inherit the behavior of a UIID and extend it with some customization.
E.g. Lets say we created a component that’s supposed to look like a title, we could do something like:
[Link]("Title");
But title might sometimes be aligned to the left (based on theme) and we always want our
component to be center aligned. However, we don’t want that to affect the actual titles in the app…
To solve this we can define a MyTitle UIID and derive the Title UIID. Then just customize that one
attribute.
Style inheritance is a problematic topic in every tool that supports such behavior. Codename
148
One styles start from a global default then have a system default applied and on top of that
have the native OS default applied to them.
At that point a developer can define the style after all of the user settings are in place.
Normally this works reasonably well, but there are some edge cases where inheriting a style
can fail.
When you override an existing style such as Button and choose to derive from Button in a
different selection mode or even a different component altogether such as Label you might
trigger a recursion effect where a theme setting in the base theme depends on something in a
base triggering an infinite loop.
To avoid this always inherit only from UIID’s you defined e.g. MyButton.
4.3.14. Fonts
• System fonts — these are very simplistic builtin fonts. They work on all platforms and come in
one of 3 sizes. However, they are ubiquitous and work in every platform in all languages.
• TTF files — you can just place a TTF file in the src directory of the project and it will appear in
the True Type combo box.
• Native fonts — these aren’t supported on all platforms but generally they allow you to use a set
of platform native good looking fonts. E.g. on Android the devices Roboto font will be used and
on iOS San Francisco or Helvetica Neue will be used. This is the recommended font type we
suggest for most use cases!
If you use a TTF file MAKE SURE not to delete the file when there MIGHT be a
reference to it. This can cause hard to track down issues!
Notice that a TTF file must have the ".ttf" extension, otherwise the build server
won’t be able to recognize the file as a font and set it up accordingly (devices need
fonts to be defined in very specific ways). Once you do that, you can use the font
from code or from the theme
149
Figure 116. Font Theme Entry
System fonts are always defined even if you use a TTF or native font. If the native
font/TTF is unavailable in a specific platform the system font will be used instead.
You can size native/TTF fonts either via pixels, millimeters or based on the size of the equivalent
system fonts:
1. System font size - the truetype font will have the same size as a small, medium or large system
font. This allows the developer to size the font based on the device DPI
2. Millimeter size - allows sizing the font in a more DPI aware size
3. Pixels - useful for some unique cases, but highly problematic in multi-DPI scenarios
You should notice that font sizing is very inconsistent between platforms we
recommend using millimeters for sizing
if([Link]()) {
Font myFont = [Link](fontName, fontFileName);
myFont = [Link](sizeInPixels, Font.STYLE_PLAIN);
// do something with the font
}
Notice that, in code, only pixel sizes are supported, so it’s up to you to decide how to convert that.
We recommend using millimeters with the convertToPixels method. You also need to derive the
font with the proper size, unless you want a 0 sized font which isn’t very useful.
The font name is the difficult bit, iOS requires the name of the font in order to load the font. This
font name doesn’t always correlate to the file name making this task rather "tricky". The actual font
name is sometimes viewable within a font viewer. It isn’t always intuitive, so be sure to test that on
the device to make sure you got it right.
150
due to copyright restrictions we cannot distribute Helvetica and thus can’t
simulate it. In the simulator you will see Roboto and not the device font unless you
are running on a Mac
The code below demonstrates all the major fonts available in Codename One with the handlee ttf
file posing as a standin for arbitrary TTF:
String[] nativeFontTypes = {
"native:MainThin", "native:MainLight",
"native:MainRegular", "native:MainBold",
"native:MainBlack", "native:ItalicThin",
"native:ItalicLight", "native:ItalicRegular",
"native:ItalicBold", "native:ItalicBlack"};
for(String s : nativeFontTypes) {
Font tt = [Link](s, s).derive(fontSize, Font.STYLE_PLAIN);
151
[Link](createForFont(tt, s));
}
add(createForFont(smallPlainMonospaceFont, "smallPlainMonospaceFont")).
add(createForFont(mediumPlainMonospaceFont, "mediumPlainMonospaceFont")).
add(createForFont(largePlainMonospaceFont, "largePlainMonospaceFont")).
add(createForFont(smallBoldMonospaceFont, "smallBoldMonospaceFont")).
add(createForFont(mediumBoldMonospaceFont, "mediumBoldMonospaceFont")).
add(createForFont(largeBoldMonospaceFont, "largeBoldMonospaceFont")).
add(createForFont(smallItalicMonospaceFont, "smallItalicMonospaceFont")).
add(createForFont(mediumItalicMonospaceFont, "mediumItalicMonospaceFont")).
add(createForFont(largeItalicMonospaceFont, "largeItalicMonospaceFont")).
add(createForFont(smallPlainProportionalFont, "smallPlainProportionalFont")).
add(createForFont(mediumPlainProportionalFont, "mediumPlainProportionalFont")).
add(createForFont(largePlainProportionalFont, "largePlainProportionalFont")).
add(createForFont(smallBoldProportionalFont, "smallBoldProportionalFont")).
add(createForFont(mediumBoldProportionalFont, "mediumBoldProportionalFont")).
add(createForFont(largeBoldProportionalFont, "largeBoldProportionalFont")).
add(createForFont(smallItalicProportionalFont, "smallItalicProportionalFont")).
add(createForFont(mediumItalicProportionalFont, "mediumItalicProportionalFont")).
add(createForFont(largeItalicProportionalFont, "largeItalicProportionalFont"));
[Link]();
}
Figure 117. The fonts running on the ipad simulator on a Mac, notice that this will look different on a PC
152
Figure 118. The same demo running on a OnePlus One device with Android 5.1
Font Effects
• Underline
• Strike thru
• 3d text raised/lowered
• 3d shadow north
The "3d" effects effectively just draw the text twice, with a sligh offest and two different colors to
create a "3d" feel.
[1] Languages that are written from right to left such as Hebrew, Arabic etc.
153
154
Chapter 5. Advanced Theming
Before we go into CSS there are a few advanced theme concepts. Notice this still applies to CSS as
features such as theme constants are used there as well…
One of the biggest advantages with UIID’s is the ability to change the UIID of a component. E.g. to
create a multiline label, one can use something like:
UIID’s can be customized via the GUI builder and allow for powerful customization of individual
components.
The class name of the component is commonly the same as the UIID, but they are
in essence separate entities
• You want the ability to customize your theme for a specific use case, e.g. let a user select larger
fonts
This is actually pretty easy to do and doesn’t require re-doing the entire theme. You can do
something very similar to the cascading effect of CSS where a theme is applied "on top" of another
theme. To do that just add a new theme using the Add Theme button.
In the new theme define the changes e.g. if you just want a larger default font define only that
property for all the relevant UIID’s and ignore all other properties!
For a non-gui builder app the theme loading looks like this by default:
155
Resources theme = [Link]("/theme");
This assumes the name of your main theme is "Theme" (not the layer theme you
just added).
The original code relies on the theme being in the 0 position in the theme name array which might
not be the case!
[Link]().addThemeProps([Link]("NameOfLayerTheme"));
The addThemeProps call will layer the secondary theme on top of the primary "Theme" and keep the
original UIID’s defined in the "Theme" intact.
If you apply theme changes to a running application you can use Form’s `refreshTheme() to update
the UI instantly and provide visual feedback for the theme changes.
Codename One allows you to override a resource for a specific platform when doing this you can
redefine a resource differently for that specific platform and also add platform specific resources.
Overriden resources take precedence over embedded resources thus allowing us to change the look
or even behavior (when overriding a GUI builder element) for a specific platform/OS.
156
Overriding the theme is dangerous as a theme has external dependencies (e.g.
image borders). The solution is to use theme layering and override the layer!
Figure 120. Override for platform, allows us to override the checked resources and replace them with
another resource
You can then click the green checkbox to define that this resource is specific to this platform. All
resources added when the platform is selected will only apply to the selected platform. If you
change your mind and are no longer interested in a particular override just delete it in the override
mode and it will no longer be overridden.
The combo box in the designer for adding a theme constant is editable, you can
just type in any value you want!
157
• getThemeConstant
• isThemeConstant
• getThemeImageConstant
Internally, Codename One has several built in constants and the list is constantly growing. As we
add features to Codename One, we try to keep this list up to date but the very nature of theme
constants is "adhoc" and some might not make it here.
Constant Description/Argument
158
Constant Description/Argument
159
Constant Description/Argument
160
Constant Description/Argument
161
Constant Description/Argument
162
Constant Description/Argument
menuButtonBottomBool When set to true this flag aligns the menu button
to the bottom portion of the title. Defaults to
false
menuButtonTopBool When set to true this flag aligns the menu button
to the top portion of the title. Defaults to false
163
Constant Description/Argument
164
Constant Description/Argument
165
Constant Description/Argument
166
Constant Description/Argument
167
Constant Description/Argument
168
Constant Description/Argument
Once a theme constant is set by a theme, it isn’t removed on a refresh when replacing the
theme.
E.g. if one would set the comboImage constant to a specific value in theme A and then switch to
theme B, that doesn’t define the comboImage, the original theme A comboImage might remain!
The reason for this is simple: when extracting the constant values, components keep the
values in cache locally and just don’t track the change in value. Furthermore, since the
components allow manually setting values, it’s impractical for them to track whether a value
was set by a constant or explicitly by the user.
The solution for this is to either manually reset undesired values before replacing a theme
(e.g. for the case, above by calling the default look and feel method for setting the combo
image with a null value), or defining a constant value to replace the existing value.
169
This effectively means your theme "derives" the style of the native theme first, similar to the
cascading effect of CSS. Internally this is exactly what the theme layering section covered.
By avoiding this flag you can create themes that look EXACTLY the same on all platforms.
If you avoid the native theming you might be on your own. A few small device
oddities such as the iOS status bar are abstracted by native theming. Without it you
will need to do everything from scratch
You can simulate different OS platforms by using the native theme menu option
Developers can pick the platform of their liking and see how the theme will appear in that
particular platform by selecting it and having the preview update on the fly.
return "[Link]=ffffff";
We can replace the theme dynamically in runtime and refresh the styles assigned to the various
components using the refreshTheme() [[Link]
[Link]#refreshTheme--] method.
170
[UIID.][type#]attribute
The UIID, corresponds to the component’s UIID e.g. Button, CheckBox [[Link]
javadoc/com/codename1/ui/[Link]] etc. It is optional and may be omitted to address the global
default style.
The type is omitted for the default unselected type, and may be one of sel (selected type), dis
(disabled type) or press (pressed type). The attribute should be one of:
• derive - the value for this attribute should be a string representing the base component.
• bgColor - represents the background color for the component, if applicable, in a web hex string
format RRGGBB e.g. ff0000 for red.
• border - an instance of the border class, used to display the border for the component.
• transparency - a String containing a number between 0-255 representing the alpha value for
the background. This only applies to the bgColor.
• margin - the margin of the component as a String containing 4 comma separated numbers for
top,bottom,left,right.
• padding - the padding of the component, it has an identical format to the margin attribute.
• backgroundType - a Byte object containing one of the constants for the background type defined in
Style [[Link] under BACKGROUND_*.
• backgroundGradient - contains an Object array containing 2 integers for the colors of the
gradient. If the gradient is radial it contains 3 floating points defining the x, y & size of the
gradient.
So to set the foreground color of a selected button to red, a theme will define a property like:
[Link]#fgColor=ff0000
This information is mostly useful for understanding how things work within Codename One, but it
can also be useful in runtime.
E.g. to increase the size of all fonts in the application, we can do something like:
171
[Link]().getCurrent().refreshTheme();
When working with a theme, we often use images for borders or backgrounds. We also use images
within the GUI for various purposes and most such images will be extracted from the resource file.
Adding a standard JPEG/PNG image to the resource file is straight forward, and the resulting image
can be viewed within the images section. However, due to the wide difference between device
types, an image that would be appropriate in size for an iPhone 3gs would not be appropriate in
size for a Nexus device or an iPhone 4 (but perhaps, surprisingly, it will be just right for iPad 1 and
iPad 2).
The density of the devices varies significantly and Codename One tries to simplify the process by
unifying everything into one set of values to indicate density. For simplicity’s sake, density is
sometimes expressed in terms of pixels, however it is mapped internally to actual screen
measurements where possible.
A multi-image is an image that has multiple varieties for different densities, and thus looks sharp in
all the densities. Since scaling on the device can’t interpolate the data (due to performance
considerations), significant scaling on the device becomes impractical. However, a multi-image will
just provide the "right" resolution image for the given device type.
From the programming perspective this is mostly seamless, a developer just accesses one image
and has no ability to access the images in the different resolutions. Within the designer, however,
we can explicitly define images for multiple resolutions and perform high quality scaling so the
"right" image is available.
We can use two basic methods to add a multi-image: quick add and standard add.
Both methods rely on understanding the source resolution of the image, e.g. if you have an icon that
you expect to be 128x128 pixels on iPhone 4, 102x102 on nexus one and 64x64 on iPhone 3gs. You
can provide the source image as the 128 pixel image and just perform a quick add option while
picking the Very High density option.
This will indicate to the algorithm that your source image is designed for the "very high" density
and it will scale for the rest of the densities accordingly.
This relies on the common use case of asking your designer to design for one high
end device (e.g. iPhone X) then you can take the resources and add them as "HD"
resources. They will automatically adapt to the lower resolutions
Alternatively, you can use the standard add multi-image dialog and set it like this:
172
Notice that we selected the square image option, essentially eliminating the height option. Setting
values to 0 prevents the system from generating a multi-image entry for that resolution, which will
mean a device in that category will fall on the closest alternative.
The percentage value will change the entire column, and it means the percentage of the screen. E.g.
We know the icon is 128 for the very high resolution, we can just move the percentage until we
reach something close to 128 in the "Very High" row and the other rows will represent a size that
should be pretty close in terms of physical size to the 128 figure.
At runtime, you can always find the host device’s approximate pixel density using the
[Link]() method. This will return one of:
Table 4. Densities
173
margins, font size, and border thickness because the results will be inconsistent on different
densities. Instead, you should use millimeters for all non-zero units of measurement.
As we now understand the complexities of DPI it should be clear why this is important.
Sometimes millimeters don’t give you enough precision for what you want to do. Currently the
designer only allows you to specify integer values for most units. However, you can achieve more
precise results when working directly in Java. The [Link]() method will allow you
to convert millimeters (or DIPS) to pixels. It also only takes an integer input, but you can use it to
obtain a multiplier that you can then use to convert any millimeter value you want into pixels.
E.g.
And now you can set the padding on an element to 1.5mm. E.g.
[Link]().setPaddingUnit(Style.UNIT_TYPE_PIXELS);
int pixels = (int) (1.5 * pixelsPerMM);
[Link]().setPadding(pixels, pixels, pixels, pixels);
174
A side menu is a crucial piece of an elegant application. We’ll explain how one creates a simple side
menu that’s elegant, portable and easy to build. This is a good "starting point" side menu from
which you can build more elaborate designs.
To get this result we will start from a native theme and a bare bones application to keep things
simple.
Toolbar tb = [Link]();
Image icon = [Link]("[Link]"); ①
Container topBar = [Link](new Label(icon));
[Link]([Link], new Label("Cool App Tagline...", "SidemenuTagline")); ②
[Link]("SideCommand");
[Link](topBar);
① This is the icon which was used in lieu of a logo it appears in the top right of the side menu
② This is the top bar containing the tagline and the icon it’s styled as if it’s a command but you can
put anything here e.g. an image etc.
③ The commands are added as usual to the side menu with no styling or functionality, the entire
look is determined by the theme
Figure 123. Open the side menu so we will get the right values in the combo box on add
Now when we press Add the side menu entries will appear in the combo box (you can type them
but this is more convenient). We’ll start with the SideNavigationPanel style:
175
Figure 124. The SideNavigationPanel has an opaque white background
The SideCommand style is a bit more elaborate, we start with a white foreground and an opaque
bluish/purple color:
Figure 125. The SideCommand has a white foreground and opaque bluish background
We’ll set padding to 3 millimeters which gives everything a good feel and spacing. This is important
for finger touch sensitivity.
Figure 126. Padding is 3mm so it will feel spacious and touch friendly
We’ll set margin to 0 except for the bottom one pixel which will leave a nice white line by showing
off the background. This means the commands will have a space between them and the white style
we gave to the SideNavigationPanel will appear thru that space.
Figure 127. Margin is 0 except for a thin line below each command
176
Setting the border to empty is crucial!
The iOS version of the side command inherits a border style so we must "remove" it by defining a
different border in this case an empty border. Since borders take precedence over color this would
have prevented the color changes we made from appearing.
Next we need to pick a good looking font and make sure it’s large enough. We use millimeters size it
correctly for all OS’s and override the derived text decoration which has a value in the iOS native
theme so it can impact the final look.
Figure 129. Pick a good looking font for the side command
Next we need to move to the selected tab and add a new side command entry that derives from the
unselected version. We’ll pick a new color that’s slightly deeper and will make the selected style
appear selected. We’ll also copy and paste this selected style to the pressed style.
The SidemenuTagline is just a SideCommand style that was slightly adapted. We’ll remove the
padding and margin because the whole section is wrapped in a side command and we don’t want
double padding. We’ll leave 1mm padding at the top for a bit of spacing from the logo.
177
Figure 132. Padding of the SidemenuTagline
We’ll also update the font to a smaller size and italic styling so it will feel like a tagline.
Figure 133. Font for the SideMenuTagline is slightly smaller and italic
The last change for the theme is for the StatusBarSideMenu UIID which is a spacing on the top of
the sidemenu. This spacing is there for iOS devices which render the clock/battery/reception
symbols on top of the app. We’ll set the padding to 0.
Figure 134. StatusBarSideMenu padding for the top of the side menu
Finally, we’ll add the icon image (or a logo if you have it) into the theme as a multi image so we can
use it within the side menu as a good looking logo. A relatively large icon image works as a 2HD
multi-image but you can use many strategies to get a fitting image for this spot.
Rounded images work well here, you can round images dynamically using
masking
These steps produce the UI above as a side menu, they might seem like a long set of steps but each
step is pretty simple as you walk thru each one. This does show off the versatility and power of
Codename One as a change to one step can create a radically different UI design.
PSD is the Adobe Photoshop file format, it’s the most common format for UI
designs in the industry
178
For this tutorial we adapt a very slick looking sign-up form found online and convert it to a
Codename One component that can be used inside an application.
1. Find the PSD design we want to use: this PSD file [[Link]
created by Adrian Chiran [[Link] (we mirrored it here
[[Link] in case it goes offline):
2. Re-create the general structure and layout of the design in a Codename One Form using nested
components and layout managers. Here is a break-down of how we structured the component
hierarchy in the Form:
3. Extract the images we needed using Photoshop - this process is often referred to as "cutting"
4. Extract the fonts, colors, and styles we needed to reproduce the design in Codename One
5. Import images into the Codename one project, and define theme styles so that our components
match the look of the original design
Here is a screenshot of the resulting component running inside the Codename One simulator:
179
Figure 137. Resulting app in the Codename One simulator
You might be missing fonts in your system so you can either install them or ignore
that. Keep in mind that some fonts might not be redistributable with your
application
In this PSD we want only one of the screen designs so initially we want to remove everything that
isn’t related so we can get our bearings more effectively:
• In the toolbar for the tool (top bar area) check the Auto Select mode
• Select the Layer Mode for auto selection (in some cases group would actually be better so feel
free to experiment)
You should end up with something like this where a layer is selected in the layers window:
180
Figure 138. Selecting a layer from the region you are interested in
Scroll up the hierarchy a bit and uncheck/recheck the eye icon on the left until you locate the right
element layer.
Figure 139. Selecting a layer from the region you are interested in
The right click menu will present different options when you click different areas
of the layer, clicking on the left area of the layer works
181
Figure 140. In the right click menu option select "Convert To Smart Object"
Once the layer hierarchy is a smart object you can just double click it which will open the sub
hierarchy in a new tab and you now only have the pieces of the image you care about.
Figure 141. Double clicking the smart object allows us to edit only the form we need
The first thing we need to do is remove from the image all of the things that we don’t really need.
The status bar area on the top is redundant as if is a part of the phones UI. We can select it using the
select tool and click the eye icon next to the layer to hide it.
Normally we’d want to have the back arrow but thanks to the material design icons that are a part
of Codename One we don’t need that icon so we can hide that too.
We don’t need the "Sign Up" or "Done" strings in the title either but before removing them we’d like
to know the font that is used.
182
To discover that I can click them to select the layer then switch to the text tool:
Figure 142. The text tool allows us to inspect the font used
Then I can double click the text area layer to find out the font in the top of the UI like this:
Notice that I don’t actually need to have the font installed in this case I don’t
(hence the square brackets)
Also notice that the color of the font is accessible in that toolbar, by clicking the color element we
get this dialog which shows the color value to be f73267, this is something we will use later
Figure 144. The color dialog lists the hex color at the bottom, we can paste that directly to the designer tool
We can now hide both text layers so they won’t pose a problem later.
The camera button includes an icon and the button background itself. You can just use that as a
single image and be done with it, but for the purpose of this tutorial I will take the harder route of
separating this into a button background and a foreground image.
When you click on the camera icon you will notice that the camera icon is comprised of two
separate layers: the camera and the "x" symbol above it. We can select both layers using ctrl-click
(command click on the Mac) and convert both to a smart object together using the same method as
before:
183
Figure 145. The camera smart object
Since the image is used as an icon we want it to be completely square which isn’t the situation here!
This is important as a non-square image can trigger misalignment when dealing with icons and the
background. So we need to use the Image → Canvas Size menu and set the values to be the same
(the higher value of the two).
Figure 146. The canvas size dialog for the [Link] file
We can now use File → Export and save the first image resource we will need into a temporary
directory. Make sure to save a PNG file to preserve quality and transparency!
Use File → Export and never use File → Save As . The latter can produce a huge size
difference as it retains image meta-data
For convenience we’ll refer to the file as [Link] when we need it later.
We can follow the exact same procedure with the parent button layer (the white portion) which we
can convert to a smart object and export [Link].
Figure 148. The camera button image set to a gray background so it will be visible
184
Now we can hide both of these elements and proceed to get the background image for the title.
Here the "smart object trick" won’t work… There is an effects layer in place and the smart object
will provide us with the real underlying image instead of the look we actually want. However,
solving this is trivial now that we hid all of the elements on top of the image!
Figure 149. The select tool and the clean image we want to select
Now drag the select tool to select the image don’t cross into the white pixels below the image. You
can use the zoom value and set it to a very high value to get the selection right.
When the selection is right click Edit → Copy Merged . Normally Copy would only copy a specific
layer but in this case we want to copy what we see on the screen!
Now click File → New it should have the Presets set to Clipboard which means the newly created
image is based on what we just copied (that is seriously great UX). Just accept that dialog and paste
(Ctrl-V or Command-V).
You can now save the image, since it’s just a background using JPEG is totally acceptable in this
case. We named it [Link].
185
The last thing we need is the colors used in the UI. We can use the "eye drop" tool in a high zoom
level to discover the colors of various elements e.g. the text color is 4d606f and the separator color is
f5f5f5:
Figure 151. The eye drop tool can be pointed at an area of the image to get the color in that region
While that was verbose it was relatively simple. We’ll create a simple barebones manual
application with the native theme.
The reason for this is to avoid "noise", if we use a more elaborate theme it would
have some existing settings. This can make the tutorial harder to follow
186
Figure 152. Simple bare bones app settings
Once the project is created double click the [Link] file and within the designer select Images →
Quick Add Multi Images . Select the 3 images we created above: [Link], [Link] & camera-
[Link]. Leave the default setting on Very High and press OK.
Then save the resource file so we can use these images from code.
Here is the source code we used to work with the UI above there are comments within the code
explaining some of the logic:
if (current != null) {
[Link]();
return;
}
// The toolbar uses the layered mode so it resides on top of the background image, the theme makes
// it transparent so we will see the image below it, we use border layout to place the background image on
// top and the "Get started" button in the south
Form psdTutorial = new Form("Signup", new BorderLayout());
Toolbar tb = new Toolbar(true);
[Link](tb);
187
// we create 4mm material arrow images for the back button and the Get started button
Style iconStyle = [Link]().getComponentStyle("Title");
FontImage leftArrow = [Link](FontImage.MATERIAL_ARROW_BACK, iconStyle, 4);
FontImage rightArrow = [Link](FontImage.MATERIAL_ARROW_FORWARD, iconStyle, 4);
// we place the back and done commands in the toolbar, we need to change UIID of the "Done" command
// so we can color it in Red
[Link]("", leftArrow, (e) -> Log.p("Back pressed"));
Command doneCommand = [Link]("Done", null, (e) -> Log.p("Done pressed"));
[Link](doneCommand).setUIID("RedCommand");
// The camera button is comprised of 3 pieces. A label containing the image and the transparent button
// with the camera icon on top. This is all wrapped in the title container where the title background image
// is placed using the theme. We chose to use a Label rather than a background using the cameraLayer so
// the label will preserve the original size of the image without scaling it and take up the space it needs
Button cameraButton = new Button([Link]("[Link]"));
Container cameraLayer = [Link](
new Label([Link]("[Link]")),
cameraButton);
[Link]("CameraButton");
Container titleContainer = [Link](
new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER),
cameraLayer, [Link]);
[Link]("TitleContainer");
// The phone and full name have vertical separators, we use two table layouts to arrange them correctly
// so the vertical separator will be in the right place
TableLayout fullNameLayout = new TableLayout(1, 3);
Container fullName = new Container(fullNameLayout);
[Link]([Link]().widthPercentage(49), firstName).
add([Link]().widthPercentage(1), createSeparator()).
add([Link]().widthPercentage(50), lastName);
Container fullPhone = [Link](3, phonePrefix, createSeparator(), phone);
// The button in the south portion needs the arrow icon to be on the right side so we place the text on the left
Button southButton = new Button("Get started", rightArrow);
[Link]([Link]);
[Link]("SouthButton");
// we add the components and the separators the center portion contains all of the elements in a box
// Y container which we allow to scroll. BorderLayout Containers implicitly disable scrolling
Container by = [Link](
fullName,
createSeparator(),
email,
createSeparator(),
password,
createSeparator(),
fullPhone,
createSeparator()
);
[Link](true);
[Link]([Link], titleContainer).
add([Link], southButton).
188
add([Link], by);
[Link]();
So the code above is most of the work but we still need to put everything together using the theme.
This is what we have so far:
Figure 153. Before applying the changes to the theme this is what we have
Figure 154. This is what we are aiming at with no additional code changes
This looks like a major set of changes but it requires exactly 10 UIID definitions to get to this look!
Open the designer and select the theme. Press the Add button and type in TitleContainer. Uncheck
derive for the background and select IMAGE_SCALED_FILL for the Type and the [Link]
image.
• Left - 3 millimeter
• Right - 3 millimeter
• Top - 8 millimeter
• Bottom - 2 millimeter
This will allow enough space for the title. Define margin as 0 on all sides. Then press OK.
Add the "Title" UIID. In the Color tab define the foreground as ffffff define transparency as 0 (fully
189
transparent so we will see the TitleContainer). Define padding as 1 millimeter on all sides and
margin as 0 on all sides.
In the Font tab select the True Type as native:MainThin. Select the True Type Size as millimeters
and set the value to 3.5.
Copy the Title UIID and paste it, change the name to "TitleCommand" and press OK to save the
changes.
Copy the Title UIID again and paste it, change the name to "RedCommand". In the Color tab set the
foreground color to f73267. In the Font tab set the True Type to native:MainLight and set the size to
3. Press OK to save the changes.
Add the "TitleArea" UIID. In the Color tab define transparency as 0 (fully transparent so we will see
the TitleContainer). Define padding and margin as 0 on all sides.
In the Border tab press the … button and select [Empty]. Press OK to save the changes.
Add the "TextField" UIID. In the Color tab define transparency as 255 (fully opaque) and the
background as ffffff (white). Define padding as 2 millimeter on all sides and margin as 0 on all
sides.
In the Border tab press the … button and select [Empty]. In the Font tab set the True Type to
native:MainLight and set the size to 2. Press OK to save the changes.
Copy the TextField UIID again and paste it, change the name to "TextHint". In the Color tab set the
foreground color to 4d606f. Press OK to save the changes.
Add the "SouthButton" UIID. In the Color tab define transparency as 255 (fully opaque) and the
background as f73267 (red) and the foreground as ffffff (white). Define Alignment as Center.
• Left - 1 millimeter
• right - 1 millimeter
• top - 2 millimeters
• bottom - 2 millimeters
Define margin as 0 on all sides. In the Font tab set the True Type to native:MainThin and set the size
to 3. Press OK to save the changes.
Add the "CameraButton" UIID. In the Color tab define transparency as 0 (fully transparent). Define
Alignment as Center.
Define padding as:
• Left - 1 millimeter
• right - 1 millimeter
190
• top - 3 millimeters
• bottom - 1 millimeter
You can now save the theme and the app should look like the final result!
There is one last piece that you would notice if you actually try to run this code. When pressing the
buttons/text fields you would see their look change completely due to the different styles for
focus/press behavior.
You can derive the regular styles from the selected/pressed styles but one of the simplest ways is to
just copy & paste the styles to the pressed/selected tabs. We can copy CameraButton, RedCommand,
SouthButton & TextField to the selected state. Then copy CameraButton, RedCommand & SouthButton to
the pressed state to get the complete app running!
191
192
Chapter 6. CSS
In this chapter we’ll discuss theming with CSS in Codename One.
You can change the CSS values while the simulator is running and the changes will
reflect in the simulator within a few seconds
To enable CSS support in Codename One you need to flip a switch in Codename One Settings.
193
Figure 156. The CSS Option in Codename One Settings Part II
Once enabled your [Link] file will regenerate from a CSS file that resides under the css
directory. Changes you make to the CSS file will instantly update the simulator as you save.
However, there are some limits to this live update so in some cases a simulator restart would be
necessary.
1. All selectors (with some specific exceptions discussed below) are interpreted as UIIDs.
If no class is specified, then the selector targets "all" states of the given component.
The following are a few possible selectors you can include in your stylesheet.
194
3. Button, TextField, Form — Defines styles for the "Button", "TextField", and "Form" UIIDs.
The following example creates a simple button with a border, and text aligned center. By default
the button will have a transparent background, but when it is pressed, it will have a gray
background:
Button {
text-align: center;
border: 1pt solid gray;
background-color: transparent;
}
[Link] {
background-color: gray;
}
The following example defines a custom Button style named "MyButton" that inherits all of the
styles of Button but changes the background color to blue.
MyButton {
cn1-derive: Button;
background-color: blue;
}
The #Device selector allows you to define which device resolutions this CSS file should target. Mutli-
images generated from this style-sheet will only be include variants for device resolutions in the
range (min-resolution, max-resolution) as defined in this section. By default all resolutions are
generated.
#Device {
min-resolution: 120dpi;
max-resolution: 480dpi;
resolution: 480dpi;
}
6.3.2. #Constants
e.g.
195
#Constants {
PopupDialogArrowBool: false;
calTitleDayStyleBool: true;
calTransitionVertBool: false;
calendarLeftImage: "[Link]";
calendarRightImage: "[Link]";
centeredPopupBool: false;
checkBoxCheckDisFocusImage: "Check-Box_Normal.png";
checkBoxCheckedFocusImage: "Check-Box_Press.png";
checkBoxCheckedImage: "Check-Box_Press.png";
checkBoxOppositeSideBool: true;
checkBoxUncheckedFocusImage: "Check-Box_Normal.png";
checkBoxUncheckedImage: "Check-Box_Normal.png";
comboImage: "[Link]";
commandBehavior: "Side";
dialogTransitionIn: "fade";
dialogTransitionOut: "fade";
dlgButtonCommandUIID: "DialogButton";
dlgCommandGridBool: true;
dlgInvisibleButtons: #1a1a1a;
formTransitionIn: "empty";
formTransitionOut: "slide";
includeNativeBool: true;
menuImage: "of_menu.png";
noTextModeBool: true;
onOffIOSModeBool: true;
otherPopupRendererBool: false;
pureTouchBool: true;
radioSelectedFocusImage: "Radio_btn_Press.png";
radioSelectedImage: "Radio_btn_Press.png";
radioUnselectedFocusImage: "Radio_btn_Normal.png";
radioUnselectedImage: "Radio_btn_Normal.png";
sideMenuImage: "[Link]";
switchMaskImage: "[Link]";
switchOffImage: "[Link]";
switchOnImage: "[Link]";
tabPlacementInt: 0;
backIconImage: "[Link]";
articleSourceIconImage: "[Link]";
articleDateIconImage: "[Link]";
articleArrowRightImage: "[Link]";
articleShareIconImage: "[Link]";
articleBookmarkIconImage: "[Link]";
articleTextIconImage: "[Link]";
articleCommentsIconImage: "[Link]";
newsIconImage: "[Link]";
channelsIconImage: "[Link]";
bookmarksIconImage: "[Link]";
overviewIconImage: "[Link]";
calendarIconImage: "[Link]";
196
timelineIconImage: "[Link]";
profileIconImage: "[Link]";
widgetsIconImage: "[Link]";
settingsIconImage: "[Link]";
SubmitIconImage: "[Link]";
SubmitIconDarkImage: "[Link]";
defaultFontSizeInt: 18;
defaultDesktopFontSizeInt: 14;
defaultSourceDPIInt: "0";
In the above example, the constants referring to an image name as a string requires that the image
exists in one of the following locations:
• res/<cssfilename>/<imageName>
• ../res/<cssfilename>/<imageName>
• ../../res/<cssfilename>/<imageName>
or that it has been defined as a background image in some selector in this CSS file.
6.3.3. Default
The Default selector is special in that it will set properties on the theme’s "default" element. The
default element is a special UIID in Codename One from which all other UIIDs in the same theme
are derived. This is a good place to set things like default fonts or background-colors.
• border-radius
• background-color
• background-repeat
• background-image
• border-image
• border-image-slice
197
• font-size (Usage is covered in the following font section)
• color
• text-align
• text-decoration(Usage below)
• opacity
• box-shadow
cn1-background-type
Used to explicitly specify the background-type that should be used for the class.
cn1-9patch
Used to explicitly specify the slices used when generating 9-piece borders. Deprecated - Use
border-image and border-image-slice for 9-piece borders.
cn1-derive
Used to specify that this UIID should derive from an existing UIID.
var(--header-color, blue);
The var() function can only be used inside property values. I.e. You cannot use it in property names
or selectors.
Syntax:
var(<custom-property-name>, <declaration-value>?)
198
The <declaration-value> is the fallback value that will be used if the variable hasn’t been defined in
the CSS file. The fallback value may include commas.
Examples
#Constants {
--main-bg-color: red;
}
MyContainer {
background-color: var(--main-bg-color);
}
#Constants {
--main-bg-color: red;
}
MyContainer {
background-color: var(--main-bg-color, blue);
}
See the MDN docs [[Link] for more details about the CSS
variable spec.
6.7.1. text-decoration
199
cn1-3d-shadow-north 3D text with north shadow. E.g. text-decoration:
cn1-3d-shadow-north;
For other CSS font settings see the Fonts section [Fonts]
6.7.2. border
The algorithm used to determine whether to use a native border or to generate a 9-piece image, is
complex, but the following guidelines may help you if you wish to design borders that can be
rendered natively in CN1:
• Non-pixel units border-width. (Except with the cn1-round-border and cn1-pill-border styles)
• Using a different border-width, border-style, or border-color for different sides of the border
• Using a filter
You can open the resulting theme file in the designer and inspect it to see if an
image was generated
Generating the image triggers slower CSS compilation and a larger binary so we generally
recommend tuning the CSS so it avoids this fallback.
Round Borders
Rounded borders can be achieved in a few different ways. The easiest methods are:
• The cn1-round-border style. This will render a circular round border in the background
natively. I.e. this doesn’t require generation of an image border
• The cn1-pill-border style. This will render a pill-shaped border in the background natively.
This also doesn’t require generation of an image border
• The border-radius property. This will round the corners of the border. If the style can be
achieved using the RoundRectBorder in CodenameOne, then it will use that border. If not, this will
cause the style to be generated as an image border
200
Examples using cn1-round-border
RoundBorder {
border: 1px #3399ff cn1-round-border;
text-align:center;
margin:2mm;
padding:3mm;
}
RoundBorderFilled {
background: cn1-round-border;
background-color: #ccc;
text-align:center;
margin:2mm;
padding:3mm;
}
PillBorder {
border: 1pt #3399ff cn1-pill-border;
text-align:center;
}
PillBorderFilled {
background: cn1-pill-border;
background-color: #3399ff;
color:white;
text-align:center;
}
RoundRectLabel {
background-color: red;
border-radius: 2mm;
}
cn1-pill-border and cn1-round-border don’t support the standard CSS box-shadow property. This is
because the box-shadow property parameters don’t map nicely onto the shadow parameters for the
Codename One RoundBorder class. To get shadows on the cn1-pill-border, you should use one or
more of the following CSS properties:
• cn1-box-shadow-spread — Accepts values in any scalar unit (e.g. px, mm, cm, etc…). This maps
directly to the border’s shadowSpread [[Link]
[Link]#shadowSpread-int-boolean-] property.
• cn1-box-shadow-h — Accepts values in real values or integers (not a scalar unit). This maps
201
directly to the border’s shadowX [
[Link] property.
• cn1-box-shadow-v — Accepts values in real values or integers (not a scalar unit). This maps
directly to the border’s shadowY [[Link]
[Link]#shadowY-float-] property.
• cn1-box-shadow-inset — Set to inset to render an inner shadow instead of the default outer
shadow spread.
Currently using the regular CSS box-shadow in conjunction with border-radius will cause a 9-piece
border to be generated rather than mapping to the RoundRectBorder. If, however, you use the cn1-
box-* properties for the shadow instead, it will use the RoundRectBorder — assuming that no other
styles are specified that trigger an image border to be generated.
Codename One also exposes per-corner elliptical radius controls that map to the RoundBorder’s X/Y
radii. You can set them directly with `cn1-border-top-left-radius-x / cn1-border-top-left-
radius-y (and the equivalent top-right, bottom-left, and bottom-right pairs) to fine tune horizontal
and vertical curvature independently. The CSS parser automatically populates these properties
when you use standard border-radius syntax, including the longhand declarations and the border-
radius: <x-radii> / <y-radii> shorthand.
EllipticalBorder {
border-radius: 2mm 4mm 6mm 1mm / 1mm 3mm 5mm 7mm;
cn1-box-shadow-spread: 1.5mm;
cn1-box-shadow-inset: inset;
}
In the example above, the four horizontal radii (2mm 4mm 6mm 1mm) populate the cn1-border--radius-x
properties clockwise from the top-left corner. The four values after the slash fill the
matching cn1-border--radius-y entries. Setting cn1-box-shadow-inset: inset; converts the shadow
into an inset glow that follows the same elliptical curvature.
6.7.3. background
202
Background Images
Gradients
Both the linear-gradient and radial-gradient CSS functions are fully supported by this library. If
Codename One is capable of rendering the gradient natively then the theme resource file generated
will only include encoded parameters for the gradients. If the gradient is not supported by
Codename One, then the module will fall back to an image background which it generates at
compile-time. It is generally preferable to try to stick to gradients that Codename One supports
natively. This will result in a smaller theme resource file since it doesn’t need to generate any
images for the gradient.
In order for a linear gradient to be natively supported by Codename One, the following properties
must be met:
1. The gradient function has exactly two color stops, and these colors have the same opacity.
2. The gradient is either perfectly horizontal or perfectly vertical. (e.g Direction can be 0deg, 90deg,
180deg, or 270deg.
Examples
MyContainer {
background: linear-gradient(0deg, #ccc, #666);
}
MyContainer {
203
background: linear-gradient(to top, #ccc, #666);
}
MyContainer {
background: linear-gradient(90deg, #ccc, #666);
}
MyContainer {
background: linear-gradient(to left, #ccc, #666);
}
204
Figure 160. Native linear gradient to left
The following are some examples of linear gradients that aren’t supported natively by Codename
One, and will result in a background image to be generated at compile-time:
MyComponent {
background: linear-gradient(45deg, #eaeaea, #666666);
}
The above example is not supported natively because the gradient direction is 45 degrees.
Codename One only supports 0, 90, 180, and 270 degrees natively. Therefore this would result in a
background image being generated at compile-time with the appropriate gradient.
205
MyComponent {
background: linear-gradient(90deg, rgba(255, 0, 0, 0.6), blue);
}
The above linear-gradient is not supported natively because the stop colors have different
transparencies. The first color has an opacity of 0.5, and the second as an opacity of 1.0 (implicitly).
Therefore, this would result in the gradient being generated as an image at compile-time.
The following syntax is supported natively for radial gradients. Other syntaxes are also supported
by the CSS library, but they will use compile-time image generation for the gradients rather than
generating them at runtime.
• <color stop> — Either a color alone, or a color followed by a percentage. 0% indicates that color
begins at center of the circle. 100% indicates that the color begins at the closest edge of the
bounding box. Higher/lower values (>0%) will shift the color further or closer to circle’s center.
If the first color stop is set to a non-zero value, the gradient cannot be rendered natively by
Codename One, and an image of the gradient will instead be generated at compile-time.
More complex gradients are supported by this library, but they will be generated at compile-time.
For more information about the radial-gradient CSS function see its MDN Wiki page
[[Link]
Examples
206
MyContainer {
background: radial-gradient(circle, gray, white);
}
MyContainer {
background: radial-gradient(circle, gray, white 200%);
}
MyContainer {
background: radial-gradient(circle at left, gray, white);
}
207
Figure 165. Radial gradient at left
MyContainer {
background: radial-gradient(circle at right, gray, white);
}
6.7.4. cn1-background-type
It also supports some special Codename One values, which are identifiers with a "cn1-" prefix. The
following special values are available. They map to the standard Codename One values we
discussed in the theming chapter:
• cn1-image-scaled
• cn1-image-scaled-fill
• cn1-image-scaled-fit
• cn1-image-tile-both
• cn1-image-tile-valign-left
• cn1-image-tile-valign-center
208
• cn1-image-tile-valign-right
• cn1-image-tile-halign-top
• cn1-image-tile-halign-center
• cn1-image-tile-halign-bottom
• cn1-image-align-bottom
• cn1-image-align-left
• cn1-image-align-right
• cn1-image-align-center
• cn1-image-align-top-left
• cn1-image-align-top-right
• cn1-image-align-bottom-left
• cn1-image-align-bottom-right
• cn1-image-border
• cn1-none
• cn1-round-border
• cn1-pill-border
6.8. Images
Images are supported as both "inputs" of the stylesheet, and as outputs to the compiled resource
file. "Input" images are usually specified via the background-image property in a selector. "Output"
images are always saved as multi-images inside the resource file.
In order to appropriately size the image, the CSS compiler needs to know what the source density of
the image is. E.g. if an image is 160x160 pixels with a source density of 160dpi (i.e. medium density -
or the same as an iPhone 3G), then the resulting multi-image will be sized at 160x160 for medium
density devices and 320x320 on very high density devices (e.g. iPhone 4S Retina) - which will result
in the same perceived size to the user of 1x1 inch.
However if the image has a source density of 320dpi, then the resulting multi-image would be
80x80 pixels on medium density devices and 160x160 pixels on very high density devices.
Some images have this density information embedded in the image itself so that the CSS processor
will know how to resize the image properly. However, it is usually better to explicitly document
your intentions by including the cn1-source-dpi property as follows:
SomeStyle {
background-image: url(images/[Link]);
cn1-source-dpi: 160;
209
}
cn1-source-dpi values are meant to fall into threshold ranges. Values less than or
equal to 120, are interpreted as low density. 121 - 160 are medium density (iPhone
3GS). 161 - 320, very high density (iPhone 4S). 321 - 480 == HD. 481 and higher ==
2HD. In general, you should try to use images that are one of these DPIs exactly:
160, 320, or 480, then images will be scaled up or down to the other densities
accordingly.
By default all images are imported as multi-images (unless you define the defaultSourceDPIInt
theme constant). If you want to import an image as a "regular" image, you can simply set cn1-
source-dpi to 0. E.g.
SomeStyle {
background-image: url(images/[Link]);
cn1-source-dpi: 0;
}
You can change the default source DPI for the whole stylesheet by adding
defaultSourceDPIInt: 0 to the theme constants. E.g.
#Constants {
}
defaultSourceDPIInt: 0;
If you have already generated images in all of the appropriate sizes for all densities, you can
provide them in the same file structure used by the Codename One XML resource files: The image
path is a directory that contains images named after the density that they are intended to be used
for. The possible names include:
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
210
• [Link]
• [Link]
• [Link]
MyStyle {
background-image: url(images/[Link]);
}
css/
+--- [Link]
+--- images/
+--- [Link]/
+--- [Link]
+--- [Link]
+--- [Link]
... etc...
Multi-image inputs are only supported for local URLs. You cannot use remote (e.g.
[Link] urls with multi-image inputs
Theme constants can be images. The convention is to suffix the constant name with "Image" so that
it will be treated as an image. In addition to the standard url() notation for specifying a constant
image, you can provide a simple string name of the image, and the CSS processor will try to find an
image by that name specified as a background image for one of the styles. If it cannot find one, it
will look inside a special directory named "res" (located in the same directory as the CSS stylesheet),
inside which it will look for a directory named the same as the stylesheet, inside which it will look
for a directory with the specified multi-image. This directory structure is the same as used for
Codename One’s XML resources directory.
radioSelectedFocusImage: "Radio_btn_Press.png";
• [Link]
• [Link]
211
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
It will then create a multi-image from these images and include them in the resource file.
It is quite useful to be able to embed images inside the resource file that is generated from the CSS
stylesheet so that you can access the images using the [Link]() method in your app and
set it as an icon on a button or label. In this case, it is easier to simply create a dummy style that you
don’t intend to use and include multiple images in the background-image property like so:
Images {
background-image: url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]),
url(images/[Link]);
}
212
Then in Java, I might do something like:
Images {
background-image: url([Link]
}
9-Piece image borders can be created using the image-border and image-border-slice properties.
E.g.
NinePiece {
border-image:url('dashbg_landscape.png');
}
In the above example we omitted the border-image-slice property, so it defaults to "40%", which
means that the image is sliced 40% from the top, 40% from the bottom, 40% from the left, and 40%
from the right.
213
If you want more specific "slice" points, you can add the border-image-slice property. E.g.
NinePiece {
border-image:url('dashbg_landscape.png');
border-image-slice:10% 49%; /*vertical horizontal*/
}
NinePiece2 {
border-image:url('dashbg_landscape.png');
border-image-slice:10% 49% 20%; /*top horizontal bottom*/
}
NinePiece3 {
border-image:url('dashbg_landscape.png');
border-image-slice:10% 30% 40% 20%; /*top right bottom left*/
}
NinePiece4 {
border-image:url('dashbg_landscape.png');
border-image-slice:10%; /*all*/
}
Component backgrounds in Codename One are a common source of confusion for newcomers
because there are 3 different properties that can be used to define what a component’s background
looks like, and they have priorities:
1. Background Color - You can specify an RGB color to be used as the background for a component.
2. Background Image - You can specify an image to be used as the background for a component.
Codename One includes settings to define how the image is treated, e.g. scale/fill, tile, etc. If a
background image is specified, it will override the background color setting - unless the image
has transparent regions.
3. Image Border - You can define a 9-piece image border which will effectively cover the entire
background of the component. If an image border is specified, it will override the background
image of the component.
A common scenario that I run into is trying to set the background color of a component and see no
change when I preview my form because the style had an image background defined - which
overrides my background color change.
The potential for confusion is mitigated somewhat, but still exists when using CSS. You can make
your intentions explicit by adding the cn1-background-type property to your style. Possible values
include:
• cn1-image-scaled
• cn1-image-scaled-fill
214
• cn1-image-scaled-fit
• cn1-image-tile-both
• cn1-image-tile-valign-left
• cn1-image-tile-valign-center
• cn1-image-tile-valign-right
• cn1-image-tile-halign-top
• cn1-image-tile-halign-center
• cn1-image-tile-halign-bottom
• cn1-image-align-bottom
• cn1-image-align-left
• cn1-image-align-right
• cn1-image-align-center
• cn1-image-align-top-left
• cn1-image-align-top-right
• cn1-image-align-bottom-left
• cn1-image-align-bottom-right
• cn1-image-border
• cn1-none
• none
MyContainer {
background-image: url([Link]);
cn1-background-type: cn1-image-scaled-fill;
}
6.11. Fonts
This library supports the font [[Link] font-size
[[Link] font-family [[Link]
CSS/font-family], font-style [[Link] font-weight
[[Link] and text-decoration [[Link]
215
en/docs/Web/CSS/text-decoration] properties, as well at the @font-face [
[Link] CSS "at" rule for including TTF/OTF fonts.
6.11.1. font-family
SideCommand {
font-family: "native:MainThin";
}
If you omit the font-family directive altogether, it will use native:MainRegular. The following native
fonts are available:
1. native:MainThin
2. native:MainLight
3. native:MainRegular
4. native:MainBold
5. native:MainBlack
6. native:ItalicThin
7. native:ItalicLight
8. native:ItalicRegular
9. native:ItalicBold
10. native:ItalicBlack
If you want to use a font other than the built-in fonts, you’ll need to define the font using the @font-
face rule. E.g.
@font-face {
font-family: "Montserrat";
src: url(res/[Link]);
}
Then you’ll be able to reference the font using the specified font-family in any CSS element. E.g.
MyLabel {
font-family: "Montserrat";
}
216
The @font-face directive’s src property will accept both local and remote URLs. E.g.
@font-face {
font-family: "MyFont";
src: url([Link]
}
In this case, it will download the [Link] file to the same directory as the CSS file. From then on
it will use that locally downloaded version of the font so that it doesn’t have to make a network
request for each build.
Fonts are automatically copied to the project’s "src" directory when the CSS file is compiled so that
they will be distributed with the app and available at runtime.
Github URLs
Fonts hosted on GitHub are accessible using a special github: protocol to make it easier to reference
such fonts. E.g. the following directive includes the "FontAwesome" font directly from Github
@font-face {
font-family: "FontAwesome";
src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/[Link]);
}
Apparently FontAwesome has removed its public repositories from Github so this
URL no longer works.
6.11.2. font-size
It is best practice to size your fonts using millimetres (rem) (or another "real-world" measurement
unit such as inches (in), centimetres (cm), millimetres (mm). This will allow the font to be sized
appropriate for all display densities. If you specify size in pixels (px), it will treat it the same as if
you sized it in points (pt), where 1pt == 1/72 inches (one seventy-second of an inch).
If you size your font in percentage units (e.g. 150%) it will set the font size relative to the medium
font size of the platform. This is different than the standard behaviour of a web browser, which
would size it relative to the parent element’s font size.
You can use system fonts, true type fonts, and native fonts in your CSS stylesheet. True Type fonts
need to be defined in a @font-face directive before they can be referenced. True-type fonts and
native fonts have the advantage that you can specify their sizes in generic terms (e.g. small, medium,
large) and in more specific units such as millimeters (mm) or pixels (px).
217
Normalizing Default Font Size
When trying to make a design look "good" across multiple platforms it can be difficult to deal
with the differing default font sizes on different platforms. You may spend hours tweaking
your UI to look perfect on iPhone X, only to find out that the fonts are too small when viewed
on an android device. We have now added theme constants to explicitly set the the default
font size in "screen-independent-pixels".
If you add the following to your stylesheet, it will set the default font size to 18 screen-
independent pixels (or 18/160th of an inch), which corresponds to the Android native default
"medium" font size.
#Constants {
defaultFontSizeInt: 18;
}
I have found that a value of 18 here gives optimum results across devices.
On the desktop, you may find that 18 is too big. You can additionally define a default font size
for for tablet and desktop using defaultDesktopFontSizeInt and defaultTabletFontSizeInt
respectively. I have found that a defaultDesktopFontSizeInt gives results that closely match
the Mac OS default font size.
#Constants {
defaultFontSizeInt: 18;
defaultDesktopFontSizeInt: 14;
}
6.11.3. text-decoration
@font-face {
font-family: "Montserrat";
src: url(res/[Link]);
}
@font-face {
218
font-family: "Montserrat-Bold";
src: url(res/[Link]);
}
@font-face {
font-family: "FontAwesome";
src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/[Link]);
}
PlainText0p5mm {
font-size: 0.5mm;
}
PlainText1mm {
font-size: 1mm;
}
PlainText2mm {
font-size: 2mm;
}
PlainText5mm {
font-size: 5mm;
}
PlainText10mm {
font-size: 10mm;
}
PlainText50mm {
font-size: 50mm;
}
PlainTextSmall {
font-size: small;
}
PlainTextMedium {
font-size: medium;
}
PlainTextLarge {
font-size: large;
}
PlainText3pt {
font-size: 3pt;
}
PlainText6pt {
font-size: 6pt;
}
219
PlainText12pt {
font-size: 12pt;
}
PlainText20pt {
font-size: 20pt;
}
PlainText36pt {
font-size: 36pt;
}
BoldText {
font-weight: bold;
}
BoldText1mm {
font-weight: bold;
font-size: 1mm;
}
BoldText2mm {
font-weight: bold;
font-size: 2mm;
}
BoldText3mm {
font-weight: bold;
font-size: 3mm;
}
BoldText5mm {
font-weight: bold;
font-size: 5mm;
}
ItalicText {
font-style: italic;
}
ItalicText3mm {
font-style: italic;
font-size: 3mm;
}
ItalicBoldText {
font-style: italic;
font-weight: bold;
}
PlainTextUnderline {
text-decoration: underline;
}
220
BoldTextUnderline {
text-decoration: underline;
font-weight: bold;
}
ItalicTextUnderline {
text-decoration: underline;
font-style: italic;
}
PlainText3d {
text-decoration: cn1-3d;
color:white;
background-color: #3399ff
}
BoldText3d {
text-decoration: cn1-3d;
font-weight: bold;
color:white;
background-color: #3399ff;
}
ItalicText3d {
text-decoration: cn1-3d;
font-style: italic;
color:white;
background-color: #3399ff;
}
PlainText3dLowered {
text-decoration: cn1-3d-lowered;
color:black;
background-color: #3399ff;
}
BoldText3dLowered {
text-decoration: cn1-3d-lowered;
font-weight: bold;
color:black;
background-color: #3399ff;
}
ItalicText3dLowered {
text-decoration: cn1-3d-lowered;
font-style: italic;
color:black;
background-color: #3399ff;
}
PlainText3dShadow {
text-decoration: cn1-3d-shadow-north;
221
color:white;
background-color: #3399ff;
}
BoldText3dShadow {
text-decoration: cn1-3d-shadow-north;
font-weight: bold;
color:white;
background-color: #3399ff;
}
ItalicText3dShadow {
text-decoration: cn1-3d-shadow-north;
font-style: italic;
color:white;
background-color: #3399ff;
}
MainThin {
font-size: 200%;
background: radial-gradient(circle at top left, yellow, blue 100%);
}
MainRegular0001 {
font-family: "native:MainRegular";
/*background: cn1-pill-border;
background-color: red;*/
color: blue;
border: 1px cn1-pill-border blue;
/*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/
padding: 2mm;
}
[Link] {
font-family: "native:MainRegular";
background: cn1-pill-border blue;
/*background-color: red;*/
color: white;
border: 1px solid white;
/*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/
padding: 2mm;
}
Heading {
font-size: 4mm;
font-family: "Montserrat-Bold";
color: black;
padding: 2mm;
text-align: center;
}
222
XMLVIewIcon {
font-family: "FontAwesome";
}
Listing 15. Example: Different font colors on Android and iOS. On Android, labels will appear green. On
iOS, they will appear red. On all other platforms, they will appear black.
Label {
color: black;
}
@media platform-and {
Label {
color: green;
}
}
@media platform-ios {
Label {
color: red;
}
}
Listing 16. Example: Different font colors based on device density. On lower densities, labels will be green.
On higher densities, labels will be red.
Label {
color: black;
}
223
Label {
color: red;
}
}
Label {
color: black;
}
@media device-desktop {
Label {
color: green;
}
}
When deploying your app using the Javascript port, it will use a platform name
derived from the "UserAgent" string in the browser, rather than the result of
[Link](), which is used for other ports. When running on
Android, then, the platform will be "and". When running on iOS, the platform will
be "ios". Etc…
You can combine multiple media queries together, separated by a comma. Queries of the same type
are "OR"ed together. Queries of different types are "AND"ed together. For example if you have a
media query that specifies two different device densities (e.g. density-low and density-high) the
query will match both devices with low density and high density. However, if the query specifies a
device density and a platform (e.g. density-low and platform-and), then it will only match a device if
it matches the platform and the density.
Listing 18. Example: Targeting styles to only Android devices with high density
Listing 19. Example: Targeting styles to iOS devices with high or low density
224
}
The order of precendence when applying styles differs slightly from the way styles would be
applied in standard CSS. The order of precendence is as follows:
1. Styles defined inside @media blocks will always take precendence over styles defined outside of
@media blocks.
2. @media blocks with more query matches will take precendence over blocks with fewer query
matches. E.g. A media block matching density, platform, and device will take precendence over
a block that only matches the density and platform.
3. If the same style is defined in two media blocks which contain the same number of query
matches, then the order precedence is platform, device, density in decreasing order. I.e. the
block that matches on platform will take precedence over the block that matches on density.
4. If the same style is defined in two media blocks with identical query matches, then the order of
precedence is undefined.
In some cases you may find that fonts are coming out too large or too small across the board on
certain types of devices. You can use standard media queries to customize font sizes, but you can
also use font-scaling constants to scale font sizes for the entires stylesheet based on platform,
device, and/or density. In some cases you may find this approach easier.
For example, consider the following simple stylesheet that defines a font size of 2mm on labels:
Label {
font-size: 3mm;
}
During testing, perhaps you find that, on desktop, the fonts are a little bit too small. In this case, you
can apply a font-scale constant that only applies to the desktop.
#Constants {
device-desktop-font-scale: "1.5";
}
Label {
font-size: 3mm;
225
}
Now, on most devices the Label style will have 3mm fonts. But on desktop, it will have 4.5mm fonts.
Label {
font-size: 3mm;
}
@media device-desktop {
Label {
font-size: 4.5mm;
}
}
#Constants {
device-phone-font-scale: "1.5";
device-tablet-font-scale: "1.2";
device-desktop-font-scale: "1.4";
platform-ios-font-scale: "0.9";
density-low-font-scale: "1.2";
platform-ios-density-low-font-scale: "1.3";
}
All matching font-scale constants will be applied to the styles. If you define 3 font-
scale constants that all match the current runtime environment, they will all be
applied. E.g. If there are 3 matching font-scale constants with "2.0", "3.0", and "4.0",
then fonts will be scaled by 2*3*4=24!
226
Chapter 7. The Components of Codename
One
This chapter covers the components of Codename One. Not all components are covered, but it tries
to go deeper than the JavaDocs [[Link]
7.1. Container
The Codename One container is a base class for many high level components; a container is a
component that can contain other components.
Every component has a parent container that can be null if it isn’t within a container at the
moment or is a top-level container. A container can have many children.
Components are arranged in containers using layout managers which are algorithms that
determine the arrangement of components within the container.
You can read more about layout managers in the basics section [[Link]
[Link]#component-container-hierarchy].
Codename One components share a very generic hierarchy of inheritance e.g. Button
[[Link] derives from Label
[[Link] and thus receives all its abilities.
However, some components are composites and derive from the Container
[[Link] class. E.g. the MultiButton
[[Link] is a composite button
that derives from Container but acts/looks like a Button. Normally this is pretty seamless for the
developer, with a few things to keep in mind.
• You should not use the Container derived methods on such a composite component (e.g. add
/remove etc.).
• You can’t cast it to the type that it relates to e.g. you can’t cast MultiButton to Button.
227
a MultiButton they will return the underlying Button. To workaround this we have
[Link]() [[Link]
[Link]#getActualComponent--].
Lead Component
Codename One has a rather unique feature for creating composite components: "lead
components". This feature effectively allows components like MultiButton to act as if they are
a single component while really being comprised of multiple components.
Lead components work by setting a single component within as the "leader" it determines the
style state for all the components in the hierarchy so if we have a Container that is lead by a
Button the button will determine if the selected/pressed state is returned for the entire
container hierarchy.
This creates a case where a single Component has multiple nested UIID’s e.g. `MultiButton has
UIID’s such as `MultiLine1 that can be customized via API’s such as setUIIDLine1
[[Link]
[Link]-].
The lead component also handles the events from a single source so clicking in one of the
other components within the hierarchy will send the event to the leading Button resulting in
action events that behave "oddly" (hence the need for getActualComponent);
7.2. Form
Form [[Link] is the top-level container of
Codename One, Form derives from Container and is the element we "show". Only one form can be
visible at any given time. We can get the currently visible Form using the code:
A form is a unique container in the sense that it has a title, a content area and optionally a
menu/menu bar area. When invoking methods such as add/remove on a form, you are in fact
invoking something that maps to this:
[Link]().add(...);
228
Figure 168. Form layout graphic
You can see that every Form has space allocated for the title area. If you don’t set the title it won’t
show up (its size will be zero), but it will still be there. The same isn’t always true for the case of the
menu bar, which can vary significantly. Effectively, the section that matters is the content pane, so
the form tries to do the "right thing" by pretending to be the content pane. However, this isn’t
always seamless and sometimes code needs to just invoke getContentPane() in order to work
directly with the container.
A good example for such a case is with layout animations. Animating the form
might not produce the right results. When in doubt its pretty easy to just use
getContentPane instead of working with the Form directly.
As you can see from the graphic, Form has two layers that reside on top of the content pane/title. The
first is the layered pane which allows you to place "always on top" components. The layered pane
added implicitly when you invoke getLayeredPane().
You still need to place components using layout managers in order to get them to
appear in the right place when using the layered pane.
229
Automatic Safe-Area Handling
Form calculates the safe region automatically, and core UI components that anchor themselves to the
screen edges apply that information without additional work:
• Toolbar and the status bar placeholder respect the top safe margins so that titles and action
buttons remain legible around notches.
• Tabs, Sheet, and FloatingActionButton instances mark their internal containers as safe areas so
navigation controls are padded above gesture/navigation bars.
• UI fragment templates can opt-in to safe areas declaratively using the safeArea flag, ensuring
generated components stay within the padded region.
This means that many applications will "just work" on devices such as the iPhone X/14 family or
Android devices with edge-to-edge displays. You only need to intervene when you use custom
containers that you position flush against a screen edge or when you perform your own painting.
If you create a container that should always stay clear of the unsafe portions of the screen (for
example, a bottom navigation bar, an on-screen joystick, or a floating tool palette), enable safe-area
padding explicitly:
[Link]([Link], bottomBar);
[Link]();
Safe-area padding is only applied when the container does not have a scrollable parent. For
scrollable content we assume the user can scroll the component into view instead.
Most layouts never need to know where the safe area begins, but if you draw manually (e.g., inside
paint() or on the glass pane) you can query it directly:
230
container in from off-screen) you can force a recalculation by calling Form#setSafeAreaChanged().
Safe-area padding is calculated relative to a "safe-area root". Forms are roots by default, but you
can mark any container as a root using Container#setSafeAreaRoot(true) when you need precise
control—for example, when preparing a side menu that starts off-screen and slides in:
Marking the drawer as both a root and a safe area prevents a "jump" the moment it becomes
visible, because the safe padding is already applied while it is off-screen.
Remember that safe areas apply across platforms. Always verify your screens on actual devices (or
in the Codename One simulator with a device skin that exposes cut-outs) to make sure critical UI
elements remain inside the padded region.
The second layer is the glass pane which allows you to draw arbitrary things on top of everything.
The order in the image is indeed accurate:
1. ContentPane is lowest
2. LayeredPane is second
Its important to notice that a layered pane is on top of the ContentPane only and
doesn’t stretch to the title. A GlassPane usually stretches all the way but only with a
"lightweight" title area e.g. the Toolbar API [[Link]
codename1/ui/[Link]].
The GlassPane allows developers to overlay UI on top of existing UI and paint as they see fit. This is
useful for things that provide notification but don’t want to intrude with application functionality.
In earlier versions of Codename One (pre-3.6), LayeredPane & GlassPane didn’t work
with "native" peer components such as media, browser, native maps etc, because
peer components were always rendered "in front" of the Codename One UI canvas.
However, current versions now allow for proper layering of peer components and
light-weight components so that LayeredPane and GlassPane can be used
seamlessly with peer components.
7.3. Dialog
A Dialog [[Link] is a special kind of Form that
can occupy only a portion of the screen, it also has the additional functionality of the modal show
method.
231
When showing a dialog we have two basic options: modeless and modal:
• Modal dialogs (the default) block the current EDT thread until the dialog is dismissed (to
understand how they do it, read about invokeAndBlock).
Modal dialogs are an extremely useful way to prompt the user since the code can assume the
user responded in the next line of execution. This promotes a linear & intuitive way of writing
code.
• Modless dialogs return immediately so a call to show such a dialog can’t assume anything in the
next line of execution. This is useful for features such as progress indicators where we aren’t
waiting for user input.
Notice that during the show call above the execution of the next line was "paused" until we got a
response from the user and once the response was returned we could proceed directly.
All usage of Dialog must be within the Event Dispatch Thread (the default thread of
Codename One). This is especially true for modal dialogs. The Dialog class knows
how to "block the EDT" without blocking it.
To learn more about invokeAndBlock which is the workhorse behind the modal dialog functionality
check out the EDT section [[Link]
The Dialog class contains multiple static helper methods to quickly show user notifications, but also
allows a developer to create a Dialog instance, add information to its content pane and show the
dialog.
When showing a dialog in this way, you can either ask Codename One to position the dialog in a
specific general location (taken from the BorderLayout [[Link]
codename1/ui/layouts/[Link]] concept for locations) or position it by spacing it (in pixels)
from the 4 edges of the screen.
E.g. you could do something like this to show a simple modal Dialog:
232
Figure 171. Custom modal Dialog in the south position
You can turn the code above to a modless Dialog by flipping the boolean true
argument to false.
We can position a Dialog absolutely by determining the space from the edges e.g. with this code we
can occupy the bottom portion of the screen:
The reason for this is that the Dialog is really a Form that takes up the whole screen. The Form that is
visible behind the Dialog is rendered as a screenshot. So customizing the actual UIID of the Dialog
won’t produce the desired results.
233
7.3.2. Tint and Blurring
By default a Dialog uses a platform specific tint color when it is showing e.g. notice the background
in the image below is tinted:
The tint color can be manipulated on the parent form, you can set it to any AARRGGBB value to set
any color using the setTintColor method. Notice that this is invoked on the parent form and not on
the Dialog!
This is an AARRGGBB value and not an RRGGBB value! This means that 0 will be
transparent.
You can also manipulate this default value globally using the theme constant tintColor. The sample
below tints the background in green:
234
Figure 174. Dialog with green tinted background
We can apply Gaussian blur to the background of a dialog to highlight the foreground further and
produce a very attractive effect. We can use the setDefaultBlurBackgroundRadius to apply this
globally, we can use the theme constant dialogBlurRadiusInt to do the same or we can do this on a
per Dialog basis using setBlurBackgroundRadius.
Not all device types support blur you can test if your device supports it using
[Link]().isGaussianBlurSupported(). If blur isn’t supported the blur
setting will be ignored.
Figure 175. The blur effect coupled with the OS default tint
It might be a bit hard to notice the blur effect with the tinting so here is the same code with tinting
disabled:
[Link](0);
235
Figure 176. The blur effect is more pronounced when the tint is disabled
A popup dialog is a common mobile paradigm showing a Dialog that points at a specific component.
It’s just a standard Dialog that is shown in a unique way:
When Codename One was young we needed a popup arrow implementation but our low level
graphics API was pretty basic. As a workaround we created a version of the 9-piece image border
that supported pointing arrows at a component.
Today Codename One supports pointing an arrow from the RoundRectBorder class. This is implicit
for the PopupDialog UI. This allows for better customization of the border (color etc.) and it looks
better on newer displays. It also works on all OSs. Right now only the iOS theme has the old image
border approach.
236
This will change with a future update where all OS’s will align and iOS will use the
lightweight popup too
You can make all OS’s act the same way by overriding the PopupDialog UIID and
defining its style to RoundRectBorder
The new RoundRectBorder support works by setting the track component property on border. When
that’s done the border implicitly points to the right location.
If you still need deeper customization of the arrow you can still use the old 9-piece border
functionality illustrated below.
One of the harder aspects of a popup dialog is the construction of the theme elements required for
arrow styling. To get that sort of behavior you will need a custom image border and 4 arrows
pointing in each direction that will be overlaid with the border.
The sizes of the arrow images should be similarly proportioned and fit within the
image borders whitespace. The block image of the dialog should have empty pixels
in the sides to reserve space for the arrow. E.g. if the arrows are all 32x32 pixels
then the PopupDialog image should have 32 pixels of transparent pixels around it.
You will need to define the following theme constants for the arrow to work:
PopupDialogArrowBool=true
PopupDialogArrowTopImage=arrow up image
PopupDialogArrowBottomImage=arrow down image
PopupDialogArrowLeftImage=arrow left image
PopupDialogArrowRightImage=arrow right image
Then style the PopupDialog UIID with the image for the Dialog itself.
7.4. InteractionDialog
Dialogs in Codename One can be modal or modeless, the former blocks the calling thread and the
latter does not. However, there is another definition to those terms: A modal dialog blocks access to
the rest of the UI while a modeless dialog "floats" on top of the UI.
In that sense, all dialogs in Codename One are modal; they block the parent form since they are
effectively just forms that show the "parent" in their background. InteractionDialog
[[Link] has an API that is
very similar to the Dialog [[Link] API but,
unlike dialog, it never blocks anything. Neither the calling thread nor the UI.
237
Container in the LayeredPane.
InteractionDialog is really just a container that is positioned within the layered pane. Notice that
because of that design, you can have only one such dialog at the moment and, if you add something
else to the layered pane, you might run into trouble.
Using the interaction dialog is pretty trivial and very similar to dialog:
This will show the dialog on the right hand side of the screen, which is pretty useful for a floating in
place dialog.
To make popup behaviour feel natural on touch devices you can call
setDisposeWhenPointerOutOfBounds(true) so the dialog auto-dismisses as soon as the user taps
outside the title or content area. Internally the dialog listens for pointer pressed/released events
and will call dispose() for you when the interaction happens beyond its bounds, so you no longer
need to wire that logic manually.
By default the dialog is placed on the form’s layered pane, but you can switch between the global
layered pane and form-specific layered pane using setFormMode(boolean). Setting form mode to true
keeps the dialog coupled with the showing form even when the global layered pane is used
elsewhere in your app.
Finally, recent updates added animation toggles so you can fine-tune presentation.
setAnimateShow(boolean) turns the show/dispose animations on or off, while
238
setRepositionAnimation(boolean) enables a "grow/shrink" reposition effect during those animations
when you want a more dynamic transition.
7.5. Label
Label [[Link] represents a text, icon or both.
Label is also the base class of Button which in turn is the base class for RadioButton & CheckBox. Thus
the functionality of the Label class extends to all of these components.
Label allows only a single line of text, line breaking is a very expensive operation on mobile devices
[1]
and so the Label class doesn’t support it.
SpanLabel supports multiple lines with a single label, notice that it does carry a
performance penalty for this functionality.
Labels support tickering and the ability to end with "…" if there isn’t enough space to render the
label. Developers can determine the placement of the label relatively to its icon in quite a few
powerful ways.
The gap between the label text & the icon defaults to 2 pixels due to legacy settings. The setGap
method of Label accepts a gap size in pixels.
239
Two pixels is low for most cases & it’s hard to customize for each Label.
You can use the theme constant labelGap which is a floating point value you can specify in
millimeters that will allow you to determine the default gap for a label. You can also customize this
manually using the method [Link](int) which determines the default gap in pixels.
One of the common requests we received over the years is a way to let text "fit" into the allocated
space so the font will match almost exactly the width available. In some designs this is very
important but it’s also very tricky. Measuring the width of a String is a surprisingly expensive
operation on some OS’s. Unfortunately, there is no other way other than trial & error to find the
"best size".
Still despite the fact that something is "slow" we might still want to use it for some cases, this isn’t
something you should use in a renderer, infinite scroll etc. and we recommend minimizing the
usage of this feature as much as possible.
This feature is only applicable to Label and its subclasses (e.g. Button), with components such as
TextArea (e.g. SpanButton) the choice between shrinking and line break would require some complex
logic.
[Link]();
240
Figure 180. Automatically sizes the fonts of the buttons/labels based on text and available space
TextArea defaults to multi-line input and TextField defaults to single line input but both can be used
in both cases. The main differences between TextField and TextArea are:
• DataChangeListener [[Link]
[Link]] is only available in TextField. This is crucial for character by character
input event tracking
• Different UIID
The semantic difference between TextField & TextArea dates back to the ancestor
of Codename One: LWUIT. Feature phones don’t have "proper" in-place editing
capabilities & thus TextField was introduced to allow such input.
Because it lacks the blinking cursor capability TextArea is often used as a multi-line label and is
used internally in SpanLabel, SpanButton etc.
TextField & TextArea support constraints for various types of input such as NUMERIC, EMAIL, URL, etc.
Those usually affect the virtual keyboard used, but might not limit input in some platforms. E.g. on
iOS even with NUMERIC constraint you would still be able to input characters.
If you need to prevent specific types of input check out the validation section.
241
The following sample shows off simple text field usage:
TableLayout tl;
int spanButton = 2;
if([Link]().isTablet()) {
tl = new TableLayout(7, 2);
} else {
tl = new TableLayout(14, 1);
spanButton = 1;
}
[Link](true);
[Link](tl);
242
The Toolbar section contains a very elaborate TextField search sample with
DataChangeListener and rather unique styling.
7.6.1. Masking
A common use case when working with text components is the ability to "mask" input e.g. in the
credit card number above we would want 4 digits for each text field and don’t want the user to tap
Next 3 times.
Masking allows us to accept partial input in one field and implicitly move to the next, this can be
used to all types of complex input thanks to the text component API. E.g with the code above we can
mask the credit card input so the cursor jumps to the next field implicitly using this code:
automoveToNext(num1, num2);
automoveToNext(num2, num3);
automoveToNext(num3, num4);
A common misconception for developers is assuming the virtual keyboard represents "keys". E.g.
developers often override the "keyEvent" callbacks which are invoked for physical keyboard typing
and expect those to occur with a virtual keyboard.
This isn’t the case since a virtual keyboard is a very different beast. With a virtual keyboard
characters typed might produce a completely different output due to autocorrect. Some keyboards
don’t even have "keys" in the traditional sense or don’t type them in the traditional sense (e.g.
swiping).
The constraint property for the TextField/TextArea is crucial for a virtual keyboard.
When working with a virtual keyboard it’s important that the parent Container for
the TextField/TextArea is scrollable. Otherwise the component won’t be reachable
243
or the UI might be distorted when the keyboard appears.
By default, the virtual keyboard on Android has a "Done" button, you can customize it to be a
search icon, a send icon, or a go icon using a hint such as this:
[Link]("searchField", [Link]);
[Link]("sendButton", [Link]);
[Link]("goButton", [Link]);
This will adapt the icon for the action on the keys.
We try to hide a lot of the platform differences in Codename One, input is very different between
OS’s. A common reliance is the ability to send the "Done" event when the user presses the Done
button. Unfortunately this button doesn’t always exist e.g. if there is an Enter button (due to
multiline input) or if there is a Next button in that place.
To make the behavior more uniform we slightly customized the iOS keyboard as such:
However, this behavior might not be desired so to block that we can do:
[Link]("iosHideToolbar", [Link]);
You can customize the color of the Done button in the toolbar by setting the
[Link] display property. E.g. To change the color to red, you could do
[Link]().setProperty("[Link]",
[Link](0xff0000)). @since 5.0
244
7.6.3. Clearable Text Field
iOS has a convention where an X can be placed after the text field to clear it. Some Android apps
have it but there is no native support for that as of this writing.
You can wrap a TextField with a clearable wrapper to get this effect on all platforms. E.g. replace
this:
[Link](myTextField);
With this:
[Link]([Link](myTextField));
You can also specify the size of the clear icon if you wish. This is technically just a Container with
the text field style and a button to clear the text at the edge.
7.7. TextComponent
When building input forms we sometimes want to adapt to the native OS behavior and create a UI
that’s a bit more distinct to the native OS. TextField and TextArea are very low level, you can create
an Android style UI with such components but it might look out of place in iOS.
E.g. this is how most of us would expect the UI to look on iOS and Android respectively:
Doing this with text fields is possible but would require code that looks a bit different and jumps
through hoops. TextComponent allows this exact UI without forcing developers to write OS specific
code:
245
TextModeLayout tl = new TextModeLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);
TextComponent title = new TextComponent().label("Title");
TextComponent price = new TextComponent().label("Price");
TextComponent location = new TextComponent().label("Location");
TextComponent description = new TextComponent().label("Description").multiline(true);
[Link]([Link]().horizontalSpan(2), title);
[Link]([Link]().widthPercentage(30), price);
[Link]([Link]().widthPercentage(70), location);
[Link]([Link]().horizontalSpan(2), description);
[Link]([Link]());
[Link]();
This code uses the TextModeLayout which is discussed in the layouts section
The text component uses a builder approach to set various values e.g.:
The code is pretty self explanatory and more convenient than typical setters/getters. It
automatically handles the floating hint style of animation when running on Android.
The validator class supports text component and it should "just work". But the cool thing is that it
uses the material design convention for error handling!
246
Figure 186. Error handling when the text is blank
Figure 187. Error handling when there is some input (notice red title label)
The underlying system is the errorMessage method which you can chain like the other methods on
TextComponent as such:
To keep the code common and generic we use the InputComponent abstract base class and derive the
other classes from that. PickerComponent is currently the only other option.
A picker can work with our existing sample using code like this:
247
TextComponent description = new TextComponent().label("Description").multiline(true);
Validator val = new Validator();
[Link](title, new LengthConstraint(2));
[Link](price, new NumericConstraint(true));
[Link]([Link]().widthPercentage(60), title);
[Link]([Link]().widthPercentage(40), date);
[Link](location);
[Link](price);
[Link]([Link]().horizontalSpan(2), description);
[Link]([Link]());
[Link]();
The one tiny thing you should notice with the PickerComponent is that we don’t construct the picker
component using new PickerComponent(). Instead we use create methods such as
[Link](new Date()). The reason for that is that we have many types of pickers
and it wouldn’t make sense to have one constructor.
These varying looks are implemented via a combination of layouts, theme constants and UIID’s. The
most important UIID’s are: TextComponent, FloatingHint & TextHint.
There are several theme constants related that can manipulate some pieces of this functionality:
• textComponentErrorColor a hex RGB color which defaults to null in which case this has no effect.
When defined this will change the color of the border and label to the given color to match the
material design styling. This implements the red border underline in cases of error and the
label text color change
248
• textComponentErrorLineBorderBool toggles the material-style underline that appears on
validation errors. Set it to false if you prefer to supply a different border when errors are
shown
• textComponentOnTopBool toggles the on top mode which makes things look like they do on
Android. This defaults to true on Android and false on other OS’s. This can also be manipulated
via the onTopMode(boolean) method in InputComponent however the layout will only use the theme
constant
• textComponentFieldUIID sets the UIID of the text field to something other than TextField this is
useful for platforms such as iOS where the look of the text field is different within the text
component. This allows us to make the background of the text field transparent when it’s within
the TextComponent and make it different from the regular text field
7.8. Button
Button [[Link] is a subclass of Label and as a
result it inherits all of its functionality, specifically icon placement, tickering, etc.
Button adds to the mix some additional states such as a pressed UIID state and pressed icon.
There are additional icon states in Button such as rollover and disabled icon.
Button also exposes some functionality for subclasses specifically the setToggle method call which
has no meaning when invoked on a Button but has a lot of implications for CheckBox & RadioButton.
Changes in a Command won’t be reflected into the Button after the command was set
to the Button.
249
Figure 191. Simple button in the iOS styling, notice iOS doesn’t have borders on buttons…
Such a button can be styled to look like a link using code like this or simply by making these settings
in the theme and using code such as [Link]("Hyperlink").
Buttons on Android’s material design UI use upper case styling which isn’t the case for iOS. To solve
this we have the method setCapsText(boolean) in Button which has the corresponding isCapsText,
isCapsTextDefault & setCapsTextDefault(boolean). This is pretty core to Codename One so to prevent
this from impacting everything unless you explicitly invoke setCapsText(boolean) the default value
of true will only apply when the UIID is Button, RaisedButton or for the builtin Dialog buttons.
We also have a theme constant: capsButtonTextBool. This constant controls caps text behavior from
the theme and is set to true in the Android native theme.
Raised button is a style of button that’s available on Android and used to highlight an important
action within a form. To confirm with the material design UI guidelines you might want to leverage
a raised button UI element on Android but use a regular button everywhere else.
First we need to know whether a raised button exists in the theme. So on Android this will return
true but on other OS’s it will return false. A potential future update might make another platform
true based on UI guidelines in other OS’s.
For this purpose we’ve got the theme constant hasRaisedButtonBool which will return true on
Android but will be false elsewhere. You can use it like this:
250
if([Link]().isThemeConstant("hasRaisedButtonBool", false)) {
// that means we can use a raised button
}
To enable this we have the RaisedButton UIID that derives from Button and will act like it except for
the places where hasRaisedButtonBool is true in which case it will look like this:
Notice that you can easily customize the colors of these buttons now since the border respects user
colors…
In this case I just set the background color to purple and the foreground to white:
The ripple effect in material design highlights the location of the finger and grows as a circle to
occupy the full area of the component as the user presses the button.
We have the ability to perform a ripple effect by darkening the touched area and growing that in a
quick animation.
Ripple effect can be applied to any component but we currently only have it turned on for buttons
on Android which also applies to things like title commands, side menu elements etc. This might not
apply at this moment to lead components like multi-buttons but that might change in the future.
Component has a property to enable the ripple effect setRippleEffect(boolean) and the corresponding
isRippleEffect(). You can turn it on or off individually in the component level. However, Button has
static setButtonRippleEffectDefault(boolean) and isButtonRippleEffectDefault(). These allow us to
define the default behavior for all the buttons and that can be configured via the theme constant
buttonRippleBool which is currently on by default on the native Android theme.
251
7.9. CheckBox/RadioButton
CheckBox [[Link] & RadioButton
[[Link] are subclasses of button that
allow for either a toggle state or exclusive selection state.
Both CheckBox & RadioButton have a selected state that allows us to determine their selection.
The CheckBox can be added to a Container like any other Component but the RadioButton must be
associated with a ButtonGroup otherwise if we have more than one set of RadioButton’s in the form
we might have an issue.
Notice in the sample below that we associate all the radio buttons with a group but don’t do
anything with the group as the radio buttons keep the reference internally. We also show the
opposite side functionality and icon behavior:
Both of these components can be displayed as toggle buttons (see the toggle button section below),
or just use the default check mark/filled circle appearance based on the type/OS.
252
7.9.1. Toggle Button
A toggle button is a button that is pressed and stays pressed. When a toggle button is pressed again
it’s released from the pressed state. Hence the button has a selected state to indicate if it’s pressed
or not exactly like the CheckBox/RadioButton components in Codename One.
To turn any CheckBox or RadioButton to a toggle button just use the setToggle(true) method.
Alternatively you can use the static createToggle method on both CheckBox and RadioButton to create
a toggle button directly.
We can easily convert the sample above to use toggle buttons as such:
That’s half the story though, to get the full effect of some cool toggle button UI’s we can use a
ComponentGroup [[Link] This
allows us to create a button bar effect with the toggle buttons.
E.g. lets enclose the CheckBox components in a vertical ComponentGroup and the RadioButton’s in a
horizontal group. We can do this by changing the last line of the code above as such:
253
[Link]([Link](cb1, cb2, cb3, cb4)).
add([Link](rb1, rb2, rb3));
7.10. ComponentGroup
ComponentGroup [[Link] is a
special container that can be either horizontal or vertical (BoxLayout [[Link]
javadoc/com/codename1/ui/layouts/[Link]] X_AXIS or Y_AXIS respectively).
ComponentGroup "restyles" the elements within the group to have a UIID that allows us to create a
"round border" effect that groups elements together.
The following code adds 4 component groups to a Container to demonstrate the various UIID
changes:
[Link]("Three Labels").
add([Link](new Label("GroupElementFirst UIID"), new Label("GroupElement UIID"), new Label
("GroupElementLast UIID"))).
add("One Label").
add([Link](new Label("GroupElementOnly UIID"))).
add("Three Buttons").
add([Link](new Button("ButtonGroupFirst UIID"), new Button("ButtonGroup UIID"), new Button
("ButtonGroupLast UIID"))).
add("One Button").
add([Link](new Button("ButtonGroupOnly UIID")));
254
Figure 198. ComponentGroup adapts the UIID’s of the components added so we can style them
Notice the following about the code above and the resulting image:
• Buttons have a different UIID than other element types. Their styling is slightly different in such
UI’s so you need to pay attention to that.
• When an element is placed alone within a ComponentGroup its a special case UIID.
When ComponentGroupBool is set to true, the component group will modify the styles of all
components placed within it to match the element UIID given to it (GroupElement by default) with
special caveats to the first/last/only elements. E.g.
1. If I have one element within a component group it will have the UIID: GroupElementOnly
2. If I have two elements within a component group they will have the UIID’s GroupElementFirst,
GroupElementLast
3. If I have three elements within a component group they will have the UIID’s GroupElementFirst,
GroupElement, GroupElementLast
4. If I have four elements within a component group they will have the UIID’s GroupElementFirst,
GroupElement, GroupElement, GroupElementLast
You can customize the UIID set by the component group by calling setElementUIID in the component
group e.g. setElementUIID("ToggleButton") for three elements result in the following UIID’s:
7.11. MultiButton
MultiButton [[Link] is a
255
composite component (lead component) that acts like a versatile Button
[[Link] It supports up to 4 lines of text (it
doesn’t automatically wrap the text), an emblem (usually navigational arrow, or check box) and an
icon.
MultiButton can be used as a button, a CheckBox or a RadioButton for creating rich UI’s.
The MultiButton was inspired by the aesthetics of the UITableView iOS component.
A common source of confusion in the MultiButton is the difference between the icon and the
emblem, since both may have an icon image associated with them. The icon is an image
representing the entry while the emblem is an optional visual representation of the action that will
be undertaken when the element is pressed. Both may be used simultaneously or individually of
one another.
[Link](oneLineIconEmblem).
add(twoLinesNoIcon).
add(twoLinesIconEmblem).
add(twoLinesIconEmblemHorizontal).
add(twoLinesIconCheckBox).
add(fourLinesIcon);
256
Figure 199. Multiple usage scenarios for the MultiButton
Since the MultiButton is a composite component setting its UIID will only impact the top level UI.
To customize everything you need to customize the UIID’s for MultiLine1, MultiLine2, MultiLine3,
MultiLine4 & Emblem.
You can customize the individual UIID’s thru the API directly using the setIconUIID, setUIIDLine1,
setUIIDLine2, setUIIDLine3, setUIIDLine4 & setEmblemUIID.
Recent versions also include a badge overlay that can be rendered in the corner of the main icon.
Use setBadgeText() to display a value (for example a notification count) and setBadgeUIID() if you
need a custom UIID instead of the default Badge styling. When you need to inspect or adjust the
badge style programmatically, getBadgeStyleComponent() returns the component whose styles are
applied to the badge so you can tweak padding, colors or borders before showing the MultiButton.
7.12. SpanButton
SpanButton [[Link] is a
composite component (lead component) that looks/acts like a Button but can break lines rather than
crop them when the text is very long.
Unlike the MultiButton it uses the TextArea internally to break lines seamlessly. The SpanButton is far
simpler than the MultiButton and as a result isn’t as configurable.
SpanButton sb = new SpanButton("SpanButton is a composite component (lead component) that looks/acts like a Button but
can break lines rather than crop them when the text is very long.");
[Link](icon);
[Link](sb);
257
7.13. SpanLabel
SpanLabel [[Link] is a
composite component (lead component) that looks/acts like a Label [[Link]
javadoc/com/codename1/ui/[Link]] but can break lines rather than crop them when the text is very
long.
SpanLabel uses the TextArea internally to break lines seamlessly and so doesn’t provide all the
elaborate configuration options of Label.
One of the features of label that moved into SpanLabel to some extent is the ability to position the
icon. However, unlike a Label the icon position is determined by the layout manager of the
composite so setIconPosition accepts a BorderLayout constraint.
SpanLabel d = new SpanLabel("Default SpanLabel that can seamlessly line break when the text is really long.");
[Link](icon);
SpanLabel l = new SpanLabel("NORTH Positioned Icon SpanLabel that can seamlessly line break when the text is really
long.");
[Link](icon);
[Link]([Link]);
SpanLabel r = new SpanLabel("SOUTH Positioned Icon SpanLabel that can seamlessly line break when the text is really
long.");
[Link](icon);
[Link]([Link]);
SpanLabel c = new SpanLabel("EAST Positioned Icon SpanLabel that can seamlessly line break when the text is really
long.");
[Link](icon);
[Link]([Link]);
[Link](d).add(l).add(r).add(c);
7.14. OnOffSwitch
The OnOffSwitch [[Link]
allows you to write an application where the user can swipe a switch between two states (on/off).
This is a common UI paradigm in Android and iOS, although it’s implemented in a radically
different way in both platforms.
258
This is a rather elaborate component because of its very unique design on iOS, but we we’re able to
accommodate most of the small behaviors of the component into our version, and it seamlessly
adapts between the Android style and the iOS style.
The image below was generated based on the default use of the OnOffSwitch:
Figure 202. The OnOffSwitch component as it appears on/off on iOS (top) and on Android (bottom)
As you can understand the difference between the way iOS and Android render this component has
triggered two very different implementations within a single component. The Android
implementation just uses standard buttons and is the default for non-iOS platforms.
You can force the Android or iOS mode by using the theme constant
onOffIOSModeBool.
7.14.1. Validation
This sample below continues from the place where the TextField sample above stopped by adding
validation to that code.
259
addConstraint(email, [Link]()).
addConstraint(phone, new RegexConstraint(phoneRegex, "Must be valid phone number")).
addConstraint(num1, new LengthConstraint(4)).
addConstraint(num2, new LengthConstraint(4)).
addConstraint(num3, new LengthConstraint(4)).
addConstraint(num4, new LengthConstraint(4));
[Link](submit);
7.15. InfiniteProgress
The InfiniteProgress [[Link]
[Link]] indicator spins an image infinitely to indicate that a background process is still
working.
InfiniteProgress can be used in one of two ways either by embedding the component into the UI
thru something like this:
[Link](new InfiniteProgress());
InfiniteProgress can also appear over the entire screen, thus blocking all input. This tints the
background while the infinite progress rotates:
// do some long operation here using invokeAndBlock or do something in a separate thread and callback later
// when you are done just call
[Link]();
260
Figure 204. Infinite progress
The image used in the InfiniteProgress animation is defined by the native theme. You can override
that definition either by defining the theme constant infiniteImage or by invoking the setAnimation
[[Link]
[Link]-] method.
Despite the name of the method setAnimation expects a static image that will be
rotated internally. Don’t use an animated image.
The motivation behind these classes is simple, say we have a lot of data to fetch from storage or
from the internet. We can fetch the data in batches and show progress indication while we do this.
Infinite scroll fetches the next batch of data dynamically as we reach the end of the Container.
InfiniteScrollAdapter & InfiniteContainer represent two similar ways to accomplish that task
relatively easily.
Let start by exploring how we can achieve this UI that fetches data from a webservice:
The first step is creating the webservice call, we won’t go into too much detail here as webservices
& IO are discussed later in the guide:
int pageNumber = 1;
[Link]<Map<String, Object>> fetchPropertyData(String text) {
try {
ConnectionRequest r = new ConnectionRequest();
261
[Link](false);
[Link]("[Link]
[Link]("pretty", "0");
[Link]("action", "search_listings");
[Link]("encoding", "json");
[Link]("listing_type", "buy");
[Link]("page", "" + pageNumber);
pageNumber++;
[Link]("country", "uk");
[Link]("place_name", text);
[Link]().addToQueueAndWait(r);
Map<String,Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r
.getResponseData()), "UTF-8"));
Map<String, Object> response = (Map<String, Object>)[Link]("response");
return ([Link]<Map<String, Object>>)[Link]("listings");
} catch(Exception err) {
Log.e(err);
return null;
}
}
The demo code here doesn’t do any error handling! This is a very bad practice and
it is taken here to keep the code short and readable. Proper error handling is used
in the Property Cross demo.
The fetchPropertyData is a very simplistic tool that just fetches the next page of listings for the
nestoria webservice. Notice that this method is synchronous and will block the calling thread
(legally) until the network operation completes.
Now that we have a webservice lets proceed to create the UI. Check out the code annotations below:
Style s = [Link]().getComponentStyle("MultiLine1");
FontImage p = [Link](FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = [Link]([Link]([Link]() * 3, [Link]() * 3), false); ①
[Link]([Link](), () -> { ②
[Link]<Map<String, Object>> data = fetchPropertyData("Leeds"); ③
MultiButton[] cmps = new MultiButton[[Link]()];
for(int iter = 0 ; iter < [Link] ; iter++) {
Map<String, Object> currentListing = [Link](iter);
if(currentListing == null) { ④
[Link]([Link](), new Component[0], false);
return;
}
String thumb_url = (String)[Link]("thumb_url");
String guid = (String)[Link]("guid");
String summary = (String)[Link]("summary");
cmps[iter] = new MultiButton(summary);
cmps[iter].setIcon([Link](placeholder, guid, thumb_url));
}
[Link]([Link](), cmps, true); ⑤
}, true); ⑥
262
② The InfiniteScrollAdapter accepts a runnable which is invoked every time we reach the edge of
the scrolling. We used a closure instead of the typical run() method override.
③ This is a blocking call, after the method completes we’ll have all the data we need. Notice that
this method doesn’t block the EDT illegally.
④ If there is no more data we call the addMoreComponents method with a false argument. This
indicates that there is no additional data to fetch.
⑤ Here we add the actual components to the end of the form. Notice that we must not invoke the
add/remove method of Container. Those might conflict with the work of the InfiniteScrollAdapter.
⑥ We pass true to indicate that the data isn’t "prefilled" so the method should be invoked
immediately when the Form is first shown
Do not violate the EDT in the callback. It is invoked on the event dispatch thread
and it is crucial
Unlike the InfiniteScrollAdapter the InfiniteContainer accepts an index and amount to fetch. This
is useful for tracking your position but also important since the InfiniteContainer also implements
Pull To Refresh as part of its functionality.
Converting the code above to an InfiniteContainer is pretty simple we just moved all the code into
the callback fetchComponents method and returned the array of Component’s as a response.
Unlike the InfiniteScrollAdapter we can’t use the ContentPane directly so we have to use a
BorderLayout and place the InfiniteContainer there:
Style s = [Link]().getComponentStyle("MultiLine1");
FontImage p = [Link](FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = [Link]([Link]([Link]() * 3, [Link]() * 3), false);
263
}
return cmps;
}
};
[Link]([Link], ic);
A Container with ~5000 nested containers within it can perform on par with a List and probably
exceed its performance when used correctly.
Larger sets of data are rarely manageable on phones or tablets so the benefits for lists are dubious.
In terms of API we found that even experienced developers experienced a great deal of pain when
wrangling the Swing styled lists and their stateless approach.
Since animation, swiping and other capabilities that are so common in mobile are so hard to
accomplish with lists we see no actual reason to use them.
We deprecated ContainerList which performs really badly and has some inherent complexity
issues. List has some unique use cases and is still used all over Codename One.
MultiList is a reasonable version of List that is far easier to use without most of the pains related to
renderer configuration.
There are cases where using List or MultiList is justified, they are just rarer than usual hence our
recommendation.
The advantage of using a List in this way is that we can display it in many ways (e.g. fixed focus
positions, horizontally, etc.), and that we can have more than a million entries without performance
overhead. We can also do some pretty nifty things, like filtering the list on the fly or fetching it
264
dynamically from the Internet as the user scrolls down the list. To achieve these things the list uses
two interfaces: ListModel [[Link] and
ListCellRenderer. List [[Link] model
represents the data; its responsibility is to return the arbitrary object within the list at a given
offset. Its second responsibility is to notify the list when the data changes, so the list can refresh.
Think of the model as an array of objects that can notify you when it changes.
The list renderer is like a rubber stamp that knows how to draw an object from the model, it’s
called many times per entry in an animated list and must be very fast. Unlike standard Codename
One components, it is only used to draw the entry in the model and is immediately discarded, hence
it has no memory overhead, but if it takes too long to process a model value it can be a big
bottleneck!
Think of the render as a translation layer that takes the "data" from the model and
translates it to a visual representation.
This is all very generic, but a bit too much for most, doing a list "properly" requires some
understanding. The main source of confusion for developers is the stateless nature of the list and
the transfer of state to the model (e.g. a checkbox list needs to listen to action events on the list and
update the model, in order for the renderer to display that state). Once you understand that it’s
easy.
• Model - Represents the data for the component (list), the model can tell us exactly how many
items are in it and which item resides at a given offset within the model. This differs from a
simple Vector (or array), since all access to the model is controlled (the interface is simpler), and
unlike a Vector/Array, the model can notify us of changes that occur within it.
• View - The view draws the content of the model. It is a "dumb" layer that has no notion of what
is displayed and only knows how to draw. It tracks changes in the model (the model sends
events) and redraws itself when it changes.
• Controller - The controller accepts user input and performs changes to the model, which in turn
cause the view to refresh.
[2]
Figure 206. Typical MVC Diagram
265
Codename One’s List [[Link] component uses
the MVC paradigm in its implementation. List itself is the Controller (with a bit of the View mixed
in). The ListCellRenderer [[Link]
interface is the rest of the View and the ListModel [[Link]
ui/list/[Link]] is (you guessed it by now) the Model.
When the list is painted, it iterates over the visible elements in the model and asks the model for
the data, it then draws them using the renderer. Notice that because of this both the model and the
renderer must be REALLY fast and that’s hard.
Since the model is a lightweight interface, it can be implemented by you and replaced in runtime if
so desired, this allows several use cases:
1. A list can contain thousands of entries but only load the portion visible to the user. Since the
model will only be queried for the elements that are visible to the user, it won’t need to load the
large data set into memory until the user starts scrolling down (at which point other elements
may be offloaded from memory).
2. A list can cache efficiently. E.g. a list can mirror data from the server into local RAM without
actually downloading all the data. Data can also be mirrored from storage for better
performance and discarded for better memory utilization.
3. The is no need for state copying. Since renderers allow us to display any object type, the list
model interface can be implemented by the application’s data structures (e.g.
persistence/network engine), which would return internal application data structures saving
you the need of copying application state into a list specific data structure. Note that this
advantage only applies with a custom renderer which is pretty difficult to get right.
4. Using the proxy pattern we can layer logic such as filtering, sorting, caching, etc. on top of
existing models without changing the model source code.
5. We can reuse generic models for several views, e.g. a model that fetches data from the server
can be initialized with different arguments, to fetch different data for different views. View
objects in different Forms can display the same model instance in different view instances, thus
they would update automatically when we change one global model.
Most of these use cases work best for lists that grow to a larger size, or represent complex data,
which is what the list object is designed to do.
Usually when working with lists, you want the list to handle the scrolling (otherwise it will perform
badly). This means you should place the list in a non-scrollable container (no parent can be
scrollable), notice that the content pane is scrollable by default, so you should disable that.
266
[Link](false);
[Link](new BorderLayout());
[Link]([Link], myList);
So after this long start lets show the first sample of creating a list using the MultiList
[[Link]
The MultiList is a preconfigured list that contains a ready made renderer with defaults that make
sense for the most common use cases. It still retains most of the power available to the List
component but reduces the complexity of one of the hardest things to grasp for most developers:
rendering.
The full power of the ListModel is still available and allows you to create a million entry list with
just a few lines of code. However the objects that the model returns should always be in the form of
Map objects and not an arbitrary object like the standard List allows.
267
createListEntry is relatively trivial:
There is one major piece missing here and that is the cover images for the books. A simple
approach would be to just place the image objects into the entries using the "icon" property as such:
Lets assume that GRRM [[Link] was really prolific and wrote 1 million books.
The default list model won’t make much sense in that case but we would still be able to render
everything in a list model.
We’ll fake it a bit but notice that 1M components won’t be created even if we somehow scroll all the
way down…
268
class GRMMModel implements ListModel<Map<String,Object>> {
@Override
public Map<String, Object> getItemAt(int index) {
int idx = index % 7;
switch(idx) {
case 0:
return createListEntry("A Game of Thrones " + index, "1996");
case 1:
return createListEntry("A Clash Of Kings " + index, "1998");
case 2:
return createListEntry("A Storm Of Swords " + index, "2000");
case 3:
return createListEntry("A Feast For Crows " + index, "2005");
case 4:
return createListEntry("A Dance With Dragons " + index, "2011");
case 5:
return createListEntry("The Winds of Winter " + index, "2016 (please, please, please)");
default:
return createListEntry("A Dream of Spring " + index, "Ugh");
}
}
@Override
public int getSize() {
return 1000000;
}
@Override
public int getSelectedIndex() {
return 0;
}
@Override
public void setSelectedIndex(int index) {
}
@Override
public void addDataChangedListener(DataChangedListener l) {
}
@Override
public void removeDataChangedListener(DataChangedListener l) {
}
@Override
public void addSelectionListener(SelectionListener l) {
}
@Override
public void removeSelectionListener(SelectionListener l) {
}
@Override
public void addItem(Map<String, Object> item) {
}
@Override
public void removeItem(int index) {
269
}
}
We can now replace the existing model by removing all the model related logic and changing the
constructor call as such:
Figure 209. It took ages to scroll this far… This goes to a million…
//This method returns the List animated focus which is animated when list selection changes
public Component getListFocusComponent(List list);
}
The most simple/naive implementation may choose to implement the renderer as follows:
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
return new Label([Link]());
}
This will compile and work, but won’t give you much, notice that you won’t see the List selection
move on the List, this is just because the renderer returns a Label [[Link]
javadoc/com/codename1/ui/[Link]] with the same style regardless if it’s selected or not.
270
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
Label l = new Label([Link]());
if (isSelected) {
[Link](true);
[Link]().setBgTransparency(100);
} else {
[Link](false);
[Link]().setBgTransparency(0);
}
return l;
} public Component getListFocusComponent(List list){
return null;
}
In this renderer we set the [Link](true) if it’s selected, calling to this method doesn’t really
give the focus to the Label, it simply renders the label as selected.
That is still not very efficient because we create a new Label each time the method is invoked.
To make the code tighter, keep a reference to the Component or extend it as DefaultListCellRenderer
[[Link] does.
public ContactsRenderer() {
setLayout(new BorderLayout());
addComponent([Link], pic);
Container cnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
271
[Link]().setBgTransparency(0);
[Link]().setFont([Link](Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
[Link]().setBgTransparency(0);
[Link](name);
[Link](email);
addComponent([Link], cnt);
[Link]().setBgTransparency(100);
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
In this renderer we want to render a Contact object to the Screen, we build the Component in the
constructor and in the getListCellRendererComponent we simply update the Labels' texts according
to the Contact object.
Notice that in this renderer we return a focus Label with semi transparency, as mentioned before,
the focus component can be modified within this method.
[Link]().setBgTransparency(100);
try {
[Link]([Link]("/[Link]"));
[Link]([Link]);
} catch (IOException ex) {
[Link]();
}
As part of the GUI builder work, we needed a way to customize rendering for a List, but the
renderer/model approach seemed impossible to adapt to a GUI builder (it seems the Swing GUI
builders had a similar issue). Our solution was to introduce the GenericListCellRenderer, which
while introducing limitations and implementation requirements still manages to make life easier,
both in the GUI builder and outside of it.
GenericListCellRenderer [[Link]
[Link]] is a renderer designed to be as simple to use as a Component-Container
hierarchy, we effectively crammed most of the common renderer use cases into one class. To enable
272
that, we need to know the content of the objects within the model, so the GenericListCellRenderer
assumes the model contains only Map objects. Since Maps can contain arbitrary data the list model is
still quite generic and allows storing application specific data. Furthermore a Map can still be
derived and extended to provide domain specific business logic.
The GenericListCellRenderer accepts two container instances (more later on why at least two, and
not one), which it maps to individual Map entries within the model, by finding the appropriate
components within the given container hierarchy. Components are mapped to the Map entries based
on the name property of the component (getName/setName) and the key/value within the Map, e.g.:
"Foo": "Bar"
"X": "Y"
"Not": "Applicable"
"Number": Integer(1)
A renderer will loop over the component hierarchy in the container, searching for components
whose name matches Foo, X, Not, and Number, and assigning the appropriate value to them.
You can also use image objects as values, and they will be assigned to labels as
expected. However, you can’t assign both an image and a text to a single label,
since the key will be taken. That isn’t a big problem, since two labels can be used
quite easily in such a renderer.
To make matters even more attractive the renderer seamlessly supports list tickering when
appropriate, and if a CheckBox [[Link]
appears within the renderer, it will toggle a boolean flag within the Map seamlessly.
One issue that crops up with this approach is that, if a value is missing from the Map, it is treated as
empty and the component is reset.
This can pose an issue if we hardcode an image or text within the renderer and we don’t want them
replaced (e.g. an arrow graphic on a Label within the renderer). The solution for this is to name the
component with Fixed in the end of the name, e.g. HardcodedIconFixed.
Naming a component within the renderer with $number will automatically set it as a counter
component for the offset of the component within the list.
Styling the GenericListCellRenderer is slightly different, the renderer uses the UIID of the Container
passed to the generic list cell renderer, and the background focus uses that same UIID with the word
"Focus" appended to it.
It is important to notice that the generic list cell renderer will grant focus to the child components
of the selected entry if they are focusable, thus changing the style of said entries. E.g. a Container
[[Link] might have a child Label that has
one style when the parent container is unselected and another when it’s selected (focused), this can
be easily achieved by defining the label as focusable. Notice that the component will never receive
direct focus, since it is still part of a renderer.
273
Last but not least, the generic list cell renderer accepts two or four instances of a Container, rather
than the obvious choice of accepting only one instance. This allows the renderer to treat the
selected entry differently, which is especially important to tickering, although it’s also useful for the
[3]
fisheye effect . Since it might not be practical to seamlessly clone the Container for the renderer’s
needs, Codename One expects the developer to provide two separate instances, they can be
identical in all respects, but they must be separate instances for tickering to work. The renderer
also allows for a fisheye effect, where the selected entry is actually different from the unselected
entry in its structure, it also allows for a pinstripe effect, where odd/even rows have different styles
(this is accomplished by providing 4 instances of the containers selected/unselected for odd/even).
The best way to learn about the generic list cell renderer and the Map model is by playing with them
in the old GUI builder. Notice they can be used in code without any dependency on the GUI builder
and can be quite useful at that.
Here is a simple example of a list with checkboxes that gets updated automatically:
274
}
This can be achieved with a custom renderer, but that is a pretty difficult task.
GenericListCellRenderer (MultiList uses GenericListCellRenderer internally) has another option.
Normally, to build the model for a renderer of this type, we use something like:
[Link]("componentName_uiid", "red");
This will apply the UIID "red" to the component, which you can then style in the theme. Notice that
once you start doing this, you need to define this entry for all entries, e.g.:
[Link]("componentName_uiid", "blue");
Otherwise the component will stay red for the next entry (since the renderer acts like a rubber
stamp).
Rendering Prototype
Because of the rendering architecture of a List its pretty hard to calculate the right preferred size
for such a component. The default behavior includes querying a few entries from the model then
constructing their renderers to get a "sample" of the preferred size value.
As you might guess this triggers a performance penalty that is paid with every reflow of the UI. The
275
solution is to use setRenderingPrototype.
setRenderingPrototype accepts a "fake" value that represents a reasonably large amount of data and
it will be used to calculate the preferred size. E.g. for a multiList that should render 2 lines of text
with 20 characters and a 5mm square icon I can do something like this:
7.17.9. ComboBox
The ComboBox UI paradigm isn’t as common on OS’s such as iOS where there is no
native equivalent to it. We recommend using either the Picker
[[Link] class or the
AutoCompleteTextField [[Link]
[Link]].
ComboBox is notoriously hard to style properly as it relies on a complex dynamic of popup renderer
and instantly visible renderer. The UIID for the ComboBox is ComboBox however if you set it to
something else all the other UIID’s will also change their prefix. E.g. the ComboBoxPopup UIID will
become MyNewUIIDPopup.
• ComboBox
• ComboBoxItem
• ComboBoxFocus
• PopupContentPane
• PopupItem
• PopupFocus
The ComboBox also defines theme constants that allow some native themes to manipulate its
behavior e.g.:
• popupTitleBool - shows the "label for" value as the title of the popup dialog
• centeredPopupBool - shows the popup dialog in the center of the screen instead of under the
276
popup
• otherPopupRendererBool - Uses a different list cell render for the popup than the one used for the
ComboBox itself. When this is false PopupItem & PopupFocus become irrelevant. Notice that the
Android native theme defines this to true.
Since a ComboBox is really a List you can use everything we learned about a List to build a ComboBox
including models, GenericListCellRenderer etc.
E.g. the demo below uses the GRRM demo data from above to build a ComboBox:
7.18. Slider
A Slider [[Link] is an empty component that
can be filled horizontally or vertically to allow indicating progress, setting volume etc. It can be
editable to allow the user to determine its value or none editable to just relay that information to
the user. It can have a thumb on top to show its current position.
The interesting part about the slider is that it has two separate style UIID’s, Slider & SliderFull. The
Slider UIID is always painted and SliderFull is rendered on top based on the amount the Slider
should be filled.
Slider is highly customizable e.g. a slider can be used to replicate a 5 star rating widget as such.
Notice that this slider will only work when its given its preferred size otherwise additional stars
will appear. That’s why we place it within a FlowLayout:
277
[Link]();
The slider itself is initialized in the code below. Notice that you can achieve almost the same result
using a theme by setting the Slider & SliderFull UIID’s (both in selected & unselected states).
In fact doing this in the theme might be superior as you could use one image that contains 5 stars
already and that way you won’t need the preferred size hack below:
This slider goes all the way to 0 stars which is less common. You can use a Label to
represent the first star and have the slider work between 0 - 8 values to provide 4
additional stars.
278
7.19. Table
Table [[Link] is a composite component
(but it isn’t a lead component), this means it is a subclass of Container [[Link]
javadoc/com/codename1/ui/[Link]]. It’s effectively built from multiple components.
In the sample above the title area and first column aren’t editable. The other two
columns are editable.
The more "interesting" capabilities of the Table class can be utilized via the TableLayout. You can use
the layout constraints (also exposed in the table class) to create spanning and elaborate UI’s.
E.g.:
279
{"Row 1", "Row A", "Row X"},
{"Row 2", "Row B can now stretch", null},
{"Row 3", "Row C", "Row Z"},
{"Row 4", "Row D", "Row K"},
}) {
public boolean isCellEditable(int row, int col) {
return col != 0;
}
};
Table table = new Table(model) {
@Override
protected [Link] createCellConstraint(Object value, int row, int column) {
[Link] con = [Link](value, row, column);
if(row == 1 && column == 1) {
[Link](2);
}
[Link](33);
return con;
}
};
[Link]([Link], table);
In order to customize the table cell behavior you can derive the Table to create a "renderer like"
widget, however unlike the list this component is "kept" and used as is. This means you can bind
listeners to this component and work with it as you would with any other component in Codename
One.
280
cell = [Link](value, row, column, editable);
}
if(row > -1 && row % 2 == 0) { ⑤
// pinstripe effect
[Link]().setBgColor(0xeeeeee);
[Link]().setBgTransparency(255);
}
return cell;
}
@Override
protected [Link] createCellConstraint(Object value, int row, int column) {
[Link] con = [Link](value, row, column);
if(row == 1 && column == 1) {
[Link](2);
}
[Link](33);
return con;
}
};
① The createCell method is invoked once per component but is similar conceptually to the List
renderer. Notice that it doesn’t return a "rubber stamp" though, it returns a full component.
③ We need to set the value of the component manually, this is crucial since the Table doesn’t "see"
this.
④ We need to track the event and update the model in this case as the Table isn’t aware of the data
change.
⑤ We set the "pinstripe" effect by coloring even rows. Notice that unlike renderers we only need to
apply the coloring once as the Components are stateful.
Figure 216. Table with customize cells using the pinstripe effect
281
Figure 217. Picker table cell during edit
To line wrap table cells we can just override the createCell method and return a TextArea
[[Link] instead of a TextField
[[Link] since the TextArea defaults to the
multi-line behavior this should work seamlessly. E.g.:
@Override
protected [Link] createCellConstraint(Object value, int row, int column) {
[Link] con = [Link](value, row, column);
[Link](33);
return con;
}
};
[Link]([Link], table);
[Link]();
Notice that we don’t really need to do anything else as binding to the TextArea is
builtin to the Table.
We must set the column width constraint when we want multi-line to work.
Otherwise the preferred size of the column might be too wide and the remaining
columns might not have space left.
282
Figure 218. Multiline table cell in portrait mode
Figure 219. Multiline table cell in landscape mode. Notice the cell row count adapts seamlessly
Sorting tables by clicking the titles is something that should generally work out of the box by using
an API like setSortSupported(true).
Notice this works with numbers, Strings and might work with dates but you can generally support
any object type by overriding the method protected Comparator createColumnSortComparator(int
column) which should return a comparator for your custom object type in the column.
7.20. Tree
Tree [[Link] allows displaying
hierarchical data such as folders and files in a collapsible/expandable UI. Like the Table it is a
composite component (but it isn’t a lead component). Like the Table it works in consort with a
model to construct its user interface on the fly but doesn’t use a stateless renderer (as List does).
The data of the Tree arrives from a model model e.g. this:
283
class StringArrayTreeModel implements TreeModel {
String[][] arr = new String[][] {
{"Colors", "Letters", "Numbers"},
{"Red", "Green", "Blue"},
{"A", "B", "C"},
{"1", "2", "3"}
};
284
Since Tree is hierarchy based we can’t have a simple model like we have for the
Table as deep hierarchy is harder to represent with arrays.
A more practical "real world" example would be working with XML data. We can use something
like this to show an XML Tree:
The try(Stream) syntax is a try with resources clogic that implicitly closes the
stream.
285
if(parent == null) {
Vector c = new Vector();
[Link](root);
return c;
}
Vector result = new Vector();
Element e = (Element)parent;
for(int iter = 0 ; iter < [Link]() ; iter++) {
[Link]([Link](iter));
}
return result;
}
7.21. ShareButton
ShareButton [[Link] is a
button you can add into the UI to let a user share an image or block of text.
The ShareButton uses a set of predefined share options on the simulator. On Android & iOS the
ShareButton is mapped to the OS native sharing functionality and can share the image/text with the
services configured on the device (e.g. Twitter, Facebook etc.).
286
Log.e(err);
}
[Link](imageFile, "image/png");
Figure 223. The share button running on the Android device and screenshot sent into twitter
The ShareButton features some share service classes to allow plugging in additional
share services. However, this functionality is only relevant to devices where native
sharing isn’t supported. So this code isn’t used on iOS/Android…
7.22. Tabs
The Tabs [[Link] Container arranges
components into groups within "tabbed" containers. Tabs is a container type that allows leafing
through its children using labeled toggle buttons. The tabs can be placed in multiple different ways
(top, bottom, left or right) with the default being determined by the platform. This class also allows
swiping between components to leaf between said tabs (for this purpose the tabs themselves can
also be hidden).
287
Since Tabs are a Container its a common mistake to try and add a Tab using the add method. That
method won’t work since a Tab can have both an Image and text String associated with it.
[Link]([Link], t);
A common usage for Tabs is the the swipe to proceed effect which is very common in iOS
applications. In the code below we use RadioButton [[Link]
codename1/ui/[Link]] and LayeredLayout [[Link]
ui/layouts/[Link]] with hidden tabs to produce that effect:
Style s = [Link]().getComponentStyle("Button");
FontImage radioEmptyImage = [Link](FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, s);
FontImage radioFullImage = [Link](FontImage.MATERIAL_RADIO_BUTTON_CHECKED, s);
((DefaultLookAndFeel)[Link]().getLookAndFeel()).setRadioButtonImages(radioFullImage, radioEmptyImage,
radioFullImage, radioEmptyImage);
288
[Link](t);
[Link]([Link](tabsFlow));
Notice that we used setRadioButtonImages to explicitly set the radio button images
to the look we want for the carousel.
289
The MediaPlayer [[Link]
allows you to control video playback. To use the MediaPlayer we need to first load the Media object
from the MediaManager [[Link]
The MediaManager is the core class responsible for media interaction in Codename One.
In the demo code below we use the gallery functionality to pick a video from the device’s video
gallery.
290
Figure 228. Video playback running on an Android device. Notice the native playback controls that appear
when the video is tapped
Video playback in the simulator will only work with JavaFX enabled. This is the
default for Java 8 or newer so we recommend using that.
7.24. ImageViewer
The ImageViewer [[Link]
allows us to inspect, zoom and pan into an image. It also allows swiping between images if you
have a set of images (using an image list model).
The ImageViewer is a complex rich component designed for user interaction. If you
just want to display an image use Label [[Link]
codename1/ui/[Link]]
ScaleImageLabel
if you want the image to scale seamlessly use
[[Link]
[Link]].
You can use the ImageViewer as a tool to view a single image which allows you to zoom in/out to that
image as such:
You can simulate pinch to zoom on the simulator by dragging the right button
away from the top left corner to zoom in and towards the top left corner to zoom
out. On Mac touchpads you can drag two fingers to achieve that.
291
Figure 229. ImageViewer as the demo loads with the image from the default icon
We can work with a list of images to produce a swiping effect for the image viewer where you can
swipe from one image to the next and also zoom in/out on a specific image:
292
Figure 231. An ImageViewer with multiple elements is indistinguishable from a single ImageViewer with
the exception of swipe
EncodedImage’s aren’t always fully loaded and so when you swipe if the images are
really large you might see delays!
You can dynamically download images directly into the ImageViewer with a custom list model like
this:
public ImageList() {
[Link] = new EncodedImage[[Link]];
}
293
public int getSize() {
return [Link];
}
[4]
Figure 232. Dynamically fetching an image URL from the internet
This fetches the images in the URL asynchronously and fires a data change event when the data
arrives to automatically refresh the ImageViewer when that happens.
294
7.25. ScaleImageLabel & ScaleImageButton
ScaleImageLabel [[Link] &
ScaleImageButton [[Link]
allow us to position an image that will grow/shrink to fit available space. In that sense they differ
from Label & Button which keeps the image at the same size.
You can use ScaleImageLabel/ScaleImageButton interchangeably. The only major difference between
these components is the buttons ability to handle click events/focus.
Here is a simple example that also shows the difference between the scale to fill and scale to
fit modes:
Figure 233. ScaleImageLabel/Button, the top row includes scale to fit versions (the default) whereas the
bottom row includes the scale to fill versions
When styling these components keep in mind that changing attributes such as
background behavior might cause an issue with their functionality or might not
work.
7.26. Toolbar
The Toolbar [[Link] API provides deep
customization of the title bar area with more flexibility e.g. placing a TextField
[[Link] for search or buttons in arbitrary
title area positions. The Toolbar API replicates some of the native functionality available on
295
Android/iOS and integrates with features such as the side menu to provide very fine grained
control over the title area behavior.
The Toolbar needs to be installed into the Form in order for it to work. You can setup the Toolbar in
one of these three ways:
1. [Link](new Toolbar()); - allows you to activate the Toolbar to a specific Form and not
for the entire application
2. [Link](true); - enables the Toolbar for all the forms in the app
The basic functionality of the Toolbar includes the ability to add a command to the following 4
places:
• Side menu bar (the drawer that opens when you click the icon on the top left or swipe the
screen from left to right) - addCommandToSideMenu
• Overflow menu (the menu that opens when you tap the 3 vertical dots in the top right corner) -
addCommandToOverflowMenu
[Link](true);
296
Figure 236. The overflow menu of the Toolbar
Normally you can just set a title with a String but if you would want the component to be a text
field or a multi line label you can use setTitleComponent(Component) which allows you to install any
component into the title area.
The code below demonstrates searching using custom code however the Toolbar
also has builtin support for search covered in the next section
The customization of the title area allows for some pretty powerful UI effects e.g. the code below
allows searching dynamically within a set of entries and uses some very neat tricks:
[Link](true);
Style s = [Link]().getComponentStyle("Title");
297
}
[Link]().animateLayout(250);
});
[Link]().addCommandToRightBar("", searchIcon, (e) -> {
[Link](); ④
});
① We use a TextField the whole time and just style it to make it (and its hint) look like a regular
title. An alternative way is to replace the title component dynamically.
③ Hidden & Visible use the opposite flag values to say similar things (e.g. when hidden is set to
false you would want to set visible to true).
Visible indicates whether a component can be seen. It will still occupy the physical space on the
screen even when it isn’t visible. Hidden will remove the space occupied by the component from
the screen, but some code might still try to paint it. Normally, visible is redundant but we use it
with hidden for good measure.
④ The search button is totally unnecessary here. We can just click the TextField!
However, that isn’t intuitive to most users so we added the button to start editing.
298
Figure 238. Search field after typing a couple of letters
While you can implement search manually using the builtin search offers a simpler and more
uniform UI.
You can customize the appearance of the search bar by using the UIID’s: ToolbarSearch,
TextFieldSearch & TextHintSearch.
In the sample below we fetch all the contacts from the device and enable search thru them, notice it
expects and image called [Link] which is really just the default Codename One icon renamed and
placed in the src folder:
299
m.setTextLine2([Link]());
Image pic = [Link]();
if(pic != null) {
[Link](fill(pic, [Link](), [Link]()));
} else {
[Link](finalDuke);
}
[Link](m);
}
[Link]();
});
});
[Link]().addSearchCommand(e -> {
String text = (String)[Link]();
if(text == null || [Link]() == 0) {
// clear search
for(Component cmp : [Link]()) {
[Link](false);
[Link](true);
}
[Link]().animateLayout(150);
} else {
text = [Link]();
for(Component cmp : [Link]()) {
MultiButton mb = (MultiButton)cmp;
String line1 = mb.getTextLine1();
String line2 = mb.getTextLine2();
boolean show = line1 != null && [Link]().indexOf(text) > -1 ||
line2 != null && [Link]().indexOf(text) > -1;
[Link](!show);
[Link](show);
}
[Link]().animateLayout(150);
}
}, 4);
[Link]();
A common feature in side menu bar is the ability to add a component to the "south" part of the side
menu.
Notice that this feature only works with the on-top and permanent versions of the side menu and
not with the legacy versions:
[Link](myComponent);
This places the component below the side menu bar. Notice that this component controls its entire
UIID & is separate from the SideNavigationPanel UIID so if you set that component you might want
to place it within a container that has the SideNavigationPanel UIID so it will blend with the rest of
300
the UI.
Modern UI’s often animate the title upon scrolling to balance the highly functional smaller title
advantage with the gorgeous large image based title. This is pretty easy to do with the Toolbar API
thru the Title animation API.
[5]
The code below shows off an attractive title based on a book by GRRM on top of text that is
scrollable. As the text is scrolled the title fades out.
[Link](true);
301
Figure 240. The Toolbar starts with the large URLImage fetched from the web
Figure 241. As we scroll down the image fades and the title shrinks in size returning to the default UIID look
Almost all of the code above just creates the "look" of the application. The key piece of code above is
this:
In the first line we create a style animation that will translate the style from the current settings to
the destination UIID (the first argument) within 200 pixels of scrolling. We then bind this animation
to the title scrolling animation event.
302
7.27. BrowserComponent & WebBrowser
BrowserComponent is a peer component, understanding this is crucial if your
application depends on such a component. You can learn about peer components
and their issues here [[Link]
peer-components].
Figure 243. Browser Component showing the Codename One website on the simulator
The scrollbars only appear in the simulator, device versions of the browser
component act differently and support touch scrolling.
You can use WebBrowser and BrowserComponent interchangeably for most basic usage. However, if you
need access to JavaScript or native browser functionality then there is really no use in going thru
303
the WebBrowser abstraction.
The BrowserComponent has full support for executing local web pages from within the jar. The basic
support uses the jar:/// URL as such:
On Android a native indicator might show up when the web page is loading. This
can be disabled using the [Link]().setProperty("WebLoadingHidden",
"true"); call. You only need to invoke this once.
When Codename One packages applications into native apps it hides a lot of details to make the
process simpler. One of the things hidden is the fact that we aren’t dealing with a JAR anymore, so
getResource/getResourceAsStream are problematic… Both of these API’s support hierarchies and a
concept of package relativity both of which might not be supported on all OS’s.
That’s why we recommend that you place files inside res files. A resource file
allows you to add arbitrary data files and you can have as many resource files as
you need.
For web developers this isn’t enough since hierarchies are used often to represent the various
dependencies, this means that many links & references are relative. To work with such hierarchies
just place all of your resources in a hierarchy under the html package in the project source
directory (src/html). The build server will tar the entire content of that package and add an
[Link] file into the native package. This tar is seamlessly extracted on the device when you
actually need the resources and only with new application versions (not on every launch). So
assuming the resources are under the html root package they can be displayed with code like this:
try {
[Link]("/[Link]");
} catch(IOException err) {
...
}
Notice that the path is relative to the html directory and starts with / but inside the HTML files you
should use relative (not absolute) paths.
Also notice that an IOException can be thrown due to the process of untarring. Its unlikely to
happen but is entirely possible.
304
7.27.2. NavigationCallback
The shouldNavigate indicates to the native code whether navigation should proceed or not. E.g. if a
user clicks a specific link we might choose to do something in the Java code so we can just return
false and block the navigation. We can invoke callSerially [[Link]
codename1/ui/[Link]#[Link]-] to do the actual task in the Java side.
Figure 244. Before the link is clicked for the "shouldNavigate" call
305
Figure 245. After the link is clicked for the "shouldNavigate" call
7.27.3. JavaScript
The JavaScript bridge is sometimes confused with the JavaScript Port. The
JavaScript bridge allows us to communicate with JavaScript from Java (and visa
versa). The JavaScript port allows you to compile the Codename One application
into a JavaScript application that runs in a standard web browser without code
changes (think GWT without source changes and with thread support).+ We
discuss the JavaScript port further later in the guide.
Codename One 4.0 introduced a new API for interacting with Javascript in Codename One. This API
is part of the BrowserComponent class, and effectively replaces the [Link] package
[[Link] which is now
deprecated.
The old API provided a synchronous wrapper around an inherently asynchronous process, and
made extensive use of invokeAndBlock() underneath the covers. This resulted in a very nice API
with high-level abstractions that played nicely with a synchronous programming model, but it
came with a price-tag in terms of performance, complexity, and predictability. Let’s take a simple
example, getting a reference to the "window" object:
This code looks harmless enough, but this is actually quite expensive. It issues a command to the
BrowserComponent, and uses invokeAndBlock() to wait for the command to go through and send back
a response. invokeAndBlock() is a magical tool that allows you to "block" without blocking the EDT,
but it has its costs, and shouldn’t be overused. Most of the Codename One APIs that use
invokeAndBlock() indicate this in their name. E.g. [Link](). This gives you
the expectation that this call could take some time, and helps to alert you to the underlying cost.
The problem with the [Link]("window") call is that it looks the same as a call to [Link](key).
There’s no indication that this call is expensive and could take time. One call like this probably isn’t
a big deal, but it doesn’t take long before you have dozens or even hundreds of calls like this littered
throughout your codebase, and they can be hard to pick out.
The new API fully embraces the asynchronous nature of Javascript. It uses callbacks instead of
return values, and provides convenience wrappers with the appropriate "AndWait()" naming
306
convention to allow for synchronous usage. Let’s look at a simple example:
In all of the sample code below, you can assume that variables named bc represent
an instance of BrowserComponent
codename1/ui/[Link]].
[[Link]
[Link](
"[Link](3+4)",
res -> Log.p("The result was "+[Link]())
);
This code should output "The result was 7" to the console. It is fully asynchronous, so you can
include this code anywhere without worrying about it "bogging down" your code. The full signature
of this form of the execute() [[Link]
[Link]#[Link]-] method is:
The first parameter is just a javascript expression. This javascript MUST call either
[Link](result) or [Link](message, errCode) at some point in order for your
callback to be called.
The second parameter is your callback that is executed from the javascript side, when
[Link](res) is called. The callback takes a single parameter of type JSRef
[[Link] which is a generic
wrapper around a javascript variable. JSRef has accessors to retrieve the value as some of the
primitive types. E.g. getBoolean(), getDouble(), getInt(), toString(), and it provides some
introspection via the getType() method.
It is worth noting that the callback method can only take a single parameter. If you
need to pass multiple parameters, you may consider including them in a single
string which you parse in your callback.
Synchronous Wrappers
As mentioned before, the new API also provides an executeAndWait() wrapper for execute() that
will work synchronously. It, as its name suggests, uses invokeAndBlock under the hood so as not to
block the EDT while it is waiting.
E.g.
307
When using the andWait() variant, it is extremely important that your Javascript
calls your callback method at some point - otherwise it will block indefinitely. We
provide variants of executeAndWait() that include a timeout in case you want to
hedge against this possibility.
Multi-use Callbacks
The callbacks you pass to execute() and executeAndWait() are single-use callbacks. You can’t, for
example, store the callback variable on the javascript side for later use (e.g. to respond to a button
click event). If you need a "multi-use" callback, you should use the addJSCallback() method instead.
Its usage looks identical to execute(), the only difference is that the callback will life on after its first
use. E.g. Consider the following code:
[Link](
"$('#somebutton').click(function(){[Link]('Button was clicked')})",
res -> Log.p([Link]())
);
The above example, assumes that jQuery is loaded in the webpage that we are interacting with, and
we are adding a click handler to a button with ID "somebutton". The click handler calls our
callback.
If you run this example, the first time the button is clicked, you’ll see "Button was clicked" printed
to the console as expected. However, the 2nd time, you’ll just get an exception. This is because the
callback passed to execute() is only single-use.
[Link](
"$('#somebutton').click(function(){[Link]('Button was clicked')})",
res -> Log.p([Link]())
);
Now it will work no matter how many times the button is clicked.
In many cases, the javascript expressions that you execute will include parameters from your java
code. Properly escaping these parameters is tricky at worst, and annoying at best. E.g. If you’re
passing a string, you need to make sure that it escapes quotes and new lines properly or it will
cause the javascript to have a syntax error. Luckily we provide variants of execute() and
addJSCallback() [[Link]
[Link]-] that allow you to pass your parameters
and have them automatically escaped.
For example, suppose we want to pass a string with text to set in a textarea within the webpage. We
can do something like:
308
[Link](
"jQuery('#bio').text(${0}); jQuery('#age').text(${1})",
new Object[]{
"A multi-line\n string with \"quotes\"",
27
}
);
The gist is that you embed placeholders in the javascript expression that are replaced by the
corresponding entry in an array of parameters. The ${0} placeholder is replaced by the first item in
the parameters array, the ${1} placeholder is replaced by the 2nd, and so on.
Proxy Objects
Or synchronously:
[Link]("href", "[Link]
309
Legacy JSObject Support
This section describes the now deprecated JSObject approach. It’s here for reference by developers
working with older code. We suggest using the new API when starting a new project.
BrowserComponent can communicate with the HTML code using JavaScript calls. E.g. we can create
HTML like this:
Figure 246. JavaScript code was invoked to append text into the browser image above
Notice that opening an alert in an embedded native browser might not work
We use the execute method above to execute custom JavaScript code. We also have an
executeAndReturnString method that allows us to receive a response value from the JavaScript side.
Coupled with shouldNavigate we can effectively do everything which is exactly what the JavaScript
Bridge tries to do.
310
The JavaScript Bridge
While it’s possible to just build everything on top of execute and shouldNavigate, both of these
methods have their limits. That is why we introduced the javascript package, it allows you to
communicate with JavaScript using intuitive code/syntax.
This conversion table is more verbose than necessary, since JavaScript functions
and arrays are, in fact Objects themselves, so those rows are redundant. All
JavaScript objects are converted to JSObject [[Link]
com/codename1/javascript/[Link]].
We can access JavaScript variables easily from the context by using code like this:
311
" </body>\n" +
"</html>", null);
[Link]([Link], bc);
[Link]("onLoad", (e) -> {
// Create a Javascript context for this BrowserComponent
JavascriptContext ctx = new JavascriptContext(bc);
Figure 247. The contents was copied from the DOM and placed in the south position of the form
Notice that when you work with numeric values or anything related to the types mentioned above
your code must be aware of the typing. E.g. in this case the type is Double and not String:
You can also query the context for objects and modify their value e.g.
JSObject jo = (JSObject)[Link]("window");
312
[Link]("location", "[Link]
});
This code effectively navigates to the Codename One home page by fetching the DOM’s window
object and setting its location property to [Link]
[[Link]
PhoneGap was one of the first web app packager tools in the market. It’s a tool that is effectively a
browser component within a native wrapper coupled with native access API’s. Cordova is the open
source extension of this popular project.
Codename One supports embedding PhoneGap/Cordova applications directly into Codename One
applications. This is relatively easy to do with the BrowserComponent and JavaScript integration. The
main aspect that this integration requires is support for Cordova plugins & its JavaScript API’s.
The effort to integrate Cordova/PhoneGap support into Codename One is handled within an open
source github project here [[Link] The chief benefits of picking
Codename One rather than using Cordova directly are:
• Build Cloud
• Better Protection Of IP
• Migration To Java
7.28. AutoCompleteTextField
The AutoCompleteTextField [[Link]
[Link]] allows us to write text into a text field and select a completion entry from
the list in a similar way to a search engine.
This is really easy to incorporate into your code, just replace your usage of TextField
[[Link] with AutoCompleteTextField and
define the data that the autocomplete should work from. There is a default implementation that
accepts a String array or a ListModel [[Link]
[Link]] for completion strings, this can work well for a "small" set of thousands (or tens of
thousands) of entries.
E.g. This is a trivial use case that can work well for smaller sample sizes:
313
Form hi = new Form("Auto Complete", new BoxLayout(BoxLayout.Y_AXIS));
AutoCompleteTextField ac = new AutoCompleteTextField("Short", "Shock", "Sholder", "Shrek");
[Link](5);
[Link](ac);
However, if you wish to query a database or a web service you will need to derive the class and
perform more advanced filtering by overriding the filter method:
[Link]();
for(String s : l) {
[Link](s);
}
return true;
}
};
[Link](5);
[Link](ac);
[Link](new SpanLabel("This demo requires a valid google API key to be set below "
+ "you can get this key for the webservice (not the native key) by following the instructions here: "
+ "[Link]
[Link](apiKey);
[Link]().addCommandToRightBar("Get Key", null, e -> [Link]().execute
("[Link]
[Link]();
}
314
[Link]("[Link]
[Link]("key", [Link]());
[Link]("input", text);
[Link]().addToQueueAndWait(r);
Map<String,Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r
.getResponseData()), "UTF-8"));
String[] res = [Link](result).getAsStringArray("//description");
return res;
}
} catch(Exception err) {
Log.e(err);
}
return null;
}
One question I got a few times is "How do you customize the results of the auto complete field"?
This sounds difficult to most people as we can only work with Strings so how do we represent
additional data or format the date correctly?
The answer is actually pretty simple, we still need to work with Strings because auto-complete is
first and foremost a text field. However, that doesn’t preclude our custom renderer from fetching
data that might be placed in a different location and associated with the result.
The following source code presents an auto-complete text field with images in the completion
popup and two lines for every entry:
final String[] characters = { "Tyrion Lannister", "Jaime Lannister", "Cersei Lannister", "Daenerys Targaryen",
"Jon Snow", "Petyr Baelish", "Jorah Mormont", "Sansa Stark", "Arya Stark", "Theon Greyjoy"
// snipped the rest for clarity
};
[Link](new ListCellRenderer() {
private final Label focus = new Label(); ②
315
private final Label line1 = new Label(characters[0]);
private final Label line2 = new Label(actors[0]);
private final Label icon = new Label(pictures[0]);
private final Container selection = [Link](
[Link](line1, line2)).add([Link], icon);
@Override
public Component getListCellRendererComponent([Link] list, Object value, int index, boolean
isSelected) {
for(int iter = 0 ; iter < [Link] ; iter++) {
if(characters[iter].equals(value)) {
[Link](characters[iter]);
if([Link] > iter) {
[Link](actors[iter]);
[Link](pictures[iter]);
} else {
[Link](""); ③
[Link](placeholder);
}
break;
}
}
return selection;
}
@Override
public Component getListFocusComponent([Link] list) {
return focus;
}
});
[Link](ac);
[Link]();
① We have duplicate arrays that are only partial for clarity. This is a separate list of data element
but you can fetch the additional data from anywhere
② We create the renderer UI instantly in the fields with the helper methods for wrapping elements
which is pretty cool & terse
③ In a renderer it’s important to always set the value especially if you don’t have a value in place
7.29. Picker
Picker [[Link] occupies the limbo
between native widget and lightweight widget. Picker is more like TextField/TextArea in the sense
that it’s a Codename One widget that calls the native code only during editing.
The reasoning for this is the highly native UX and functionality related to this widget type which
316
should be quite obvious from the screenshots below.
• Time
• Date
• Strings
If a platform doesn’t support native pickers an internal fallback implementation is used. This is the
implementation we always use in the simulator so assume different behavior when building for the
device.
While Android supports Date, Time native pickers it doesn’t support the Date &
Time native picker UX and will fallback in that case.
[Link](new Date());
[Link](new Date());
[Link](10 * 60); // 10:00AM = Minutes since midnight
[Link]("A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows",
"A Dance With Dragons", "The Winds of Winter", "A Dream of Spring");
[Link]("A Game of Thrones");
[Link](datePicker).add(dateTimePicker).add(timePicker)
.add(stringPicker).add(durationPicker)
.add(minuteDurationPicker).add(hourDurationPicker);
[Link]();
317
Figure 251. The various picker components
Figure 254. Date & time picker on Android. Notice it didn’t use a builtin widget since there is none
318
Figure 255. String picker on the native Android device
319
Figure 259. Minutes Duration picker on Android device
The text displayed by the picker on selection is generated automatically by the updateValue()
method. You can override it to display a custom formatted value and call setText(String) with the
correct display string.
A common use case is to format date values based on a specific appearance and Picker has builtin
support for a custom display formatter. Just use the setFormatter(SimpleDateFormat) method and set
the appearance for the field.
7.30. SwipeableContainer
The SwipeableContainer [[Link]
allows us to place a component such as a MultiButton [[Link]
codename1/components/[Link]] on top of additional "options" that can be exposed by swiping
the component to the side.
This swipe gesture is commonly used in touch interfaces to expose features such as delete, edit etc.
It’s trivial to use this component by just determining the components placed on top and bottom (the
revealed component).
We can combine some of the demos above including the Slider stars demo to rank GRRM’s books in
an interactive way:
320
button);
}
7.31. EmbeddedContainer
EmbeddedContainer [[Link]
solves a problem that exists only within the GUI builder and the class makes no sense outside of the
context of the GUI builder. The necessity for EmbeddedContainer came about due to iPhone inspired
designs that relied on tabs (iPhone style tabs at the bottom of the screen) where different features
of the application are within a different tab.
This didn’t mesh well with the GUI builder navigation logic and so we needed to rethink some of it.
We wanted to reuse GUI as much as possible while still enjoying the advantage of navigation being
completely managed for me.
Android does this with Activities and the iPhone itself has a view controller, both approaches are
problematic for Codename One. The problem is that you have what is effectively two incompatible
hierarchies to mix and match.
Navigation replaces the content of the embedded container; it finds the embedded container based
on the component that broadcast the event. If you want to navigate manually just use the
showContainer() method which accepts a component, you can give any component that is under
the EmbeddedContainer you want to replace and Codename One will be smart enough to replace only
that component.
The nice part about using the EmbeddedContainer is that the resulting UI can be very easily refactored
to provide a more traditional form based UI without duplicating effort and can be easily adapted to
a more tablet oriented UI (with a side bar) again without much effort.
321
7.32. MapComponent
The MapComponent uses a somewhat outdated tiling API which is not as rich as
modern native maps. We recommend using the GoogleMap’s Native cn1lib
[[Link] to integrate native mapping
functionality into the Codename One app.
The code was contributed by Roman Kamyk and was originally used for a LWUIT application.
try {
//get the current location from the Location API
Location loc = [Link]().getCurrentLocation();
322
[Link]();
}
[Link]();
[Link]([Link], mc);
[Link](new BackCommand());
[Link](new BackCommand());
[Link]();
323
+ "google places api's"
, "Ok", null);
return;
}
Image im = [Link]("/red_pin.png");
PointsLayer pl = new PointsLayer();
[Link](im);
[Link](new ActionListener() {
[Link](pl);
[Link]();
[Link]();
}
};
[Link]("[Link]
[Link](false);
[Link]("location", "" + [Link]() + "," + [Link]());
[Link]("radius", "500");
[Link]("types", "food");
[Link]("sensor", "false");
[Link]("key", key);
[Link]().addToQueue(req);
}
catch (IOException ex) {
[Link]();
}
}
324
7.33. Chart Component
The charts package enables Codename One developers to add charts and visualizations to their
apps without having to include external libraries or embedding web views. We also wanted to
harness the new features in the graphics pipeline to maximize performance.
Since the charts package makes use of 2D transformations and shapes, it requires some of the
graphics features that are not yet available on all platforms. Currently the following platforms are
supported:
1. Simulator
2. Android
3. iOS
7.33.2. Features
1. Built-in support for many common types of charts including bar charts, line charts, stacked
charts, scatter charts, pie charts and more.
The [Link] package includes models and renderers for many different types of
charts. It is also extensible so that you can add your own chart types if required. The following
screen shots demonstrate a small sampling of the types of charts that can be created.
325
Figure 264.
Cubic Line
Charts
Figure 266.
Stacked Bar
Charts
Figure 267.
Range Bar
Charts
326
Figure 269.
Doughnut
Charts
Figure 270.
Scatter Charts
Figure 272.
Combined
Charts
Figure 273.
Bubble Charts
327
Figure 274.
Time Charts
The above screenshots were taken from the ChartsDemo app [[Link]
codenameone/codenameone-demos/tree/master/ChartsDemo]. You can start playing with
this app by checking it out from our git repository.
1. Build the model. You can construct a model (aka data set) for the chart using one of the existing
model classes in the [Link] package. Essentially, this is just where you add
the data that you want to display.
2. Set up a renderer. You can create a renderer for your chart using one of the existing renderer
classes in the [Link] package. The renderer allows you to specify how
the chart should look. E.g. the colors, fonts, styles, to use.
3. Create the Chart View. Use one of the existing view classes in the [Link]
package.
/**
* Creates a renderer for the specified colors.
*/
private DefaultRenderer buildCategoryRenderer(int[] colors) {
DefaultRenderer renderer = new DefaultRenderer();
[Link](15);
[Link](15);
[Link](new int[]{20, 30, 15, 0});
for (int color : colors) {
SimpleSeriesRenderer r = new SimpleSeriesRenderer();
[Link](color);
[Link](r);
}
return renderer;
}
/**
328
* Builds a category series using the provided values.
*
* @param titles the series titles
* @param values the values
* @return the category series
*/
protected CategorySeries buildCategoryDataset(String title, double[] values) {
CategorySeries series = new CategorySeries(title);
int k = 0;
for (double value : values) {
[Link]("Project " + ++k, value);
}
return series;
}
// Create the chart ... pass the values and renderer to the chart object.
PieChart chart = new PieChart(buildCategoryDataset("Project budget", values), renderer);
7.34. Calendar
The Calendar [[Link] class allows us to
display a traditional calendar picker and optionally highlight days in various ways.
We normally recommend developers use the Picker UI rather than use the
calendar to pick a date. It looks better on the devices.
329
Form hi = new Form("Calendar", new BorderLayout());
Calendar cld = new Calendar();
[Link]((e) -> Log.p("You picked: " + new Date([Link]())));
[Link]([Link], cld);
7.35. ToastBar
The ToastBar [[Link] class allows
us to display none-obtrusive status messages to the user at the bottom of the screen. This is useful
for such things as informing the user of a long-running task (like downloading a file in the
background), or popping up an error message that doesn’t require a response from the user.
330
Status status = [Link]().createStatus();
[Link]("Hello world");
[Link](true);
[Link]();
We can automatically clear a status message/progress after a timeout using the setExpires method
as such:
We can also delay the showing of the status message using showDelayed as such:
// ... Some time later, clear the status... this may be before it shows at all
[Link]();
331
7.35.1. Actions In ToastBar
Probably the best usage example for actions in toast is in the gmail style undo. If you are not a
gmail user then the gmail app essentially never prompts for confirmation!
It just does whatever you ask and pops a "toast message" with an option to undo. So if you clicked
by mistake you have 3-4 seconds to take that back.
This simple example shows you how you can undo any addition to the UI in a similar way to gmail:
[Link](e -> {
Label l = new Label("Added this");
[Link](l);
[Link]();
[Link]("Added, click here to undo...", FontImage.MATERIAL_UNDO,
ee -> {
[Link]();
[Link]();
});
});
[Link](add);
[Link]();
7.36. SignatureComponent
The SignatureComponent [[Link]
[Link]] provides a widget that allows users to draw their signature in the app.
332
Figure 279. The signature Component
7.37. Accordion
The Accordion [[Link] displays
collapsible content panels.
[Link](accr);
[Link]();
333
Figure 280. The Accordion Component
Figure 281. The FloatingHint component with one component that contains text and another that doesn’t
334
application.
FloatingActionButton [[Link]
[Link]] is a round button that resides on top of the UI typically in the bottom right
hand side.
It has a drop shadow to distinguish it from the UI underneath and it can hide two or more
additional actions under the surface. E.g. we can create a simple single click button such as this:
Which will place a + sign button that will perform the action. Alternatively we can create a nested
action where a click on the button will produce a submenu for users to pick from e.g.:
Those familiar with this widget know that there are many nuances to this UI that we might
implement/expose in the future. At the moment we chose to keep the API simple and minimal for
the common use cases and refine it based on feedback.
Floating buttons can also be used to badge an arbitrary component in the style popularized by
iOS/Mac OS. A badge appears at the top right corner and includes special numeric details such as
"unread count"..
335
[Link]([Link]());
[Link]().revalidate();
});
[Link](changeBadgeValue);
[Link]();
The code above results in this, notice you can type into the text field to change the badge value:
7.40. SplitPane
The split pane component is a bit desktop specific but works reasonably well on devices. To get the
image below we changed [Link] in the kitchen sink by changing this:
[Link](encloseInMaximize(grid, cmp1)).
add(encloseInMaximize(grid, cmp2));
return grid;
}
To:
336
Figure 284. Split Pane in the Kitchen Sink Demo
This is mostly self explanatory but only "mostly". We have 5 arguments the first 3 make sense:
• Split orientation
• Components to split
The last 3 arguments seem weird but they also make sense once you understand them, they are:
[1] String width is the real expensive part here, the complexity of font kerning and the recursion required to reflow text is a big
performance hurdle
[2] Image by RegisFrey - Own work, Public Domain, [Link]
[3] Fisheye is an effect where the selection stays in place as the list moves around it
[4] Image was fetched from [Link]
[5] The text below is from A Wiki of Ice & Fire: [Link]
337
338
Chapter 8. Animations
There are many ways to animate and liven the data within a Codename One application, layout
animations are probably chief among them. But first we need to understand some basics such as
layout reflows.
Like many such rules exceptions occur. E.g. if the device is rotated or window size
changes a layout will occur automatically.
When adding a component to a UI that is already visible, the component will not show by default.
When adding a component to a form which isn’t shown on the screen, there is no
need to tell the UI to repaint or reflow. This happens implicitly.
E.g. imagine adding 100 components to a form. If the form was laid out automatically, layout would
have happened 100 times instead of once when adding was finished. In fact layout reflows are
often considered the #1 performance issue for HTML/JavaScript applications.
Smart layout reflow logic can alleviate some of the pains of the automatic layout
reflows however since the process is implicit it’s almost impossible to optimize
complex usages across browsers/devices. A major JavaScript performance tip is to
use absolute positioning which is akin to not using layouts at all!
That is why, when you add components to a form that is already showing, you should invoke
revalidate() or animate the layout appropriately. This also enables the layout animation behavior
explained below.
This won’t work since these methods are meant for the layout manager, which is implicitly invoked
when a form is shown (internally in Codename One). The layout manager uses these methods to
position/size the components based on the hints given to it.
If you add components to a form that is currently showing, it is your responsibility to invoke
339
revalidate/layoutContainer to arrange the newly added components (see Layout Reflows).
animateLayout() method is a fancy form of revalidate that animates the components into their laid
out position. After changing the layout & invoking this method the components move to their new
sizes/positions seamlessly. Form exposes convenience wrappers such as animateLayout*() that simply
forward to the underlying content pane, so you can usually call the methods directly on the form
unless you specifically need to animate a nested container.
This sort of behavior creates a special case where setting the size/position makes sense. When we
set the size/position in the demo code here we are positioning the components at the animation
start position above the frame.
There are a couple of things that you should notice about this example:
① We used a button to do the animation rather than doing it on show. Since show() implicitly lays
out the components it wouldn’t have worked correctly.
② We used [Link](20000);, which delegates to the Form content pane. If you need to
animate a specific container (e.g. a nested layout), call animateLayout() on that container instead.
340
341
342
8.2.1. Unlayout Animations
While layout animations are really powerful effects for adding elements into the UI and drawing
attention to them. The inverse of removing an element from the UI is often more important. E.g.
when we delete or remove an element we want to animate it out.
Layout animations don’t really do that since they will try to bring the animated item into place.
What we want is the exact opposite of a layout animation and that is the "unlayout animation".
[Link](int, int, Runnable) and the [Link]*() helpers let you
trigger this transition either asynchronously (with a completion callback) or synchronously via the
AndWait variants.
The "unlayout animation" takes a valid laid out state and shifts the components to an invalid state
that we defined in advance. E.g. we can fix the example above to flip the "fall" button into a "rise"
button when the buttons come into place and this will allow the buttons to float back up to where
they came from in the exact reverse order.
You will notice some similarities with the unlayout animation but the differences represent the
exact opposite of the layout animation:
343
① We loop over existing components (not newly created ones)
② We set the desired end position not the desired starting position
④ After the animation completes we need to actually remove the elements since the UI is now in
an invalid position with elements outside of the screen but still physically there!
A common trick for animating Components in Codename One is to set their preferred size to 0 and
then invoke animateLayout() thus triggering an animation to hide said Component. There are several
issues with this trick but one of the biggest ones is the fact that setPreferredSize has been
deprecated for quite a while.
Instead of using that trick you can use setHidden/isHidden who effectively encapsulate this
functionality and a bit more.
One of the issues setHidden tries to solve is the fact that preferred size doesn’t include the margin in
the total and thus a component might still occupy space despite being hidden. When you request
the margin adjustment the current margins are cached, the component is given zero margins while
hidden, and those cached values are restored when it is shown again—without resetting the UIID or
other style state.
This functionality might be undesirable which is why there is a version of the setHidden method
that accepts a boolean flag indicating whether the margin cache should be manipulated. You can
effectively hide/show a component without deprecated code using something like this:
Notice that the code above uses setVisible(), which shouldn’t be confused with
setHidden. setVisible() just toggles the visibility of the component it would still
occupy the same amount of space
344
• Standard animation e.g. animateLayout(int) or the non-blocking animateUnlayout(int, int,
Runnable)
The standard animation is invoked when we don’t care about the completion of the animation. We
can do this for a standard animation.
Unlayout animations always leave the container in an invalid state, so even the
standard helper expects you to tidy up either in the callback or immediately after
the animation finishes.
The AndWait variant blocks the calling thread until the animation completes. This is really useful for
sequencing animations one after the other e.g this code from the kitchen sink demo:
arrangeForInterlace(effects);
[Link](800, 20);
[Link](800, 20);
First the UI goes thru an "unlayout" animation, once that completes the layout itself is performed.
The AndWait calls needs to be invoked on the Event Dispatch Thread despite being
"blocking". This is a common convention in Codename One powered by a unique
capability of Codename One: invokeAndBlock.
You can learn more about invokeAndBlock in the EDT section
[[Link]
The callback variant is similar to the invokeAndBlock variant but uses a more conventional callback
semantic which is more familiar to some developers. It accepts a Runnable callback that will be
invoked after the fact. E.g. we can change the unlayout call from before to use the callback
semantics as such:
There are several additional variations on the standard animate methods. Several methods accept a
numeric fade argument. This is useful to fade out an element in an "unlayout" operation or fade in
a regular animation.
The value for the fade argument is a number between 0 and 255 where 0 represents full
transparency and 255 represents full opacity.
345
Some animate layout methods are hierarchy based. They work just like the regular animateLayout
methods but recurse into the entire Container [[Link]
[Link]] hierarchy. These methods work well when you have components in a nested
hierarchy that need to animate into place. This is demonstrated in the opening sequence of the
kitchen sink demo:
The demoComponents Vector contains components from separate containers and this code would not
work with a simple animate layout.
We normally recommend avoiding the hierarchy version. Its slower but more
importantly, it’s flaky. Since the size/position of the Container might be affected by
the layout the animation could get clipped and skip. These are very hard issues to
debug.
[Link](myButton);
int componentCount = [Link]();
[Link](300);
[Link](myButton);
if(componentCount == [Link]()) {
// this will happen...
}
The reason this happens is that the second remove gets postponed to the end of the animation so it
won’t break the animation. This works for remove and add operations on a Container
[[Link] as well as other animations.
346
The simple yet problematic fix would be:
[Link](myButton);
int componentCount = [Link]();
[Link](300);
[Link](myButton);
if(componentCount == [Link]()) {
// this probably won't happen...
}
[1]
Events come in constantly during the run of the EDT , so an event might come in that might
trigger an animation in your code. Even if you are on the EDT keep in mind that you don’t actually
block it and an event might come in.
In those cases an animation might start and you might be unaware of that animation and it might
still be in action when you expect remove to work.
We can flush the animation queue and run synchronously after all the animations finished and
before new ones come in by using something like this:
[Link](myButton);
int componentCount = [Link]();
[Link](300);
[Link]().flushAnimation(() -> {
[Link](myButton);
if(componentCount == [Link]()) {
// this shouldn't happen...
}
});
This is helpful since the callback will always occur on the event dispatch thread.
Every component in Codename One contains an animate() method that returns a boolean value,
you can also implement the Animation [[Link]
animations/[Link]] interface in an arbitrary component to implement your own animation. In
347
order to receive animation events you need to register yourself within the parent form, it is the
responsibility of the parent for to call animate().
If the animate method returns true then the animation will be painted (the paint method of the
Animation interface would be invoked).
If you derive from a component, which has its own animation logic you might damage its
animation behavior by deregistering it, so tread gently with the low level API’s.
E.g. you can add additional animation logic using code like this:
[Link](this);
Animations are comprised of two parts, the logic (deciding the position etc) and the painting. The
paint method should be dedicated to painting only, not to the actual moving of the components.
The separation of concerns allows us to avoid redundant painting e.g. if animate didn’t trigger a
change just return false to avoid the overhead related to animations.
We discuss low level animations in more details within the animation section of the clock demo
[[Link]
8.4. Transitions
Transitions allow us to replace one component with another, most typically forms or dialogs are
replaced with a transition however a transition can be applied to replace any arbitrary component.
Developers can implement their own custom transition and install it to components by deriving the
Transition [[Link] class,
although most commonly the built in CommonTransitions [[Link]
codename1/ui/animations/[Link]] class is used for almost everything.
348
You can define transitions for forms/dialogs/menus globally either via the theme constants or via
the LookAndFeel [[Link] class.
Alternatively you can install a transition on top-level components via setter methods.
In/Out Transitions
When defining a transition we define the entering transition and the exiting transition. For
most cases only one of those is necessary and we default to the exiting (out transition) as a
convention.
So for almost all cases the method setFormTransitonIn should go unused. That API exists for
some elaborate custom transitions that might need to have a special effect both when
transitioning in and out of a specific form. However, most of these effects are easier to
achieve with layout animations (e.g. components dropping into place etc.).
In the case of Dialog the transition in shows its appearance and the transition out shows its
disposal. So in that case both transitions make a lot of sense.
Back/Forward Transitions
Transitions have a direction and can all be played either in incoming or outgoing direction. A
[2]
transition can be flipped (played in reverse) when we use an RTL language or when we
simply traverse backwards in the form navigation hierarchy.
Normally [Link]() displays the next Form with an incoming transition based on the current
RTL mode. If we use [Link]() it will play the transition in reverse.
When working with high level animations you can select Slow Motion option in
the simulator to slow down animations and inspect their details
Themes define the default transitions used when showing a form, these differ based on the OS. In
most platforms the default is Slide whereas in iOS the default is SlideFade which slides the content
pane and title while fading in/out the content of the title area.
SlideFade is problematic without a title area. If you have a Form that lacks a title
area we would recommend to disable SlideFade at least for that Form.
Check out the full set of theme constants in the Theme Constants Section
[[Link]
8.4.1. Replace
To apply a transition to a component we can just use the [Link]() method as such:
349
[Link](replace);
[Link]((e) -> {
[Link]().replaceAndWait(replace, replaceDestiny, [Link](CommonTransitions
.SLIDE_VERTICAL, true, 800));
[Link]().replaceAndWait(replaceDestiny, replace, [Link](CommonTransitions
.SLIDE_VERTICAL, true, 800));
});
Replace even works when you have a layout constraint in place e.g. replacing a
component in a border layout will do the "right thing". However, some layouts
such as TableLayout might be tricky in such cases so we recommend wrapping a
potentially replaceable Component in a border layout and replacing the content.
[Link]() can also be used with a null transition at which point it replaces instantly with
no transition.
The slide transitions are used to move the Form/Component in a sliding motion to the side or up/down.
There are 4 basic types of slide transitions:
2. Fast Slide - historically this provided better performance for old device types. It is no longer
recommended for newer devices
3. Slide Fade - the iOS default where the title area features a fade transition
4. Cover/Uncover - a type of slide transition where only the source or destination form slides while
the other remains static in place
The code below demonstrates the usage of all the main transitions:
[Link](true);
Form hi = new Form("Transitions", new BoxLayout(BoxLayout.Y_AXIS));
Style bg = [Link]().getUnselectedStyle();
[Link](255);
[Link](0xff0000);
Button showTransition = new Button("Show");
Picker pick = new Picker();
[Link]("Slide", "SlideFade", "Cover", "Uncover", "Fade", "Flip");
[Link]("Slide");
TextField duration = new TextField("10000", "Duration", 6, [Link]);
CheckBox horizontal = [Link]("Horizontal");
[Link]((e) -> {
String s = [Link]().toLowerCase();
[Link]([Link]("slide") || [Link]("cover") > -1);
});
[Link](true);
[Link](showTransition).
add(pick).
add(duration).
add(horizontal);
350
[Link](0xff);
[Link](
[Link]().addCommandToLeftBar("Back", null, (e) -> [Link]()));
[Link]((e) -> {
int h = CommonTransitions.SLIDE_HORIZONTAL;
if(![Link]()) {
h = CommonTransitions.SLIDE_VERTICAL;
}
switch([Link]()) {
case "Slide":
[Link]([Link](h, true, [Link](3000)));
[Link]([Link](h, true, [Link](3000)));
break;
case "SlideFade":
[Link]([Link](true, [Link](3000)));
[Link]([Link](true, [Link](3000)));
break;
case "Cover":
[Link]([Link](h, true, [Link](3000)));
[Link]([Link](h, true, [Link](3000)));
break;
case "Uncover":
[Link]([Link](h, true, [Link](3000)));
[Link]([Link](h, true, [Link](3000)));
break;
case "Fade":
[Link]([Link]([Link](3000)));
[Link]([Link]([Link](3000)));
break;
case "Flip":
[Link](new FlipTransition(-1, [Link](3000)));
[Link](new FlipTransition(-1, [Link](3000)));
break;
}
[Link]();
});
[Link]();
Figure 285. The slide transition moves both incoming and outgoing forms together
351
Figure 287. Slide fade fades in the destination title while sliding the content pane it is the default on iOS
SlideFade is problematic without a title area. If you have a Form that lacks a title
area we would recommend to disable SlideFade at least for that Form.
Figure 288. With cover transitions the source form stays in place as it is covered by the destination. This
transition can be played both horizontally and vertically
Figure 289. Uncover is the inverse of cover. The destination form stays in place while the departing form
moves away
The fade transition is pretty trivial and only accepts a time value since it has no directional context.
352
Figure 291. Fade transition is probably the simplest one around
BubbleTransiton [[Link]
morphs a component into another component using a circular growth motion.
The BubbleTransition accepts the component that will grow into the bubble effect as one of its
arguments. It’s generally designed for Dialog transitions although it could work for more creative
use cases:
The code below manipulates styles and look. This is done to make the code more
"self contained". Real world code should probably use the theme
353
[Link](0xff);
[Link](0xff);
[Link]([Link], true);
});
[Link]();
Android’s material design has a morphing effect where an element from the previous form
(activity) animates into a different component on a new activity. Codename One has a morph effect
in the Container [[Link] class but it
doesn’t work as a transition between forms and doesn’t allow for multiple separate components to
transition at once.
Since the transition is created before the form exists we can’t reference explicit components within
the form when creating the morph transition (in order to indicate which component becomes
which) so we need to refer to them by name. This means we need to use setName(String) on the
components in the source/destination forms so the transition will be able to find them.
354
// ...
[Link](backCommand);
[Link](
[Link](3000).morph(
[Link](),
"DemoLabel"));
[Link](
[Link](3000).
morph([Link](),
"DemoLabel"));
[Link]();
8.4.6. SwipeBackSupport
iOS7+ allows swiping back one form to the previous form, Codenmae One has an API to enable back
swipe transition:
[Link](currentForm, destination);
That one command will enable swiping back from currentForm. LazyValue
[[Link] allows us to pass a value lazily:
/**
* Useful when passing a value that might not exist to a function, e.g. when we
* pass a form that we might need to construct dynamically later on.
*/
public interface LazyValue<T> {
/**
* Returns the actual value.
*
* @param args optional arguments for the creation of the lazy value
* @return the value
*/
T get(Object... args);
}
This effectively allows us to pass a form and only create it as necessary (e.g. for a GUI builder app
we don’t have the actual previous form instance), notice that the arguments aren’t used for this
case but will be used in other cases.
The code below should work for the transition sample above. Notice that this API was designed to
work with "Slide Fade" transition and might have issues with other transition types:
355
[2] Right to left/bidi language such as Hebrew or Arabic
356
Chapter 9. The EDT - Event Dispatch Thread
9.1. What Is The EDT
Codename One allows developers to create as many threads as they want; however in order to
interact with the Codename One user interface components a developer must use the EDT. The EDT
stands for "Event Dispatch Thread" but it handles a lot more than just "events".
The EDT is the main thread of Codename One, by using just one thread Codename One can avoid
complex synchronization code and focus on simple functionality that assumes only one thread.
This has huge advantages for your code. You can normally assume that all code
will occur on a single thread and avoid complex synchronization logic.
while(codenameOneRunning) {
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
}
Normally, every call you receive from Codename One will occur on the EDT. E.g. every event, calls
to paint(), lifecycle calls (start etc.) should all occur on the EDT.
This is pretty powerful, however it means that as long as your code is processing nothing else can
happen in Codename One!
If your code takes too long to execute then no painting or event processing
will occur during that time, so a call to [Link]() will actually stop
everything!
The solution is pretty simple, if you need to perform something that requires intensive CPU you can
spawn a thread.
Codename One’s networking code automatically spawns its own network thread (see the
NetworkManager [[Link]
However, this also poses a problem…
Codename One assumes all modifications to the UI are performed on the EDT but if we spawned a
separate thread. How do we force our modifications back into the EDT?
357
isEDT() is useful for generic code that needs to test whether the current code is executing on the
EDT.
You can write this code more concisely using Java 8 lambda code as such:
This allows code to leave the EDT and then later on return to it to perform things within the EDT.
The callSeriallyAndWait(Runnable) method blocks the current thread until the method completes,
this is useful for cases such as user notification e.g.:
If you are unsure use callSerially. The use cases for callSeriallyAndWait are very
rare. When you do need to wait, the overload that accepts a timeout lets
358
background threads marshal results back to the EDT without risking an indefinite
stall if the EDT is busy.
If the work you are posting back to the EDT is expensive and can wait until the UI finishes its
current pass, use callSeriallyOnIdle(). This queues the runnable for execution only after the EDT
completes painting, animations, and any other queued tasks. Deferring longer tasks to the idle
queue helps avoid starving animations and transitions when bursts of background callbacks arrive
together.
One of the misunderstood topics is why would we ever want to invoke callSerially when we are
still on the EDT. This is best explained by example. Say we have a button that has quite a bit of
functionality tied to its events e.g.:
However, this might cause a problem if the first event that we handle (the dialog) might cause an
issue to the following events. E.g. a dialog will block the EDT (using invokeAndBlock), events will keep
happening but since the event we are in "already happened" the button repaint and the framework
logging won’t occur. This might also happen if we show a form which might trigger logic that relies
on the current form still being present.
One of the solutions to this problem is to just wrap the action listeners body with a callSerially. In
this case the callSerially will postpone the event to the next cycle (loop) of the EDT and let the
other events in the chain complete. Notice that you shouldn’t use this normally since it includes an
overhead and complicates application flow, however when you run into issues in event processing
we suggest trying this to see if its the cause.
You should never invoke callSeriallyAndWait on the EDT since this would
effectively mean sleeping on the EDT. We made that method throw an exception if
its invoked from the EDT.
If you need to run logic on the EDT but must ensure nothing inside it blocks, the Display class also
provides invokeWithoutBlocking() and invokeWithoutBlockingWithResultSync(). These helpers
temporarily disable invokeAndBlock() for the duration of the runnable. Any nested call to
invokeAndBlock() while blocking is disabled results in a BlockingDisallowedException. Framework
code that wraps user callbacks can use these methods to guard against accidental nested blocking
that would otherwise deadlock or stall the UI, yet still safely obtain a return value when needed via
invokeWithoutBlockingWithResultSync().
359
2. Invoking UI code on a separate thread
Codename One provides a tool to help you detect some of these violations some caveats may apply
though…
It’s an imperfect tool. It might fire "false positives" meaning it might detect a violation for perfectly
legal code and it might miss some illegal calls. However, it is a valuable tool in the process of
detecting hard to track bugs that are sometimes only reproducible on the devices (due to race
condition behavior).
To activate this tool just select the Debug EDT menu option in the simulator and pick the level of
output you wish to receive:
Full output will include stack traces to the area in the code that is suspected in the violation.
This is best explained by an example. When we write typical code in Java we like that code is in
sequence as such:
doOperationA();
doOperationB();
doOperationC();
This works well normally but on the EDT it might be a problem, if one of the operations is slow it
might slow the whole EDT (painting, event processing etc.). Normally we can just move operations
into a separate thread e.g.:
360
doOperationA();
new Thread() {
public void run() {
doOperationB();
}
}).start();
doOperationC();
Unfortunately, this means that operation C will happen in parallel to operation B which might be a
problem…
E.g. instead of using operation names lets use a more "real world" example:
updateUIToLoadingStatus();
readAndParseFile();
updateUIWithContentOfFile();
Notice that the first and last operations must be conducted on the EDT but the middle operation
might be really slow! Since updateUIWithContentOfFile needs readAndParseFile to occur before it
starts doing the new thread won’t be enough.
updateUIToLoadingStatus();
new Thread() {
public void run() {
readAndParseFile();
updateUIWithContentOfFile();
}
}).start();
But updateUIWithContentOfFile should be executed on the EDT and not on a random thread. So the
right way to do this would be something like this:
updateUIToLoadingStatus();
new Thread() {
public void run() {
readAndParseFile();
[Link]().callSerially(new Runnable() {
public void run() {
updateUIWithContentOfFile();
}
});
}
}).start();
This is perfectly legal and would work reasonably well, however it gets complicated as we add
361
more and more features that need to be chained serially after all these are just 3 methods!
Invoke and block solves this in a unique way you can get almost the exact same behavior by using
this:
updateUIToLoadingStatus();
[Link]().invokeAndBlock(new Runnable() {
public void run() {
readAndParseFile();
}
});
updateUIWithContentOfFile();
updateUIToLoadingStatus();
[Link]().invokeAndBlock(() -> readAndParseFile());
updateUIWithContentOfFile();
Invoke and block effectively blocks the current EDT in a legal way. It spawns a separate thread that
runs the run() method and when that run method completes it goes back to the EDT.
All events and EDT behavior still work while invokeAndBlock is running, this is because
invokeAndBlock() keeps calling the main thread loop internally.
Notice that invokeAndBlock comes at a slight performance penalty. Also notice that
nesting invokeAndBlock calls (or over using them) isn’t recommended.
However, they are very convenient when working with multiple threads/UI.
Even if you never call invokeAndBlock directly you are probably using it indirectly in API’s such as
Dialog [[Link] that show a dialog while
blocking the current thread e.g.:
Notice that the dialog show method will block the calling thread until the user clicks OK or Not OK…
362
To explain how invokeAndBlock works we can return to the sample above of how the EDT works:
while(codenameOneRunning) {
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
}
void invokeAndBlock(Runnable r) {
openThreadForR(r);
while(r is still running) {
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
}
}
So the EDT is effectively "blocked" but we "redo it" within the invokeAndBlock method…
As you can see this is a very simple approach for thread programming in UI, you don’t need to
block your flow and track the UI thread. You can just program in a way that seems sequential (top
to bottom) but really uses multi-threading correctly without blocking the EDT.
363
364
Chapter 10. Graphics, Drawing, Images &
Fonts
Drawing is considered a low level API that might introduce some platform
fragmentation.
You can gain access to a Graphics using one of the following methods:
◦ paint(Graphics) - invoked to draw the component, this can be overridden to draw the
component from scratch.
◦ paintComponent(Graphics) - allows painting only the components contents while leaving the
default paint behavior to the style.
• Implement the painter interface, this interface can be used as a GlassPane or a background
painter.
The painter interface is a simple interface that includes 1 paint method, this is a useful way to
allow developers to perform custom painting without subclassing Component. Painters can be
chained together to create elaborate paint behavior by using the PainterChain
[[Link] class.
◦ Glass pane - a glass pane allows developers to paint on top of the form painting. This allows
an overlay effect on top of a form.
For a novice it might seem that a glass pane is similar to overriding the Form’s paint method
and drawing after [Link](g) completed. This isn’t the case. When a component repaints
(by invoking the repaint() method) only that component is drawn and Form
[[Link] paint() method wouldn’t be
365
invoked. However, the glass pane painter is invoked for such cases and would work exactly
as expected.
◦ Background painter - the background painter is installed via the style, by default
Codename installs a custom background painter of its own. Installing a custom painter
allows a developer to completely define how the background of the component is drawn.
Notice that a lot of the background style behaviors can be achieved using styles alone.
// draw hi world in white text at the top left corner of the screen
[Link](0xffffff);
[Link]("Hi World", getX(), getY());
}
});
[Link]();
366
Figure 295. Hi world demo code, notice that the blue bar on top is the iOS7+ status bar
Solid colors are only the starting point. Graphics also understands "paint" objects that describe
gradients and other patterns. You can pass an instance of Paint [[Link]
com/codename1/ui/[Link]] to setColor(Paint) in place of an integer color value to activate a
gradient for subsequent fill and draw operations. The LinearGradientPaint
[[Link] class is the most
common option and accepts a list of color stops along a line:
The fillLinearGradient() convenience methods (with optional repeat flag) provide a shorthand
when you just need a two-color gradient without constructing your own Paint object.
367
Figure 296. Form layout graphic
Essentially the glass pane is a painter that allows us to draw an overlay on top of the Codename One
application.
Overriding the paint method of a form isn’t a substitute for glasspane as it would appear to work
initially, when you enter a Form. However, when modifying an element within the form only that
element gets repainted not the entire Form!
The glass pane is called whenever a component gets painted, it only paints within the clipping
region of the component hence it won’t break the rest of the components on the Form which weren’t
modified.
[Link](new Painter() {
@Override
public void paint(Graphics g, Rectangle rect) {
}
});
368
[Link]().setMargin(5, 5, 5, 5);
[Link](tf1);
[Link]((g, rect) -> {
int x = [Link]() + [Link]();
int y = [Link]();
x -= [Link]() / 2;
y += ([Link]() / 2 - [Link]() / 2);
[Link](warningImage, x, y);
});
[Link]();
Figure 297. The glass pane draws the warning sign on the border of the component partially peeking out
Notice that perspective transform is missing from the desktop/simulator port. Unfortunately there
is no real equivalent to perspective transform in JavaSE that we could use.
The center of the app is the DrawingCanvas class, which extends Component
[[Link]
369
public void addPoint(float x, float y){
// To be written
}
@Override
protected void paintBackground(Graphics g) {
[Link](g);
Stroke stroke = new Stroke(
strokeWidth,
Stroke.CAP_BUTT,
Stroke.JOIN_ROUND, 1f
);
[Link](strokeColor);
@Override
public void pointerPressed(int x, int y) {
addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY());
}
}
The implementation of the paintBackground() method (shown above) should be fairly straight
forward. It creates a stroke of the appropriate width, and sets the color on the graphics context.
Then it calls drawShape() to render the path of points.
The addPoint method is designed to allow us to add points to the drawing. A simple implementation
that uses straight lines rather than curves might look like this:
370
} else {
[Link](x, y);
}
lastX = x;
lastY = y;
repaint();
}
We introduced a couple house-keeping member vars (lastX and lastY) to store the last point that
was added so that we know whether this is the first tap or a subsequent tap. The first tap triggers a
moveTo() call, whereas subsequent taps trigger lineTo() calls, which draw lines from the last point
to the current point.
Our previous implementation of addPoint() used lines for each segment of the drawing. Let’s make
an adjustment to allow for smoother edges by using quadratic curves instead of lines.
Codename One’s GeneralPath class includes two methods for drawing curves:
1. quadTo() [[Link]
quadTo(float,%20float,%20float,%20float)] : Appends a quadratic bezier curve. It takes 2 points: a
control point, and an end point.
2. curveTo() [[Link]
curveTo(float,%20float,%20float,%20float,%20float,%20float)] : Appends a cubic bezier curve, taking 3
points: 2 control points, and an end point.
371
follows:
} else {
float controlX = odd ? lastX : x;
float controlY = odd ? y : lastY;
[Link](controlX, controlY, x, y);
}
odd = !odd;
lastX = x;
lastY = y;
repaint();
}
This change should be fairly straight forward except, perhaps, the business with the odd variable.
Since quadratic curves require two points (in addition to the implied starting point), we can’t
simply take the last tap point and the current tap point. We need a point between them to act as a
control point. This is where we get the curve from. The control point works by exerting a sort of
"gravity" on the line segment, to pull the line towards it. This results in the line being curved. I use
the odd marker to alternate the control point between positions above the line and below the line.
The DrawingCanvas example is a bit naive in that it assumes that the device supports the shape API. If
I were to run this code on a device that doesn’t support the Shape [[Link]
javadoc/com/codename1/ui/geom/[Link]] API, it would just draw a blank canvas where I expected my
shape to be drawn. You can fall back gracefully if you make use of the [Link]()
[[Link] method. E.g.
372
@Override
protected void paintBackground(Graphics g) {
[Link](g);
if ( [Link]() ){
// do my shape drawing code here
} else {
// draw an alternate representation for device
// that doesn't support shapes.
// E.g. you could defer to the Pisces
// library in this case
}
10.6. Transforms
The Graphics [[Link] class has included
limited support for 2D transformations for some time now including scaling, rotation, and
translation:
scale() and rotate() methods are only available on platforms that support Affine
transforms. See table X for a compatibility list.
All current Codename One ports expose affine transforms (i.e. scale() and rotate()). Use the
following table as a quick reference when deciding whether you need a fallback path.
Simulator/Desktop Yes
iOS Yes
Android Yes
JavaScript Yes
UWP Yes
373
e.g.
} else {
// Fallback behavior here
}
}
1. The tick marks. E.g. most clocks will have a tick mark for each second, larger tick marks for
each hour, and sometimes even larger tick marks for each quarter hour.
2. The numbers. We will draw the clock numbers (1 through 12) in the appropriate positions.
3. The hands. We will draw the clock hands to point at the appropriate points to display the
current time.
@Override
public void paintBackground(Graphics g) {
// Draw the clock in this method
}
}
Before we actually draw anything, let’s take a moment to figure out what values we need to know
374
in order to draw an effective clock. Minimally, we need two values:
In addition, I am adding the following parameters to to help customize how the clock is rendered:
1. The padding (i.e. the space between the edge of the component and the edge of the clock circle.
2. The tick lengths. I will be using 3 different lengths of tick marks on this clock. The longest ticks
will be displayed at quarter points (i.e. 12, 3, 6, and 9). Slightly shorter ticks will be displayed at
the five-minute marks (i.e. where the numbers appear), and the remaining marks
(corresponding with seconds) will be quite short.
// Clock radius
double r = [Link](getWidth(), getHeight())/2-padding;
// Center point.
double cX = getX()+getWidth()/2;
double cY = getY()+getHeight()/2;
//Tick Styles
int tickLen = 10; // short tick
int medTickLen = 30; // at 5-minute intervals
int longTickLen = 50; // at the quarters
int tickColor = 0xCCCCCC;
Stroke tickStroke = new Stroke(2f, Stroke.CAP_BUTT, Stroke.JOIN_ROUND, 1f);
375
double di = (double)i; // tick num as double for easier math
This example uses a little bit of trigonometry to calculate the (x,y) coordinates of
the tick marks based on the angle and the radius. If math isn’t your thing, don’t
worry. This example just makes use of the identities: x=r*cosθ and y=r*sinθ.
At this point our clock should include a series of tick marks orbiting a blank center as shown below:
376
10.7.4. Drawing the Numbers
The [Link](str, x, y) method allows you to draw text at any point of a component.
The tricky part here is calculating the correct x and y values for each string so that the number
appears in the correct location.
For the purposes of this tutorial, we will use the following strategy. For each number (1 through 12):
1. Use the [Link](x,y) method to apply a translation from the clock’s center point to
the point where the number should appear.
2. Draw number (using drawString()) at the clock’s center. It should be rendered at the correct
point due to our translation.
// Calculate the position along the edge of the clock where the number should
// be drawn
// Get the angle from 12 O'Clock to this tick (radians)
double angleFrom12 = di/12.0*2.0*[Link];
// For 6 and 12 we will shift number slightly so they are more even
if ( i == 6 ){
ty -= charHeight/2;
} else if ( i == 12 ){
ty += charHeight/2;
}
377
// Draw number at clock center.
[Link](numStr, (int)cX-charWidth/2, (int)cY-charHeight/2);
// Undo translation
[Link](-tx, -ty);
This example is, admittedly, a little contrived to allow for a demonstration of the
[Link]() method. We could have just as easily passed the exact
location of the number to drawString() rather than draw at the clock center and
translate to the correct location.
Now, we should have a clock with tick marks and numbers as shown below:
The clock will include three hands: Hour, Minute, and Second. We will use a separate GeneralPath
[[Link] object for each hand. For
the positioning/angle of each, I will employ the following strategy:
1. Draw the hand at the clock center pointing toward 12 (straight up).
3. Rotate the hand at the appropriate angle for the current time, using the clock center as a pivot
point.
For the "second" hand, we will just use a simple line from the clock center to the inside edge of the
medium tick mark at the 12 o’clock position.
378
GeneralPath secondHand = new GeneralPath();
[Link]((float)cX, (float)cY);
[Link]((float)cX, (float)(cY-(r-medTickLen)));
And we will translate it down slightly so that it overlaps the center. This translation will be
performed on the GeneralPath object directly rather than through the Graphics context:
The rotation of the second hand will be performed in the Graphics context via the rotate(angle, px,
py) method. This requires us to calculate the angle. The px and py arguments constitute the pivot
point of the rotation, which, in our case will be the clock center.
Remember to call resetAffine() after you’re done with the rotation, or you will see
some unexpected results on your form.
The mechanism for drawing the hour and minute hands is largely the same as for the minute hand.
There are a couple of added complexities though:
379
1. We’ll make these hands trapezoidal, and almost triangular rather than just using a simple line.
Therefore the GeneralPath construction will be slightly more complex.
2. Calculation of the angles will be slightly more complex because they need to take into account
multiple parameters. E.g. The hour hand angle is informed by both the hour of the day and the
minute of the hour.
380
double angle = hour/12.0*2.0*[Link];
[Link]((float)angle, (int)absCX, (int)absCY);
[Link](0x000000);
[Link](translatedHourHand);
[Link]();
The current clock component is cool, but it is static. It just displays the time at the point the clock
was created. We discussed low level animations in the animation section of the guide, here we will
show a somewhat more elaborate example.
In order to animate our clock so that it updates once per second, we only need to do two things:
1. Implement the animate() method to indicate when the clock needs to be updated/re-drawn.
2. Register the component with the form so that it will receive animation "pulses".
@Override
public boolean animate() {
if ( [Link]()/1000 != lastRenderedTime/1000){
[Link]([Link]());
return true;
}
return false;
381
}
This method will be invoked on each "pulse" of the EDT. It checks the last time the clock was
rendered and returns true only if the clock hasn’t been rendered in the current "time second"
interval. Otherwise it returns false. This ensures that the clock will only be redrawn when the time
changes.
So the code to instantiate the clock, and start the animation would be something like:
382
// We create a 50 x 100 shape, this is arbitrary since we can scale it easily
GeneralPath path = new GeneralPath();
[Link](20,0);
[Link](30, 0);
[Link](30, 100);
[Link](20, 100);
[Link](20, 15);
[Link](5, 40);
[Link](5, 25);
[Link](20,0);
[Link]();
Figure 303. Shape Clipping used to clip the image of duke within the given shape
Notice that this functionality isn’t available on all platforms so you normally need
to test if shaped clipping is supported using isShapeClipSupported()
[[Link]
isShapeClipSupported--].
383
Figure 304. The Codename One graphics coordinate space
Therefore the screen origin is at the top left corner of the screen. Given this information, consider
the method call on the Graphics [[Link]
context g:
If you answered something something like "10 pixels from the top, and 10 pixels from the left of the
screen", you might be right. It depends on whether the graphics has a translation or transform
applied to it. If there is currently a translation of (20,20) (i.e. 20 pixels to the right, and 20 pixels
down), then the rectangle would be rendered at (30, 30).
You can always find out the current translation of the graphics context using the
[Link]() and [Link]() methods:
This example glosses over issues such as clipping and transforms which may cause
it to not work as you expect. E.g. When painting a component inside its paint()
method, there is a clip applied to the context so that only the content you draw
within the bounds of the component will be seen.
If, in addition, there is a transform applied that rotates the context 45 degrees clockwise, then the
rectangle will be drawn at a 45 degree angle with its top left corner somewhere on the left edge of
384
the screen.
Luckily you usually don’t have to worry about the exact screen coordinates for the things you paint.
Most of the time, you will only be concerned with relative coordinates.
Usually, when you are drawing onto a Graphics context, you are doing so within the context of a
Component’s paint() method (or one of its variants). In this case, you generally don’t care what the
exact screen coordinates are of your drawing. You are only concerned with their relative location
within the coordinate. You can leave the positioning (and even sizing) of the coordinate up to
Codename One. Thank you for reading.
The x and y coordinates that are passed to the drawRect(x,y,w,h) method are
relative to the component’s parent’s origin — not the component itself .. its
parent. This is why we the x position is getX()+5 and not just 5.
Unlike the Graphics drawXXX primitives, methods for setting transformations, including scale(x,y)
and rotate(angle), are always applied in terms of screen coordinates. This can be confusing at first,
because you may be unsure whether to provide a relative coordinate or an absolute coordinate for
a given method.
385
The general rule is:
1. All coordinates passed to the drawXXX() and fillXXX() methods will be subject to the
graphics context’s transform and translation settings.
Let’s take our RectangleComponent as an example. Suppose we want to rotate the rectangle by 45
degrees, our first attempt might look something like:
@Override
protected Dimension calcPreferredSize() {
return new Dimension(250,250);
}
The behavior of this rotation will vary based on where the component is rendered on the screen. To
demonstrate this, let’s try to place five of these components on a form inside a BorderLayout
[[Link] and see how it looks:
public MyForm() {
super("Rectangle Rotations");
for ( int i=0; i< 10; i++ ){
[Link](new RectangleComponent());
}
}
}
386
Figure 306. Rotating the rectangle
This may not be an intuitive outcome since we drew 10 rectangle components, be we only see a
portion of one rectangle. The reason is that the rotate(angle) method uses the screen origin as the
pivot point for the rotation. Components nearer to this pivot point will experience a less dramatic
effect than components farther from it. In our case, the rotation has caused all rectangles except the
first one to be rotated outside the bounds of their containing component - so they are being clipped.
A more sensible solution for our component would be to place the rotation pivot point somewhere
inside the component. That way all of the components would look the same. Some possibilities
would be:
Center:
387
public void paint(Graphics g) {
[Link](0x0000ff);
[Link](
(float)([Link]/4.0),
getAbsoluteX()+getWidth()/2,
getAbsoluteY()+getHeight()/2
);
[Link](getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10);
[Link](
-(float)([Link]/4.0),
getAbsoluteX()+getWidth()/2,
getAbsoluteY()+getHeight()/2
);
}
Figure 308. Rotating the rectangle with the center pivot point
You could also use the [Link]() class to apply rotations and other complex
transformations (including 3D perspective transforms), but I’ll leave that for its own topic as it is a
little bit more complex.
So far we have relied on the per-pixel alpha stored in images and gradients. Graphics also lets you
apply a global alpha multiplier to every draw call by using setAlpha(int) or concatenateAlpha(int)
after checking isAlphaSupported(). Both methods accept values from 0 (fully transparent) to 255
(fully opaque) and remain active until you change them again. concatenateAlpha() is especially
handy when you need to temporarily fade a component because it returns the previous alpha so
you can restore it later.
388
10.10.4. Event Coordinates
The coordinate system and event handling are closely tied. You can listen for touch events on a
component by overriding the pointerPressed(x,y) method. The coordinates received in this method
will be absolute screen coordinates, so you may need to do some conversions on these
coordinates before using them in your drawXXX() methods.
In this case we translated these points so that they would be relative to the origin of the parent
component. This is because the drawXXX() methods for this component take coordinates relative to
the parent component.
10.11. Images
Codename One has quite a few image types: loaded, RGB (builtin), RGB (Codename One), Mutable,
EncodedImage, SVG, MultiImage, FontImage & Timeline. There are also URLImage,
FileEncodedImage, FileEncodedImageAsync, StorageEncodedImage/Async that will be covered in the
IO section.
All image types are mostly seamless to use and will just work with drawImage and various image
related image API’s for the most part with caveats on performance etc.
For animation images the code must invoke the animate() method on the image
(this is done automatically by Codename One when placing the image as a
background or as an icon!
You only need to do it if you invoke drawImage in code rather than use a builtin
component).
Performance and memory wise you should read the section below carefully and be aware of the
image types you use. The Codename One designer tries to conserve memory and be "clever" by
using only EncodedImage. While these are great for low memory you need to understand the
complexities of image locking and be aware that you might pay a penalty if you don’t.
Here are the pros/cons and logic behind every image type. This covers the logic of how it’s created:
This is the basic image you get when loading an image from the jar or network using
[Link](String) [[Link]
[Link]-], [Link](InputStream) [[Link]
codename1/ui/[Link]#[Link]-] & [Link](byte array,int,int)
[[Link] …
389
Some other API’s might return this image type but those API’s do so explicitly!
In some platforms calling getGraphics() on an image like this will throw an exception as it’s
immutable). This is true for almost all other images as well.
The image is stored in RAM based on device logic and should be reasonably efficient in terms of
drawing speed. However, it usually takes up a lot of RAM.
To calculate the amount of RAM taken by a loaded image we use the following formula:
The logic behind this is simple, every pixel contains 3 color channels and an alpha component
hence 3 bytes for color and one for alpha.
This isn’t the case for all images but it’s very common and we prefer calculating
for the worst case scenario. Even with JPEG’s that don’t include an alpha channel
some OS’s might reuire that additional byte.
There are two types of RGB constructed images that are very different from one another but since
they are both technically "RGB image’s" we are bundling them under the same subsection.
Internal
This is a close cousin of the loaded image. This image is created using the method
[Link](int array, int, int) [[Link]
createImage-int:A-int-int-] and receives the AARRGGBB data to form the image. It’s more efficient than
the Codename One RGB image but can’t be modified, at least not on the pixel level.
The goal of this image type is to provide an easy way to render RGB data that isn’t modified
efficiently at platform native speeds. It’s technically a standard "Loaded Image" internally.
RGBImage class
On most platforms this is quite inefficient but for some pixel level manipulations there is just no
other way.
An RGBImage is constructed with an int array (int[]) that includes width*height elements. You can
then modify the colors and alpha channel directly within the array and draw the image to any
source using standard image drawing API’s.
390
This is very inefficient in terms of rendering speed and memory overhead. Only
use this technique if there is absolutely no other way!
10.11.3. EncodedImage
The EncodedImage is effectively a loaded image that is "hidden" and extracted as needed to remove
the memory overhead associated with loaded image. When creating an EncodedImage only the PNG
(or JPEG etc.) is loaded to an array in RAM. Normally such images are very small (relatively) so they
can be kept in memory without much overhead.
When image information is needed (pixels) the image is decoded into RAM and kept in a weak/sort
reference. This allows the image to be cached for performance and allows the garbage collector to
reclaim it when the memory becomes scarce.
Since the fully decoded image can be pretty big (width X height X 4) the ability to store just the
encoded image can be pretty stark. E.g. taking our example above a 50x100 image will take up
20,000 bytes of RAM for a loaded image but an EncodedImage can reduce that to 1kb-2kb of RAM.
When drawing an EncodedImage it checks the weak reference cache and if the image is cached then
it is shown otherwise the image is loaded the encoded image cache it then drawn.
EncodedImage is not final and can be derived to produce complex image fetching strategies e.g. the
URLImage [[Link] class that can
dynamically download its content from the web.
EncodedImage can be instantiated via the create methods in the EncodedImage class. Pretty much any
image can be converted into an EncodedImage via the createFromImage(Image, boolean)
[[Link]
[Link]-boolean-] method.
EncodedImage Locking
Naturally loading the image is more expensive so we want the images that are on the current
form to remain in cache (otherwise GC will thrash a lot). That’s where lock() kicks in, when
lock() is active we keep a hard reference to the actual native image so it won’t get GC’d. This
significantly improves performance!
Internally this is invoked automatically for background images, icons etc. which results in a
huge performance boost. This makes sense since these images are currently showing and
they will be in RAM anyway. However, if you use a complex renderer or custom drawing UI
391
you should lock() your images where possible!
To verify that locking might be a problem you can launch the performance monitor tool
(accessible from the simulator menu), if you get log messages that indicate that an unlocked
image was drawn you might have a problem.
10.11.4. MultiImage
Multi images don’t physically exist as a concept within the Codename One API so there is no way to
actually create them and they are in no way distinguishable from EnclodedImage.
The only builtin support for multi images is in the resource file loading logic where a MultiImage is
decoded and only the version that matches the current DPI is physically loaded. From that point on
user code can treat it like any other EnclodedImage.
9-image borders use multi images by default to keep their appearance more refined on the different
DPI’s.
You can generate icon fonts using free tools on the internet such as this [[Link] Icon
fonts are a remarkably simple and powerful technique to create a small, modern applications.
Icon fonts can be created in 2 basic ways the first is explicitly by defining all of the elements within
the font.
392
Figure 309. Icon font from material design icons created with the fixed size of display width
The samples use the builtin material design icon font. This is for convenience so
the sample will work out of the box, for everyone. However you should be able to
do this with any arbitrary icon font off the internet as long as its a valid TTF file.
A more common and arguably "correct" way to construct such an icon would be thru the Style
[[Link] object. The Style object can
provide the color, size and background information needed by FontImage.
There are two versions of this method, the first one expects the Style object to have the correct icon
font set to its font attribute. The second accepts a Font object as an argument. The latter is useful for
a case where you want to reuse the same Style object that you defined for a general UI element e.g.
we can set an icon for a Button like this and it will take up the style of the Button:
Notice that for this specific version of the method the size of the font is used to
determine the icon size. In the other methods for FontImage creation the size of the
font is ignored!
There are many icon fonts in the web, the field is rather volatile and constantly changing. However,
we wanted to have builtin icons that would allow us to create better looking demos and builtin
components.
393
That’s why we picked the material design icon font for inclusion in the Codename One distribution.
It features a relatively stable core set of icons, that aren’t IP encumbered.
You can use the builtin font directly as demonstrated above but there are far better ways to create a
material design icon. To find the icon you want you can check out the material design icon gallery
[[Link] E.g. we used the save icon in the samples above.
Notice that the icon is smaller now as it’s calculated based on the font size of the
Button UIID.
This will produce the same result for slightly shorter syntax.
FontImage can conflict with some complex API’s that expect a "real" image
underneath. Some odd issues can often be resolved by using the toImage() or
toEncodedImage() methods to convert the scaled FontImage to a loaded image.
10.11.6. Timeline
Timeline’s allow rudimentary animation and enable GIF importing using the Codename One
Designer. Effectively a timeline is a set of images that can be moved rotated, scaled & blended to
provide interesting animation effects. It can be created manually using the Timeline
[[Link] class.
Image masking allows us to manipulate images by changing the opacity of an image according to a
mask image. The mask image can be hardcoded or generated dynamically, it is then converted to a
Mask object that can be applied to any image. Notice that the masking process is computationally
intensive, it should be done once and cached/saved.
394
The code below can convert an image to a rounded image:
[Link](true);
Form hi = new Form("Rounder", new BorderLayout());
Label picture = new Label("", "Container");
[Link]([Link], picture);
[Link]().setBgColor(0xff0000);
[Link]().setBgTransparency(255);
Style s = [Link]().getComponentStyle("TitleCommand");
Image camera = [Link](FontImage.MATERIAL_CAMERA, s);
[Link]().addCommandToRightBar("", camera, (ev) -> {
try {
int width = [Link]().getDisplayWidth();
Image capturedImage = [Link]([Link](width, -1));
Image roundMask = [Link](width, [Link](), 0xff000000);
Graphics gr = [Link]();
[Link](0xffffff);
[Link](0, 0, width, width, 0, 360);
Object mask = [Link]();
capturedImage = [Link](mask);
[Link](capturedImage);
[Link]();
} catch(IOException err) {
Log.e(err);
}
});
Figure 312. Picture after the capture was complete and the resulted image was rounded. The background
was set to red so the rounding effect will be more noticeable
Notice that this example is simplistic in order to be self contained. We often recommend that
developers ship "ready made" mask images with their application which can allow very complex
effects on the images.
10.11.8. URLImage
395
How Does URLImage Work?
The reason for the size restriction lies in the implementation of URLImage. URLImage is
physically an animated image and so the UI thread tries to invoke its animate() method to
refresh. The URLImage uses that call to check if the image was fetched and if not fetches it
asynchronously.
Once the image was fetched the animate() method returns true to refresh the UI. During the
loading process the placeholder is shown, the reason for the restriction in size is that image
animations can’t "grow" the image. They are assumed to be fixed so the placeholder must
match the dimensions of the resulting image.
Alternatively you can use the similar [Link] method instead of the Storage
[[Link] version.
This image can now be used anywhere a regular image will appear, it will initially show the
placeholder image and then seamlessly replace it with the file after it was downloaded and stored.
Notice that if you make changes to the image itself (e.g. the scaled method) it will generate a new
image which won’t be able to fetch the actual image.
If the file in the URL contains an image that is too big it will scale it to match the size of the
placeholder precisely!
There is also an option to fail if the sizes don’t match. Notice that the image that will be saved is the
scaled image, this means you will have very little overhead in downloading images that are the
wrong size although you will get some artifacts.
The last argument is really quite powerful, its an interface called [Link]
[[Link] and you can
implement it to adapt the downloaded image in any way you like. E.g. you can use an image mask
to automatically create a rounded version of the downloaded image.
In the adapter interface and just return the processed encoded image. If you do heavy processing
396
(e.g. rounded edge images) you would need to convert the processed image back to an encoded
image so it can be saved. You would then also want to indicate that this operation should run
asynchronously via the appropriate method in the class.
If you need to download the file instantly and not wait for the image to appear before download
initiates you can explicitly invoke the fetch() method which will asynchronously fetch the image
from the network. Notice that the downloading will still take time so the placeholder is still
required.
Mask Adapter
A URLImage can be created with a mask adapter to apply an effect to an image. This allows us to
round downloaded images or apply any sort of masking e.g. we can adapt the round mask code
above as such:
URLImage In Lists
The biggest problem with image download service is with lists. We decided to attack this issue at
the core by integrating URLImage [[Link]
support directly into GenericListCellRenderer [[Link]
list/[Link]] which means it will work with MultiList [[Link]
javadoc/com/codename1/ui/list/[Link]], List [[Link] &
ContainerList [[Link] To use this
support just define the name of the component (name not UIID) to end with _URLImage and give it an
icon to use as the placeholder. This is easy to do in the multilist by changing the name of icon to
icon_URLImage then using this in the data:
[Link]("icon_URLImage", urlToActualImage);
Make sure you also set a "real" icon to the entry in the GUI builder or in handcoded applications.
This is important since the icon will be implicitly extracted and used as the placeholder value.
Everything else should be handled automatically. You can use setDefaultAdapter & setAdapter on
the generic list cell renderer to install adapters for the images. The default is a scale adapter
although we might change that to scale fill in the future.
Style s = [Link]().getComponentStyle("Button");
FontImage p = [Link](FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = [Link]([Link]([Link]() * 3, [Link]() * 4), false);
397
ArrayList<Map<String, Object>> data = new ArrayList<>();
Figure 313. A URL image fetched dynamically into the list model
10.12. Charts
Codename One includes a charting toolkit in the [Link] package that is designed to
integrate with regular UI layouts. Charts are drawn by creating an appropriate dataset and
renderer pair, instantiating the matching chart view class, and wrapping it in a ChartComponent
[[Link] so it can be added to a
398
form.
399
Chart class Dataset & renderer Notes
400
Chapter 11. Events
Most events in Codename One are routed via the high level events (e.g. action listener) but
sometimes we need access to low level events (e.g. when drawing via Graphics) that provide more
fine grained access. Typically working with the higher level events is far more potable since it
might map to different functionality on different devices.
All events are fired on the Event Dispatch Thread, the EventDispatcher makes sure
of that.
Since all events fire on the EDT some complexities occur. E.g.:
We have two listeners monitoring the same event (or related events e.g. pointer event and button
click event both of which will fire when the button is touched).
When the event occurs we can run into a scenario like this:
This happens because events are processed in-order per cycle. Since the old EDT cycle is stuck
(because of the Dialog) the rest of the events within the cycle can’t complete. New events are in a
new EDT cycle so they can finish just fine!
A workaround to this issue is to wrap the code in a callSerially, you shouldn’t do this universally
as this can create a case of shifting the problem to the next EDT cycle. However, using callSerially
will allow the current cycle to flush which should help.
Another workaround for the issue is avoiding blocking calls within an event chain.
401
[Link]] by using:
Notice that the click will work whether the button was touched using a mouse, finger or keypad
shortcut seamlessly with an action listener. Many components work with action events e.g. buttons,
text components, slider etc.
There are quite a few types of high level event types that are more specific to requirements.
When an action event is fired it is given a type, however this type might change as the event
evolves e.g. a command triggered by a pointer event won’t include details of the original pointer
event.
Modern gesture components also publish custom action types. For example, SwipeableContainer
[[Link] dispatches
[Link] [[Link]
[Link]#Swipe-] when the top component is fully opened, allowing code to react to swipe
gestures without monitoring low-level drags. Listening for these higher level events keeps gesture
handling portable across touch and desktop targets.
Source Of Event
ActionEvent has a source object, what that source is depends heavily on the event type. For most
component based events this is the component but there are some nuances.
The getComponent() method might not get the actual component. In case of a lead component such
as MultiButton [[Link] the
underlying Button [[Link] will be returned
and not the MultiButton itself.
402
To get the component that you would logically think of as the source component use the
getActualComponent() method.
Event Consumption
An ActionEvent can be consumed, once consumed it will no longer proceed down the chain of event
processing. This is useful for some cases where we would like to block behavior from proceeding
down the path.
E.g. the event dispatch thread allows us to listen to errors on the EDT using:
[Link]().addEdtErrorHandler((e) -> {
Exception err = (Exception)[Link]();
// ...
});
This will work great but you will still get the default error message from the EDT over that
exception. To prevent the event from proceeding to the default error handling you can just do this:
[Link]().addEdtErrorHandler((e) -> {
[Link]();
Exception err = (Exception)[Link]();
// ...
});
Notice that you can check if an event was already consumed using the isConsumed() method but it’s
pretty unlikely that you will receive a consumed event as the system will usually stop sending it.
NetworkEvent
[Link]().addErrorListener(new ActionListener<NetworkEvent>() {
public void actionPerformed(NetworkEvent ev) {
// now we have access to the methods on NetworkEvent that provide more information about the network specific
flags
}
});
[Link]().addErrorListener((ev) -> {
// now we have access to the methods on NetworkEvent that provide more information about the network specific flags
});
The NetworkEvent allows the networking code to reuse the EventDispatcher infrastructure and to
403
simplify event firing thru the EDT. But you should notice that some code might not be equivalent
e.g. we could do this to read the input stream:
These seem very similar but they have one important distinction. The latter code is invoked on the
EDT, so if data is big it might slow down processing significantly. The ConnectionRequest is invoked
on the network thread and so can process any amount of data without slowing down the UI
significantly.
Bridging to native code often means passing messages between Codename One Java and platform
specific code. The cross-platform message bus provides a high level abstraction for that by letting
you post messages to the native layer and subscribe for messages that originate there. Use
[Link]() [[Link]
[Link]-] to send an MessageEvent [[Link]
com/codename1/ui/events/[Link]] to the platform, and [Link]()
[[Link]
[Link]-] to receive events that arrive from native code, JavaScript
bridges, or background services. Message events include helpers like isPromptForAudioRecorder(),
isPromptForAudioPlayer(), and getPromptPromise() that allow you to hook into permission prompts
emitted by the JavaScript port so custom UI can respond to platform requests while keeping the
event dispatch on the EDT. This API complements NetworkEvent by covering native-to-Java
messaging without requiring direct low level callbacks.
11.1.3. DataChangeListener
404
DataChangeListener fires with every key entered and thus allows functionality such as "auto
complete" and is indeed used internally in the Codename One AutoCompleteTextField.
There is a very exhaustive example of search that is implemented using the DataChangedListener in
the Toolbar section [[Link]
11.1.4. FocusListener
The focus listener allows us to track the currently "selected" or focused component. It’s not as
useful as it used to be in feature phones.
You can bind a focus listener to the Component itself and receive an event when it gained focus, or
you can bind the listener to the Form and receive events for every focus change event within the
hierarchy.
11.1.5. ScrollListener
Normally scrolling is seamless and this event isn’t necessary, however if developers wish to
"shrink" or "fade" an element on scrolling this interface can be used to achieve that. Notice that you
should bind the scroll listener to the actual scrollable component and not to an arbitrary
component.
E.g. in this code from the Flickr demo the Toolbar [[Link]
ui/[Link]] is faded based on scroll position:
public CustomToolbar() {
}
public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) {
alpha = scrollY;
alpha = [Link](alpha, 0);
alpha = [Link](alpha, 255);
}
405
}
There is a better way of implementing this exact effect using title animations
illustrated here [[Link]
section].
11.1.6. SelectionListener
SelectionListener gets fired too often for events and that might result in a performance penalty.
When running on non-touch devices list selection could be changed with the keypad and only a
specific fire button click would fire the action event, for those cases SelectionListener made a lot of
sense. However, in touch devices this API isn’t as useful.
11.1.7. StyleListener
[Link]().setFgColor(0xffffff);
This will trigger a style event that will eventually lead to the component being repainted. This is
quite important for the component class but not a very important event for general user code. It is
recommended that developers don’t bind a style listener.
Component instances now publish lifecycle hooks that fire when they become initialized on a form
and when they are removed. You can subscribe with [Link]()
[[Link]
[Link]-] to receive ComponentStateChangeEvent
[[Link] instances
that indicate whether the component is transitioning to the initialized state. This is especially useful
for running setup or teardown logic alongside focus, scroll, and selection listeners.
When creating your own components and objects you sometimes want to broadcast your own
events, for that purpose Codename One has the EventDispatcher [[Link]
javadoc/com/codename1/ui/util/[Link]] class which saves a lot of coding effort in this regard.
E.g. if you wish to provide an ActionListener [[Link]
events/[Link]] event for components you can just add this to your class:
406
private final EventDispatcher listeners = new EventDispatcher();
E.g. one platform might send a very large number of events during drag while another might send
only a few. Normally the high level event handling hides those complexities but some of them
trickle down into the low level event handling.
Codename One tries to hide some of the complexities from the low level events as
well. However, due to the nature of the event types it’s a more challenging task.
When you override event callbacks on a Component the Component in question must
be focusable and have focus at that point. This can be an advantage for some use
cases as it will save you the need of handling unrelated events.
• 'Form' based events and callbacks deliver pointer events in the 'Form' coordinate space.
• 'Form' based events can block existing functionality from proceeding thru the event chain e.g.
you can avoid calling super in a form event and thus block other events from happening (e.g.
407
block a listener or component event from triggering).
There are two basic types of low level events: Key and Pointer.
Key events are only relevant to physical keys and will not trigger on virtual
keyboard keys, to track those use a TextField [[Link]
com/codename1/ui/[Link]] with a DataChangeListener as mentioned above.
The pointer events (touch events) can be intercepted by overriding one or more of these methods in
Component or Form. Notice that unless you want to block functionality you should probably invoke
super when overriding:
Notice that most pointer events have a version that accepts an array as an argument, this allows for
multi-touch event handling by sending all the currently touched coordinates. Desktop and pen-
enabled devices can also trigger hover events without a press. To respond to those you can override
the pointerHover* callbacks on Form or Component, which are invoked before a button receives focus
or a drag begins on those platforms.
While you can override longPointerPress, there is usually no need. The dedicated
[Link]() [[Link]
[Link]#[Link]-] helper wires long press
detection into an action listener so you can keep gesture logic in the high-level API.
Drag and drop lifecycles also expose a completion hook. When [Link]()
is registered it receives [Link] [[Link]
408
codename1/ui/events/[Link]#DragFinished-] once the framework has completed its cleanup,
allowing you to reset state or trigger follow-up actions that should only occur after the drag image
is hidden.
Drag events are quite difficult to handle properly across devices. Some devices send a ridiculous
number of events for even the lightest touch while others send too little. It seems like too many
drag events wouldn’t be a problem, however if we drag over a button then it might disable the
buttons action event (since this might be the user trying to scroll).
Drag sensitivity is really about the component being dragged which is why we have the method
getDragRegionStatus that allows us to "hint" to the drag API whether we are interested in drag
events or not and if so in which directional bias.
E.g. if our component is a painting app where we are trying to draw using drag gestures we would
use code such as:
This indicates that we want all drag events on both AXIS to be sent as soon as possible. Notice that
this doesn’t completely disable event sanitation.
11.3. BrowserNavigationCallback
The BrowserNavigationCallback [[Link]
[Link]] isn’t quite an "event" but there is no real "proper" classification for it.
The callback method of this interface is invoked off the EDT! You must NEVER
block this method and must not access UI or Codename One sensitive elements in
this method!
The browser navigation callback is invoked directly from the native web component as it navigates
to a new page. Because of that it is invoked on the native OS thread and gives us a unique
opportunity to handle the navigation ourselves as we see fit. That is why it MUST be invoked on the
native thread, since the native browser is pending on our response to that method, spanning an
invokeAndBlock/callSerially would be to slow and would bog down the browser.
You can use the browser navigation callback to change the UI or even to invoke Java code from
JavaScript code e.g.:
[Link]((url) -> {
if([Link]("[Link] {
409
[Link]().callSerially(() -> [Link]("fnc('<p>You clicked!</p>')"));
return false;
}
return true;
});
410
Chapter 12. File System, Storage, Network &
Parsing
12.1. Jar Resources
Resources that are packaged within the "JAR" don’t really belong in this section of the developer
guide but since they are often confused with Storage/FileSystemStorage this might be the best place
to clarify what they are.
You can place arbitrary files within the src directory of a Codename One project. This file will get
packaged into the final distribution. In standard Java SE you can usually do something like:
InputStream i = getClass().getResourceAsStream("/myFile");
This isn’t guaranteed to work on all platforms and will probably fail on some. Instead you should
use something such as:
This isn’t the only limitation though, you can use hierarchies so something like this would fail:
You can’t use relative paths either so this will fail as well (notice the lack of the first slash):
The reason for those limitations is portability, on iOS and Android resources behave quite
differently so supporting the full Java SE semantics is unrealistic.
This is even worse in some regards. Because of the way iOS works with resources
some unique file names might fail e.g. if you use the @ character or have a . more
than once (e.g. [Link])
Notice that just like in Java SE, the entries within the "JAR" are read only and can’t be modified. You
can’t gain access to the actual file, only to the stream!
12.2. Storage
Storage [[Link] is accessed via the Storage
class. It is a flat filesystem like interface and contains the ability to list/delete and write to named
storage entries.
411
The Storage API also provides convenient methods to write objects to Storage and read them from
Storage specifically readObject & writeObject.
The objects in Storage are usually deleted when an app is uninstalled but are
retained between application updates. A notable exception is Android which, on
some devices, keeps by default objects in Storage after app uninstalling. To force
Android to remove them on app uninstalling, use the build hint
[Link]=false
The sample code below demonstrates listing the content of the storage, adding/viewing and deleting
entries within the storage:
[Link](true);
Form hi = new Form("Storage", new BoxLayout(BoxLayout.Y_AXIS));
[Link]().addCommandToRightBar("+", null, e -> {
TextField tf = new TextField("", "File Name", 20, [Link]);
TextArea body = new TextArea(5, 20);
[Link]("File Body");
Command ok = new Command("OK");
Command cancel = new Command("Cancel");
Command result = [Link]("File Name", [Link](tf).add([Link], body), ok, cancel);
if(ok == result) {
try(OutputStream os = [Link]().createOutputStream([Link]());) {
[Link]([Link]().getBytes("UTF-8"));
createFileEntry(hi, [Link]());
[Link]().animateLayout(250);
} catch(IOException err) {
Log.e(err);
}
}
});
for(String file : [Link]().listEntries()) {
createFileEntry(hi, file);
}
[Link]();
412
});
[Link](content);
}
Storage also offers a very simple API in the form of the Preferences [[Link]
javadoc/com/codename1/io/[Link]] class. The Preferences class allows developers to store
simple variables, strings, numbers, booleans etc. in storage without writing any storage code. This is
a common use case within applications e.g. you have a server token that you need to store you can
store it like this:
[Link]("token", myToken);
The backing store filename defaults to "[Link]", but you can override it by calling
[Link](String) before your app touches the API. This is useful when
you need to segregate preference namespaces per user or plug in an encrypted storage layer.
When you have a batch of updates, prefer the [Link](Map<String,Object>) overload so that
413
all values are persisted with a single disk write.
Preferences can also notify interested parties when a key changes. Register a PreferenceListener via
addPreferenceListener() to react to updates made in other parts of your code (or even triggered
remotely via Codename One Push). Remember to remove listeners you no longer need to avoid leaks.
This gets somewhat confusing with primitive numbers e.g. if you use
[Link]("primitiveLongValue", myLongNumber) then invoke
[Link]("primitiveLongValue", 0) you might get an exception!
This would happen because the value is physically a Long object but you are trying
to get an Integer. The workaround is to remain consistent and use code like this
[Link]("primitiveLongValue", (long)0).
Notice that the file system API is somewhat platform specific in its behavior. All paths used the API
should be absolute otherwise they are not guaranteed to work.
The main reason [Link] & [Link] weren’t supported directly has a lot to do
with the richness of those two API’s. They effectively allow saving a file anywhere, however mobile
devices are far more restrictive and don’t allow apps to see/modify files that are owned by other
apps.
All paths in FileSystemStorage are absolute, this simplifies the issue of portability significantly since
the concept of relativity and current working directory aren’t very portable.
All URL’s use the / as their path separator we try to enforce this behavior even in Windows.
Directories end with the / character and thus can be easily distinguished by their name.
The FileSystemStorage API provides a getRoots() call to list the root directories of the file system
(you can then "dig in" via the listFiles API). However, this is confusing and unintuitive for
developers.
To simplify the process of creating/reading files we added the getAppHomePath() method. This
method allows us to obtain the path to a directory where files can be stored/read.
We can use this directory to place an image to share as we did in the share sample
[[Link]
A common Android hack is to write files to the SDCard storage to share them
among apps. Android 4.x disabled the ability to write to arbitrary directories on
414
the SDCard even when the appropriate permission was requested.
@Override
public boolean isLeaf(Object node) {
return ![Link]().isDirectory((String)node);
}
};
Tree t = new Tree(tm) {
@Override
protected String childToDisplayLabel(Object child) {
String n = (String)child;
int pos = [Link]("/");
if(pos < 0) {
return n;
}
return [Link](pos);
}
};
[Link]([Link], t);
[Link]();
415
Figure 316. Simple sample of a tree for the FileSystemStorage API
The question of storage vs. file system is often confusing for novice mobile developers. This embeds
two separate questions:
The main reasons for the 2 API’s are technical. Many OS’s provide 2 ways of accessing data specific
to the app and this is reflected within the API. E.g. on Android the FileSystemStorage maps to API’s
such as [Link] whereas the Storage maps to [Link]().
The secondary reason for the two API’s is conceptual. FileSystemStorage is more powerful and in a
sense provides more ways to fail, this is compounded by the complex on-device behavior of the API.
Storage is designed to be friendlier to the uninitiated and more portable.
You should pick Storage unless you have a specific requirement that prevents it. Some API’s such as
Capture expect a FileSystemStorage URI so in those cases this would also be a requirement.
Another case where FileSystemStorage is beneficial is the case of hierarchy or native API usage. If
you need a a directory structure or need to communicate with a native API the FileSystemStorage
approach is usually easier.
In some OS’s the FileSystemStorage API can find the content of the Storage API. As
one is implemented on top of the other. This is undocumented behavior that can
change at any moment!
Main Use Case General application Low level access Ship data within the
Data app
416
Option Storage File System JAR Resource
12.4. SQL
Most new devices contain one version of sqlite or another; sqlite is a very lightweight SQL database
designed for embedding into devices. For portability we recommend avoiding SQL altogether since
it is both fragmented between devices (different sqlite versions) and isn’t supported on all devices.
In general SQL seems overly complex for most embedded device programming tasks.
Portability Of SQLite
SQLite is supported on iOS, Android, JavaScript, Desktop/Simulator, and UWP builds. The
JavaScript port relies on the browser’s WebSQL implementation, so it may be unavailable in
environments that have already removed that feature.
The biggest issue with SQLite portability is in iOS. The SQLite version for most platforms is
threadsafe and as a result very stable. However, the iOS version is not!
This might not seem like a big deal normally, however if you forget to close a connection the
GC might close it for you thus producing a crash. This is such a common occurrence that
Codename One logs a warning when the GC collects a database resource on the simulator.
SQL is pretty powerful and very well suited for common tabular data. The Codename One SQL API
is similar in spirit to JDBC but considerably simpler since many of the abstractions of JDBC designed
for pluggable database architecture make no sense for a local database.
Database db = [Link]().openOrCreate("databaseName");
Some SQLite apps ship with a "ready made" database. We allow you to replace the DB file by using
the code:
You can then use the FileSystemStorage class to write the content of your DB file into the path.
Notice that it must be a valid SQLite file!
getDatabasePath() is not supported in the Javascript port. It will always return null.
This is very useful for applications that need to synchronize with a central server or applications
417
that ship with a large database as part of their core product.
Working with a database is pretty trivial, the application logic below can send arbitrary queries to
the database and present the results in a Table. You can probably integrate this code into your app
as a debugging tool:
[Link](true);
Style s = [Link]().getComponentStyle("TitleCommand");
FontImage icon = [Link](FontImage.MATERIAL_QUERY_BUILDER, s);
Form hi = new Form("SQL Explorer", new BorderLayout());
[Link]().addCommandToRightBar("", icon, (e) -> {
TextArea query = new TextArea(3, 80);
Command ok = new Command("Execute");
Command cancel = new Command("Cancel");
if([Link]("Query", query, ok, cancel) == ok) {
Database db = null;
Cursor cur = null;
try {
db = [Link]().openOrCreate("[Link]");
if([Link]().startsWith("select")) {
cur = [Link]([Link]());
int columns = [Link]();
[Link]();
if(columns > 0) {
boolean next = [Link]();
if(next) {
ArrayList<String[]> data = new ArrayList<>();
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = [Link](iter);
}
while(next) {
Row currentRow = [Link]();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = [Link](iter);
}
[Link](currentRowArray);
next = [Link]();
}
Object[][] arr = new Object[[Link]()][];
[Link](arr);
[Link]([Link], new Table(new DefaultTableModel(columnNames, arr)));
} else {
[Link]([Link], "Query returned no results");
}
} else {
[Link]([Link], "Query returned no results");
}
} else {
[Link]([Link]());
[Link]([Link], "Query completed successfully");
}
[Link]();
} catch(IOException err) {
Log.e(err);
[Link]();
418
[Link]([Link], "Error: " + err);
[Link]();
} finally {
[Link](db);
[Link](cur);
}
}
});
[Link]();
Figure 317. Querying the temp demo generated by the SQLDemo application
NetworkManager effectively alleviates the need for managing network threads by managing the
complexity of network threading. The connection request class can be used to facilitate web service
requests when coupled with the JSON/XML parsing capabilities.
419
ConnectionRequest request = new ConnectionRequest(url, false);
[Link]((e) -> {
// process the response
});
Notice that you can also implement the same thing and much more by avoiding the response
listener code and instead overriding the methods of the ConnectionRequest class which offers
multiple points to override e.g.
You don’t need to close the output/input streams passed to the request methods.
They are implicitly cleaned up.
NetworkManager also supports synchronous requests which work in a similar way to Dialog via the
[1]
invokeAndBlock call and thus don’t block the EDT illegally. E.g. you can do something like this:
Notice that in this case the addToQueueAndWait method returned after the connection completed. Also
notice that this was totally legal to do on the EDT!
420
12.5.1. Timeouts & Retries
Each request can override the global timeout using setTimeout(int), which expects a duration in
milliseconds. This is especially useful when you are talking to endpoints that occasionally need a
longer window than the default NetworkManager setting. You can also configure how many times
Codename One should retry a failing call silently by using setSilentRetryCount(int). When the
silent retry limit is reached the standard error handling kicks in (e.g. listeners fire, fail dialogs
show, etc.).
12.5.2. Threading
By default the NetworkManager launches with a single network thread. This is sufficient for very
simple applications that don’t do too much networking but if you need to fetch many images
concurrently and perform web services in parallel this might be an issue.
Once you increase the thread count there is no guarantee of order for your
requests. Requests might not execute in the order with which you added them to
the queue!
[Link]().updateThreadCount(4);
All the callbacks in the ConnectionRequest occur on the network thread and not on the EDT!
There is one exception to this rule which is the postResponse() method designed to update the UI
after the networking code completes.
Never change the UI from a ConnectionRequest callback. You can either use a
listener on the ConnectionRequest, use postResponse() (which is the only exception
to this rule) or wrap your UI code with callSerially.
NetworkManager nm = [Link]();
if ([Link]()) {
boolean vpnActive = [Link]();
}
Both Android and iOS implementations rely on platform networking metadata and interface-name
patterns (e.g. tun, ppp, utun, ipsec). This means the result can be wrong in either direction:
421
• False negatives: Some VPNs may hide or avoid these patterns.
Use this API only for UX or diagnostics (e.g. warnings, telemetry, or logging), not for access control
or fraud prevention by itself.
HTTP/S is a complex protocol that expects complex encoded data for its requests. Codename One
tries to simplify and abstract most of these complexities behind common sense API’s while still
providing the full low level access you would expect from such an API.
Arguments
HTTP supports several "request methods", most commonly GET & POST but also a few others such as
HEAD, PUT, DELETE etc.
Arguments in HTTP are passed differently between GET and POST methods. That is what the setPost
method in Codename One determines, whether arguments added to the request should be placed
using the GET semantics or the POST semantics.
This will implicitly add a get argument with the content of value. Notice that we don’t really care
what value is. It’s implicitly HTTP encoded based on the get/post semantics. In this case it will use
the get encoding since we passed false to the constructor.
This would be almost identical but doesn’t provide the convenience for switching back and forth
between GET/POST and it isn’t as fluent.
We can skip the encoding in complex cases where server code expects illegal HTTP characters (this
happens) using the addArgumentNoEncoding method. We can also add multiple arguments with the
same key using addArgumentArray.
Methods
As we explained above, the setPost() method allows us to manipulate the get/post semantics of a
request. This implicitly changes the POST or GET method submitted to the server.
However, if you wish to have finer grained control over the submission process e.g. for making a
422
HEAD request you can do this with code like:
Headers
When communicating with HTTP servers we often pass data within headers mostly for
authentication/authorization but also to convey various properties.
Some headers are builtin as direct API’s e.g. content type is directly exposed within the API since it’s
a pretty common use case. We can set the content type of a post request using:
We can also add any arbitrary header type we want, e.g. a very common use case is basic
authorization where the authorization header includes the Base64 encoded user/password
combination as such:
This can be quite tedious to do if you want all requests from your app to use this header. For this
use case you can just use:
Server Headers
Server returned headers are a bit trickier to read. We need to subclass the connection request and
override the readHeaders method e.g.:
423
// just read from the response input stream
}
};
[Link]().addToQueue(request);
Here we can extract the headers one by one to handle complex headers such as cookies,
authentication etc.
Error Handling
As you noticed above practically all of the methods in the ConectionRequest throw IOException. This
allows you to avoid the try/catch semantics and just let the error propagate up the chain so it can be
handled uniformly by the application.
There are two distinct placed where you can handle a networking error:
Notice that the NetworkManager error handler takes precedence thus allowing you to define a global
policy for network error handling by consuming errors.
E.g. if I would like to block all network errors from showing anything to the user I could do
something like this:
[Link]().addToQueue(request);
[Link]().addErrorListener((e) -> [Link]());
We can also override the error callbacks of the various types in the request e.g. in the case of a
server error code we can do:
424
The error callback callback is triggered in the network thread!
As a result it can’t access the UI to show a Dialog or anything like that.
Another approach is to use the setFailSilently(true) method on the ConnectionRequest. This will
prevent the ConnectionRequest from displaying any errors to the user. It’s a very powerful strategy if
you use the synchronous version of the API’s e.g.:
This code will only work with the synchronous "AndWait" version of the method
since the response code will take a while to return for the non-wait version.
Error Stream
When we get an error code that isn’t 200/300 we ignore the result. This is problematic as the result
might contain information we need. E.g. many webservices provide further XML/JSON based details
describing the reason for the error code.
Calling setReadResponseForErrors(true) will trigger a mode where even errors will receive the
readResponse callback with the error stream. This also means that API’s like getData and the listener
API’s will also work correctly in case of error.
12.5.5. GZIP
Gzip is a very common compression format based on the lz algorithm, it’s used by web servers
around the world to compress data.
By default GZConnectionRequest doesn’t request gzipped data (only unzips it when its received) but
its pretty easy to do so just add the HTTP header Accept-Encoding: gzip e.g.:
425
[Link]("Accept-Encoding", "gzip");
Do the rest as usual and you should have smaller responses from the servers.
You can always submit data in the buildRequestBody but this is flaky and has some limitations in
terms of devices/size allowed. HTTP standardized file upload capabilities thru the multipart request
protocol, this is implemented by countless servers and is well documented. Codename One supports
this out of the box:
Since we assume most developers reading this will be familiar with Java here is the way to
implement the multipart upload in the servlet API:
@Override
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
Collection<Part> parts = [Link]();
Part data = [Link]().next();
try(InputStream is = [Link]();) {}
// store or do something with the input stream
}
}
}
12.5.7. Parsing
Codename One has several built in parsers for JSON, XML, CSV & Properties formats. You can use
those parsers to read data from the Internet or data that is shipping with your product. E.g. use the
CSV data to setup default values for your application.
426
All our parsers are designed with simplicity and small distribution size; they don’t validate and will
fail in odd ways when faced with broken data. The main logic behind this is that validation takes up
CPU time on the device where CPU is a precious resource.
Parsing CSV
CSV is probably the easiest to use, the "Comma Separated Values" format is just a list of values
separated by commas (or some other character) with new lines to indicate another row in the table.
These usually map well to an Excel spreadsheet or database table and are supported by default in
all spreadsheets.
Figure 319. CSV parsing results, notice the properly escaped parentheses and comma
The data contains a two dimensional array of the CSV content. You can change the delimiter
character by using the CSVParser constructor that accepts a character.
Notice that we used CharArrayReader from the [Link] package for this
sample. Normally you would want to use [Link] for real
world data.
JSON
The JSON ("Java Script Object Notation") format is popular on the web for passing values to/from
webservices since it works so well with JavaScript. Parsing JSON is just as easy but has two different
variations. You can use the JSONParser [[Link]
[Link]] class to build a tree of the JSON data as such:
427
Hashtable response = [Link](reader);
The response is a Map containing a nested hierarchy of Collection ([Link]), Strings and
numbers to represent the content of the submitted JSON. To extract the data from a specific path
just iterate the Map keys and recurs into it.
The sample below uses results from an API of ice and fire [[Link] that queries
structured data about the "Song Of Ice & Fire" book series. Here is a sample result returned from
the API for the query [Link] :
[
{
"url": "[Link]
"name": "Chayle",
"culture": "",
"born": "",
"died": "In 299 AC, at Winterfell",
"titles": [
"Septon"
],
"aliases": [],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"[Link]
"[Link]
"[Link]
],
"povBooks": [],
"tvSeries": [],
"playedBy": []
},
{
"url": "[Link]
"name": "Gillam",
"culture": "",
"born": "",
"died": "",
"titles": [
"Brother"
],
"aliases": [],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"[Link]
428
],
"povBooks": [],
"tvSeries": [],
"playedBy": []
},
{
"url": "[Link]
"name": "High Septon",
"culture": "",
"born": "",
"died": "",
"titles": [
"High Septon",
"His High Holiness",
"Father of the Faithful",
"Voice of the Seven on Earth"
],
"aliases": [
"The High Sparrow"
],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"[Link]
"[Link]
],
"povBooks": [],
"tvSeries": [
"Season 5"
],
"playedBy": [
"Jonathan Pryce"
]
}
]
We will place that into a file named "[Link]" in the src directory to make the next
sample simpler:
429
[Link]<String> aliases = ([Link]<String>)[Link]("aliases");
if(aliases != null && [Link]() > 0) {
name = [Link](0);
}
}
MultiButton mb = new MultiButton(name);
if(titles != null && [Link]() > 0) {
mb.setTextLine2([Link](0));
}
[Link]((e) -> [Link]().execute(url));
[Link](mb);
}
} catch(IOException err) {
Log.e(err);
}
[Link]();
① The JSONParser returns a Map which is great if the root object is a Map but in some cases its a list of
elements (as is the case above). In this case a special case "root" element is created to contain the
actual list of elements.
② We rely that the entries are all maps, this might not be the case for every API type.
③ Notice that the "titles" and "aliases" entries are both lists of elements. We use [Link] to
avoid a clash with [Link].
Figure 320. Parsed JSON result, clicking the elements opens the URL from the JSON
The structure of the returned map is sometimes unintuitive when looking at the
raw JSON. The easiest thing to do is set a breakpoint on the method and use the
inspect variables capability of your IDE to inspect the returned element hierarchy
while writing the code to extract that data
An alternative approach is to use the static data parse() method of the JSONParser class and
implement a callback parser e.g.:
[Link](reader, callback);
Notice that a static version of the method is used! The callback object is an instance of the
JSONParseCallback interface, which includes multiple methods. These methods are invoked by the
parser to indicate internal parser states, this is similar to the way traditional XML SAX event
parsers work.
430
XML Parsing
We’ve had a great sample of working with XMLParser in the Tree Section
[[Link] of this guide.
XPath Processing
Lets start by demonstrating how to process a response from the Google Reverse Geocoder API
[[Link] Lets start with this XML snippet:
431
<address_component>
<long_name>Canada</long_name>
<short_name>CA</short_name>
<type>country</type>
<type>political</type>
</address_component>
</result>
</GeocodeResponse>
We want to extract some of the data above into simpler string results. We can do this using:
If you are at all familiar with processing responses from webservices, you will notice that what
would normally require several lines of code of selecting and testing nodes in regular java can now
be done in a single line using the new path expressions.
To use the expression processor when calling a webservice, you could use something like the
following to parse JSON (notice this is interchangeable between JSON and XML):
@Override
protected void readResponse(InputStream input) throws IOException {
Result result = [Link](input, [Link]);
country = [Link]("/results/address_components[types='country']/long_name");
region = [Link](
"/results/address_components[types='administrative_area_level_1']/long_name");
city = [Link]("/results/address_components[types='locality']/long_name");
json = [Link]();
}
@Override
432
protected void postResponse() {
[Link]();
[Link](country);
[Link](region);
[Link](city);
[Link](new SpanLabel(json));
[Link]();
}
};
[Link]("application/json");
[Link]("Accept", "application/json");
[Link]("sensor", "true");
[Link]("latlng", [Link]() + "," + [Link]());
[Link]().addToQueue(request);
});
[Link]();
[source,java]
The returned JSON looks something like this (notice it’s snipped because the data is too long):
{
"status": "OK",
"results": [
{
"place_id": "ChIJJ5T9-iFawokRTPGaOginEO4",
"formatted_address": "280 Broadway, New York, NY 10007, USA",
"address_components": [
{
"short_name": "280",
"types": ["street_number"],
"long_name": "280"
},
{
"short_name": "Broadway",
"types": ["route"],
"long_name": "Broadway"
},
{
"short_name": "Lower Manhattan",
"types": [
"neighborhood",
"political"
],
"long_name": "Lower Manhattan"
},
{
"short_name": "Manhattan",
"types": [
"sublocality_level_1",
"sublocality",
"political"
],
"long_name": "Manhattan"
433
},
{
"short_name": "New York",
"types": [
"locality",
"political"
],
"long_name": "New York"
},
{
"short_name": "New York County",
"types": [
"administrative_area_level_2",
"political"
],
"long_name": "New York County"
},
{
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
],
"long_name": "New York"
},
{
"short_name": "US",
"types": [
"country",
"political"
],
"long_name": "United States"
},
{
"short_name": "10007",
"types": ["postal_code"],
"long_name": "10007"
},
{
"short_name": "1868",
"types": ["postal_code_suffix"],
"long_name": "1868"
}
],
"types": ["street_address"],
"geometry": {
"viewport": {
"northeast": {
"lng": -74.0044642197085,
"lat": 40.7156470802915
},
434
"southwest": {
"lng": -74.0071621802915,
"lat": 40.7129491197085
}
},
"location_type": "ROOFTOP",
"location": {
"lng": -74.00581319999999,
"lat": 40.7142981
}
}
}
/* SNIPED the rest */
]
}
The XML processor currently handles global selections by using a double slash anywhere within the
expression, for example:
// get all address_component names anywhere in the document with a type "political"
String array[] = [Link]("//address_component[type='political']/long_name");
// get all types anywhere under the second result (dimension is 0-based)
String array[] = [Link]("/result[1]//type");
Notice that Google’s JSON webservice uses plural form for each of the node names
in that API (ie. results, address_components, and types) where they don’t in the
XML services (ie result, address_component etc.)
Second Example
It also possible to do some more complex expressions. We’ll use the following XML fragment for the
next batch of examples:
435
delta="0" singlespoints="485000" doublespoints="675"
deductedpoints="0" totalpoints="485675">
<firstname>Bernard</firstname>
<lastname>Tomic</lastname>
<town>SOUTHPORT</town>
<state>QLD</state>
<dob>1992-10-21</dob>
</player>
<player id="2585" coretennisid="1500" rank="2"
delta="0" singlespoints="313500" doublespoints="12630"
deductedpoints="0" totalpoints="326130">
<firstname>Mathew</firstname>
<lastname>Ebden</lastname>
<town>CHURCHLANDS</town>
<state>WA</state>
<dob>1987-11-26</dob>
</player>
<player id="6457" coretennisid="287" rank="3"
delta="0" singlespoints="132500" doublespoints="1500"
deductedpoints="0" totalpoints="134000">
<firstname>Lleyton</firstname>
<lastname>Hewitt</lastname>
<town>EXETER</town>
<state>SA</state>
<dob>1981-02-24</dob>
</player>
<!-- ... etc ... -->
</rankings>
Above, if you want to select the IDs of all players that are ranked in the top 2, you can use an
expression like:
Notice above that the expression is using an attribute for selecting both rank and
id. In JSON documents, if you attempt to select an attribute, it will look for a child
node under the attribute name you ask for)
If a document is ordered, you might want to select nodes by their position, for example:
It is also possible to select parent nodes, by using the ‘..’ expression. For example:
436
int id = [Link]("//lastname[text()='Hewitt']/../@id");
Above, we globally find a lastname element with a value of ‘Hewitt’, then grab the parent node of
lastname which happens to be the player node, then grab the id attribute from the player node.
Alternatively, you could get the same result from the following simpler statement:
int id = [Link]("//player[lastname='Hewitt']/@id");
String id=[Link]("//player[//address[country/isocode='CA']]/@id");
In the above example, if the player node had an address object, we’d be selecting all players from
Canada. This is a simple example of a nested expression, but they can get much more complex,
which will be required as the documents themselves get more complex.
Moving on, to select a node based on the existence of an attribute:
Above, we selected the IDs of all ranked players. Conversely, we can select the non-ranked players
like this:
Above, we selected all players that have a middle name.<br/> Keep in mind that the Codename One
path expression language is not a full implementation of XPath 1.0, but does already handle many
of the most useful features of the specification.
Properties Files
Notice that properties file both in Java proper and in Codename One don’t support non-ascii
characters. In order to encode unicode values into the properties file format you should use the
native2ascii tool that ships with the JDK.
437
One major difference between standard Java properties and the ones in Codename One is that
Codename One sorts properties alphabetically when saving. Java uses random order based on the
Hashtable natural ordering.
Codename One includes a Network Monitor tool which you can access via the simulator menu
option. This tool reflects all the requests made through the connection requests and displays them
in the left pane. This allows you to track issues in your code/web service and see everything the is
"going through the wire".
This is a remarkably useful tool for optimizing and for figuring out what exactly is happening with
your server connection logic.
[Link](fileName) &
[Link](fileName);
Downloading Images
Codename One has multiple ways to download an image and the general recommendation is the
URLImage [[Link] However, the
URLImage assumes that you know the size of the image in advance or that you are willing to resize it.
In that regard it works great for some use cases but not so much for others.
The download methods mentioned above are great alternatives but they are a bit verbose when
working with images and don’t provide fine grained control over the ConnectionRequest e.g. making
438
a POST request to get an image.
Adding global headers is another use case but you can use addDefaultHeader
[[Link]
[Link]-] to add those.
To make this process simpler there is a set of helper methods in ConnectionRequest that downloads
images directly [[Link]
[Link]-].
These methods complement the Util methods but go a bit further and feature very terse syntax e.g.
you can just download a ConnectionRequest to Storage using code like this:
URLImage Caching
However, when we introduced it we didn’t have support for the cache filesystem or for the
JavaScript port. The cache filesystem is probably the best place for images of URLImage so supporting
that as a target is a "no brainer" but JavaScript seems to work so why would it need a special case?
JavaScript already knows how to download and cache images from the web. URLImage is actually a
step back from the things a good browser can do so why not use the native abilities of the browser
when we are running there and fallback to using the cache filesystem if it’s available and as a last
resort go to storage…
public static Image createCachedImage(String imageName, String url, Image placeholder, int resizeRule);
There are a few important things you need to notice about this method:
• It returns Image and not URLImage. This is crucial. Down casting to `URLImage* will work on
the simulator but might fail in some platforms (e.g. JavaScript) so don’t do that!
Since this is implemented natively in JavaScript we need a different abstraction for that
platform.
• It doesn’t support image adapters and instead uses a simplified resize rule. Image adapters
work on URLImage since we have a lot of control in that class. However, in the browser our
control is limited and so an adapter won’t work.
If you do use this approach it would be far more efficient when running in the JavaScript port and
will make better use of caching in most OS’s.
439
12.7. Rest API
The Rest API makes it easy to invoke a restfull webservice without many of the nuances of
ConnectionRequest. You can use it to define the HTTP method and start building based on that. So if I
want to get a parsed JSON result from a URL you could do:
For a lot of REST requests this will fail because we need to add an HTTP header indicating that we
accept JSON results. We have a special case support for that:
Notice the usage of post and the body builder method. There are MANY methods in the builder class
that cover pretty much everything you would expect and then some when it comes to the needs of
rest services.
• .priority(byte) lets you change the underlying ConnectionRequest priority when you need
certain calls to jump the queue.
• .cookiesEnabled(boolean) controls whether cookies are persisted for the request when you need
stateless behavior.
• .useBoolean(boolean) and .useLongs(boolean) toggle how the JSON parser materializes number
and boolean types inside the resulting Map, which is handy when your backend is strict about
data types.
I changed the code in the kitchen sink webservice sample to use this API. I was able to make it
shorter and more readable without sacrificing anything.
The best way to explain the usage of this API is via a concrete "real world" example. Twilio provides
many great telephony oriented webservices to developers. One of those is an SMS sending
webservice which can be useful for things such as "device activation".
To get started you would need to signup to Twilio [[Link] and have the following 3 variable
values:
440
String accountSID = "----------------";
String authToken = "---------------";
String fromPhone = "your Twilio phone number here";
You can open a trial Twilio account and it just tags all of your SMS’s. Notice you
would need to use a US based number if you don’t want to pay
We can now send hello world as an SMS to the end user. Once this is in place sending an SMS via
REST is just a matter of using the Rest API:
That’s pretty cool as the curl command maps almost directly to the Rest API call!
What we do here is actually pretty trivial, we open a connection the the api messages URL. We add
arguments to the body of the post request and define the basic authentication data.
The result is in JSON form we mostly ignore it since it isn’t that important but it might be useful for
error handling. This is a sample response (redacted keys):
{
"sid": "[sid value]",
"date_created": "Sat, 09 Sep 2017 19:47:30 +0000",
"date_updated": "Sat, 09 Sep 2017 19:47:30 +0000",
"date_sent": null,
"account_sid": "[sid value]",
"to": "[to phone number]",
"from": "[from phone number]",
"messaging_service_sid": null,
"body": "Sent from your Twilio trial account - Hello World",
"status": "queued",
"num_segments": "1",
"num_media": "0",
"direction": "outbound-api",
"api_version": "2010-04-01",
441
"price": null,
"price_unit": "USD",
"error_code": null,
"error_message": null,
"uri": "/2010-04-01/Accounts/[sid value]/Messages/[Link]",
"subresource_uris": {
"media": "/2010-04-01/Accounts/[sid value]/Messages/[message value]/[Link]"
}
}
Notice the error message entry which is null meaning there was no error, if there was an error
we’d have a message there or an error code that isn’t in the 200-210 range.
This should display an error message to the user if there was a problem sending the SMS:
if([Link]() != null) {
String error = (String)[Link]().get("error_message");
if(error != null) {
[Link](error);
}
} else {
[Link]("Error sending SMS: " + [Link]());
}
There are limits to the types of values that can be passed via the webservice wizard protocol but it
is highly efficient since it is a binary protocol and very extensible thru object externalization. All
methods are provided both as asynchronous and synchronous calls for the convenience of the
developer.
Figure 323. The first step in creating a client/server connection using the webservice wizard is to create a
web application
442
Figure 324. Any name will do
Normally you should have a server setup locally. I use Tomcat since it’s really trivial and I don’t
really need much but there are many great Java webservers out there and this should work with all
of them!
Figure 326. Configure the application server to the newly created app, notice the application context value
which we will use later
443
Figure 327. In the main Codename One project right click and select the WebService Wizard option
Figure 328. Set the package and class name for the webservice abstraction (notice this isn’t your main class
name) and then add the methods you want in the webservice
Figure 329. Add the methods and their arguments/return types. Once you finished adding all of those press
the "Generate" button
The types of arguments are pretty limited however you can pass an arbitrary
Externalizable object which can be "anything"
444
Figure 330. Pick the directory in the server project to which the source files will be generated by default this
is the src/java directory under the project we created in the first step
Figure 331. If you saved to the right location the server project directory should look like this
We can now open the [Link] file in the server and it looks like this:
All we need to do is fill in the code, for this example we’ll only implement the first method for
simplicity:
445
}
}
Now lets open the client side code, in the [Link] file we see this
//...
}
The destination URL needs to point at the actual server which you will recall from the new project
creation should include "HelloWebServiceWizard". So we can fix the URL to:
You would naturally need to update the host name of the server for running on a device otherwise
the device would need to reside within your internal network and point to your IP address.
It is now time to write the actual client code that calls this. Every method we defined above is now
defined as a static method within the GameOfThronesService class with two permutations. One is a
synchronous permutation that behaves exactly as expected. It blocks the calling thread while
calling the server and might throw an IOException if something failed.
This type of method (synchronous method) is very easy to work with since it’s completely legal to
call it from the event dispatch thread and it’s very easy to map it to application logic flow.
The second type of method uses the async JavaScript style callbacks and accepts the callback
interface. It returns immediately and doesn’t throw any exception. It will call onSuccess/onError
based on the server result.
You can pick either one of these approaches based on your personal preferences. Here we
demonstrate both uses with the server API:
[Link]((e) -> {
try {
String[] books = [Link]();
[Link]("--- SYNC");
for(String b : books) {
[Link](b);
}
[Link]();
} catch(IOException err) {
Log.e(err);
446
}
});
[Link]((e) -> {
[Link](new Callback<String[]>() {
@Override
public void onSucess(String[] value) {
[Link]("--- ASYNC");
for(String b : value) {
[Link](b);
}
[Link]();
}
@Override
public void onError(Object sender, Throwable err, int errorCode, String errorMessage) {
Log.e(err);
}
});
});
HTTP provides two ways to do that the ETag [[Link] and Last-Modified
[[Link] While both are great they are
non-trivial to use and by no definition seamless.
We just added an experimental feature to connection request that allows you to set the caching
mode to one of 4 states either globally or per connection request:
• SMART means all get requests are cached intelligently and caching is "mostly" seamless
• MANUAL means that the developer is responsible for the actual caching but the system will not
do a request on a resource that’s already "fresh"
447
• OFFLINE will fetch data from the cache and wont try to go to the server. It will generate a 404
error if data isn’t available
You can toggle these in the specific request by using setCacheMode(CachingMode) and set the global
default using setDefaultCacheMode(CachingMode).
Caching only applies to GET operations, it will not work for POST or other methods
12.9.1. getCachedData()
This returns the cached data. This is invoked to implement readResponse(InputStream) when
running offline or when we detect that the local cache isn’t stale.
The smart mode implements this properly and will fetch the right data. However, the manual mode
doesn’t store the data and relies on you to do so. In that case you need to return the data you stored
at this point and must implement this method for manual mode.
12.9.2. cacheUnmodified()
This is a callback that’s invoked to indicate a cache hit, meaning that we already have the data.
The default implementation still tries to call all the pieces for compatibility (e.g. readResponse).
However, if this is unnecessary you can override that method with a custom implementation or
even a blank implementation to block such a case.
These methods are pretty self explanatory. Notice one caveat though…
When you download a file or a storage element we don’t cache them and rely on the file/storage
element to be present and serve as "cache". When purging we won’t delete a file or storage element
you downloaded and thus these might remain.
However, we do remove the ETag and Last-Modified data so the files might get refreshed the next
time around.
448
URLImage always uses the local copy). This isn’t a bad thing, it’s pretty efficient.
However, it might be important to update the image if it changed but we still want caching.
The CachedDataService will fetch data if it isn’t cached locally and cache it. When you "refresh" it
will send a special HTTP request that will only send back the data if it has been updated since the
last refresh:
[Link]();
CachedData d = (CachedData)[Link]().readObject("LocallyCachedData");
if(d == null) {
d = new CachedData();
[Link]("[Link]
}
// check if there is a new version of this on the server
[Link](d, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
// invoked when/if the data arrives, we now have a fresh cache
[Link]().writeObject("LocallyCachedData", d);
}
});
Codename One doesn’t support the Java SE Serialization API due to the size issues and complexities
related to obfuscation.
The major objects that are supported by default in the Codename One Externalizable are: String,
Collection, Map, ArrayList, HashMap, Vector, Hashtable, Integer, Double, Float, Byte, Short, Long,
Character, Boolean, Object[], byte[], int[], float[], long[], double[].
However, notice that some things aren’t polymorphic e.g. if we will externalize a String[] we will
get back an Object[] since String arrays aren’t detected by the implementation.
449
The externalization process caches objects so the app will seem to work and only
fail on restart!
Implementing the Externalizable interface is only important when we want to store a proprietary
object. In this case we must register the object with the [Link] class so the
externalization algorithm will be able to recognize it by name by invoking:
[Link]("MyClass", [Link]);
You should do this early on in the app e.g. in the init(Object) but you shouldn’t do
it in a static initializer within the object as that might never be invoked!
An Externalizable object must have a default public constructor and must implement the
following 4 methods:
The getVersion() method returns the current version of the object allowing the stored data to
change its structure in the future (the version is then passed when internalizing the object). The
object id is a String uniquely representing the object; it usually corresponds to the class name (in
the example above the Unique Name should be MyClass).
Developers need to write the data of the object in the externalize method using the methods in the
data output stream and read the data of the object in the internalize method e.g.:
450
}
[Link](expires);
}
Since strings might be null sometimes we also included convenience methods to implement such
externalization. This effectively writes a boolean before writing the UTF to indicate whether the
string is null:
Assuming we added a new date field to the object we can do the following. Notice that a Date is
really a long value in Java that can be null. For completeness the full class is presented below:
public MyClass() {}
451
}
Notice that we only need to check for compatibility during the reading process as the writing
process always writes the latest version of the data.
The process of showing a progress bar for a long IO operation such as downloading is automatically
452
mapped to the IO stream in Codename One using the SliderBridge [
[Link] class.
You can simulate network delays and disconnected network in the Simulator
menu
The SliderBridge class can bind a ConnectionRequest to a Slider and effectively indicate the progress
of the download. E.g.:
Figure 333. SliderBridge progress for downloading the image in the slow network mode
As part of the premium cloud features it is possible to invoke [Link]() in order to email a log
directly to the developer account. Codename One can do that seamlessly based on changes printed
into the log or based on exceptions that are uncaught or logged e.g.:
[Link](Log.REPORTING_DEBUG);
[Link](true, 2);
This code will send a log every 2 minutes to your email if anything was changed. You can place it
453
within the init(Object) method of your application.
For a production application you can use Log.REPORTING_PRODUCTION which will only email the log on
exception.
12.14. Sockets
At this moment Codename One only supports TCP sockets. Server socket (listen/accept) is only
available on Android and the simulator but not on iOS.
You can check if Sockets are supported using the [Link](). You can test for server
socket support using [Link]().
To use sockets you can use the [Link](String host, int port, SocketConnection
eventCallback) method.
To listen on sockets you can use the [Link](int port, Class scClass) method which will
instantiate a SocketConnection instance (scClass is expected to be a subclass of SocketConnection) for
every incoming connection.
This simple example allows you to create a server and a client assuming the device supports both:
454
}
@Override
public void connectionEstablished(InputStream is, OutputStream os) {
try {
int counter = 1;
while(isConnected()) {
[Link](("Hi: " + counter).getBytes());
counter++;
[Link](2000);
}
} catch(Exception err) {
[Link]();
}
}
});
});
[Link](new BoxLayout(BoxLayout.Y_AXIS));
[Link](btn);
[Link](connect);
[Link](host);
[Link]();
}
@Override
public void connectionError(int errorCode, String message) {
[Link]("Error");
}
@Override
public void connectionEstablished(InputStream is, OutputStream os) {
try {
byte[] buffer = new byte[8192];
while(isConnected()) {
int pending = [Link]();
if(pending > 0) {
int size = [Link](buffer, 0, 8192);
if(size == -1) {
return;
}
455
if(size > 0) {
updateLabel(new String(buffer, 0, size));
}
} else {
[Link](50);
}
}
} catch(Exception err) {
[Link]();
}
}
}
12.15. Properties
In standard Java we usually have a POJO (Plain Old Java Object) which has getters/setters e.g. we
can have a simple Meeting class like this:
456
public void setAttendance(int attendance) {
[Link] = attendance;
}
}
That’s a classic POJO and it is the force that underlies JavaBeans and quite a few tools in Java.
The properties are effectively the getters/setters e.g. subject, when etc. but properties have several
features that are crucial:
• They can be manipulated in runtime by a tool that had no knowledge of them during compile
time
These features are crucial since properties allow us all kinds of magic e.g. hibernate/ORM uses
properties to bind Java objects to a database representation, jaxb does it to parse XML directly into
Java objects and GUI builders use them to let us customize UI’s visually.
POJO’s don’t support most of that so pretty much all Java based tools use a lot of reflection &
bytecode manipulation. This works but has a lot of downsides e.g. say I want to map an object both
to the Database and to XML/JSON.
And how do I write custom generic code that uses such abilities? Do I need to manipulate the VM?
These are all very abstract ideas, lets look at how we think properties should look in Java and how
we can benefit from this moving forward.
This is the same class as the one above written with properties:
@Override
public PropertyIndex getPropertyIndex() {
return idx;
}
}
This looks a bit like a handful so let’s start with usage which might clarify a few things then dig into
the class itself.
457
When we used a POJO we did this:
Encapsulation
At first glance it looks like we just created public fields (which we did) but if you will look closely at
the declaration you will notice the final keyword:
[Link] = otherValue;
So all setting/getting must happen thru the set/get methods and they can be replaced. E.g. this is
valid syntax that prevents setting the property to null and defaults it to an empty string:
We’ll discuss the reason for returning the Meeting instance below
Since Property is a common class it’s pretty easy for introspective code to manipulate properties.
However, it can’t detect properties in an object without reflection.
That’s why we have the index object and the PropertyBusinessObject interface (which defines
getPropertyIndex).
458
The PropertyIndex class provides meta data for the surrounding class including the list of the
properties within. It allows enumerating the properties and iterating over them making them
accessible to all tools.
Furthermore all properties are observable with the property change listener. I can just write this to
instantly print out any change made to the property:
That’s the simple stuff that can be done with properties, but they can do much more!
For starters all the common methods of Object can be implemented with almost no code:
@Override
public PropertyIndex getPropertyIndex() {
return idx;
}
@Override
public boolean equals(Object obj) {
return [Link]() == getClass() && [Link](((TodoTask)obj).getPropertyIndex());
}
@Override
public int hashCode() {
return [Link]();
}
}
We already have some simple code that can convert an object to/from JSON Maps e.g. this can fill
the property values from parsed JSON:
[Link]().populateFromMap(jsonParsedData);
459
String jsonString = [Link]();
We also have a very simple ORM solution that maps values to table columns and can create tables.
It’s no hibernate but sqlite isn’t exactly big iron so it might be good enough.
Constructors
One of the problematic issues with constructors is that any change starts propagating everywhere.
If I have fields in the constructor and I add a new field later I need to keep the old constructor for
compatibility.
That is why every property in the definition needed the Meeting generic and the set method returns
the Meeting instance…
We are pretty conflicted on this feature and are thinking about removing it.
Seamless Serialization
Lets assume I have an object called Contacts which includes contact information of contact e.g.:
@Override
public PropertyIndex getPropertyIndex() {
return idx;
}
public Contact() {
[Link]("Name");
[Link]("E-Mail");
460
[Link]("Phone");
[Link]("Date Of Birth");
[Link]("Gender");
[Link]("Rank");
}
}
Standard Java Objects can be serialized in Codename One by implementing the Codename One
Externalizable interface. You also need to register the Externalizable object so the implementation
will be aware of it. Codename One business objects are seamlessly Externalizable and you just need
to register them.
new Contact().getPropertyIndex().registerExternalizable();
After you do that once you can write/read contacts from storage if you so desire:
[Link]().writeObject("MyContact", contact);
This will obviously also work for things like List<Contact> etc…
Writing SQL code can be tedious. Which is why SQLMap is such an important API for some of us.
SQLMap allows CRUD (Create, Read, Update, Delete) operations on the builtin SQLite database using
property objects.
If we continue the example from above to show persistence to the SQL database we can just do
something like this:
try {
Contact c = new Contact();
db = [Link]().openOrCreate("[Link]"); ①
sm = [Link](db); ②
[Link](c, [Link]); ③
[Link](c); ④
} catch(IOException err) {
Log.e(err);
}
461
}
③ Define the primary key for contact as id and set it to auto increment which will give it a unique
value from the database
Notice that at this time altering a created table isn’t possible so if you add a new
property you might need to detect that and do an alter call manually
[Link](myContact);
[Link](myContact);
[Link](myContact);
for(PropertyBusinessObject cc : contacts) {
Contact currentContact = (Contact)cc;
// ...
}
• Page to start with - in this case if we have more than 1000 elements we can fetch the next page
462
using [Link](c, [Link], true, 1000, 1)
There are many additional configurations where we can fine tune how a specific property maps to
a column etc.
The SQLMap API is very simplistic and doesn’t try to be Hibernate/JPA for mobile. So basic things
aren’t available at this time and just won’t work. This isn’t necessarily a problem as mobile
databases don’t need to be as powerful as server databases.
Relational Mappings/JOIN
Right now we can’t map an object to another object in the database with the typical one-many, one-
one etc. relationships that would could do with JPA. The SQLMap API is really simplistic and isn’t
suited for that level of mapping at this time.
If there is demand for this it’s something we might add moving forward but our goal isn’t to re-
invent hibernate.
Threading
SQLite is sensitive to threading issues especially on iOS. We mostly ignored the issue of threading
and issue all calls in process. This can be a problem for larger data sets as the calls would usually go
on the EDT.
This is something we might want to fix for the generic SQLite API so low level SQL queries will
work with our mapping in a sensible way.
Alter
Right now we don’t support table altering to support updated schemas. This is doable and shouldn’t
be too hard to implement correctly so if there is demand for doing it we’ll probably add support for
this.
Complex SQL/Transactions
You can use SQL directly to use all of these capabilities e.g. if you begin a transaction before
inserting/updating or deleting this will work as advertised however if a rollback occurs our
mapping will be unaware of that so you will need to re-fetch the data.
You will notice we mapped auto-increment so we will generally try to map things that make sense
for various use cases, if you have such a use case we’d appreciate pull requests and feedback on the
implementation.
Caching/Collision
As mentioned above, we don’t cache anything and there might be a collision if you select the same
object twice you will get two separate instances that might collide if you update both (one will
"win").
463
That means you need to pay attention to the way you cache objects to avoid a case of a modified
version of an object kept with an older version.
Preferences Binding
Some objects make sense as global objects, we can just use the Preferences API to store that data
directly but then we don’t have the type safety that property objects bring to the table. That’s where
the binding of property objects to preferences makes sense. E.g. say we have a global Settings
property object we can just bind it to preferences using:
[Link](settingsInstance).bind();
So if settings has a property called companyName it would bind into Preferences under the
[Link] entry.
[Link](settingsInstance).
setPrefix("MySettings-").
setName([Link], "company").
bind();
This would customize all entry keys to start with MySettings- instead of Settings.. This would also
set the company name entry to company so in this case instead of [Link] we’d have
MySettings-company.
UI Binding
One of the bigger features of properties are their ability to bind UI to a property. E.g. if we continue
the sample above with the Contact class let’s say we have a text field on the form and we want the
property (which we mapped to the database) to have the value of the text field. We could do
something like this:
[Link]([Link]());
[Link](e -> [Link]([Link]()));
That would work nicely but what if we changed the property value, that wouldn’t be reflected back
into the text field?
Also that works nicely for text field but what about other types e.g. numbers, check boxes, pickers
etc. this becomes a bit more tedious with those.
Binding makes this all seamless. E.g. the code above can be written as:
464
The cool thing is that this works with multiple component types and property types almost
magically. Binding works by using an adapter class to convert the data to/from the component. The
adapter itself works with a generic converter e.g. this code:
[Link](myRankTextField, [Link]);
Seems similar to the one above but it takes a String that is returned by the text field and seamlessly
converts it to the integer needed by rank. This also works in the other direction…
We can easily build a UI that would allow us to edit the Contact property in memory:
465
add(rankTf);
① Notice I use the label of the property which allows better encapsulation
③ We can bind multiple radio buttons to a single property to allow the user to select the gender,
notice that labels and values can be different e.g. "Male" selection will translate to "M" as the
value
We skipped a couple of fact about the bind() method. It has an additional version that accepts a
ComponentAdapter which allows you to adapt the binding to any custom 3rd party component. That’s
a bit advanced for now but I might discuss this later.
However, the big thing I "skipped" was the return value… bind returns a [Link] object
when performing the bind. This object allows us to manipulate aspects of the binding specifically
unbind a component and also manipulate auto commit for a specific binding.
Auto commit determines if a property is changed instantly or on commit. This is useful for a case
where we have an "OK" button and want the changes to the UI to update the properties only when
"OK" is pressed (this might not matter if you keep different instances of the object). When auto-
commit is on (the default which you can change via setAutoCommit in the UiBinding) changes reflect
instantly, when it’s off you need to explicitly call commit() or rollback() on the Binding class.
commit() applies the changes in the UI to the properties, rollback() restores the UI to the values
from the properties object (useful for a "reset changes" button).
Binding also includes the ability to "unbind" this is important if you have a global object that’s
bound to a UI that’s discarded. Binding might hold a hard reference to the UI and the property
object might create a memory leak.
By using the disconnect() method in Binding we can separate the UI from the object and allow the
GC to cleanup.
466
UI Generation
Up until now this was pretty cool but if you looked at the UI construction code above you would see
that it’s pretty full of boilerplate code. The thing about boilerplate is that it shows where
automation can be applied, that’s the exact idea behind the magical "InstantUI" class. This means
that the UI above can be generated using this code:
① The id property is useful for database storage but we want to exclude it from the UI
② This implements the gender toggle button selection, we provide a hint to the UI so labels and
values differ
③ We create the UI from the screenshot above with one line and it’s seamlessly bound to the
properties of myContact. The second argument indicates the "auto commit" status.
This still carries most of the flexibilities of the regular binding e.g. I can still get a binding object
using:
[Link] b = [Link](cnt);
You might not have noticed this but in the previous vebose code we had lines like:
[Link]([Link]);
You might be surprised to know that this will still work seamlessly without doing anything, as
would the picker component used to pick a date…
The picker component implicitly works for date type properties, numeric constraints and numbers
are implicitly used for number properties and check boxes are used for booleans.
But how do we know to use an email constraint for the email property?
We have some special case defaults for some common property names, so if your property is
named email it will use an email constraint by default. If it’s named url or password etc. it will do
the "right thing" unless you explicitly state otherwise. You can customize the constraint for a
specific property using something like:
[Link]([Link], [Link]);
This will override the defaults we have in place. The goal of this tool is to have sensible "magical"
defaults that "just work".
467
[1] Event Dispatch Thread
468
Chapter 13. Push Notifications
This chapter discusses push support in Codename One applications. It covers how to set up push on
the various platforms, how to respond to push notifications in your app, and how to send push
notifications to your app.
Push notifications require a Codename One Pro account or higher. You must
register your app to
[Link]().registerPush();
receive push notification using
For a quick reference on setting up Push notifications, check out the Push
Cheatsheet [[Link]
Messages may contain a short title and text body that will be displayed to the user in their device’s
messages stream. They may also specify a badge to display on the app’s icon (e.g. that red circle on
your mail app icon that indicates how many unread messages you have), a sound to play then the
message arrives, an image attachment, and a set of "actions" that the user can perform directly in
the push notification.
In addition to messages that the user sees, a push notification can contain non-visual information
that is silently sent to your app.
// ....
/**
* Invoked when the push notification occurs
*
* @param value the value of the push notification
*/
public void push(String value) {
[Link]("Received push message: "+value);
}
/**
* Invoked when push registration is complete to pass the device ID to the application.
469
*
* @param deviceId OS native push id you should not use this value and instead use <code>[Link]()</code>
* @see Push#getPushKey()
*/
public void registeredForPush(String deviceId) {
[Link]("The Push ID for this device is "+[Link]());
}
/**
* Invoked to indicate an error occurred during registration for push notification
* @param error descriptive error string
* @param errorCode an error code
*/
public void pushRegistrationError(String error, int errorCode) {
[Link]("An error occurred during push registration.");
}
}
There will be additional steps required to deploy to each platform (e.g. iOS requires you to generate
push certificates, Android needs you to register your app ID with their cloud messaging platform,
etc…), but, fundamentally, this is all that is required to enable push support in your app.
13.3.1. Registration
When your application first opens, it needs to register with the platform’s central cloud messaging
infrastructure. On Android this involves a call to their GCM/FCM server; on iOS, the call will be to
the APNS server, on Windows (UWP) the call will be to the WNS server. And so on. That server will
return a unique device ID that can be used to send push notifications to the device. This device ID
will then be passed to your registeredForPush() method as the deviceId parameter so that you can
save it somewhere. Typically you would send this value to your own web service so that you can
use it to send notifications to the device later on. The device ID will generally not change unless you
uninstall and reinstall the app, but you will receive the callback every time the app starts.
The deviceId parameter cannot be used directly when sending push messages via
the Codename One push service. It needs to be prefixed with a platform identifier
so the that push server knows which messaging service to route the message
through. You can obtain the complete device ID, including the platform prefix, by
calling [Link]()
If the registration failed for some reason, the the pushRegistrationError() callback will be fired
instead.
Notice that all of this happens seamlessly behind the scenes when your app loads. You don’t need to
initiate any of this workflow.
Codename One provides a secure REST API for sending push notifications. As an HTTP API, it is
470
language agnostic. You can send push notifications to your app using Java, PHP, Python, Ruby, or
even by hand using something like curl. Each HTTP request can contain a push message and a list
of device IDs to which the message should be sent. You don’t need to worry about whether your app
is running on Android, iOS, Windows, or the web. A single HTTP request can send a message to
many devices at once.
There are two different scenarios to be aware of when it comes to receiving push notifications. If
your app is running in the foreground when the message arrives, then it will be passed directly to
your push() callback. If your app is either in the background, or not running, then the notification
will be displayed in your device’s notifications. If the user then taps the notification, it will open
your app, and the push() callback will be run with the contents of the message.
Some push message types include hidden content that will not be displayed in your device’s
notifications. These hidden messages (or portions of messages) are passed directly to the push()
callback of your app for processing.
On iOS, hidden push messages (push type 2) will not be delivered when the app is
in the background.
To handle this, you can opt-in to manually signaling the completion of the push task using the
delayPushCompletion build hint.
Example:
471
}
});
} else {
// For standard messages, we can notify immediately or let it timeout (safest to notify)
[Link]().notifyPushCompletion();
}
}
How it works:
• Android: The system acquires a PARTIAL_WAKE_LOCK when the push is received, keeping the CPU
running even if the screen is off. Calling notifyPushCompletion() releases this lock. The lock has
a safety timeout (e.g., 30 seconds) to prevent battery drain if you forget to call it.
• iOS: The system delays calling the completion handler passed to the push delegate. This gives
your app background execution time. Calling notifyPushCompletion() invokes the system
completion handler.
472
Figure 335. The "Push Simulation" menu item in the simulator opens the push simulator tool.
The "Registered Successfully" button will trigger your app’s registeredForPush() method, and the
"Registration Error" button will trigger your app’s pushRegistrationError() method.
The "Send" button will trigger the push() callback with the message body that you place in the "Send
Message" field.
The "Push Type" drop-down allows you to select the "type" of the push message. This dictates how
the message body (i.e. the contents of the "Send Message" field) is interpreted. Some push types
simply pass the message to the device verbatim, while others assume that the message contains
structure that is meant to be parsed by the client to extract such things as badges, sounds, images,
and actions that are associated with the message. We’ll go over the available push types in a
moment, but for now, we’ll keep it simple by just using a push type of "1" - which just sends the
message verbatim.
Figure 336. Sending a basic hello world push from the push simulator
Let’s try a simple "hello world" push message. Select "1" from the "Push Type" drop-down menu,
and enter "Hello World" into the "Send Message" field as shown above. Then press "send".
473
Assuming your push() method looks like:
This experiment simulated a push notification while the app is running in the foreground. Now let’s
simulate the case where the app is not running, or running in the background. We can simulate this
by pausing the app. Return to the Codename One simulator window, and select "Pause App" from
the "Simulate" menu as shown below.
Figure 337. Pausing the app in the simulator so we can simulate push notifications while app is in the
background.
When the app is paused it will simply display a white screen in the simulator with the text "Paused"
in the middle.
Now return to the push simulator again, and press "Send" again with same values in the other fields
(Push type 1, and Message "Hello World"). Rather than running the push() callback this time, it will
display a popup dialog outside the app, as shown below.
474
Figure 338. Push message causes a popup dialog in the simulator when the app is paused.
While this popup dialog doesn’t replicate what a push notification will look like in a device’s
notifications stream when the app is closed, it does simulate the conceptual workflow. The process
whereby the user is notified of the message outside of the app, and the app is not notified
until/unless the user taps on the notification.
If you monitor the console for your app, you should notice that the push() callback hasn’t been
called yet for this notification, but if you click "OK" in the dialog, your push() callback will be run.
Clicking OK is analogous to the user tapping on the notification. If you simply close the dialog box
(by clicking the "x" in the corner), this would be analogous to the user dismissing the notification. In
this case the push() callback would not be called at all.
• 0, 1 - The default push types, they work everywhere and present the string as the push alert to
the user
• 2 - hidden, non-visual push. This won’t show any visual indicator on any OS!
In Android (before API 27) this will trigger the push(String) call with the message body. After
API 27, it will trigger the message body the moment the app is put in the foreground. In iOS this
will only happen if the application is in the foreground otherwise the push will be lost.
• 3 - 1 + 2 = 3 allows combining a visual push with a non-visual portion. Expects a message in the
form: This is what the user will see;This is something he won’t see. E.g. you can bundle a
special ID or even a JSON string in the hidden part while including a friendly message in the
visual part.
When active this will trigger the push(String) method twice, once with the visual and once with
the hidden data.
• 4 - Allows splitting a visual push request based on the format title;body to provide better visual
representation in some OS’s.
• 5 - Sends a regular push message but doesn’t play a sound when the push arrives
• 99 - The message body is expected to be XML, where the root element contains at least type and
body attributes which correspond to one of the other push push types and message body
respectively. This push type supports additional information such as image attachments and
push actions. E.g. <push type="1" body="Hello World"/>
475
• 100 - Applicable only to iOS and Windows. Allows setting the numeric badge on the icon to the
given number. The body of the message must be a number e.g. unread count.
• 101 - identical to 100 with an added message payload separated with a space. E.g. 30 You have 30
unread messages will set the badge to "30" and present the push notification text of "You have 30
unread messages". Supported on Android, iOS, and Windows.
• 102 - Combines badge, title, and body in a single payload formatted as badge;title;body.
Supported on Android, iOS, and Windows.
The following sections will show examples of the various kinds of pushes. You can try them out
yourself by opening the push simulator.
Figure 340. Push type 1 "Hello World" message in Android when app is in background.
Figure 341. Push type 1 "Hello World" message in iOS when app is in background.
In all cases, if the user taps/clicks the notification, it will bring the app to the foreground and call
the push() callback with "Hello World" as the argument.
Push type 2 is a hidden push so it will behave differently on different platforms. On Android
(before API 27), the push() callback will be fired even if the app is in the background. After API 27, it
will be fired the moment the app is put in the foreground. On iOS, it will simply be ignored if the
app is in the background.
If the app is in the foreground, this will trigger the push() callback with "Hello World" as the
476
argument.
You can determine the the type of push that has been received in your push()
callback by calling [Link]().getProperty("pushType"). This will return
a String of the push type. E.g. in this case
[Link]().getProperty("pushType") will return "2".
Push type 3 combines an alert message with some hidden content that the user won’t see. In the
example above, the alert message is "Hello World" and the hidden content is a JSON string that will
be passed to our app to be parsed.
If the app is in the background, then the alert message will be posted to the user’s notifications. See
"Example Push Type 1" above as this message will be identical.
Figure 343. Push type 3 shows only the alert message (the portion before the first ";").
This push will result in our push() callback being fired twice; once with the alert message, and once
with the hidden content. When it is fired with the alert message,
[Link]().getProperty("pushType") will report a type of "1". When it is fired with the
JSON hidden content, it will report a push type of "2".
Push type 4 specifies a title and a message body. In this example, alert title will be "Hello World",
and the body will by "I’m just saying hello".
Figure 345. Push type 4 "Hello World" message in Android when app is in background.
477
Figure 346. Push type 4 "Hello World" message in iOS when app is in background.
With this push type, the push() callback will be fired only if the user taps/opens the notification, and
the argument will contain the entire message ("Title;Body").
On some platforms, the argument of the push() callback will only include the
"body" portion of the payload, and in other platforms it will include the full
"Title;Body" payload.
Push type 5 will behave identically to push type 1, except that the notification won’t make any
sound on the device. On some platforms, [Link]().getProperty("pushType") will report
a push type of "1", when it receives a push of type 5.
Push type 100 just expects an integer in the message body. This is interpreted as the badge that
should be set on the app. This is currently only supported on Windows and iOS.
Figure 348. Push type 100 on iOS, setting the badge to "5"
Push type 101 combines a badge with an alert message. The badge number should be the first thing
478
in the payload, followed by a space, and the remainder is the alert message.
On platforms that do not support badges, Push type 101 will behave exactly as push type 1, except
that the badge prefix will be stripped from the message.
The push() callback will be called only if the user taps the notification.
[Link]().getProperty("pushType") will return "1" for this type.
Push Type 102; Message Body "5;Hello World;You have 5 new tasks"
Push type 102 allows you to set the badge, title, and body in a single payload. The first segment
(before the first semicolon) is the badge value, the second segment is the notification title, and the
remainder after the second semicolon is the body that will be displayed to the user.
On platforms that do not support badges or titles, the payload will fall back to the features that are
supported. When your push() callback is invoked it will receive the title;body portion ("Hello
World;You have 5 new tasks" in this example).
Badging on iOS
The badge number can be set thru code as well, this is useful if you want the badge to
represent the unread count within your application.
To truly utilize this you might need to disable the clearing of the badges on startup which you
can do with the build hint [Link]=false.
When you attach an image to a push notification, it will appear as a large image in the push
notification on the user’s device if that device supports it. iOS supports image attachments in iOS
10, Android supports them in API 26. The Javascript port, and Windows (UWP) port do not
currently support image attachments. If a platform that doesn’t support image attachments
receives a push notification with an image attachment, it will just ignore it.
Push type "99" is used to send rich push notifications. It is sort of a "meta" push type, or a
"container", as it can be used to send any of the other push types, but to attach additional content,
479
such as image attachments.
The message body should be an XML string. A minimal example of a push type 99 that actually
sends a push type 1, which message "Hello World", but with an attached image is:
To avoid hand-coding this XML you can use the [Link] helper which
assembles the payload for you and returns the correct push type to send:
You can then send payload as the message body with pushType as the type via the REST API, the Push
helper, or any of the other examples below.
The image URL must be a secure URL (i.e. start with "https:" and not "http:",
otherwise, iOS will simply ignore it.
480
Figure 350. Push type 99 with attached image in Android when app is in background.
Figure 351. Push type 99 with attached image in iOS when app is in background.
The image will only be shown if you press and pull down on the notification.
When the notification initially appears in the user’s notifications it will appear like
a normal alert - but possibly with the image shown as a small thumbnail.
The push() callback will receive "Hello World" as its argument and
[Link]().getProperty("pushType") will return "1" in this example.
You can access additional information about the push content using the
481
[Link] class, as follows:
Make sure to only call [Link]() once inside your push() callback, and
store the return value. [Link]() works like a queue of size=1, and it pops
off the item from the front of the queue when it is called. If you call it twice, the
second time will return null.
PushContent exposes all of the fields that might accompany a rich notification: getTitle()/getBody()
for the visible text, getMetaData() for hidden metadata (such as the second segment of a type 3
payload), getImageUrl() for attachments, getCategory()/getActionId() for actions, and
getTextResponse() for any user-entered reply text.
When you include actions in a push notification, the user will be presented with buttons as part of
the notification on supported platforms. E.g. if the notification is intended to invite the user to an
event, you might want to include buttons/actions like "Attending", "Not Attending", "Maybe", so that
the user can respond quickly to the notification and not necessarily have to open your app.
You can determine whether the user has pressed a particular button on the notification using the
`[Link]()` method inside your `push()` callback.
How it works
Your app defines which action categories it supports, and associates a set of actions with each
category. If a push notification includes a "category" attribute, then the notification will be
presented with the associated actions manifested as buttons.
You can specify the available categories and actions for your app by implementing the
[Link] interface in your app’s main class.
E.g.
import [Link];
import [Link];
482
import [Link];
...
...
@Override
public PushActionCategory[] getPushActionCategories() {
return new PushActionCategory[]{
new PushActionCategory("invite", new PushAction[]{
new PushAction("yes", "Yes"),
new PushAction("no", "No"),
new PushAction("maybe", "Maybe"),
new PushAction("reply", "Reply", null, "Type your response...", "Send")
})
};
}
}
In the above example, we create only a single category, "invite" that has actions "yes", "no",
"maybe", and a "reply" action that prompts the user for text input. A text-input action is enabled by
providing either a placeholder, button text, or both when constructing the PushAction.
Now we can test our new category. In the push simulator, you can select Push Type "99", with the
message body:
Figure 352. Push notification with "invite" category on the simulator will show dialog with buttons to select
between the actions defined in the "invite" category.
Figure 353. Push notification with "invite" category on the android will show dialog with buttons to select
between the actions defined in the "invite" category.
483
Figure 354. Push notification with "invite" category on the android will show dialog with buttons to select
between the actions defined in the "invite" category.
Figure 355. Push notification with "invite" category on the Chrome desktop includes a "More" dropdown
where user can select the action.
The push() callback will be fired after the user taps on the notification, or one of its actions. If the
user taps the notification itself, and not one of the actions, then your [Link]() will
return null. If they selected one of the actions, then the action ID of that action can be obtained
from getActionId(). For text-input actions, the user’s reply text is returned by
[Link]().
The category of the notification will be made available via the getCategory() method of
PushContent.
E.g.
484
}
}
To set the push icon place a 24x24 icon named ic_stat_notify.png under the
native/android folder of the app. The icon can be white with transparency areas
Android Push goes thru Google servers and to do that we need to register with Google to get keys
for server usage. Google uses Firebase for its cloud messaging, so we’ll begin by creating a Firebase
project.
485
Figure 357. Enter project name
Enter the project name, select your country, read/accept their terms, and press "Create Project".
Once the project has been created (should take only a few seconds), you’ll be sent to your new
project’s dashboard.
Expand the "Grow" section of the left menu bar, then click on the "Cloud Messaging" link.
On the next screen, click on the Android icon where is says "Add an app to get started".
Figure 360. Click on the "Android" icon to add an Android App to the project
This will bring us to the "Add Application Form", which visually shows us the remainder of the
486
steps.
Fill in the Android package name with the package name of your project, and the app nickname
with your app’s name.
The Debug signing certificate SHA-1 is optional, but you can paste the SHA-1 from your app’s
certificate here if you like.
Press "Register app" once you have filled in the required fields.
This will expand "Step 2" of this form: "Download config file".
All we need to do here is press the "Download [Link]" file, then copy the file into your
project’s native/android directory.
Firebase console directs you to copy the [Link] file into the "app"
directory of your project. Ignore this direction as it only applies for Android studio
projects. For Codename One, this file goes into the native/android directory of your
project.
There is one last piece of information that we need so that we can send push notifications to our
app: The FCM_SERVER_API_KEY value.
Go to your project dashboard in Firebase console. Then click the "Settings" menu (the "Gear" icon
next to "Project Overview" in the upper left):
487
Figure 363. Select "Project settings"
The "Server Key" displayed here is the FCM_SERVER_API_KEY that we refer to throughout this
document. It will be used to send push notifications to your app from a server, or from another
device. You can copy and paste this value now, or you can retrieve it later by logging into the
Firebase console.
The Sender ID shown in the above is not required for our Android app, however it
it is helpful/required to support Push notifications in Javascript builds (in Chrome).
This value is referred to elsewhere in this document as GCM_SENDER_ID.
Push on iOS is much harder to handle than the Android version, however we simplified this
significantly with the certificate wizard.
The certificate wizard can generate these additional push certificates and do quite a few other
things if you just check this flag in the end of the wizard:
488
Figure 365. Enable Push in the certificate wizard
If you already have signing certificated defined for your app just skip the
certificate generation phase (answer no) the rest will work as usual.
You can then install the push certificates locally and use them later on but there is an easier way.
You need to host the push certificates in the cloud so they will be reachable by the push servers,
Codename One that for you seamlessly.
Once you go thru the wizard you should get an automated email containing information about
push and the location of the push certificates, it should start like this:
iOS Push certificates have been created for your app with bundle ID [Link]. Please file this
email away for safe keeping as you will need the details about the certificate locations and passwords to use Push
successfully in your apps.
The URL’s and passwords are everything that you will need later on to get push working on iOS.
Notice that the wizard also performs a couple of other tasks specifically it sets the [Link]
build hint to true & adds push to the provisioning profile etc.
You can read more about the certificate wizard in the signing section [[Link]
manual/[Link]].
Push on UWP requires only that you register your app in the Windows Store Dashboard. You will
then be provided with credentials (Package Security Identifier (SID) and a secret key) that you can
use to send push notifications to your app. To begin this process, go to the Windows Dev Center
489
[[Link] and select "Dashboard".
You can read more about the registering your app in the Windows store here
[[Link]
Once you have registered your app in the Windows Store, and completed the corresponding setup
in Codename One settings (e.g. generated a certificate), you should proceed to configure your app
for push notifications.
Navigate to the App overview page for your app inside the Windows store dashboard. Under the
"Services" menu (left side), select "Push notifications".
Then, select the "WNS/MPNS" option that appears in the left menu under "Push notifications"
This will bring you to a page with information about WNS push notifications. You’ll be interested in
the paragraph shown here:
You’ll be prompted to log in using your Windows Store account. Then you’ll be taken to a page that
contains the push credentials that you can use for sending push messages to your app. You’ll be
interested in two values:
490
Figure 370. WNS Client secret
You will use these two values for sending push notifications to your app.
Codename One apps support push in browsers that implement the Web Push API. At time of
writing, this list includes:
• MS Edge
Firefox doesn’t require any special setup for Push. If your main class implements the PushCallback
interface, it should just work.
Chrome uses FCM for its push notifications - the same system that Android uses. The directions for
setting up a FCM account are the same as provided here, and you can reuse the same GCM_SENDER_ID
and FCM_API_SERVER_KEY values. For Chrome push support you will need to add the GCM_SENDER_ID in
the gcm.sender_id build hint so that the GCM_SENDER_ID will be added to the app’s manifest file:
gcm.sender_id=GCM_SENDER_ID
Push support requires that your app be served over https with a valid SSL
certificate. It will not work with the "preview" version of your app. You’ll need to
download the .zip or .war file and host the file on your own site - with a valid SSL
certificate.
491
The push token is a unique "key" that you can use to send push thru your Codename One account. It
allows you to send push messages without placing your Codename One email or password into
your source files.
You can get it by going to the Codename One build server dashboard at
[Link] and selecting the Account tab.
The instructions for extracting the API key for Google are listed above.
The instructions for extracting the SID and Client Secret for Windows are listed above.
When sending push to iOS devices we have two modes: - Production - Distribution
This allows you to debug the push related functionality without risking the possibility of sending a
push into a production app. Its important to send the values to the right server during
development/production.
iOS needs a certificate in order to send a push, this allows you to prove to Apples push servers that
you are who you claim to be (the author of the app).
These are not the signing certificates and are completely separate from them!
You can obtain these two certificates (for development/appstore) via the certificate wizard as
explained above.
492
private static final String ITUNES_DEVELOPMENT_PUSH_CERT = "[Link]
private static final String ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD = "DevPassword";
While normally sending a push message to a device should involve a server code there might be
cases (e.g. instant messaging/social) where initiating a push from one client to another makes sense.
The "builder" style API used in the above sample was added post Codename One 3.6 to facilitate the
addition of new Push services. If you are building against Codename one 3.6 or earlier, you should
use the static [Link]() instead as shown below:
This will send the push message "Hello World" to the device with the key deviceKey. The 1 argument
represents the standard push message type, which we discussed previously.
493
13.9.2. Sending Push Message From A Java or Generic Server
Sending a push message from the server is a more elaborate affair and might require sending push
messages to many devices in a single batch.
• token - your developer token to identify the account sending the push - PUSH_TOKEN
• device - one or more device keys to send the push to. You can send push to up to 500 devices
with a single request -
• type - the message type identical to the old set of supported types in the old push servers
• auth - the Google push auth key - FCM_SERVER_API_KEY (also used for sending to Chrome
Javascript Apps)
• cert - http or https URL containing the push certificate for an iOS push -
ITUNES_DEVELOPMENT_PUSH_CERT or ITUNES_PRODUCTION_PUSH_CERT
We can thus send a push from Java EE using code like this:
494
int c = [Link]();
// read response JSON
Notice that you can send a push to 500 devices. To send in larger batches you need to split the push
requests into 500 device batches.
The push servers send responses in JSON form. It’s crucial to parse and manage those as they might
contain important information.
If there is an error that isn’t fatal such as quota exceeded etc. you will get an error message like
this:
{"error":"Error message"}
[
{"id"="deviceId","status"="error","message"="Invalid Device ID"},
{"id"="cn1-gcm-nativegcmkey","status"="updateId", "newId"="cn1-gcm-newgcmkey"},
{"id"="cn1-gcm-okgcmkey","status"="OK"},
{"id"="cn1-gcm-errorkey","status"="error","message"="Server error message"},
{"id"="cn1-ios-iphonekey","status"="inactive"},
]
• If the response contains status=updateId it means that the GCM server wants you to update the
device id to a new device id. You should do that in the database and avoid sending pushes to the
old key
• iOS doesn’t acknowledge device receipt but it does send a status=inactive result which you
should use to remove the device from the list of devices
APNS (Apple’s push service) returns uppercase key results. This means that code
for managing the keys in your database must be case insensitive
Apple doesn’t always send back a result for a device being inactive and might fail
silently
495
496
Chapter 14. Miscellaneous Features
14.1. Phone Functions
Most of the low level phone functionality is accessible in the Display [[Link]
javadoc/com/codename1/ui/[Link]] class. Think of it as a global central class covering your access to
the "system".
14.1.1. SMS
Codename One supports sending SMS messages but not receiving them as this functionality isn’t
portable. You can send an SMS using either the Display singleton or the static helper methods on CN.
Both APIs throw IOException, which you should handle in case the native layer reports a failure:
try {
[Link]().sendSMS("+999999999", "My SMS Message");
// Or: [Link]("+999999999", "My SMS Message");
} catch(IOException err) {
Log.e(err);
[Link]("SMS Failed", "Unable to send the SMS", "OK", null);
}
Android supports sending SMS messages in the background without any UI. iOS doesn’t provide
that ability, so the best it can offer is to launch the native SMS app with your message composed for
the user to send. Android supports that interactive flow as well (launching the OS native SMS app).
The default sendSMS API ignores that difference and simply works interactively on iOS while sending
in the background for Android when the platform allows it.
• SMS_SEAMLESS - sendSMS will not show a UI and will just send in the background
• SMS_BOTH - sendSMS can support both seamless and interactive mode, this currently only works
on Android
The sendSMS can accept an interactive argument: sendSMS(String phoneNumber, String message,
boolean interactive)
The last argument will be ignored unless SMS_BOTH is returned from getSMSSupport at which point
you would be able to choose one way or the other. The default behavior (when not using that flag)
is the background sending which is the current behavior on Android.
497
try {
switch([Link]().getSMSSupport()) {
case Display.SMS_NOT_SUPPORTED:
return;
case Display.SMS_SEAMLESS:
showUIDialogToEditMessageData();
[Link]().sendSMS(phone, data);
return;
default:
[Link]().sendSMS(phone, data);
return;
}
} catch(IOException err) {
Log.e(err);
[Link]("SMS Failed", "Unable to send the SMS", "OK", null);
}
14.1.2. Dialing
Dialog the phone is pretty trivial, this should open the dialer UI without physically dialing the
phone as that is discouraged by device vendors.
[Link]().dial("+999999999");
Codename One includes a generic call detection API via [Link]() and
[Link]().
This API is intentionally best effort and should only be used as a UX hint (for example, to defer a
non-critical animation while the app is interrupted).
On iOS, isInCall() is inferred from app interruption lifecycle events, which means it can report
true for non-call interruptions (e.g. Control Center, app switching, permission sheets), and some call
flows may still be missed.
On Android, call detection is currently unsupported because robust detection would require
invasive telephony permissions that are intentionally avoided.
498
14.1.4. E-Mail
You can send an email via the platforms native email client with code such as this:
You need to use files from FileSystemStorage and NOT Storage files!
You can add more than one attachment by putting them directly into the attachment map e.g.:
Some features such as attachments etc. don’t work correctly in the simulator but
should work on iOS/Android
If you want the theme system to automatically scale its fonts when larger text is enabled, enable the
UIManager [[Link] setting:
[Link]().setUseLargerTextScale(true);
You can also enable this behavior in the theme by setting the useLargerTextScaleBool theme
constant to true.
If you need to apply the scale manually for custom fonts or layout calculations, read the values
directly:
499
}
Notice that on some platforms this will prompt the user for permissions and the user might choose
not to grant that permission. To detect whether this is the case you can invoke
isContactsPermissionGranted() after invoking getAllContacts(). This can help you adapt your error
message to the user.
Here you can specify true only for the attributes that actually matter to you.
Another capability of the contacts API is the ability to extract all of the contacts very quickly. This
isn’t supported on all platforms but platforms such as Android can really get a boost from this API
as extracting the contacts one by one is remarkably slow on Android.
You can check if a platform supports the extraction of all the contacts quickly thru
[Link]().
When retrieving all the contacts, notice that you should probably not retrieve all
the data and should set some fields to false to perform a more efficient query
You can then extract all the contacts using code that looks a bit like this, notice that we use a thread
so the UI won’t be blocked!
500
[Link]().animateLayout(150);
});
});
[Link]();
Notice that we didn’t fetch the image of the contact as the performance of loading these images
might be prohibitive. We can enhance the code above to include images by using slightly more
complex code such as this:
[Link]().scheduleBackgroundTask(() -> {
Contact[] contacts = [Link](true, true, false, true, false, false);
[Link]().callSerially(() -> {
[Link]();
for(Contact c : contacts) {
MultiButton mb = new MultiButton([Link]());
[Link](fi);
mb.setTextLine2([Link]());
[Link](mb);
[Link]("id", [Link]());
[Link]().scheduleBackgroundTask(() -> {
Contact cc = [Link]([Link](), false, true, false, false, false);
[Link]().callSerially(() -> {
Image photo = [Link]();
if(photo != null) {
[Link]([Link](size, size));
[Link]();
}
});
});
}
[Link]().animateLayout(150);
});
});
501
Figure 373. Contacts with the default photos on the simulator, on device these will use actual user photos
when available
Notice that the code above uses callSerially & scheduleBackgroundTask in a liberal
nested way. This is important to avoid an EDT violation
You can use createContact(String firstName, String familyName, String officePhone, String
homePhone, String cellPhone, String email) to add a new contact and deleteContact(String id) to
delete a contact.
Place locale-specific properties files inside an l10n directory directly under your module’s main
directory (e.g. common/src/main/l10n). Each file inside this directory is treated as a resource bundle
and will be packaged automatically with your app, allowing you to version translations alongside
the rest of your source code.
When you are iterating on translations you can also use the simulator to capture bundles
automatically. Open the Simulator menu and enable Auto Update Default Bundle so that running
your app in the Codename One simulator will create any missing resource bundles on the fly as you
interact with the UI, which makes it simple to populate keys without manually editing files during
development. You can install the bundle using code like this:
[Link]().setBundle(res.getL10N("l10n", local));
The device language (as an ISO 639 two letter code) could be retrieved with this:
502
Once installed a resource bundle takes over the UI and every string set to a label (and label like
components) will be automatically localized based on the bundle. You can also use the localize
method of UIManager [[Link] to
perform localization on your own:
The list of available languages in the resource bundle could be retrieved like this. Notice that this a
list that was set by you and doesn’t need to confirm to the ISO language code standards:
An exception for localization is the TextField/TextArea components both of which contain user data,
in those cases the text will not be localized to avoid accidental localization of user input.
You can export and import resource bundles as standard Java properties files, CSV
and XML. The formats are pretty standard for most localization shops, the XML
format Codename One supports is the one used by Android’s string bundles which
means most localization specialists should easily localize it
The resource bundle is just a map between keys and values e.g. the code below displays "This Label
is localized" on the Label with the hardcoded resource bundle. It would work the same with a
resource bundle loaded from a resource file:
It allows formatting numbers/dates & time based on platform locale. It also provides a great deal of
the information you need such as the language/locale information you need to pick the proper
resource bundle.
503
Form hi = new Form("L10N", new TableLayout(16, 2));
L10NManager l10n = [Link]();
[Link]("format(double)").add([Link](11.11)).
add("format(int)").add([Link](33)).
add("formatCurrency").add([Link](53.267)).
add("formatDateLongStyle").add([Link](new Date())).
add("formatDateShortStyle").add([Link](new Date())).
add("formatDateTime").add([Link](new Date())).
add("formatDateTimeMedium").add([Link](new Date())).
add("formatDateTimeShort").add([Link](new Date())).
add("getCurrencySymbol").add([Link]()).
add("getLanguage").add([Link]()).
add("getLocale").add([Link]()).
add("isRTLLocale").add("" + [Link]()).
add("parseCurrency").add([Link]([Link]("33.77$"))).
add("parseDouble").add([Link]([Link]("34.35"))).
add("parseInt").add([Link]([Link]("56"))).
add("parseLong").add("" + [Link]("4444444"));
[Link]();
14.4.2. RTL/Bidi
RTL stands for right to left, in the world of internationalization it refers to languages that are
written from right to left (Arabic, Hebrew, Syriac, Thaana).
Most western languages are written from left to right (LTR), however some languages are written
from right to left (RTL) speakers of these languages expect the UI to flow in the opposite direction
otherwise it seems weird just like reading this word would be to most English speakers: "drieW".
The problem posed by RTL languages is known as BiDi (Bi-directional) and not as RTL since the
"true" problem isn’t the reversal of the writing/UI but rather the mixing of RTL and LTR together.
E.g. numbers are always written from left to right (just like in English) so in an RTL language the
direction is from right to left and once we reach a number or English text embedded in the middle
of the sentence (such as a name) the direction switches for a duration and is later restored.
The main issue in the Codename One world is in the layouts, which need to reverse on the fly.
Codename One supports this via an RTL flag on all components that is derived from the global RTL
504
flag in UIManager [[Link]
Resource bundles can also include special case constant @rtl, which indicates if a language is
written from right to left. This allows everything to automatically reverse.
When in RTL mode the UI will be the exact mirror so WEST will become EAST, RIGHT will become LEFT
and this would be true for paddings/margins as well.
If you have a special case where you don’t want this behavior you will need to wrap it with an isRTL
check. You can also use setRTL on a per Component basis to disable RTL behavior for a specific
Component.
Most UI API’s have special cases for BiDi instead of applying it globally e.g. AWT
introduced constants such as LEADING instead of making WEST mean the opposite
direction. We think that was a mistake since the cases where you wouldn’t want
the behavior of automatic reversal are quite rare.
• Bidi algorithm - allows converting between logical to visual representation for rendering
• Global RTL flag - default flag for the entire application indicating the UI should flow from right
to left
• Individual RTL flag - flag indicating that the specific component/container should be presented
as an RTL/LTR component (e.g. for displaying English elements within a RTL UI).
Most of Codename One’s RTL support is under the hood, the LookAndFeel
[[Link] global RTL flag can be
enabled using:
[Link]().getLookAndFeel().setRTL(true);
Once RTL is activated all positions in Codename One become reversed and the UI becomes a mirror
of itself. E.g. Adding a Toolbar command to the left will actually make it appear on the right.
Padding on the left becomes padding on the right. The scroll moves to the left etc.
This applies to the layout managers (except for group layout) and most components. Bidi is mostly
seamless in Codename One but a developer still needs to be aware that his UI might be mirrored for
these cases.
Some strings in iOS need to be localized using iOS’s native mechanisms - namely providing *.lproj
directories with .strings files. For example, if you want the app to have a different bundle display
name for each language, or you want to translate the "UsageDescription" strings of your [Link]
into multiple languages, you would need to use iOS' native localization facilities
[[Link]
505
Example: Localizing the App Name
The app name, as it is displayed to the user, is defined in using the CFBundleDisplayName key of the
app’s [Link] file. Normally, this will be automatically set to your app’s display name, as defined
in your codenameone_settings.properties file. This works fine if your app will have the same name
in every locale, but suppose you want your app to take on a different name in French than in
English. E.g. You want your app to be called "Hello App" for English-speaking users, and "Bonjour
App" for French-speaking users.
In this case, you need to add iOS localization bundles "[Link]" and "[Link]", each with a file
named "[Link]". If you are using Maven, then you can add these directly inside the
ios/src/main/strings directory of your project.
You will need to create the strings directory manually, if it doesn’t exist yet.
Figure 376. Maven project with English, French, and Spanish localizations for [Link]. English and
French language bundles are contained in the ios/src/main/strings directory. The Spanish bundle is
included as a zip file in ios/src/main/resources. Both methods are supported (zipped in resources and
unzipped in strings).
"CFBundleDisplayName"="Hello App";
"CFBundleDisplayName"="Bonjour App";
The strings format is similar to the properties file format, except that both the
"key" and the "value" must be wrapped in quotes. And if there are multiple strings,
then they must be delimited by a semi-colon ;.
506
Legacy Ant Projects
Legacy Ant projects have a different directory structure. They have no equivalent location to
the Maven ios/src/main/strings directory, but the legacy ios/src/main/resources content can
be replicated under native/ios. To include native iOS localizations in those projects, place
zipped versions of your .lproj directories inside the native/ios directory. E.g. [Link],
[Link], etc.
iOS requires you to supply usage descriptions for many features that will be displayed to the user
when the app requests permission to use the feature. For example, the NSCameraUsageDescription
[[Link]
language=objc] string must be provided if your app needs to use the camera. You can specify these
values as build hints using the pattern [Link]=This feature is needed blah blah
blah. In the NSCameraUsageDescription case, you might include the build hint:
Ultimately these descriptions are embedded in your app’s [Link] file, so they can be localized the
same way you localize other [Link] values - in the localized [Link] file.
See the above example for instructions on localizing values in the [Link] file. Then simply add
translations to the [Link] file for your usage descriptions.
"CFBundleDisplayName"="Hello App";
"NSCameraUsageDescription"="This app needs to use your camera to scan bar codes";
"CFBundleDisplayName"="Bonjour App";
"NSCameraUsageDescription"="Cette application doit utiliser votre appareil photo pour scanner les codes à barres";
The Simulator includes a Location Simulation tool that you can launch to
507
determine the current position of the simulator and debug location events
The most basic usage for the API allows us to just fetch a device Location, notice that this API is
blocking and can take a while to return:
In order for location to work on iOS you MUST define the build hint
[Link] and describe why your application needs access to
location. Otherwise you won’t get location updates!
The getCurrentLocationSync() method is very good for cases where you only need to fetch a current
location once and not repeatedly query location. It activates the GPS then turns it off to avoid
excessive battery usage. However, if an application needs to track motion or position over time it
should use the location listener API to track location as such:
Notice that there is a method called getCurrentLocation() which will return the
current state immediately and might not be accurate for some cases.
On Android location maps to low level API’s if you disable the usage of Google Play
Services. By default location should perform well if you leave the Google Play
Services on
Polling location is generally expensive and requires a special permission on iOS. Its also
implemented rather differently both in iOS and Android. Both platforms place restrictions on the
location API usage in the background.
Because of the nature of background location the API is non-trivial. It starts with the venerable
LocationManager but instead of using the standard API you need to use
setBackgroundLocationListener.
Instead of passing a LocationListener instance you need to pass a Class object instance. This is
important because background location might be invoked when the app isn’t running and an object
508
would need to be allocated.
Notice that you should NOT perform long operations in the background listener callback. IOS wake-
up time is limited to approximately 10 seconds and the app could get killed if it exceeds that time
slice.
Notice that the listener can also send events when the app is in the foreground, therefore it is
recommended to check the app state before deciding how to process this event. You can use
[Link]() to determine if the app is currently running or in the background.
• The class passed to the API is a public class in the global scope. Not an inner class or anything
like that!
[Link]()
.addGeoFencing([Link], gf);
On Android 11 (API level 30) and higher, requesting background location permission requires a
two-step process. First, foreground location permissions must be granted. Then, the app must
request background location access, which will direct the user to the system settings to select "Allow
all the time". Codename One handles this flow automatically when you use LocationManager.
For Android 11+ (API 30+), Codename One detects if background location is needed and presents a
dialog explaining the requirement before redirecting the user to the app settings. You can
customize the permission prompt message using the localization key
[Link].ACCESS_BACKGROUND_LOCATION.
@Override
public void onEntered(String id) {
if([Link]().isMinimized()) {
[Link]().callSerially(() -> {
509
[Link]("Welcome", "Thanks for arriving", "OK", null);
});
} else {
LocalNotification ln = new LocalNotification();
[Link]("Welcome");
[Link]("Thanks for arriving!");
[Link]().scheduleLocalNotification(ln, 10, false);
}
}
}
This support isn’t totally portable since the Android and iOS approaches for background music
playback differ a great deal. To get this to work on Android you need to use the API:
[Link]().
You should use that API when you want to create a media stream that will work even when your
app is minimized.
For iOS you will need to use a special build hint: ios.background_modes=music.
Which should allow background playback of music on iOS and would work with the
createBackgroundMedia() method.
Just captures and returns a path to a photo you can either open it using the Image
[[Link] class or save it somewhere.
The returned file is a temporary file, you shouldn’t store a reference to it and
instead copy it locally or work with the Image object
510
}
When running on the simulator the Capture API opens a file chooser API instead of
physically capturing the data. This makes debugging device or situation specific
issues simpler
We can capture an image from the camera using an API like this:
[Link]([Link], iv);
[Link]();
511
Figure 377. Captured photos previewed in the ImageViewer
The sample below captures audio recordings (using the 'Capture' API) and copies them locally
under unique names. It also demonstrates the storage and organization of captured audio:
FileSystemStorage fs = [Link]();
String recordingsDir = [Link]() + "recordings/";
[Link](recordingsDir);
try {
for(String file : [Link](recordingsDir)) {
MultiButton mb = new MultiButton([Link]([Link]("/") + 1));
[Link]((e) -> {
try {
Media m = [Link](recordingsDir + file, false);
[Link]();
} catch(IOException err) {
Log.e(err);
}
});
[Link](mb);
}
512
try {
Media m = [Link](filePath, false);
[Link]();
} catch(IOException err) {
Log.e(err);
}
});
[Link](mb);
[Link]();
}
} catch(IOException err) {
Log.e(err);
}
});
} catch(IOException err) {
Log.e(err);
}
[Link]();
Alternatively, you can use the Media, MediaManager and MediaRecorderBuilder APIs to capture audio,
as a more customizable approach than using the Capture API:
513
// mime type supported by Simulator: audio/wav
// more info: [Link]
[Link](l -> {
try {
// every time we have to create a new instance of Media to make it working correctly (as reported in
the Javadoc)
microphone[0] = [Link](options);
if (speaker[0] != null && speaker[0].isPlaying()) {
return; // do nothing if the audio is currently recorded or played
}
[Link](false);
[Link](true);
Log.p("Audio recording started", [Link]);
if ([Link](playBtn)) {
[Link](playBtn, stopBtn, [Link]());
[Link]();
}
if (speaker[0] != null) {
speaker[0].pause();
}
microphone[0].play();
startWatch(time);
} catch (IOException ex) {
Log.p("ERROR recording audio", [Link]);
Log.e(ex);
}
});
[Link](l -> {
if (!microphone[0].isPlaying() && (speaker[0] == null || !speaker[0].isPlaying())) {
return; // do nothing if the audio is NOT currently recorded or played
514
}
[Link](true);
[Link](true);
Log.p("Audio recording stopped");
if (microphone[0].isPlaying()) {
microphone[0].pause();
} else if (speaker[0] != null) {
speaker[0].pause();
} else {
return;
}
stopWatch(time);
if ([Link](stopBtn)) {
[Link](stopBtn, playBtn, [Link]());
[Link]();
}
if ([Link]().exists(output)) {
Log.p("Audio saved to: " + output);
} else {
[Link]("Error recording audio", 5000);
Log.p("ERROR SAVING AUDIO");
}
});
[Link](l -> {
// every time we have to create a new instance of Media to make it working correctly (as reported in the
Javadoc)
if (microphone[0].isPlaying() || (speaker[0] != null && speaker[0].isPlaying())) {
return; // do nothing if the audio is currently recorded or played
}
[Link](false);
[Link](true);
if ([Link](playBtn)) {
[Link](playBtn, stopBtn, [Link]());
[Link]();
}
if ([Link]().exists(output)) {
try {
speaker[0] = [Link](output, false, () -> {
// callback on completation
[Link](true);
if (speaker[0].isPlaying()) {
speaker[0].pause();
}
stopWatch(time);
if ([Link](stopBtn)) {
[Link](stopBtn, playBtn, [Link]());
[Link]();
}
});
speaker[0].play();
startWatch(time);
} catch (IOException ex) {
Log.p("ERROR playing audio", [Link]);
Log.e(ex);
}
}
});
[Link](l -> {
if (microphone[0].isPlaying()) {
microphone[0].pause();
}
if (speaker[0] != null && speaker[0].isPlaying()) {
speaker[0].pause();
}
515
if ([Link](stopBtn)) {
[Link](stopBtn, playBtn, [Link]());
[Link]();
}
stopWatch(time);
[Link](true);
[Link](output);
});
return [Link](recordingUI);
516
14.7.1. Capture Asynchronous API
The Capture API also includes a callback based API that uses the ActionListener interface to
implement capture. E.g. we can adapt the previous sample to use this API as such:
14.8. Gallery
The gallery API allows picking an image and/or video from the cameras gallery (camera roll).
Like the Capture API the image returned is a temporary image that should be
517
copied locally, this is due to device restrictions that don’t allow direct
modifications of the gallery
We can adapt the Capture sample above to use the gallery as such:
[Link]([Link], iv);
There is no need for a screenshot as it will look identical to the capture image
screenshot above
The last value is the type of content picked which can be one of: Display.GALLERY_ALL,
Display.GALLERY_VIDEO or Display.GALLERY_IMAGE.
Analytics is pretty seamless for the old GUI builder since navigation occurs via the Codename One
518
API and can be logged without developer interaction. However, to begin the instrumentation one
needs to add the line:
[Link](true);
[Link](agent, domain);
To get the value for the agent value just create a Google Analytics account and add a domain, then
copy and paste the string that looks something like UA-99999999-8 from the console to the agent
string. Once this is in place you should start receiving statistic events for the application.
If your application is not a GUI builder application or you would like to send more detailed data
you can use the [Link]() method to indicate that you are entering a specific page.
In 2013 Google introduced an improved application level analytics API that is specifically built for
mobile apps. However, it requires a slightly different API usage. You can activate this specific mode
by invoking setAppsMode(true).
When using this mode you can also report errors and crashes to the Google analytics server using
the sendCrashReport(Throwable, String message, boolean fatal) method.
We generally recommend using this mode and setting up an apps analytics account as the results
are more refined.
The Analytics API can also be enhanced to support any other form of analytics solution of your own
choosing by deriving the AnalyticsService class.
This allows you to integrate with any 3rd party via native or otherwise by overriding methods in
the AnalyticsService class then invoking:
[Link](new MyAnalyticsServiceSubclass());
Notice that this removes the need to invoke the other init method or setAppsMode(boolean).
519
14.10.1. Getting Started - Web Setup
To get started first you will need to create a facebook app on the Facebook developer portal at
[Link]
You need to repeat the process for web, Android & iOS (web is used by the simulator):
For the first platform you need to enter the app name:
For iOS we need the bundle ID which is the exact same thing we used in the Google+ login to
identify the iOS app its effectively your package name:
520
You should end up with something that looks like this:
The Android process is pretty similar but in this case we need the activity name too.
The activity name should match the main class name followed by the word Stub
(uppercase s). E.g. for the main class SociallChat we would use SocialChatStub as
the activity name
To build the native Android app we must make sure that we setup the keystore correctly for our
application. If you don’t have an Android certificate you can use the visual wizard (in the Android
section in the project preferences the button labeled Generate) or use the command line:
keytool -genkey -keystore [Link] -alias [alias_name] -keyalg RSA -keysize 2048 -validity 15000 -dname "CN=[full
name], OU=[ou], O=[comp], L=[City], S=[State], C=[Country Code]" -storepass [password] -keypass [password]
You can reuse the certificate in all your apps, some developers like having a
different certificate for every app. This is like having one master key for all your
doors, or a huge keyring filled with keys.
With the certificate we need an SHA1 key to further authenticate us to Facebook and we do this
using the keytool command line on Linux/Mac:
keytool -exportcert -alias (your_keystore_alias) -keystore (path_to_your_keystore) | openssl sha1 -binary | openssl
base64
And on Windows:
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\[Link] | openssl sha1 -binary | openssl
base64
521
You can read more about it on the Facebook guide here [[Link]
getting-started].
Figure 386. Hash generation process, notice the command lines are listed as part of the web wizard
Lastly you need to publish the Facebook app by flipping the switch in the apps "Status & Review"
page as such:
Figure 387. Without flipping the switch the app won’t "appear"
We now need to set some important build hints in the project so it will work correctly. To set the
build hints just right click the project select project properties and in the Codename One section
pick the second tab. Add this entry into the table:
[Link]=...
The app ID will be visible in your Facebook app page in the top left position.
To bind your mobile app into the Facebook app you can use the following code:
Login fb = [Link]();
[Link]("9999999");
[Link]("[Link]
[Link]("-------");
522
// Sets a LoginCallback listener
[Link](new LoginCallback() {
public void loginSuccessful() {
// we can now start fetching stuff from Facebook!
}
All of these values are from the web version of the app!
They are only used in the simulator and on "unsupported" platforms as a fallback.
Android and iOS will use the native login
In order to post something to Facebook you need to request a write permission, you can only do
write operations within the callback which is invoked when the user approves the permission.
You can prompt the user for publish permissions by using this code on a logged in FacebookConnect
[[Link]
[Link]()askPublishPermissions(new LoginCallback() {
public void loginSuccessful() {
// do something...
}
public void loginFailed(String errorMessage) {
// show error or just ignore
}
});
Notice that this won’t always prompt the user, but its required to verify that your
token is valid for writing.
523
While this API still works, it is no longer useful on iOS as it redirects to Safari to perform login, and
Apple no longer allows this practice.
The new, approved API is called Google Sign-In. Rather than using Safari to handle login (on iOS), it
uses an embedded web view, which is permitted by Apple.
OAuth Setup is required for using Google Sign-In in the simulator, and for accessing other Google
APIs in Android.
Short Version
Long Version
524
Figure 390. Pick a platform
Now enter an app name and the bundle ID for your app on the form below. The app name doesn’t
necessary need to match your app’s name, but the bundle ID should match the package name of
your app.
Select your country, and then click the "Choose and Configure Services" button.
525
Then press the "Enable Google Sign-In" button that appears.
You should then be presented with another button to "Generate Configuration Files" as shown
below
Press this button to download the [Link] file. Then copy this into the "native/ios"
directory of your Codename One project.
Figure 397. Project file structure after placing the [Link] into the native/ios directory
At this point, your app should be able to use Google Sign-In. Notice that we don’t require any build
hints. Only that the [Link] file is added to the project’s native/ios directory.
Short Version
Long Version
526
Figure 398. Set up mobile app form on Google
Now enter an app name and the platform for your app on the form below. The app name doesn’t
necessary need to match your app’s name, but the package name should match the package name
of your app.
Select your country, and then click the "Choose and Configure Services" button.
527
Then you’ll be presented with a field to enter the Android Signing Certificate SHA-1.
The value that you enter here should be obtained from the certificate that you are using to build
your app. You an use the keytool app that is distributed with the JDK to extract this value
The snippet above assumes that your keystore is located at /path/to/[Link], and the
certificate alias is "myAlias". You’ll be prompted to enter the password for your keystore, then the
output will look something like:
Extensions:
You will be interested in SHA1 fingerprint. In the snippet above, the SHA1 fingerprint is:
528
76:BA:AA:11:A9:22:42:24:93:82:6D:33:7E:48:BC:AF:45:4D:79:B0
You would paste this value into the "Android Signing Certificate SHA-1" field in the web form.
After pasting that in, you’ll see a new button with label "Enable Google Sign-in"
Press this button and you’ll be presented with another button to "Generate Configuration Files" as
shown below
Press this button to download the [Link] file. Then copy this into the "native/android"
directory of your Codename One project.
Figure 405. Project file structure after placing the [Link] into the native/android directory
At this point, your app should be able to use Google Sign-In. Notice that we don’t require any build
hints. Only that the [Link] file is added to the project’s native/android directory.
If you want to access additional information about the logged in user using
Google’s REST APIs, you will require an OAuth2.0 client ID of type Web Application
for this project as well. See Section 14.11.3, “OAuth Setup (Simulator and REST API
Access)” for details.
Getting Google Sign-In to work in the Codename One simulator requires an additional step after
you’ve set up iOS and/or Android apps. The Simulator can’t use the native Google Sign-In APIs, so it
uses the standard Web Application OAuth2.0 API. In addition, the Android App requires a Web
Application OAuth2.0 client ID to access additional Google REST APIs.
If you’ve set up the Google Sign-In API for either Android or iOS, then Google will have already
529
automatically generated a Web Application OAuth2.0 client ID for you. You just need to provide the
ClientID and ClientSecret to the GoogleConnect instance (in your java code).
3. Click on "Credentials" in the left menu. You’ll see a screen like this
4. Under the "OAuth2.0 Client IDs", find the row with "Web application" listed in the type column
6. Make note of the "Client ID" and "Client Secret" on this page, as you’ll need to add them to your
Java source in the next step.
7. In the "Authorized redirect URIs" section, you will need to enter the URL to the page that the
user will be sent to after a successful login. This page will only appear in the simulator for a
split second, as Codename One’s BrowserComponent will intercept this request to obtain the
access token upon successful login. You can use any URL you like here, but it must match the
value you give to [Link]() in Section 14.11.5, “The Code”.
The Javascript port can use the same OAuth2.0 credentials as the simulator does. It doesn’t require
your Client Secret or redirect URL. It only requires your Client ID, which you can specify using the
[Link]() method.
Login gc = [Link]();
[Link]("*****************.[Link]");
[Link]("[Link]
[Link]("-------------------");
530
// trigger the login if not already logged in
if(![Link]()){
[Link]();
} else {
// get the token and now you can query the Google API
String token = [Link]().getToken();
// NOTE: On Android, this token will be null unless you provide valid
// client ID and secrets.
}
The client ID and client secret values here are the ones from your OAuth2.0 Web
Application.
The Client ID and Client Secret values are used on both the Simulator and on
Android. On simulator these values are required for login to work at all. On
Android these values are required to obtain an access token to query the Google
API further using its various REST APIs. If you do not include these values on
Android, login will still work, but [Link]().getToken() will return null.
Using a Container provides us a lot of flexibility in terms of layout & functionality for a specific
component. MultiButton is a great example of that. It’s a Container internally that is composed of 5
labels and a Button.
Codename One makes the MultiButton "feel" like a single button thru the use of
setLeadComponent(Component) which turns the button into the "leader" of the component.
When a Container hierarchy is placed under a leader all events within the hierarchy are sent to the
leader, so if a label within the lead component receives a pointer pressed event this event will
really be sent to the leader.
E.g. in the case of the MultiButton the internal button will receive that event and send the action
531
performed event, change the state etc.
[Link]((e) -> {
if([Link]() == myMultiButton) {
// this won't occur since the source component is really a button!
}
if([Link]() == myMultiButton) {
// this will happen...
}
});
The leader also determines the style state, so all the elements being lead are in the same state. E.g. if
the the button is pressed all elements will display their pressed states, notice that they will do so
with their own styles but they will each pick the pressed version of that style so a Label UIID within
a lead component in the pressed state would return the Pressed state for a Label not for the Button.
This is very convenient when you need to construct more elaborate UI’s and the cool thing about it
is that you can do this entirely in the designer which allows assembling containers and defining the
lead component inside the hierarchy.
532
public String getText() {
return [Link]();
}
The Component class has two methods that allow us to exclude a component from lead behavior:
setBlockLead(boolean) & isBlockLead().
Effectively when you have a Component within the lead hierarchy that you would like to treat
differently from the rest you can use this method to exclude it from the lead component behavior
while keeping the rest in line…
This should have no effect if the component isn’t a part of a lead component.
The sample below is based on the Accordion component which uses a lead component internally.
533
Container header = [Link](title).
add([Link], delete);
[Link](header, t);
[Link](200);
}
This allows us to add/edit entries but it also allows the delete button above to actually work
separately. Without a call to setBlockLead(true) the delete button would cat as the rest of the
accordion title.
Figure 406. Accordion with delete button entries that work despite the surrounding lead
Just invoke addPullToRefresh(Runnable) on a scrollable container (or form) and the runnable
method will be invoked when the refresh operation occurs.
534
14.14. Running 3rd Party Apps Using Display’s execute
The Display [[Link] class’s execute method
allows us to invoke a URL which is bound to a particular application.
This works rather well assuming the application is installed. E.g. this list [[Link]
IPhone_URL_Schemes] contains a set of valid URL’s that can be used on iOS to run common
applications and use builtin functionality.
Some URL’s might not be supported if an app isn’t installed, on Android there isn’t much that can be
done but iOS has a canOpenURL method for Objective-C.
On iOS you can use the [Link]() method which returns a Boolean instead of a boolean
which allows us to support 3 result states:
3. null - we have no idea whether the URL will work on this platform.
The sample below launches a "godfather" search on IMDB only when this is sure to work (only on
iOS currently). We can actually try to search in the case of null as well but this sample plays it safe
by using the http link which is sure to work:
A good example for a common problem developers face is location code that doesn’t work in iOS.
This is due to the [Link] build hint that’s required. The reason that build hint
was added is a requirement by Apple to provide a description for every app that uses the location
service.
/**
* Returns the build hints for the simulator, this will only work in the debug environment and it's
* designed to allow extensions/API's to verify user settings/build hints exist
* @return map of the build hints that isn't modified without the [Link]. prefix
535
*/
public Map<String, String> getProjectBuildHints() {}
/**
* Sets a build hint into the settings while overwriting any previous value. This will only work in the
* debug environment and it's designed to allow extensions/API's to verify user settings/build hints exist.
* Important: this will throw an exception outside of the simulator!
* @param key the build hint without the [Link]. prefix
* @param value the value for the hint
*/
public void setProjectBuildHint(String key, String value) {}
Both of these allow you to detect if a build hint is set and if not (or if it’s set incorrectly) set its
value…
So if you will use the location API from the simulator and you didn’t define
[Link] Codename One will implicitly define a string there. The cool thing is
that you will now see that string in your settings and you would be able to customize it easily.
However, this gets way better than just that trivial example!
The real value is for 3rd party cn1lib authors. E.g. Google Maps or Parse. They can inspect the build
hints in the simulator and show an error in case of a misconfiguration. They can even show a setup
UI. Demos that need special keys in place can force the developer to set them up properly before
continuing.
This is possible in Java but non-trivial, the thing is that this is relatively easy to do in Codename One
with tools such as callSerially I can let arbitrary code run on the EDT. Why not offer that to any
random thread?
That’s why I created EasyThread which takes some of the concepts of Codeame One’s threading and
makes them more accessible to an arbitrary thread. This way you can move things like resource
loading into a separate thread and easily synchronize the data back into the EDT as needed…
EasyThread e = [Link]("ThreadName");
536
But it gets better, say you want to return a value:
Lets break that down… We ran the thread with the success callback on the new thread then the
callback got invoked on the EDT as a result. So this code (success) →
[Link](doThisOnTheThread()) ran off the EDT in the thread and when we invoked the
onSuccess callback it sent it asynchronously to the EDT here: (myResult) → onEDTGotResult(myRsult).
These asynchronous calls make things a bit painful to wade thru so instead I chose to wrap them in
a simplified synchronous version:
EasyThread e = [Link]("Hi");
int result = [Link](() -> {
[Link]("This is a thread");
return 3;
});
There are a few other variants like runAndWait and there is a kill() method which stops a thread
and releases its resources.
@Override
protected void initComponent() {
[Link]();
getComponentForm().setEnableCursors(true);
}
Once this is enabled you can set the cursor over a specific region using [Link]() which
accepts one of the cursor constants defined in Component.
When we first started committing to git we used something like this for netbeans projects:
537
*.jar
nbproject/private/
build/
dist/
lib/CodenameOne_SRC.zip
Removing the jars, build, private folder etc. makes a lot of sense but there are a few nuances that
are missing here…
14.18.1. cn1lib’s
You will notice we excluded the jars which are stored under lib and we exclude the Codename One
source zip. But I didn’t exclude cn1libs… That was an omission since the original project we
committed didn’t have cn1libs. But should we commit a binary file to git?
I don’t know. Generally git isn’t very good with binaries but cn1libs make sense. In another project
that did have a cn1lib I did this:
*.jar
nbproject/private/
build/
dist/
lib/CodenameOne_SRC.zip
lib/impl/
native/internal_tmp/
The important lines are lib/impl/ and native/internal_tmp/. Technically cn1libs are just zips. When
you do a refresh libs they unzip into the right directories under lib/impl and native/internal_tmp.
By excluding these directories we can remove duplicates that can result in conflicts.
Committing the res file is a matter of personal choice. It is committed in the git ignore files above
but you can remove it. The res file is at risk of corruption and in that case having a history we can
refer to, matters a lot.
But the resource file is a bit of a problematic file. As a binary file if we have a team working with it
the conflicts can be a major blocker. This was far worse with the old GUI builder, that was one of
the big motivations of moving into the new GUI builder which works better for teams.
Still, if you want to keep an eye of every change in the resource file you can switch on the File →
XML Team Mode which should be on by default. This mode creates a file hierarchy under the res
directory to match the res file you opened. E.g. if you have a file named src/[Link] it will create
a matching res/[Link] and also nest all the images and resources you use in the res directory.
That’s very useful as you can edit the files directly and keep track of every file in git. However, this
has two big drawbacks:
538
• It’s flaky - while this mode works it never reached the stability of the regular res file mode
• It conflicts - the simulator/device are oblivious to this mode. So if you fetch an update you also
need to update the res file and you might still have conflicts related to that file
Ultimately both of these issues shouldn’t be a deal breaker. Even though this mode is a bit flaky it’s
better than the alternative as you can literally "see" the content of the resource file. You can easily
revert and reapply your changes to the res file when merging from git, it’s tedious but again not a
deal breaker.
Building on the gitignore we have for NetBeans the eclipse version should look like this:
.DS_Store
*.jar
build/
dist/
lib/impl/
native/internal_tmp/
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*.zip
*~.nib
[Link]
.settings/
.loadpath
.recommenders
.externalToolBuilders/
*.launch
*.pydevproject
.cproject
.factorypath
.buildpath
.project
.classpath
14.18.4. IntelliJ/IDEA
.DS_Store
*.jar
build/
dist/
lib/impl/
native/internal_tmp/
539
*.zip
.idea/**/[Link]
.idea/**/[Link]
.idea/dictionaries
.idea/**/dataSources/
.idea/**/[Link]
.idea/**/[Link]
.idea/**/[Link]
.idea/**/[Link]
.idea/**/[Link]
.idea/**/[Link]
.idea/**/[Link]
.idea/**/libraries
*.iws
/out/
[Link]
540
Chapter 15. Performance, Size & Debugging
15.1. Reducing Resource File Size
It’s easy to lose track of size/performance when you are working within the comforts of a visual
tool like the Codename One Designer. When optimizing resource files you need to keep in mind one
thing: it’s all about image sizes.
Images will take up 95-99% of the resource file size; everything else pales in
comparison.
Like every optimization the first rule is to reduce the size of the biggest images which will provide
your biggest improvements, for this purpose we introduced the ability to see image sizes in
kilobytes. To launch that feature use the menu item Images → Image Sizes (KB) in the designer.
Figure 408. Image sizes window that allows us to find the biggest impact on our RAM/Storage
This produces a list of images sorted by size with their sizes. Often the top entries will be multi-
images, which include HD resolution values that can be pretty large. These very high-resolution
images take up a significant amount of space!
Just going to the multi-images, selecting the unnecessary resolutions & deleting these images can
saves significant amounts of space:
You can see the size in KB at the top right side in the designers image viewer
Applications using the old GUI builder can use the Images → Delete Unused Images menu option
(it’s also under the Images menu). This tool allows detecting and deleting images that aren’t used
541
within the theme/GUI.
If you have a very large image that is opaque you might want to consider converting it to JPEG and
replacing the built in PNG’s. Notice that JPEG’s work on all supported devices and are typically
smaller.
You can use the excellent OptiPng [[Link] tool to optimize image files right
from the Codename One designer. To use this feature you need to install OptiPng then select Images
→ Launch OptiPng from the menu. Once you do that the tool will automatically optimize all your
PNG’s.
When faced with size issues make sure to check the size of your res file, if your JAR file is large
open it with a tool such as 7-zip and sort elements by size. Start reviewing which element justifies
the size overhead.
The simulator contains some tools to measure performance overhead of a specific component and
also detect EDT blocking logic. Other than that follow these guidelines to create more performance
code:
• Avoid round rect borders - they have a huge overhead on all platforms. Use image borders
instead (counter intuitively they are MUCH faster)
• Avoid Gradients - they perform poorly on most OS’s. Use a background image instead
• Use larger images when tiling or building image borders, using a 1 pixel (or event a few pixels)
wide or high image and tiling it repeatedly can be very expensive
• Shrink resource file sizes - Otherwise data might get collected by the garbage collector and
reloading data might be expensive
• Check that you don’t have too many image lock misses - this is discussed in the graphics
section
• On some platforms mutable images are slow - mutable images are images you can draw on
(using getGraphics()). On some platforms they perform quite badly (e.g. iOS) and should
542
generally be avoided. You can check if mutable images are fast in a platform using
[Link]()
• * Make components either transparent or opaque * - a translucent component must paint it’s
parent every time. This can be expensive. An opaque component might have margins that
would require that we paint the parent so there is often overdraw in such cases (overdraw
means the same pixel being painted twice).
Figure 411. Main tab of the performance monitor: Logs and timings
The first tab of the performance monitor includes a table of the drawn components. Each entry
includes the number of times it was drawn and the slowest/fastest and average drawing time. The
toolbar across the top includes Pause/Continue buttons so you can freeze the counters while you
inspect the current snapshot, a "Clear Data" action to reset the tables, and a "GC" button that
invokes the simulator’s garbage collector so you can see how memory usage changes.
This is useful if a Form is slow. You might be able to pinpoint it to a specific component using this
tool.
The Log on the bottom includes debug related information. E.g. it warns about the usage of mutable
images which might be slow on some platforms. This also displays warnings when an unlocked
image is drawn etc. A live "Image Memory Overhead" meter summarizes how much native image
memory the current form consumes so you can correlate spikes with your drawing code.
543
Figure 412. Rendering tree
The rendering tree view allows us to inspect the hierarchy painting. You can press the refresh
button which will trigger the painting of the current Form. Every graphics operation is logged and so
is the stack to it.
You can then inspect the hierarchy and see what was drawn by the various components. You can
click the "stack" buttons to see the specific stack trace that lead to that specific drawing operation.
This is a remarkably powerful debugging tool as you can literally see "overdraw" within this tool.
E.g if you see fillRect or similar API’s invoked in the parent and then again and again in the
children this could indicate a problem.
This feature is actually more useful for general debugging however it’s sometimes useful to
simulate a slow/disconnected network to see how this affects performance.
For this purpose the Codename One simulator allows you to slow down networking or even fake a
disconnected network to see how your application handles such cases.
544
15.5. Debugging Codename One Sources
When you debug your app with our source code you can place breakpoints deep within Codename
One and gain unique insight. You can also use the profilers and profile into Codename One to gain
similar performance specific insight.
When you run into a bug or a missing feature you can push that feature/fix back to Codename One
[[Link] using a pull request. Github makes that process trivial and in this new
video and slides below we show you how. The steps to use the code are:
3. Clone the git URL’s from the projects into the IDE using the Team → Git → Clone menu option.
Notice that you must deselect projects in the IDE for the menu to appear.
5. Unzip the cn1-binaries project and make sure the directory has the name cn1-binaries. Verify
that cn1-binaries, CodenameOne and codenameone-skins are within the same parent directory.
.In your own project remove the jars both in the build & run libraries section. Replace the build
libraries with the CodenameOne/CodenameOne project. Replace the runtime libraries with the
CodenameOne/Ports/JavaSEPort project.
This allows you to run the existing Codename One project with the Codename One source code and
debug into Codename One. You can now also commit, push and send a pull request with the
changes.
To get started with the testing framework, launch the application and open the test recorder in the
simulator menu.
545
Figure 414. The test recorder tool in the simulator
Once you press record a test will be generate for you as you use the application.
Figure 415. Test recording in progress, when done just press the save icon
You can build tests using the Codename One testing package to manipulate the Codename One UI
programmatically and perform various assertions.
Unlike frameworks such as JUnit which assign a method per test, the Codename One test
framework uses a class per test. This allows the framework to avoid reflection and thus allows it to
work properly on the device.
546
Google improved on this a bit by allowing users to submit stack traces for failures on Android
devices but this requires the users approval for sending personal data which you might not need if
you only want to receive the stack trace and maybe some basic application state (without violating
user privacy).
For quite some time Codename One had a very powerful feature that allows you to both catch and
report such errors, the error reporting feature uses the Codename One cloud which is exclusive for
pro/enterprise users. Normally in Codename One we catch all exceptions on the EDT (which is
where most exceptions occur) and just display an error to the user as you can see in the picture.
Unfortunately this isn’t very helpful to us as developers who really want to see the stack;
furthermore we might prefer the user doesn’t see an error message at all!
Codename One allows us to grab all exceptions that occur on the EDT and handle them using the
method addEdtErrorHandler in the Display [[Link]
[Link]] class. Adding this to the Log’s ability to report errors directly to us and we can get a
very powerful tool that will send us an email with information when a crash occurs!
[Link](true);
We normally place this in the init(Object) method so all future on-device errors are emailed to
you. Internally this method uses the [Link]().addEdtErrorHandler() API to bind error
listeners to the EDT. When an exception is thrown there it is swallowed (using
[Link]()). The Log data is then sent using [Link]().
If your crash handler runs while networking is unavailable or you want to avoid blocking the EDT,
use [Link]() instead. It performs the upload in a background thread and is what
Codename One’s lifecycle helper falls back to when regular error reporting fails.
You can also plug in your own crash reporting pipeline by calling
[Link]().setCrashReporter(CrashReport). The CrashReport [[Link]
javadoc/com/codename1/system/[Link]] callback will receive the exception, device information,
and log payload so you can forward it to services like Firebase Crashlytics or your in-house tools.
To truly benefit from this feature we need to use the Log class for all logging and exception handling
instead of API’s such as [Link].
To log standard printouts you can use the Log.p(String) method and to log exceptions with their
stack trace you can use Log.e(Throwable).
During our debugging of the contacts demo that is a part of the new kitchen sink demo we noticed
its performance was sub par. We assumed this was due to the implementation of getAllContacts &
that there is nothing to do. While debugging another issue we noticed an anomaly during the
547
loading of the contacts.
This led to the discovery that we are loading the same resource file over and over again for every
single contact in the list!
In the new Contacts demo we have a share button for each contact, the code for constructing a
ShareButton looks like this:
public ShareButton() {
setUIID("ShareButton");
[Link](this, FontImage.MATERIAL_SHARE);
addActionListener(this);
[Link](new SMSShare());
[Link](new EmailShare());
[Link](new FacebookShare());
}
This seems reasonable until you realize that the constructors for SMSShare, EmailShare &
FacebookShare load the icons for each of those…
These icons are in a shared resource file that we load and don’t properly cache. The initial
workaround was to cache this resource but a better solution was to convert this code:
public SMSShare() {
super("SMS", [Link]().getImage("[Link]"));
}
public SMSShare() {
super("SMS", null);
}
@Override
public Image getIcon() {
Image i = [Link]();
if(i == null) {
i = [Link]().getImage("[Link]");
setIcon(i);
}
return i;
}
This small change boosted the loading performance and probably the general performance due to
less memory fragmentation.
548
The lesson that we should learn every day is to never assume about performance…
Another performance pitfall in this same demo came during scrolling. Scrolling was janky
(uneven/unsmooth) right after loading finished would recover after a couple of minutes.
To hasten the loading of contacts we load them all without images. We then launch a thread that
iterates the contacts and loads an individual image for a contact. Then sets that image to the contact
and replaces the placeholder image.
This performed well in the simulator but didn’t do too well even on powerful mobile phones. We
assumed this wouldn’t be a problem because we used [Link]() to yield CPU time but that
wasn’t enough.
Often when we see performance penalty the response is: "move it to a separate thread". The
problem is that this separate thread needs to compete for the same system resources and merge its
changes back into the EDT. When we perform something intensive we need to make sure that the
CPU isn’t needed right now…
In this and past cases we solved this using a class member indicating the last time a user interacted
with the UI.
Here we defined:
This effectively sleeps when the user interacts with the UI and only loads the images if the user
hasn’t touched the UI in a while.
Notice that we also check if the scroll changes, this allows us to notice cases like the animation of
scroll winding down.
All we need to do now is update the lastScroll variable whenever user interaction is in place. This
works for user touches:
549
[Link](e -> lastScroll = [Link]());
[Link](new ScrollListener() {
int initial = -1;
@Override
public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) {
// scrolling is sensitive on devices...
if(initial < 0) {
initial = scrollY;
}
lastScroll = [Link]();
...
}
});
550
Chapter 16. Monetization
Codename One tries to make the lives of software developers easier by integrating several forms of
built-in monetization solutions such as ad network support, in-app-purchase etc.
A lot of the monetization options are available as 3rd party cn1lib’s [[Link]
[Link]] that you can install thru the Codename One website.
ca-app-pub-8610616152754010/3413603324
There’s a special ad unit id to use for test ads. If you specify ca-app-pub-
3940256099942544/6300978111, you’ll get test ads for your development phase. This is important
because you’re not allowed to click on your own ads. When it’s time to create a production release,
you should replace this with the real value you generated in adMob.
In-app purchase support centers around your set of SKUs that you want to sell. Each product that
you sell, whether it be a 1-month subscription, an upgrade to the "Pro" version, "10 disco credits",
will have a SKU (stock-keeping-unit). Ideally you will be able to use the same SKU across all the
stores that you sell your app in.
551
16.2.2. Types of Products
1. Non-consumable Product - This is a product that the user purchases once to "own". They
cannot re-purchase it. One example is a product that upgrades your app to a "Pro" version.
2. Consumable Product - This is a product that the user can buy more than once. E.g. You might
have a product for "10 Credits" that allows the user to buy items in a game.
3. Non-Renewable Subscription - A subscription that you buy once, and will not be "auto-
renewed" by the app store. These are almost identical to consumable products, except that
subscriptions need to be transferable across all the user’s devices. This means that non-
renewable subscriptions require that you have a server that keeps track of the subscriptions.
4. Renewable Subscriptions - A subscription that the app store manages. The user will be
automatically billed when the subscription period ends, and the subscription will renew.
Let’s start with a simple example of an app that sells "Worlds". The first thing we do is pick the SKU
for our product. I’ll choose "[Link]" for the SKU.
While we chose to use the package name convention for an SKU you can use any
name you want e.g UA8879
Next, our app’s main class needs to implement the PurchaseCallback interface
@Override
public void itemPurchased(String sku) {
...
}
@Override
public void itemPurchaseError(String sku, String errorMessage) {
...
}
552
@Override
public void paymentFailed(String paymentCode, String failureReason) {
...
}
@Override
public void paymentSucceeded(String paymentCode, double amount, String currency) {
...
}
Using these callbacks, we’ll be notified whenever something changes in our purchases. For our
simple app we’re only interested in itemPurchased() and itemPurchaseError(). The legacy
itemRefunded(), subscriptionStarted(), and subscriptionCanceled() hooks have been deprecated in
the core API and are no longer dispatched by the stores. Instead of relying on callbacks for long-
term entitlement state, query the current receipts at runtime using helpers such as
[Link](…), [Link](…), or by iterating through
[Link]() so that your UI always reflects the latest data provided by the underlying
store.
Now in the start method, we’ll add a button that allows the user to buy the world:
[Link](buyWorld);
[Link]();
}
At this point, we already have a functional app that will track the sale of the world. To make it more
interesting, let’s add some feedback with the ToastBar to show when the purchase completes.
@Override
public void itemPurchased(String sku) {
[Link]("Thanks. You now own the world", FontImage.MATERIAL_THUMB_UP);
553
}
@Override
public void itemPurchaseError(String sku, String errorMessage) {
[Link]("Failure occurred: "+errorMessage);
}
You can test out this code in the simulator without doing any additional setup and
it will work. If you want the code to work properly on Android and iOS, you’ll need
to set up the app and in-app purchase settings in the Google Play and iTunes stores
respectively as explained below
In the simulator, clicking on the "Buy World" button will bring up a prompt to ask you if you want
to approve the purchase.
554
Now if I try to buy the product again, it pops up the dialog to let me know that I already own it.
In the "Buy World" example above, the "world" product was non-consumable, since we could only
buy the world once. We could change it to a consumable product by disregarding whether it was
purchased before & keeping track of how many times it had been purchased.
We’ll use storage to keep track of the number of worlds that the user purchased. We need two
methods to manage this count. One method gets the number of worlds that we own, and another
adds a world to this count.
555
}
[Link](e->{
if ([Link]("Confirm", "You own "+getNumWorlds()+
" worlds. Do you want to buy another one?", "Yes", "No")) {
[Link]().purchase(SKU_WORLD);
}
});
@Override
public void itemPurchased(String sku) {
addWorld();
[Link]("Thanks. You now own "+getNumWorlds()+" worlds", FontImage.MATERIAL_THUMB_UP);
}
When we set up the products in the iTunes store we will need to mark the product
as a consumable product or iTunes will prevent us from purchasing it more than
once
1. Non-renewable
2. Auto-renewable
Non-renewable subscriptions are the same as consumable products, except that they are shareable
across devices. Auto-renewable subscriptions will continue as long as the user doesn’t cancel the
subscription. They will be re-billed automatically by the appropriate app-store when the chosen
period expires, and the app-store handles the management details itself.
The Purchase class includes both a purchase() method and a subscribe() method.
On some platforms it makes no difference which one you use, but on Android it
matters. If the product is set up as a subscription in Google Play, then you must use
subscribe() to purchase the product. If it is set up as a regular product, then you
556
must use purchase(). Since we enter "Non-renewable" subscriptions as regular
products in the play store, we would use the purchase() method.
Apple allows you to present discounted introductory pricing to existing subscribers via
promotional offers [[Link] Codename One
surfaces this capability through overloads of both [Link](String, PromotionalOffer) and
[Link](String, PromotionalOffer), which forward the promotional context to StoreKit
when you initiate the transaction. Promotional offers are only honoured by iOS, so the overloads
simply fall back to the regular purchase flow on other platforms.
To build the signed discount payload required by Apple you can use the ApplePromotionalOffer
helper:
Apple generates the signature and timestamp from your App Store Connect server notifications
endpoint; Codename One simply passes them to the native StoreKit APIs. For one-time products you
can call purchase(sku, offer) instead of subscribe(…).
Both Apple and Google provide built-in user interfaces for restoring past purchases and managing
subscription billing preferences. Codename One exposes these entry points so you can surface the
native flows without reimplementing them yourself.
Because these flows are handled by the underlying store your UI doesn’t need to rebuild any billing
screens. Simply gate the buttons on the capability checks above so that iOS and Android users get
557
the familiar restore/manage dialogs while other platforms can fall back to your own help copy.
Since a subscription purchased on one user device needs to be available across the user’s devices
(Apple’s rules for non-renewable subscriptions), our app will need to have a server-component. In
this section, we’ll gloss over that & "mock" the server interface. We’ll go into the specifics of the
server-side below.
Subscriptions, in Codename One use the "Receipts" API. It’s up to you to register a receipt store with
the In-App purchase instance, which allows Codename one to load receipts (from your server), and
submit new receipts to your server. A Receipt includes information such as:
1. Store code (since you may be dealing with receipts from itunes, google play & Microsoft)
2. SKU
4. Expiry Date
5. Cancellation date
6. Purchase date
7. Order Data (that you can use on the server-side to verify the receipt and load receipt details
directly from the store it originated from).
The Purchase provides a set of methods for interacting with the receipt store, such as:
1. isSubscribed([skus]) - Checks to see if the user is currently subscribed to any of the provided
skus.
3. synchronizeReceipts() - Synchronizes the receipts with the receipt store. This will attempt to
submit any pending purchase receipts to the receipt store, and the reload receipts from the
receipt store.
In order for any of this to work, you must implement the ReceiptStore interface, and register it with
the Purchase instance. Your receipt store must implement two methods:
We’ll expand on the theme of "Buying" the world for this app, except, this time we will "Rent" the
world for a period of time. We’ll have two products:
558
1. A 1 month subscription
2. A 1 year subscription
Notice that we create two separate SKUs for the 1 month and 1 year subscription. Each
subscription period must have its own SKU. I have created an array (PRODUCTS) that contains both
of the SKUs. This is handy, as you’ll see in the examples ahead, because the APIs for checking status
and expiry date of a subscription take the SKUs in a "subscription group" as input.
Different SKUs that sell the same service/product but for different periods form a
"subscription group". Conceptually, customers are not subscribing to a particular
SKU, they are subscribing to the subscription group of which that SKU is a
member. As an example, if a user purchases a 1 month subscription to "the world",
they are actually subscribing to "the world" subscription group.
It’s up to you to know the grouping of your SKUs. Any methods in the Purchase class that check
subscription status or expiry date of a SKU should be passed all SKUs of that subscription group.
E.g. If you want to know if the user is subscribed to the SKU_WORLD_1_MONTH subscription, it would not
be sufficient to call [Link](SKU_WORLD_1_MONTH), because that wouldn’t take into account if
the user had purchased a 1 year subscription. The correct way is to always call
[Link](SKU_WORLD_1_MONTH, SKU_WORLD_1_YEAR), or simply [Link](PRODUCTS) since
we have placed both SKUs into our PRODUCTS array.
The receipt store is intended to interface with a server so that the subscriptions
can be synced with multiple devices, as required by Apple’s guidelines. For this
post we’ll just store our receipts on device using internal storage. Moving the logic
to a server is a simple matter that we will cover in a future post when we cover the
server-side.
559
Figure 421. The Receipt store is a layer between your server and Codename One
1. fetchReceipts
2. submitReceipt
Generally we’ll register it in our app’s init() method so that it’s always available.
[Link]().setReceiptStore(new ReceiptStore() {
@Override
public void fetchReceipts(SuccessCallback<Receipt[]> callback) {
// Fetch receipts from storage and pass them to the callback
}
@Override
public void submitReceipt(Receipt receipt, SuccessCallback<Boolean> callback) {
// Save a receipt to storage. Make sure to call callback when done.
}
});
560
}
These methods are designed to be asynchronous since real-world apps will always be connecting to
some sort of network service. Therefore, instead of returning a value, both of these methods are
passed instances of the SuccessCallback class. It’s important to make sure to call
[Link]() ALWAYS when the methods have completed, even if there is an error, or the
Purchase class will just assume that you’re taking a long time to complete the task, and will
continue to wait for you to finish.
@Override
public void fetchReceipts(SuccessCallback<Receipt[]> callback) {
Storage s = [Link]();
Receipt[] found;
synchronized(RECEIPTS_KEY) {
if ([Link](RECEIPTS_KEY)) {
List<Receipt> receipts = (List<Receipt>)[Link](RECEIPTS_KEY);
found = [Link](new Receipt[[Link]()]);
} else {
found = new Receipt[0];
}
}
// Make sure this is outside the synchronized block
[Link](found);
}
This is fairly straight forward. We’re checking to see if we already have a list of receipts stored. If so
we return that list to the callback. If not we return an empty array of receipts.
The submitReceipt() method is a little more complex, as it needs to calculate the new expiry date for
our subscription.
@Override
public void submitReceipt(Receipt receipt, SuccessCallback<Boolean> callback) {
Storage s = [Link]();
synchronized(RECEIPTS_KEY) {
List<Receipt> receipts;
if ([Link](RECEIPTS_KEY)) {
receipts = (List<Receipt>)[Link](RECEIPTS_KEY);
561
} else {
receipts = new ArrayList<Receipt>();
}
// Check to see if this receipt already exists
// This probably won't ever happen (that we'll be asked to submit an
// existing receipt, but better safe than sorry
for (Receipt r : receipts) {
if ([Link]().equals([Link]()) &&
[Link]().equals([Link]())) {
// If we've already got this receipt, we'll just this submission.
return;
}
}
[Link](newExpiry);
[Link](receipt);
[Link](RECEIPTS_KEY, receipts);
}
// Make sure this is outside the synchronized block
562
[Link]([Link]);
}
The main logic of this method involves iterating through all of the existing receipts to find the
latest current expiry date, so that when the user purchases a subscription, it’s added onto the end
of the current subscription (if one exists) rather than going from today’s date. This enables users to
safely renew their subscription before the subscription has expired.
The iTunes store and Play store have no knowledge of your subscription durations.
This is why it’s up to you to set the expiry date in the submitReceipt method. Non-
renewable subscriptions are essentially no different than regular consumable
products. It’s up to you to manage the subscription logic - and Apple, in particular,
requires you to do so using a server.
Synchronizing Receipts
In order for your app to provide you with current data about the user’s subscriptions and expiry
dates, you need to synchronize the receipts with your receipt store. Purchase provides a set of
methods for doing this. Generally I’ll call one of them inside the start() method, and I may
resynchronize at other strategic times if I suspect that the information may have changed.
...
563
// Update the UI as necessary to reflect
});
}
And we also provide a button to allow the user to manually synchronize the receipts.
[Link](e->{
[Link](0, res->{
// Update the UI
});
});
Now that we have a receipt store registered, and we have synchronized our receipts, we can query
the Purchase instance to see if a SKU or set of SKUs is currently subscribed. There are three useful
methods in this realm:
1. boolean isSubscribed(String… skus) - Checks to see if the user is currently subscribed to any of
the provided SKUs.
2. Date getExpiryDate(String… skus) - Gets the latest expiry date of a set of SKUs.
3. Receipt getFirstReceiptExpiringAfter(Date dt, String… skus) - This method will return the
earliest receipt with an expiry date after the given date. This is needed in cases where you need
to decide if the user should have access to some content based on its publication date. E.g. If you
published an issue of your e-zine on March 1, and the user purchased a subscription on March
15th, then they should get access to the March 1st issue even though it doesn’t necessarily fall in
the subscription period. Being able to easily fetch the first receipt after a given date makes it
easier to determine if a particular issue should be covered by a subscription.
If you need to know more information about subscriptions, you can always just call getReceipts()
to obtain a list of all of the current receipts and determine for yourself what the user should have
access to.
In the hello world app we’ll use this information in a few different places. On our main form we’ll
include a label to show the current expiry date, and we allow the user to press a button to
synchronize receipts manually if they think the value is out of date.
// ...
[Link](e->{
564
[Link](0, res->{
if ([Link](PRODUCTS)) {
[Link]("World rental expires "+[Link](PRODUCTS));
} else {
[Link]("You don't currently have a subscription to the world");
}
[Link]();
});
});
You should now have all of the background required to implement the Hello World Subscription
app. So we’ll return to the code and see how the user purchases a subscription.
In the main form, we want two buttons to subscribe to the "World", for one month and one year
respectively. They look like:
565
if ([Link]("Confirm", msg, "Yes", "No")) {
[Link]().purchase(SKU_WORLD_1_YEAR);
// Note: since this is a non-renewable subscription it is just a regular
// product in the play store - therefore we use the purchase() method.
// If it were a "subscription" product in the play store, then we
// would use subscribe() instead.
}
});
① In the event handler we check if the user is subscribed by calling isSubscribed(PRODUCTS). Notice
that we check it against the array of both the one month and one year subscription SKUs.
② We are able to tell the user when the current expiry date is so that they can gauge whether to
proceed.
1. purchase(sku)
2. subscribe(sku)
Which one you use depends on the type of product that is being purchased. If your product is set up
as a subscription in the Google Play store, then you should use subscribe(sku). Otherwise, you
should use purchase(sku).
The purchase callbacks are very similar to the ones that we implemented in the regular in-app
purchase examples:
@Override
public void itemPurchased(String sku) {
Purchase iap = [Link]();
@Override
public void itemPurchaseError(String sku, String errorMessage) {
[Link]("Failure occurred: "+errorMessage);
}
Notice that, in itemPurchased() we don’t need to explicitly create any receipts or submit anything to
the receipt store. This is handled for you automatically. We do make a call to
synchronizeReceiptsSync() but this is just to ensure that our toast message has the new expiry date
loaded already.
566
16.2.11. Screenshots
567
Figure 424. Simulator confirm dialog when purchasing a subscription
16.2.12. Summary
This section demonstrated how to set up an app to use non-renewable subscriptions using in-app
purchase. Non-renewable subscriptions are the same as regular consumable products except for
the fact that they are shared by all of the user’s devices, and thus, require a server component. The
app store has no knowledge of the duration of your non-renewable subscriptions. It’s up to you to
specify the expiry date of purchased subscriptions on their receipts when they are submitted.
Google play doesn’t formally have a "non-renewable" subscription product type. To implement
them in Google play, you would just set up a regular product. It’s how you handle it internally that
makes it a subscription, and not just a regular product.
Codename One uses the Receipt class as the foundation for its subscriptions infrastructure. You, as
the developer, are responsible for implementing the ReceiptStore interface to provide the receipts.
The Purchase instance will load receipts from your ReceiptStore, and use them to determine
whether the user is currently subscribed to a subscription, and when the subscription expires.
Auto-renewable subscriptions provide, arguably, an easier path to recurring revenue than non-
renewable subscriptions because all of the subscription stuff is handled by the app store. You defer
almost entirely to the app store (iTunes for iOS, and Play for Android) for billing and management.
If there is a down-side, it would be that you are also subject to the rules of each app store - and they
568
take their cut of the revenue.
1. For more information about Apple’s auto-renewable subscription features and rules see this
document [[Link]
2. For more information about subscriptions in Google play, see this document
[[Link]
When deciding between auto-renewable and non-renewable subscriptions, as always, the answer
will depend on your needs and preferences. Auto-renewables are nice because it takes the process
completely out of your hands. You just get paid. On the other hand, there are valid reasons to want
to use non-renewables. E.g. You can’t cancel an auto-renewable subscription for a user. They have
to do that themselves. You may also want more control over the subscription and renewal process,
in which case a non-renewable might make more sense.
On a practical level, if you are using auto-renewable subscriptions (and therefore subscription
products in the Google play store) you must use the [Link](sku) method for initiating
the purchase workflow. For non-renewable subscriptions (and therefore regular products in the
Google play store), you must use the [Link](sku) method.
In this section we’ll describe the general workflow of subscription management on the server. We
also demonstrate how use Apple’s and Google’s web services to validate receipts and stay informed
of important events (such as when users cancel or renew their subscriptions).
To aid in this process, we’ve created a fully-functional in-app purchase demo project that includes
both a client app [[Link] and a server app
[[Link]
1. Create a new Codename One project in Netbeans, and choose the "Bare-bones Hello World
Template". You should make your package name something unique so that you are able to
create real corresponding apps in both Google Play and iTunes connect.
569
3. Add the Generic Web Service Client [[Link] library
to your project by going to "Codename Settings" > "Extensions", finding that library, and click
"Download". Then "Refresh CN1 libs" as it suggests.
4. Change the localHost property to point to your local machine’s network address. Using
"[Link] is not going to cut it here because when the app is running on a phone, it needs
to be able to connect to your web server over the network. This address will be your local
network address (e.g. [Link], or something like that).
5. Add the [Link] build hint to your project with the value
"<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/>
</dict>". This is so that we can use http urls in iOS. Since we don’t intend to fully publish this
app, we can cut corners like this. If you were creating a real app, you would use proper secure
URLs.
Download the CN1-IAP-Server demo project from Github, and run its "install-deps" ANT task in
order to download and install its dependencies to your local Maven repo.
For the following commands to work, make sure you have "ant", "mvn", and "git"
in your environment PATH.
1. Create a new database in your preferred DBMS. Call it anything you like.
2. Create a new table named "RECEIPTS" in this database with the following structure:
570
primary key (TRANSACTION_ID, STORE_CODE)
)
If you’re not sure how to create a data source, see my previous tutorial on connecting to a MySQL
database [[Link]
At this point we should be able to test out the project in the Codename One simulator to make sure
it’s working.
1. Build and Run the server project in Netbeans. You may need to tell it which application server
you wish to run it on. I am running it on the Glassfish 4.1 that comes bundled with Netbeans.
2. Build and run the client project in Netbeans. This should open the Codename One simulator.
571
Figure 426. First screen of app
This screen is for testing consumable products, so we won’t be making use of this right now.
Open the hamburger menu and select "Subscriptions". You should see something like this:
Click on the "Subscribe 1 Month No Ads" button. You will be prompted to accept the purchase:
572
Figure 428. Approve purchase dialog
Upon completion, the app will submit the purchase to your server, and if all went well, it will
retrieve the updated list of receipts from your server also, and update the label on this form to say
"No Ads. Expires <some date>":
This project is set up to use an expedited expiry date schedule for purchases from
the simulator. 1 month = 5 minutes. 3 months = 15 minutes. This helps for testing.
That is why your expiry date may be different than expected.
Just to verify that the receipt was inserted correctly, you should check the contents of your
"RECEIPTS" table in your database. In Netbeans, I can do this easily from the "Services" pane.
Expand the database connection down to the RECEIPTS table, right click "RECEIPTS" and select
"View Data". This will open a data table similar the the following:
573
Figure 430. Receipts table after insertion
1. The "username" was provided by the client. It’s hard-coded to "admin", but the idea is that you
would have the user log in and you would have access to their real username.
If you delete the receipt from your database, then press the "Synchronize Receipts" button in your
app, the app will again say "No subscriptions." Similarly if you wait 5 minutes and hit "Synchronize
receipts" the app will say no subscriptions found, and the "ads" will be back.
Troubleshooting
Let’s not pretend that everything worked for you on the first try. There’s a lot that could go wrong
here. If you make a purchase and nothing appears to happen, the first thing you should do is check
the Network Monitor in the simulator ("Simulate" > "Network" > "Network Monitor"). You should
see a list of network requests. Some will be GET requests and there will be at least one POST
request. Check the response of these requests to see if they succeeded.
Common problems would be that the URL you have set in the client app for endpointURL is incorrect,
or that there is a database connection problem.
Now that we’ve set up and built the app, let’s take a look at the source code so you can see how it all
works.
Client Side
574
from inside my ReceiptStore implementation to load receipts from the web service, and insert new
receipts to the database.
@Override
public void fetchReceipts(SuccessCallback<Receipt[]> callback) {
[Link] query = new [Link]() {
@Override
protected void setupConnectionRequest(RESTfulWebServiceClient client, ConnectionRequest req) {
[Link](client, req);
[Link](receiptsEndpoint);
}
};
[Link](query, rowset->{
List<Receipt> out = new ArrayList<Receipt>();
for (Map m : rowset) {
Result res = [Link](m);
Receipt r = new Receipt();
[Link]([Link]("transactionId"));
[Link](new Date([Link]("purchaseDate")));
[Link](1);
[Link]([Link]("storeCode"));
[Link]([Link]("sku"));
}
[Link]([Link](new Receipt[[Link]()]));
});
}
@Override
public void submitReceipt(Receipt r, SuccessCallback<Boolean> callback) {
Map m = new HashMap();
[Link]("transactionId", [Link]());
[Link]("sku", [Link]());
[Link]("purchaseDate", [Link]().getTime());
[Link]("orderData", [Link]());
[Link]("storeCode", [Link]());
[Link](m, callback);
}
};
}
575
Notice that we are not doing any calculation of expiry dates in our client app, as we did in the
previous post (on non-renewable receipts). Since we are using a server now, it makes sense to move
all of that logic over to the server.
/**
* Creates a REST client to connect to a particular endpoint. The REST client
* generated here will automatically add the Authorization header
* which tells the service what platform we are on.
* @param url The url of the endpoint.
* @return
*/
private RESTfulWebServiceClient createRESTClient(String url) {
return new RESTfulWebServiceClient(url) {
@Override
protected void setupConnectionRequest(ConnectionRequest req) {
try {
[Link]("Authorization", "Basic " + [Link]((getUsername()+":"+getPassword()).
getBytes("UTF-8")));
} catch (Exception ex) {}
}
};
}
Server-Side
On the server-side, our REST controller is a standard JAX-RS REST interface. I used Netbeans web
service wizard to generate it and then modified it to suit my purposes. The methods of the
ReceiptsFacadeREST class pertaining to the REST API are shown here:
@Stateless
@Path("[Link]")
public class ReceiptsFacadeREST extends AbstractFacade<Receipts> {
// ...
@POST
@Consumes({"application/xml", "application/json"})
public void create(Receipts entity) {
// Save the receipt first in case something goes wrong in the validation stage
[Link](entity);
576
// Let's validate the receipt
validateAndSaveReceipt(entity);
// validates the receipt against appropriate web service
// and updates database if expiry date has changed.
}
// ...
@GET
@Override
@Produces({"application/xml", "application/json"})
public List<Receipts> findAll() {
String username = credentialsWithBasicAuthentication(request).getName();
return getEntityManager()
.createNamedQuery("[Link]")
.setParameter("username", username)
.getResultList();
}
}
The magic happens inside that validateAndSaveReceipt() method, which I’ll cover in detail soon.
Notifications
It’s important to note that you will not be notified by apple or google when changes are made to
subscriptions. It’s up to you to periodically "poll" their web service to find if any changes have been
made. Changes we would be interested in are primarily renewals and cancellations. In order to
deal with this, set up a method to run periodically (once-per day might be enough). For testing, I
actually set it up to run once per minute as shown below:
577
}
}
}
That method simply finds all of the receipts in the database that haven’t been validated in some
period of time, and validates it. Again, the magic happens inside the validateAndSaveReceipt()
method which we cover later.
In this example we only validate receipts from the iTunes and Play stores because
those are the only ones that we currently support auto-renewing subscriptions on.
For the purpose of this tutorial, I created a library to handle receipt validation in a way that hides
as much of the complexity as possible. It supports both Google Play receipts and iTunes receipts.
As you can see from this snippet, the complexity of receipt validation has been reduced to entering
three configuration strings:
1. APPLE_SECRET - This is a "secret" string that you will get from iTunes connect when you set up
your in-app products.
2. GOOGLE_DEVELOPER_API_CLIENT_ID - A client ID that you’ll get from the google developer API
console when you set up your API service credentials.
3. GOOGLE_DEVELOPER_PRIVATE_KEY - A PKCS8 encoded string with an RSA private key that you’ll
receive at the same time as the GOOGLE_DEVELOPER_API_CLIENT_ID.
You are now ready to see the full magic of the validateAndSaveReceipt() method in all its glory:
578
/**
* Validates a given receipt, updating the expiry date,
* @param receipt The receipt to be validated
* @param forInsert If true, then an expiry date will be calculated even if there is no validator.
*/
private Receipts[] validateAndSaveReceipt(Receipts receipt) {
EntityManager em = getEntityManager();
Receipts managedReceipt = getManagedReceipt(receipt);
// managedReceipt == receipt if receipt is in database or null otherwise
if (Receipt.STORE_CODE_SIMULATOR.equals([Link]())) { ①
if ([Link]() == null && managedReceipt == null) {
//Not inserted yet and no expiry date set yet
Date dt = calculateExpiryDate([Link](), true);
if (dt != null) {
[Link]([Link]());
}
}
if (managedReceipt == null) {
// Receipt is not in the database yet. Add it
[Link](receipt);
return new Receipts[]{receipt};
} else {
// The receipt is already in the database. Update it.
[Link](managedReceipt);
return new Receipts[]{managedReceipt};
}
} else {
// It's not a simulator receipt
IAPValidator validator = [Link]([Link]());
if (validator == null) {
// Receipt must have come from a platform other than iTunes or Play
// Because there is no validator
}
if (managedReceipt == null) {
[Link](receipt);
return new Receipts[]{receipt};
} else {
[Link](managedReceipt);
return new Receipts[]{managedReceipt};
}
579
[Link](GOOGLE_DEVELOPER_API_CLIENT_ID);
[Link](GOOGLE_DEVELOPER_PRIVATE_KEY);
// Create a dummy receipt with only transaction ID and order data to pass
// to the validator. Really all it needs is order data to be able to validate
Receipt r2 = Receipt();
[Link]([Link]());
[Link]([Link]());
try {
Receipt[] result = [Link](r2);
// Depending on the platform, result may contain many receipts or a single receipt
// matching our receipt. In the case of iTunes, none of the receipt transaction IDs
// might match the original receipt's transactionId because the validator
// will set the transaction ID to the *original* receipt's transaction ID.
// If none match, then we should remove our receipt, and update each of the returned
// receipts in the database.
Receipt matchingValidatedReceipt = null;
for (Receipt r3 : result) {
if ([Link]().equals([Link]())) {
matchingValidatedReceipt = r3;
break;
}
}
if (matchingValidatedReceipt == null) {
// Since the validator didn't find our receipt,
// we should remove the receipt. The equivalent
// is stored under the original receipt's transaction ID
if (managedReceipt != null) {
[Link](managedReceipt);
managedReceipt = null;
}
}
List<Receipts> out = new ArrayList<Receipts>();
// Now go through and
for (Receipt r3 : result) {
if ([Link]() == null) {
// No order data found in receipt. Setting it to the original order data
[Link]([Link]());
}
Receipts eReceipt = new Receipts();
[Link]([Link]());
[Link]([Link]());
Receipts eManagedReceipt = getManagedReceipt(eReceipt);
if (eManagedReceipt == null) {
copy(eReceipt, r3);
[Link]([Link]());
[Link]([Link]());
[Link](eReceipt);
[Link](eReceipt);
} else {
copy(eManagedReceipt, r3);
[Link]([Link]());
[Link]([Link]());
[Link](eManagedReceipt);
580
[Link](eManagedReceipt);
}
}
}
}
}
① We need to handle the case where the app is being used in the CN1 simulator. We’ll treat this as
a non-renewable receipt, and we’ll calculate the expiry date using an "accelerated" clock to
assist in testing.
In many of the code snippets for the Server-side code, you’ll see references to both
a Receipts class and a Receipt class. I know this is slightly confusing. The Receipts
class is a JPA entity the encapsulates a row from the "receipts" table of our SQL
database. The Receipt class is [Link]. It’s used to interface
with the IAP validation library.
In order to test out in-app purchase on an Android device, you’ll need to create an app the Google
Play Developer Console [[Link] I won’t describe the process in this
section, but there is plenty of information around the internet on how to do this. Some useful
references for this include:
You are required to upload some screenshots and feature graphics. Don’t waste time making these
perfect. For the screenshots, you can just use the "Screenshot" option in the simulator. (Use the
Nexus 5 skin). For the feature graphics, I used this site [[Link]
generator/] that will generate the graphics in the correct dimensions for Google Play. You can also just
leave the icon as the default Codename One icon.
581
Creating Test Accounts
You cannot purchase in-app products from your app using your publisher account.
You need to set up at least one test account for the purpose of testing the app.
In order to test your app, you need to set up a test account. A test account must be associated with a
real gmail email address. If you have a domain that is managed by Google apps, then you can also
use an address from that domain.
The full process for testing in-app billing can be found in this google document
[[Link] However, I personally found this
documentation difficult to follow.
For your purposes, you’ll need to set up a tester list in Google Play. Choose "Settings" > "Tester Lists".
Then create a list with all of the email address that you want to have treated as test accounts. Any
purchases made by these email addresses will be treated as "Sandbox" purchases, and won’t
require real money to change hands.
In order to test in-app purchase on Android, you must first publish your app. You can’t just build
and install your app manually. The app needs to be published on the Play store, and it must be
installed through the play store for in-app purchase to work. Luckily you can publish to an Alpha
channel so that your app won’t be publicly available.
For more information about setting up alpha testing on Google play see this Google support
document on the subject [[Link]
Once you have set your app up for alpha testing, you can send an invite link to your test accounts.
You can find the link in the Google Play console under the APK section, under the "Alpha" tab (and
assuming you’ve enabled alpha testing.
The format of the link is [Link] in case you can’t find it.
You can email this to your alpha testers. Make sure that you have added all testers to your tester
lists so that their purchases will be made in the sandbox environment.
Also, before proceeding with testing in-app purchases, you need to add the in-app products in
Google Play.
582
Adding In-App Products
After you have published your APK to the alpha channel, you can create the products. For the
purposes of this tutorial, we’ll just add two products:
Since we will be adding products as "Subscriptions" in the pay store, your app
must use the [Link](sku) method for initiating a purchase on these
products, and not the [Link](sku) method. If you accidentally use
purchase() to purchase a subscription on Android, the payment will go through,
but your purchase callback will receive an error.
2. Click on "In-app Products" in the menu. Then click the "Add New Product" button.
3. Select "Subscription", and enter "[Link]" for the Product ID. Then click
"Continue"
Now fill in the form. You can choose your own price and name for the product. The following is a
screenshot of the options I chose.
583
Figure 434. Add product to google
Follow the same process as for the 1 month subscription except use "[Link]"
for the product ID, and select "3 months" for the billing period instead of "Monthly".
At this point we should be ready to test our app. Assuming you’ve installed the app using the invite
link you sent yourself from Google play, as a test account that is listed on your testers list, you
should be good to go.
Open the app, click on "Subscriptions", and try to purchase a 1-month subscription. If all goes well,
it should insert the subscription into your database. But with no expiry date, since we haven’t yet
implemented receipt validation yet. We’ll do that next.
Google play receipt validation is accomplished via the android-publisher Purchases: get API
[[Link] The CN1-IAP-Validation
library shields you from most of the complexities of using this API, but you still need to obtain a
"private key" and a "client id" to access this API. Both of these are provided when you set up an
OAuth2 Service Account [[Link] for your
app.
The following steps assume that you have already created your app in Google play
and have published it to at least the alpha channel. See my previous post on this
topic here (Link to be provided).
Steps:
1. Open the Google API Developer Console [[Link] and select your
584
App from the the menu.
2. Click on the "Library" menu item in the left menu, and then click the "Google Play Developer
API" link.
3. Click on the button that says "Enable". (If you already have it enabled, then just proceed to the
next step).
5. In the "Credentials" drop-down menu, select the "Service Account Key" option.
6. You will be presented with a new form. In the "Service Account" drop-down, select "New Service
Account". This will give you some additional options.
7. Enter anything you like for the "Service account name". For the role, we’ll select "Project" >
"Owner" for now just so we don’t run into permissions issues. You’ll probably want to
investigate further to fine a more limited role that only allows receipt verification, but for now, I
don’t want any unnecessary road blocks for getting this to work. We’re probably going to run
into "permission denied" errors at first anyways, so the fewer reasons for this, the better.
9. Finally, for the "Key type", select "JSON". Then click the "Create" button.
This should prompt the download of a JSON file that will have contents similar to the following:
585
"type": "service_account",
"project_id": "iapdemo-152500",
"private_key_id": "1b1d39************7d839826b8a",
"private_key": "-----BEGIN PRIVATE KEY----- ... some private key string -----END PRIVATE KEY-----\n",
"client_email": "iapdemo@[Link]",
"client_id": "117601572633333082772",
"auth_uri": "[Link]
"token_uri": "[Link]
"auth_provider_x509_cert_url": "[Link]
"client_x509_cert_url": "[Link]
[Link]"
}
This is where we get the information we’re looking for. The "client_email" is what we’ll use for your
googleClientId, and the "private_key" is what we’ll use for the googlePrivateKey.
Use the "client_email" value as our client ID, not the "client_id" value as you might
be tempted to do.
...
[Link](GOOGLE_DEVELOPER_API_CLIENT_ID);
[Link](GOOGLE_DEVELOPER_PRIVATE_KEY);
Before we can use these credentials to verify receipts for our app, we need to link our app to this
new service account from within Google Play.
Steps:
2. You should see your app listed on this page. Click the "Link" button next to your app.
3. This should reveal some more options on the page. You should see a "Service Accounts" section
with a list of all of the service accounts that you have created. Find the one we just created, and
click the "Grant Access" button in its row.
4. This will open a dialog titled "Add New User". Leave everything default, except change the
"Role" to "Administrator". This provides "ALL" permissions to this account, which probably isn’t
586
a good idea for production. Later on, after everything is working, you can circle back and try to
refine permissions. For the purpose of this tutorial, I just want to pull out all of the potential
road blocks.
At this point, the service account should be active so we can try to validate receipts.
The ReceiptsFacadeREST class includes a flag to enable/disable play store validation. By default it’s
disabled. Let’s enable it:
Then build and run the server app. The validateSubscriptionsCron() method is set to run once per
minute, so we just need to wait for the timer to come up and it should try to validate all of the play
store receipts.
I’m assuming you’ve already added a receipt in the previous test that we did. If
necessary, you should purchase the subscription again in your app.
After a minute or so, you should see "----------- VALIDATING RECEIPTS ---------" written in the Glassfish
log, and it will validate your receipts. If it works, your receipt’s expiry date will get populated in the
database, and you can press "Synchronize Receipts" in your app to see this reflected. If it fails, there
will like be a big ugly stack trace and exception readout with some clues about what went wrong.
Realistically, your first attempt will fail for some reason. Use the error codes and stack traces to
help lead you to the problem. And feel free to post questions here.
The process for setting up and testing your app on iOS is much simpler than on Android (IMHO). It
took me a couple hours to get the iTunes version working, vs a couple days on the Google Play side
of things. One notable difference that makes things simpler is that you don’t need to actually upload
587
your app to the store to test in-app purchase. You can just use your debug build on your device. It’s
also much easier to roll a bunch of test accounts than on Google Play. You don’t need to set up an
alpha program, you just create a few "test accounts" (and this is easy to do) in your iTunes connect
account, and then make sure to use one of these accounts when making a purchase. You can easily
switch accounts on your device from the "Settings" app, where you can just log out of the iTunes
store - which will cause you to be prompted in your app the next time you make a purchase.
The process to add products in iTunes connect is outlined in this apple developer document
[[Link]
iTunesConnectInAppPurchase_Guide/Chapters/[Link]#//apple_ref/doc/uid/
TP40013727-CH3-SW1]. We’ll add our two SKUs:
Just make sure you add them as auto-renewable subscriptions, and that you specify the appropriate
renewal periods. Use the SKU as the product ID. Both of these products will be added to the same
subscription group. Call the group whatever you like.
In order to test purchases, you need to create some test accounts. See this apple document
[[Link]
Chapters/[Link]#//apple_ref/doc/uid/TP40011225-CH25-SW10] for details on how to create
these test accounts. Don’t worry, the process is much simpler than for Android. It should take you
under 5 minutes.
Once you have the test accounts created, you should be set to test the app.
2. Log out from the app store. The process is described here [[Link]
If all went well, you should see the receipt listed in the RECEIPTS table of your database. But the
expiry date will be null. We need to set up receipt verification in order for this to work.
In order for receipt verification to work we simply need to generate a shared secret in iTunes
connect. The process is described here [[Link]
LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/
[Link]].
Once you have a shared secret, update the ReceiptsFacadeREST class with the value:
588
public static final String APPLE_SECRET = "your-shared-secret-here";
If you rebuild and run the server project, and wait for the validateSubscriptionsCron() method to
run, it should validate the receipt. After about a minute (or less), you’ll see the text "-----------
VALIDATING RECEIPTS ---------" written to the Glassfish log file, followed by some output from
connecting to the iTunes validation service. If all went well, you should see your receipt expiration
date updated in the database. If not, you’ll likely see some exception stack traces in the Glassfish
log.
Sandbox receipts in the iTunes store are set to run on an accelerated schedule. A 1
month subscription is actually 5 minutes, 3 months is 15 minutes etc… Also
sandbox subscriptions don’t seem to persist in perpetuity until the user has
cancelled it. I have found that they usually renew only 4 or 5 times before they are
allowed to lapse by Apple.
589
590
Chapter 17. Advanced Topics/Under the
Hood
17.1. Sending Arguments To The Build Server
When sending a build to the server we can provide additional parameters to the build, which are
incorporated into the build process on the server to "hint" on multiple different build time options.
These hints are often referred to as "build hints" or "build arguments", they are effectively very
much like souped up compiler flags that you can use to tune the build server’s behavior. This is
useful for fast iteration on new functionality without building plugin UI for every change. This is
also useful for exposing very low level behavior such as customizing the Android manifest XML or
the iOS plist.
You can set these hints by right clicking the project in the IDE and selecting Codename One →
Codename One Settings → Build Hints . The hints use the key=value style of data.
You can set the build hints in the codenameone_settings.properties file directly notice that when you
do that all settings need to start with the [Link]. prefix. When editing the properties file
directly we would need to define something like [Link]=true as
[Link]=true.
Here is the current list of supported arguments, notice that build hints are added all the time so
consult the discussion forum if you don’t find what you need here:
Name Description
591
Name Description
592
Name Description
593
Name Description
[Link] This is a special case build hint. You can use any
prefix to the build hint and the convention is to
use your cn1lib name. It’s identical to
[Link] with the
exception that the "highest version wins". That
way if your cn1lib requires play services 9+ and
uses: [Link]=9.0.0 and
another library has
[Link]=10.0.0 then
play services will be 10.0.0
594
Name Description
595
Name Description
596
Name Description
597
Name Description
598
Name Description
599
Name Description
600
Name Description
601
Name Description
602
Name Description
603
Name Description
604
Name Description
605
Name Description
606
Name Description
607
Name Description
608
However, sometimes developers might find the permissions that come up a bit confusing and might
not understand why a specific permission came up. This maps Android permissions to the
methods/classes in Codename One that would trigger them. Notice that this list isn’t exhaustive as
the API is rather large:
Starting with Marshmallow (Android 6+ API level 23) Android shifted to a permissions system that
prompts users for permission the first time an API is used e.g. when accessing contacts the user will
receive a prompt whether to allow contacts access.
Permission can be denied and a user can later on revoke/grant a permission via
external settings UI
This is really great as it allows apps to be installed with a single click and no permission prompt
during install which can increase conversion rates!
Enabling Permissions
Codename One’s Gradle 8 based Android builder detects the highest Android SDK you have
installed and uses that value (with a minimum of API 33) for both the compile and target SDK
609
versions, so the modern runtime permission flow is enabled by default. If you override the target
version via the [Link] build hint the builder will honour it, but lowering the
target may disable some compatibility libraries. Keeping the target current is strongly
recommended for Play Store compliance.
Permission Prompts
[Link]();
If you explicitly lower the target SDK (e.g. [Link]=21) and install this app on an
Android 6 device you will still see the legacy install prompt with all permissions listed up front:
When you keep the default target (API 33+) the installer defers to the runtime permission flow and
the install UI looks like this instead:
610
Figure 444. Install UI when using the new permissions system
When we launch the UI under the old permissions system we see the contacts instantly. In the new
system we are presented with this UI:
If we accept and allow all is good and the app loads as usual but if we deny then Codename One
gives the user another chance to request the permission. Notice that in this case you can customize
the prompt string as explained below.
If we select don’t ask then you will get a blank screen since the contacts will return as a 0 length
611
array. This makes sense as the user is aware he denied permission and the app will still function as
expected on a device where no contacts are available. However, if the user realizes his mistake he
can double back and ask to re-prompt for permission in which case he will see this native prompt:
Notice that denying this second request will not trigger another Codename One prompt.
Code Changes
There are no explicit code changes needed for this functionality to "just work". The respective API’s
will work just like they always worked and will prompt the user seamlessly for permissions.
Some behaviors that never occurred on Android but were perfectly legal in the
past might start occurring with the switch to the new API. E.g. the location
manager might be null and your app must always be ready to deal with such a
situation
When permission is requested a user will be seamlessly prompted/warned. You can customize the
permission text via Display properties. E.g. to customize the rationale text of the contacts
permission:
[Link]().setProperty(
"[Link].READ_CONTACTS",
"MyCoolChatApp needs access to your contacts so we can show you which of your friends already have MyCoolChatApp
installed");
The Android port also checks [Link]() for localized values before falling back to
Display properties. You can provide localized strings for the permission body and dialog
buttons/title using keys based on the permission name:
612
For example, if the permission key is [Link].READ_CONTACTS, you can localize these keys:
• [Link].READ_CONTACTS.title
• [Link].READ_CONTACTS.askAgain
• [Link].READ_CONTACTS.dontAsk
This is optional as there is a default value defined. You can define this once in the init(Object)
method but for some extreme cases permission might be needed for different things e.g. you might
ask for this permission with one reason at one point in the app and with a different reason at
another point in the app.
Simulating Prompts
You can simulate permission prompts by checking that option in the simulator menu.
This will produce a dialog to the user whenever this happens in Android and will try to act in a
similar way to the device. Notice that you can test it in the iOS simulator too.
AndroidNativeUtil’s checkForPermission
If you write Android native code using native interfaces you are probably familiar with the
AndroidNativeUtil class from the [Link] package.
This class provides access to many low level capabilities you would need as a developer writing
native code. Since native code might need to request a permission we introduced the same
underlying logic we used namely: checkForPermission.
if () {
613
// you didn't get the permission, you might want to return here
}
// you have the permission, do what you need
This will prompt the user with the native UI and later on with the fallback option as described
above. Notice that the checkForPermission method is a blocking method and it will return when
there is a final conclusion on the subject. It uses invokeAndBlock and can be safely invoked on the
event dispatch thread without concern.
By default, fallback prompts are displayed using Codename One’s [Link](…). If you are
writing native Android code and need to use your own prompt implementation, you can install a
custom callback:
[Link](new PermissionPromptCallback() {
@Override
public boolean showPermissionPrompt(String permission, String title, String body, String positiveButtonText, String
negativeButtonText) {
return [Link](title, body, positiveButtonText, negativeButtonText);
}
@Override
public void showPermissionMessage(String permission, String title, String body, String okButtonText) {
[Link](title, body, okButtonText, null);
}
});
In iOS this is usually strait forward, just open the project with xcode and run it optionally disabling
bitcode. Unzip the .bz2 file and open the .xcworkspace file if it’s available otherwise open the
.xcodeproj file inside the dist directory.
With Android Studio this is sometimes as very easy task as it is possible to actually open the gradle
project in Android Studio and just run it. However, due to the fragile nature of the gradle project
this stopped working for some builds and has been "flaky".
By default you should be able to open the gradle project in Android Studio and just run it. To get
this to work open the Android Studio Setting and select gradle 2.11.
614
Figure 449. Gradle settings UI in Android Studio (notice you need gradle 2.11 and not 2.8 as pictured here)
If this works for you then you can ignore the section below.
In some cases the gradle project might not work or this might fail with a change from Google.
1. Check the include source flag in the IDE and send a build
4. Make sure to use the same package and app name as you did in the Codename One project,
select to not create an activity
5. Unzip the [Link] file and copy the main directory from its src directory to the Android
Studio projects src directory make sure to overwrite files/directories.
7. Copy the source gradle dependencies content to the destination gradle file
8. Connect your device and press the Debug button for the IDE
You might need to copy additional gradle file meta-data such as multi-dexing etc.
You might not need to repeat the whole thing with every build. E.g. it might be practical to only
copy the [Link] from the libs directory to get the latest version of your code. You can copy
the src/main directory to get the latest up to date Android port.
615
17.4.1. Introduction
Notice that when we say "native" we do not mean C/C++ always but rather the platforms "native"
environment. So in the case of Android the Java code will be invoked with full access to the Android
API, in case of iOS an Objective-C message would be sent and so forth.
You can still access C code under Android either by using JNI from the Android
native code or by using a library
Native interfaces are designed to only allow primitive types, Strings, arrays of primitive types
(single dimension only) & PeerComponent [[Link]
[Link]] values. Any other type of parameter/return type is prohibited. However, once in
the native layer the native code can act freely and query the Java layer for additional information.
The reason for the limits is the disparity between the platforms. Mapping a Java
Object to an Objective-C NSObject is possible but leads to odd edge cases and
complexity e.g. GC vs. ARC in a disparate object graph
Furthermore, native methods should avoid features such as overloading, varargs (or any Java 5+
feature for that matter) to allow portability for languages that do not support such features.
2. Creating the proper native implementation hierarchy based on the call conventions for every
platform within the native directory
We now need to right click the class in the IDE and select the Generate Native Access menu item:
616
Figure 450. Generating the native code
Figure 451. Once generated we are prompted that the native code is in the "native" directory
We can now look int the native directory in the project root (in NetBeans you can see that in the
Files tab) and you can see something that looks like this:
Figure 452. Native directory structure containing stubs for the various platforms
These are effectively stubs you can edit to implement the methods in native code.
If you re-run the Generate Native Access tool you will get this dialog, if you answer
yes all the files will be overwritten, if you answer no only files you
deleted/renamed will be recreated
Figure 453. Running "Generate Native Access" when some/all of the native files exist already
617
For now lets leave the stubs and come back to them soon. From the Codename One Java code we
can call the implementation of this native interface using:
MyNative my = [Link]([Link]);
if (my != null && [Link]()) {
Log.p([Link]("Hi"));
}
Notice that for this to work you must implement the native code on all supported platforms.
We’ll start with Android which should be familiar and intuitive to many developers. The helper
MyNativeImplStub class below mirrors the stub that the tooling generates under the native/android
directory:
class MyNativeImplStub {
public String helloWorld(String param) {
return null;
}
The stub implementation always returns false, null or 0 by default. The isSupported also defaults to
false thus allowing us to implement a NativeInterface on some platforms and leave the rest out
without really knowing anything about these platforms.
① Notice that we are using the Android native [Link] class which isn’t accessible from
standard Codename One code
618
③ Notice that there is no constructor and the class is public. It is crucial that the system will be able
to allocate the class without obstruction. You can use a constructor but it can’t have any
arguments and you shouldn’t rely on semantics of construction.
The IDE won’t provide completion suggestions and will claim that there are errors
in the code!
Codename One doesn’t include the native platforms in its bundle e.g. the full
Android SDK or the full xcode Objective-C runtime. However, since the native code
is compiled on the servers (where these runteims are present) this shouldn’t be a
problem
When implementing a non-trivial native interface, send a server build with the
"Include Source" option checked. Implement the native interface in the native IDE
then copy and paste the native code back into Codename One
The implementation of this interface is nearly identical for Android, J2ME & Java SE.
iOS, Android & pretty much any modern OS has an EDT like thread that handles events etc. The
problem is that they differ in their nuanced behavior. E.g. Android will usually respect calls off of
the EDT and iOS will often crash. Some OS’s enforce EDT access rigidly and will throw an exception
when you violate that…
Normally you don’t need to know about these things, hidden functionality within our
implementation bridges between our EDT and the native EDT to provide consistent cross platform
behavior. But when you write native code you need awareness.
Calling into the native EDT includes overhead and it might not be necessary for some features
(e.g. IO, polling etc.). Furthermore, some calls might work well with asynchronous calls while
others might need synchronous results and we can’t know in advance which ones you would
need.
[Link]().runOnUiThread(new Runnable() {
public void run() {
// your native code here...
}
});
619
This will execute the block within run() asynchronously on the native Android UI thread. If you
need synchronous execution we have a special method for Codename One:
[Link](new Runnable() {
public void run() {
// your native code here...
}
});
This blocks in a way that’s OK with the Codename One EDT which is unique to our Android port.
Gradle Dependencies
Integrating a native OS library isn’t hard but it sometimes requires some juggling. Most instructions
target developers working with xcode or Android Studio & you need to twist your head around
them. In Android the steps for integration in most modern libraries include a gradle dependency.
dependencies {
compile '[Link]:intercom-sdk:3.+'
}
Which instantly raises the question: "How in the world do I do that in Codename One"?
Well, it’s actually pretty simple. You can add the build hint:
[Link]=compile '[Link]:intercom-sdk:3.+'
You might need to define the specific version of the Android SDK used and specific version of
Google play services version used. Intercom is pretty sensitive about those and demanded that we
also add:
[Link]=9.8.0
[Link]=25
Once those were defined the native code for the Android implementation became trivial to write
and the library was easy as there were no jars to include.
620
17.4.2. Objective-C (iOS)
When generating the Objective-C code the "Generate Native Sources" tool produces two files:
com_mycompany_myapp_MyNativeImpl.h & com_mycompany_myapp_MyNativeImpl.m.
The .m files are the Objective-C equivalent of .c files and .h files contain the header/include
information. In this case the com_mycompany_myapp_MyNativeImpl.h contains:
#import <Foundation/Foundation.h>
-(NSString*)helloWorld:(NSString*)param;
-(BOOL)isSupported;
@end
#import "com_mycompany_myapp_MyNativeImpl.h"
@implementation com_mycompany_myapp_MyNativeImpl
-(NSString*)helloWorld:(NSString*)param{
return nil;
}
-(BOOL)isSupported{
return NO;
}
@end
#import "com_mycompany_myapp_MyNativeImpl.h"
@implementation com_mycompany_myapp_MyNativeImpl
-(NSString*)helloWorld:(NSString*)param{
NSLog(@"MyApp: %@", param);
return @"Tada";
621
}
-(BOOL)isSupported{
return YES;
}
@end
iOS has a native thread you should use for all calls just like Android. Check out the Native EDT on
Android section above for reference.
On iOS this is pretty similar to Android (if you consider objective-c to be similar). This is used for
asynchronous invocation:
dispatch_async(dispatch_get_main_queue(), ^{
// your native code here...
});
You can use this for synchronous invocation, notice the lack of the a in the dispatch call:
dispatch_sync(dispatch_get_main_queue(), ^{
// your native code here...
});
The problem with the synchronous call is that it will block the caller thread, if the caller thread is
the EDT this can cause performance issues and even a deadlock. It’s important to be very cautious
with this call!
CocoaPods allow us to add a native library dependency to iOS far more easily than Gradle. By
default we target iOS 7.0 or newer which is supported by Intercom only for older versions of the
library. Annoyingly CocoaPods might seem to work but some specific API’s won’t work since it fell
back to an older version… To solve this you have to explicitly define the build hint
[Link]=8.0 to force iOS 8 or newer. You might need to force it to even newer versions as
some libraries force an iOS 9 minimum etc.
Including intercom itself required a single build hint: [Link]=Intercom which you can obviously
extend by using commas to include multiple libraries. You can search the cocoapods website
[[Link] for supported 3rd party libraries which includes everything you would expect.
One important advantage when working with CocoaPods is the faster build time as the upload to
the Codename One website is smaller and the bandwidth we have to CocoaPods is faster. Another
advantage is the ability to keep up with the latest developments from the library providers.
622
17.4.3. Javascript
Native interfaces in Javascript look a little different than the other platforms since Javascript
doesn’t natively support threads or classes. The native implementation should be placed in a file
with name matching the name of the package and the class name combined where the "." elements
are replaced by underscores.
The default generated stubs for the JavaScript build look like this com_mycompany_myapp_MyNative:
(function(exports){
var o = {};
o.isSupported_ = function(callback) {
[Link](false);
};
exports.com_mycompany_myapp_MyNative= o;
})(cn1_get_native_interfaces());
(function(exports){
var o = {};
o.isSupported_ = function(callback) {
[Link](true);
};
exports.com_my_code_MyNative = o;
})(cn1_get_native_interfaces());
Notice that we use the complete() method of the provided callback to pass the return value rather
than using the return statement. This is to work around the fact that Javascript doesn’t natively
support threads. The Java thread that is calling your native interface will block until your method
calls [Link](). This allows you to use asynchronous APIs inside your native method
while still allowing Codename One to work use your native interface via a synchronous API.
623
Make sure you call either [Link]() or [Link]() in your method
at some point, or you will cause a deadlock in your app (code calling your native
method will just sit and "wait" forever for your method to return a value).
The naming conventions for the methods themselves are modeled after the naming conventions
shown in the previous examples:
<method-name>__<param-1-type>_<param-2-type>_…<param-n-type>
Where <method-name> is the name of the method in Java, and the `<param-X-type>`s are a string
representing the parameter type. The general rule for these strings are:
1. Primitive types are mapped to their type name. (E.g. int to "int", double to "double", etc…).
2. Reference types are mapped to their fully-qualified class name with '.' replaced with
underscores. E.g. [Link] would be "java_lang_String".
3. Array parameters are marked by their scalar type name followed by an underscore and
"1ARRAY". E.g. int[] would be "int_1ARRAY" and String[] would be "java_lang_String_1ARRAY".
JavaScript Examples
Java API:
becomes
Java API:
interface JavaScriptNativeApiAddTwoArgs {
int add(int a, int b);
}
becomes
624
interface JavaScriptNativeApiAddArray {
int add(int[] a);
}
becomes
interface NativePeerProvider {
PeerComponent createPeer();
}
class AndroidPeerImplementation {
public View createPeer() {
return null;
}
}
- (UIView*)createPeer;
Not all platforms support native peers. Specifically JavaSE doesn’t support them
due to the way the JavaSE native interfaces are mapped to their implementation.
Note that this won’t limit the code from running on an unsupported platform. Only
that specific method won’t work.
Javascript would expect a DOM Element (e.g. a <div> tag to be returned.). E.g.
625
o.createHelloComponent_ = function(callback) {
var c = jQuery('<div>Hello World</div>')
.css({'background-color' : 'yellow', 'border' : '1px solid blue'});
[Link]([Link](0));
};
Notice that if you want to use a native library (jar, .a file etc.) just places it within the appropriate
native directory and it will be packaged into the final executable. You would only be able to
reference it from the native code and not from the Codename One code, which means you will need
to build native interfaces to access it.
Several rules govern the creation of NativeInterfaces and we only briefly covered some of them.
• The implementation class must have a default public constructor or no constructor at all
• A native method can’t have the name init as this is a reserved method in Objective-C
• Native implementations can’t rely on pass by reference/value semantics as those might change
between platforms
• hashCode, equals & toString are reserved and won’t be mapped to native code
626
Java Android JavaSE Obj-C C#
JavaScript is excluded from the table above as it isn’t a type safe language and thus
has no such type mapping
The examples below demonstrate the signatures for this method on all platforms:
interface AllTypesNativeApi {
void test(byte b, boolean boo, char c, short s,
int i, long l, float f, double d, String ss,
byte[] ba, boolean[] booa, char[] ca, short[] sa, int[] ia,
long[] la, float[] fa, double[] da,
PeerComponent cmp);
}
class AndroidAllTypesImplementation {
public void test(byte param, boolean param1, char param2,
short param3, int param4, long param5, float param6,
double param7, String param8, byte[] param9,
boolean[] param10, char[] param11, short[] param12,
int[] param13, long[] param14, float[] param15,
double[] param16, [Link] param17) {
}
}
-(void)test:(char)param param1:(BOOL)param1
param2:(int)param2 param3:(short)param3 param4:(int)param4
param5:(long long)param5 param6:(float)param6
param7:(double)param7 param8:(NSString*)param8
param9:(NSData*)param9 param10:(NSData*)param10
param11:(NSData*)param11 param12:(NSData*)param12
param13:(NSData*)param13 param14:(NSData*)param14
627
param15:(NSData*)param15 param16:(NSData*)param16
param17:(void*)param17;
We had to break lines for the print version, the JavaScript version is a really long
method name that literally broke the book!
o.test__byte_boolean_char_short_int_long_float_double
_java_lang_String_byte_1ARRAY_boolean_1ARRAY_char_1ARRAY
_short_1ARRAY_int_1ARRAY_long_1ARRAY_float_1ARRAY_double
_1ARRAY_com_codename1_ui_PeerComponent = function(param1, param2, param3, param4, param5, param6, param7, param8, param9,
param10, param11, param12, param13, param14, param15, param16, param17, param18, callback) {
[Link](new Error("Not implemented yet"));
};
class JavaScriptAllTypesImplementation {
public void test(byte param, boolean param1, char param2, short param3, int param4,
long param5, float param6, double param7, String param8, byte[] param9,
boolean[] param10, char[] param11, short[] param12, int[] param13,
long[] param14, float[] param15, double[] param16,
PeerComponent param17) {
}
}
public void test(byte param, bool param1, char param2, short param3, int param4, long param5, float param6, double
param7, String param8, byte[] param9, boolean[] param10, char[] param11, short[] param12, int[] param13, long[] param14,
float[] param15, double[] param16, FrameworkElement param17) {
}
Normally permissions in Codename One are seamless. Codename One traverses the bytecode and
automatically assigns permissions to Android applications based on the API’s used by the
developer.
However, when accessing native functionality this just won’t work since native code might require
specialized permissions and we don’t/can’t run any serious analysis on it (it can be just about
anything).
So if you require additional permissions in your Android native code you need to define them in
the build arguments using [Link].<PERMISSION_NAME>=true for each permission you
want to include. A full list of permissions are listed in Android’s [Link]
documentation [[Link]
E.g.
628
[Link].ADD_VOICEMAIL=true
[Link].BATTERY_STATS=true
...
You can specify the maximum SDK version in which the permission is needed using the
[Link].<PERMISSION_NAME>.maxSdkVersion build hint. You can also specify whether the
permission is required for the app to run using the [Link].<PERMISSION_NAME>.required
build hint.
E.g.
[Link].ADD_VOICEMAIL=true
[Link].BATTERY_STATS=true
[Link].ADD_VOICEMAIL.required=false
[Link].ADD_VOICEMAIL.maxSdkVersion=18
...
You can alternatively use the [Link] build hint to inject <uses-permission> tags into
the manifest file. E.g.:
You need to include the full XML snippet. You can unify multiple lines into a single
line in the GUI as XML allows that.
If you do any native interfaces programming in Android you should be familiar with the
AndroidNativeUtil class which allows you to access native device functionality more easily from the
native code. E.g. many Android API’s need access to the Activity which you can get by calling
[Link]().
The native util class includes quite a few other features such as:
629
not render properly and require custom code to implement the transferal to a native Bitmap,
this API allows you to do just that.
You can work with AndroidNativeUtil using native code such as this:
class NativeCallsImpl {
public void nativeMethod() {
[Link]().runOnUiThread(new Runnable() {
public void run() {
// ...
}
});
}
// ...
}
A common way to implement features in Android is the BroadcastReceiver API. This allows
intercepting operating system events for common use cases.
A good example is intercepting incoming SMS which is specific to Android so we’d need a
broardcast receiver to implement that. This is often confusing to developers who sometimes derive
the impl class from broadcast receiver. That’s a mistake…
The solution is to place any native Android class into the native/android directory. It will get
compiled with the rest of the native code and "just works". So you can place this class under
native/android/com/codename1/sms/intercept:
@Override
public void onReceive(Context cntxt, Intent intent) {
if ([Link]().equals("[Link].SMS_RECEIVED")) {
Bundle bundle = [Link]();
SmsMessage[] msgs = null;
if (bundle != null) {
try {
Object[] pdus = (Object[]) [Link]("pdus");
msgs = new SmsMessage[[Link]];
for (int i = 0; i < [Link]; i++) {
msgs[i] = [Link]((byte[]) pdus[i]);
String msgBody = msgs[i].getMessageBody();
[Link](msgBody);
}
} catch (Exception e) {
Log.e(e);
[Link](e);
}
630
}
}
}
}
The code above is pretty standard native Android code, it’s just a callback in which most of the logic
is similar to the native Android code mentioned in this stackoverflow question
[[Link]
manifeststat].
But there is still more you need to do. In order to implement this natively we need to register the
permission and the receiver in the [Link] file as explained in that question. This is how their
native manifest looked:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="[Link]" />
<category android:name="[Link]" />
</intent-filter>
</activity>
<receiver android:name="[Link]"
android:enabled="true"
android:permission="[Link].BROADCAST_SMS"
android:exported="true">
<intent-filter android:priority="2147483647">//this doesnt work
<category android:name="[Link]" />
<action android:name="[Link].SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
We only need the broadcast permission XML and the permission XML. Both are doable via the build
hints. The former is pretty easy:
631
[Link]=<uses-permission android:name="[Link].RECEIVE_SMS" />
The latter isn’t much harder, notice I took multiple lines and made them into a single line for
convenience:
<receiver android:name="[Link]"
android:enabled="true"
android:permission="[Link].BROADCAST_SMS"
android:exported="true">
<intent-filter android:priority="2147483647">
<category android:name="[Link]" />
<action android:name="[Link].SMS_RECEIVED" />
</intent-filter>
</receiver>
You will notice that these don’t include the actual binding or permission prompts you would expect
for something like this. To do this we need a native interface.
The native sample in stack overflow bound the listener in the activity but here we want the app
code to decide when we should bind the listening:
That’s easy!
Notice that isSupported() returns false for all other OS’s so we won’t need to ask whether this is
"Android" we can just use isSupported().
632
IntentFilter filter = new IntentFilter();
[Link]("[Link].SMS_RECEIVED");
[Link]().registerReceiver(smsListener, filter); ②
}
}
① This will trigger the permission prompt on Android 6 and newer. Even though the permission is
declared in XML this isn’t enough for 6+. Notice that even when you run on Android 6 you still
need to declare permissions in XML!
② Here we actually bind the listener, this allows us to grab one SMS and not listen in on every SMS
coming thru
Native interfaces standardize the invocation of native code from Codename One, but it doesn’t
standardize the reverse of callbacks into Codename One Java code. The reverse is naturally more
complicated since its platform specific and more error prone.
A common "trick" for calling back is to just define a static method and then trigger it from native
code. This works nicely for Android, Java SE, Blackberry & Java ME since those platforms use Java
for their "native code". Mapping this to iOS requires some basic understanding of how the iOS VM
works.
For the purpose of this explanation lets pretend we have a class called NativeCallback in the src
hierarchy under the package [Link] that has the method: public static void callback().
633
}
So if I want to call it from Android or all of the Java based platforms I can just write this in the
"native" code:
#include "com_mycompany_NativeCallback.h"
#include "CodenameOne_GLViewController.h"
com_mycompany_NativeCallback_callback__(CN1_THREAD_STATE_PASS_SINGLE_ARG);
The VM passes the thread context along method calls to save on API calls (thread context is heavily
used in Java for synchronization, gc and more).
634
public static void callback(int arg) {
// do stuff
}
com_mycompany_NativeCallback_callback___int(CN1_THREAD_GET_STATE_PASS_ARG intValue);
Notice that there is no comma between the CN1_THREAD_GET_STATE_PASS_ARG and the value!
Why No Comma?
The comma is included as part of the macro which makes for code that isn’t as readable.
[1]
The reason for this dates to the migration from XMLVM to the current ParparVM
implementation. CN1_THREAD_GET_STATE_PASS_ARG is defined as nothing in XMLVM since
it didn’t use that concept. Yet under ParparVM it will include the necessary comma.
A common use case is passing string values to the Java side, or really NSString* which is iOS
equivalent. Assuming a method like this:
You would need to convert the NSString* value you already have to a [Link] which the
callback expects.
The fromNSString function also needs this special argument so you will need to modify the method
as such:
com_mycompany_NativeCallback_callback___java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG fromNSString
(CN1_THREAD_GET_STATE_PASS_ARG nsStringValue));
And finally you might want to return a value from callback as such:
This is tricky since the method name changes to support covariant return types and so the
signature would be:
635
com_mycompany_NativeCallback_callback___int_R_int(intValue);
The upper case R allows us to differentiate between void callback(int,int) and int callback(int).
Covariant return types are a little known Java 5 feature. E.g. the method Object
getX() can be overriden by MyObject getX(). However, in the VM level they can
both exist side by side.
The mechanism for invoking static callback methods from Javascript (for the Javascript port only) is
similar to Objective-C’s. The this object in your native interface method contains a property named
$GLOBAL$ that provides access to static java methods. This object will contain Javascript mirror
objects for each Java class (though the property name is mangled by replacing "." with
underscores). Each mirror object contains a wrapper method for its underlying class’s static
methods where the method name follows the same naming convention as is used for the Javascript
native methods themselves (and very similar to the naming conventions used in Objective-C).
For example, the Google Maps project includes the static callback method:
class MapContainerCallbacks {
static void fireMapChangeEvent(int mapId, final int zoom, final double lat, final double lon) {
// ...
}
}
This method is called from Javascript inside a native interface using the following code:
In this example we first obtain a reference to the fireMapChangeEvent method, and then call it later.
However, we could have called it directly also.
636
Callbacks of the SMS Receiver
The SMS Broadcast Receiver code from before also used callbacks such as this:
package [Link]; ①
import [Link];
import [Link];
import static [Link].*;
class SMSCallback {
static SuccessCallback<String> onSuccess;
static FailureCallback onFail;
① Notice that the package is the same as the native code and the other classes. This allows the
callback class to be package protected so it isn’t exposed via the API (the class doesn’t have the
public modifier)
② We wrap the callback in call serially to match the Codename One convention of using the EDT by
default. The call will probably arrive on the Android native thread so it makes sense to
normalize it and not expose the Android native thread to the user code
One of the problematic aspects of calling back into Java from Javascript is that Javascript has no
637
notion of multi-threading. Therefore, if the method you are calling uses Java’s threads at all (e.g. It
includes a wait(), notify(), sleep(), callSerially(), etc…) you need to call it asynchronously from
Javascript. You can call a method asynchronously by appending $async to the method name. E.g.
With the Google Maps example above, you would change :
this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double;
to
this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double$async;
This will cause the call to be wrapped in the appropriate bootstrap code to work properly with
threads - and it is absolutely necessary in cases where the method may use threads of any kind. The
side-effect of calling a method with the $async suffix is that you can’t use return values from the
method.
In most cases you should use the async version of a method when calling it from
your native method. Only use the synchronous (default) version if you are
absolutely sure that the method doesn’t use any threading primitives.
The first source of confusion is changing the classpath. You should NEVER change the classpath or
add an external JAR via the IDE classpath UI. The reasoning here is very simple, these IDE’s don’t
package the JAR’s into the final executable and even if they did these JAR’s would probably use
features unavailable or inappropriate for the device (e.g. [Link] etc.).
Figure 454. Don’t change the classpath, this is how it should look for a typical Java 8 Codename One
application
Cn1libs are Codename One’s file format for 3rd party extensions. It’s physicially a zip file
containing other zip files and some meta-data.
638
17.5.1. Why Not Use JAR?
A jar can be compiled with usage of any Java API that might not be supported, it can be compiled
with a Java target version that isn’t tested.
Jars don’t include support for writing native code, you could use JNI in jars (awkwardly) but that
doesn’t match Codename One’s needs for native support (see section above).
Jars don’t support "proper" code completion, a common developer trick is to stick source code into
the jar but that prevents usage with proprietary code. Cn1libs provide full IDE code completion
(with JavaDoc hints) without exposing the sources.
There are two use cases for wanting JAR’s and they both have very different solutions:
1. Modularity
Cn1lib’s address the modularity aspect allowing you to break that down. Existing jars can
sometimes be used native code settings but for the most part you would want to adapt the code to
abide by Codename One restrictions.
Once refreshed the content of the cn1lib will be available to code completion and you could just use
it.
Notice that some cn1libs require additional configurations such as build hints etc.
so make sure to read the developers instructions when integrating a 3rd party
library.
Refresh cn1lib files invokes the ant task refresh-libs. You could automatically trigger a
refresh as part of your build process by invoking that ant task manually.
Technically that task invokes a custom task that unzips the content of the cn1lib into a set of
639
directories accessible to the build process. Classes and stub sources are installed in
lib/impl/cls & lib/impl/stubs respectively.
The native files are extracted to lib/impl/native. The classpath for the main project and the
ant build process know about these directories and include them within their path.
Creating a cn1lib is trivial, we will get into more elaborate uses soon enough but for a hello world
cn1lib we can just use this 2 step process:
Figure 457. Select the file name/destination. Notice that a Java 8 cn1lib requires Java 8 support in the parent
project!
Once we go thru these steps we can define any source file within the library and it will be
accessible to the users of the library.
Some cn1libs are pretty simple to install, just place them under the lib directory and refresh.
However, many of the more elaborate cn1libs need some pretty complex configurations. This is the
case when native code is involved where we need to add permissions or plist entries for the various
native platforms to get everything to work. This makes the cn1lib’s helpful but less than seamless
which is where we want to go.
Codename One cn1libs include two files that can be placed into the root:
codenameone_library_required.properties & codenameone_library_appended.properties.
In these files you can just write a build hint as [Link]=… for the various
hints.
Notice the usage of the properties syntax for the build hint with the [Link]
prefix you would also need to escape reserved characters for properties files.
640
The best way to discover the right syntax for such build hints is to set them via the
build hints GUI in a regular project and copy/paste them from
codenameone_settings.properties into the cn1lib file.
Required build hints can be something like [Link]=true which we want to always work. E.g. if a
cn1lib defines [Link]=true and another cn1lib defines [Link]=false things won’t work since one
cn1lib won’t get what it needs…
In this case we’d want the build to fail so we can remove the faulty cn1lib.
If two cn1libs define [Link]=true there will be no collision as the value would be
identical
Notice that this can still collide e.g. if a different cn1lib defines its own background mode. However,
there are many valid cases where [Link] can be used for other things. In this case we’ll
append the content of the [Link] into the build hint if it’s not already there.
• Properties are merged with every "refresh libs" call not dynamically on the server. This means
it should be pretty simple for the developer to investigate issues in this process.
• Changing flags is problematic - there is no "uninstall" process. Since the data is copied into the
codenameone_settings.properties file. If you need to change a flag later on you might need to
alert users to make changes to their properties essentially negating the value of this feature…
So be very careful when adding properties here.
It’s your responsibility as a library developer to decide which build hint goes into which file!
Codename One can’t automate this process as the whole process of build hints is by definition an ad
hoc process.
The rule of thumb is that a build hint with a numeric or boolean value is always a required
property. If an entry has a string that you can append with another string then its probably an
appended entry.
[Link]
[Link]
[Link]
[Link]
android.stack_size
android.statusbar_hidden
[Link]
641
[Link]
[Link]
[Link]
[Link]
android.supportV4
[Link]
android.cusom_layout1
[Link]
[Link]
[Link]
[Link]
android.min_sdk_version
[Link]
[Link]
[Link]
[Link]
android.web_loading_hidden
[Link]
[Link]
[Link]
ios.project_type
[Link]
ios.prerendered_icon
ios.application_exits
[Link]
ios.xcode_version
javascript.inject_proxy
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
642
[Link]
noExtraResources
[Link]
[Link].<PERMISSION_NAME>
[Link]
[Link]
android.xintent_filter
android.facebook_permissions
[Link]
[Link]
[Link]
android.xapplication_attr
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
ios.interface_orientation
[Link]
ios.facebook_permissions
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
ios.add_libs
The cb1lib file format is quite simple, it’s a zip file containing zip files within it with fixed
names to support the various features.
The table below covers the files that can/should be a part of a cn1lib file:
643
File Name Required Purpose
If you need to integrate such a library into your native calls you have the following options:
1. The first option (and the easiest one) is to just place a Jar file in the native/android directory.
This will link your binary with the jar file. Just place the jar under the native/android and the
build server will pick it up and will add it to the classpath.
Notice that Android release apps are obfuscated by default which might cause issues with such
libraries if they reference API’s that are unavailable on Android. You can workaround this by
adding a build hint to the proguard obfuscation code that blocs the obfuscation of the
problematic classes using the build hint:
[Link]=-keep class [Link] { *; }`
2. Another option is the aar file is a binary format Google introduced to represent an Android
Library project (similarly to the cn1lib format). One of the problem with the Android Library
projects was the fact that it required the project sources which made it difficult for 3rd party
vendors to publish libraries.
As a result so android introduced the aar file which is a binary format that represents a Library
project. To learn more about arr you can read this [[Link]
[Link]#aar-contents].
You can link an aar file by placing it under the native/android and the build server will link it to
the project.
3. There is another obsolete approach that we are mentioning for legacy purposes (e.g. if you
644
need to port code written with this legacy option). This predated the aar option from Google…
Not all 3rd party tools can be packaged as a simple jar, some 3rd party tools need to declare
activities add permissions, resources, assets, and/or even add native code (.so files).
To link a Library project to your Codename One project open the Library project in Eclipse or
Android Studio and make sure the project builds, after the project was built successfully remove
the bin directory from the project and zip the whole project.
Rename the extension from .zip to .andlib and place the andlib file under the native/android
directory. The build server will pick it up and will link it to the project.
In Codename One only components can be dragged and drop targets are always components. The
logic of actually performing the operation indicated by the drop is the responsibility of the person
implementing the drop.
Some platforms e.g. AWT allow dragging abstract concepts such as mime type
elements. This allows dragging things like a text file into the app, but that use case
isn’t realistic in mobile
The code below allows you to rearrange the items based on a sensible order. Notice it relies on the
default Container drop behavior.
645
Figure 458. Drag and drop demo
When dragging on top of a child component of a drop target the code recursively searches for a
drop target parent. Dropping a component on the child will automatically find the right drop target,
hence there is no need to make "everything" into a drop target.
• getDragImage - this generates an image preview of the component that will be dragged. This
automatically generates a sensible default so you don’t need to override it.
• drawDraggedImage - this method will be invoked to draw the dragged image at a given location, it
might be useful to override it if you want to display some drag related information such an
additional icon based on location etc. (e.g. a move/copy icon).
• draggingOver - returns true if a drop operation at this point is permitted. Otherwise releasing the
component will have no effect.
• dragEnter/Exit - useful to track and cleanup state related to dragging over a specific component.
• drop - the logic for dropping/moving the component must be implemented here!
To customize the colors of the native ActionBar on Lollipop define a [Link] file in the
native/android directory of your project. It should look like this:
<resources>
<color name="colorPrimary">#ff00ff00</color>
<color name="colorPrimaryDark">#80ff0000</color>
646
<color name="colorAccent">#800000ff</color>
</resources>
In Android we can use intents which are pretty elaborate and can be used via [Link],
however what if you would like to expose the functionality of your application to a different
application running on the device. This would allow that application to launch your application.
This isn’t something we builtin to Codename One, however it does expose enough of the platform
capabilities to enable that functionality rather easily on Android.
On Android we need to define an intent filter which we can do using the android.xintent_filter
build hint, this accepts the XML to filter whether a request is relevant to our application:
You can read more about it in this stack overflow question [[Link]
android-ios-custom-uri-protocol-handling].
To bind the myapp:// URL to your application. As a result typing myapp://x into the Android browser
will launch the application.
You can access the value of the URL that launched the app using:
647
}
}
This value would be null if the app was launched via the icon.
iOS is practically identical to Android with some small caveats, iOS’s equivalent of the manifest is
the plist.
You can inject more data into the plist by using the [Link] build hint.
However, that can conflict with the Facebook integration if you use FacebookConnect which needs
access to the schemes. To workaround it you can use the build hint [Link] e.g.:
[Link]=<string>myapp</string>
Codename One draws all of its widgets on its own, this is a concept which was modeled in part after
Swing. This allows functionality that can’t be achieved in native widget platforms:
1. The Codename One GUI builder & simulator are almost identical to the device - notice that this
also enables the build cloud, otherwise device specific bugs would overwhelm development and
make the build cloud redundant.
2. Ability to override everything - paint, pointer, key events are all overridable and replaceable.
Developers can also paint over everything e.g. glasspane and layered pane.
3. Consistency - provides identical functionality on all platforms for the most part.
This all contributes to our ease of working with Codename One and maintaining Codename One.
More than 95% of Codename One’s code is in Java hence its really portable and pretty easy to
maintain!
We need the native device to do input, html rendering etc. these are just too big and too complex
tasks for Codename One to do from scratch.
648
They are sometimes impossible to perform without the native platform. E.g. the virtual keyboard
input on the devices is tied directly to the native text input. It’s impractical to implement everything
from scratch for all languages, dictionaries etc. The result would be sub-par.
A web browser can’t be implemented in this day and age without a JavaScript JIT and including a
JIT within an iOS app is prohibited by Apple.
Codename One does pretty much everything on the EDT (Event Dispatch Thread), this provides a lot
of cool features e.g. modal dialogs, invokeAndBlock etc.
This effectively means that all peer components are drawn on top of the Codename One
components.
Codename One grabs a screenshot of the peer, hide it and then we can just show the screenshot.
Since the screenshot is static it can be rendered via the standard UI. Naturally we can’t do that
always since grabbing a screenshot is an expensive process on all platforms and must be performed
on the native device thread.
Since the form title/footer etc. are drawn by Codename One the peer component might paint itself
on top of them. Clipping a peer component is often pretty difficult. Furthermore, if the user drags
his finger within the peer component he might trigger the native scroll within the might collide
with our scrolling?
There is also another problem that might be counter intuitive. iOS has screenshot images
representing the first form. If your first page is an HTML or a native map (or other peer widget) the
screenshot process on the build server will show fallback code instead of the real thing thus
providing sub-par behavior.
Its impractical to support something like HTML for the screenshot process since it would also look
completely different from the web component running on the device.
649
You can read more about the screenshot process here [[Link]
manual/[Link]#section-ios-screenshots].
Before we begin, we’ll need to review the Android and iOS SDKs.
When designing the Codename One API, we should begin by looking at the Javadocs
[[Link]
for the native Android SDK. If the class hierarchy doesn’t look too elaborate, we may decide to
model our Codename One public API fairly closely on the Android API. On the other hand, if we
only need a small part of the SDK’s functionality, we may choose to create my abstractions around
just the functionality that we need.
In the case of the FreshDesk SDK, it looks like most of the functionality is handled by one central
class Mobihelp, with a few other POJO classes for passing data to and from the service. This is a good
candidate for a comprehensive Codename One API.
Before proceeding, we also need to look at the iOS API to see if there are any features that aren’t
included. While naming conventions in the iOS API are a little different than those in the Android
API, it looks like they are functionally the same.
Therefore, I choose to create a class hierarchy and API that closely mirrors the Android SDK.
A Codename One library that wraps a native SDK, will generally consist of the following:
1. Public Java API, consisting of pure Java classes that are intended to be used by the outside
650
world.
2. Native Interface(s). The Native Interface(s) act as a conduit for the public Java API to
communicate to the native SDK. Parameters in native interface methods are limited to primitive
types, arrays of primitive types, and Strings, as are return values.
3. Native code. Each platform must include an implementation of the Native Interface(s). These
implementations are written in the native language of the platform (e.g. Java for Android, and
Objective-C for iOS).
4. Native dependencies. Any 3rd party libraries required for the native code to work, need to be
included for each platform. On android, this may mean bundling .jar files, .aar files, or .andlib
files. On iOS, this may mean bundling .h files, .a files, .framework, and .bundle files.
5. Build hints. Some libraries will require you to add some extra build hints to your project. E.g.
On Android you may need to add permissions to the manifest, or define services in the
<Application> section of the manifest. On iOS, this may mean specifying additional core
frameworks for inclusion, or adding build flags for compilation.
Figure 459. Relationship between native & Codename One API UML Diagram
In the specific case of our FreshDesk API, the public API and classes will look like:
651
Figure 460. Freshdesk API Integration
Things to Notice
2. The only way for the public API to communicate with the native SDK is via the MobihelpNative
[[Link]
[Link]] interface.
We have already looked at the final product of the public API in the previous step, but let’s back up
and walk through the process step-by-step.
I wanted to model my API closely around the Android API, and the central class that includes all of
the functionality of the SDK is the [Link] class
[[Link] so we
begin there.
We’ll start by creating our own package ([Link]) and our own Mobihelp class
inside it.
652
Adapting Method Signatures
Hence, the method signature public static final void setUserFullName (Context context, String
name) will simply become public static final void setUserFullName (String name) in our public
API.
Non-Primitive Parameters
Although our public API isn’t constrained by the same rules as our Native Interfaces with respect to
parameter and return types, we need to be cognizant of the fact that parameters we pass to our
public API will ultimately be funnelled through our native interface. Therefore, we should pay
attention to any parameters or return types that can’t be passed directly to a native interface, and
start forming a strategy for them. E.g. consider the following method signature from the Android
Mobihelp class:
We’ve already decided to just omit the Context parameter in our API, so that’s a non-issue. But what
about the ArrayList<String> tags parameter? Passing this to our public API is no problem, but when
we implement the public API, how will we pass this ArrayList to our native interface, since native
interfaces don’t allow us to arrays of strings as parameters?
1. Encode the parameter as either a single String (e.g. using JSON or some other easily parseable
format) or a byte[] array (in some known format that can easily be parsed in native code).
2. Store the parameter on the Codename One side and pass some ID or token that can be used on
the native side to retrieve the value.
3. If the data structure can be expressed as a finite number of primitive values, then simply design
the native interface method to take the individual values as parameters instead of a single
object. E.g. If there is a User [[Link]
class with properties name and phoneNumber, the native interface can just have name and
phoneNumber parameters rather than a single `user parameter.
In this case, because an array of strings is such a simple data structure, I decided to use a variation
on strategy number 1: Merge the array into a single string with a delimiter.
In any case, we don’t have to come up with the specifics right now, as we are still on the public API,
653
but it will pay dividends later if we think this through ahead of time.
Callbacks
It is quite often the case that native code needs to call back into Codename One code when an event
occurs. This may be connected directly to an API method call (e.g. as the result of an asynchronous
method invocation), or due to something initiated by the operating system or the native SDK on its
own (e.g. a push notification, a location event, etc..).
Native code will have access to both the Codename One API and any native APIs in your app, but on
some platforms, accessing the Codename One API may be a little tricky. E.g. on iOS you’ll be calling
from Objective-C back into Java which requires knowledge of Codename One’s java-to-objective C
conversion process. In general, I have found that the easiest way to facilitate callbacks is to provide
abstractions that involve static java methods (in Codename One space) that accept and return
primitive types.
In the case of our Mobihelp class, the following method hints at the need to have a "callback plan":
class AppArgSnippet {
public void readArgument() {
String arg = [Link]().getProperty("AppArg", null);
}
}
I.e. If we were to implement this method (which I plan to do), we need to have a way for the native
code to call the [Link]() method of the passed parameter.
2. How to call the [Link]() method from native code at the right time.
For the first issue, we’ll use strategy #2 that we mentioned previously: (Store the parameter on the
Codename One side and pass some ID or token that can be used on the native side to retrieve the
value).
For the second issue, we’ll create a static method that can take the token generated to solve the first
issue, and call the stored callback object’s onResult() method. We abstract both sides of this process
using the MobihelpNativeCallback class [[Link]
demo/src/com/codename1/freshdesk/[Link]].
654
void onResult(MobihelpCallbackStatus status, Integer count);
}
3. The fireUnreadUpdatesCallback() method would be called later from native code. Its first
parameter is the token for the callback to call.
4. We wrap the onResult() call inside a [Link]() invocation to ensure that the
callback is called on the EDT. This is a general convention that is used throughout Codename
One, and you’d be well-advised to follow it. Event handlers should be run on the EDT unless
there is a good reason not to - and in that case your documentation and naming conventions
should make this clear to avoid accidentally stepping into multithreading hell!
Initialization
Most Native SDKs include some sort of initialization method where you pass your developer and
application credentials to the API. When I filled in FreshDesk’s web-based form to create a new
application, it generated an application ID, an app "secret", and a "domain". The SDK requires me to
pass all three of these values to its init() method via the MobihelpConfig class.
Note, however, that FreshDesk (and most other service provides that have native SDKs) requires me
to create different Apps for each platform. This means that my App ID and App secret will be
different on iOS than they will be on Android.
Therefore our public API needs to enable us to provide multiple credentials in the same app, and
our API needs to know to use the correct credentials depending on the device that the app is
running on.
There are many solutions to this problem, but the one I chose was to provide two different init()
methods:
public static void fireUnreadUpdatesCallback(int callbackId, final int status, final int count) {
final UnreadUpdatesCallback cb = [Link](callbackId);
655
if (cb != null) {
[Link](callbackId);
[Link]().callSerially(new Runnable() {
});
}
}
}
and
656
The Native Interface
The final native interface is nearly identical to our public API, except in cases where the public API
included non-primitive parameters.
//Retrieve the number of unread items across all the conversations for the user asynchronously
public final static void getUnreadCountAsync(UnreadUpdatesCallback callback) {
// ...
}
//Initialize the Mobihelp support section with necessary app configuration.
public final static void initAndroid(MobihelpConfig config) {
// ...
}
Notice also, that the native interface includes a set of methods with names prefixed with config__.
This is just a naming conventions I used to identify methods that map to the MobihelpConfig class. I
657
could have used a separate native interface for these, but decided to keep all the native stuff in one
class for simplicity and maintainability.
So we have a public API, and we have a native interface. The idea is that the public API should be a
thin wrapper around the native interface to smooth out rough edges that are likely to exist due to
the strict set of rules involved in native interfaces. We’ll, therefore, use delegation inside the
Mobihelp class to provide it a reference to an instance of MobihelpNative:
We’ll initialize this peer inside the init() method of the Mobihelp class. Notice, though that init() is
private since we have provided abstractions for the Android and iOS apps separately:
658
// ...
}
//Clears User information.
public final static void clearUserData() {
// ...
}
//Retrieve the number of unread items across all the conversations for the user synchronously i.e.
public final static int getUnreadCount() {
return 0;
}
//Retrieve the number of unread items across all the conversations for the user asynchronously
public final static void getUnreadCountAsync(UnreadUpdatesCallback callback) {
// ...
}
//Initialize the Mobihelp support section with necessary app configuration.
public final static void initAndroid(MobihelpConfig config) {
// ...
}
Things to Notice:
1. The initAndroid() and initIOS() methods include a check to see if they are running on the
correct platform. Ultimately they both call init().
For most of the methods in the Mobihelp class, we can see that the public API will just be a thin
wrapper around the native interface. E.g. the public API implementation of setUserFullName(String)
is:
class MobihelpInitMethods {
//Initialize the Mobihelp support section with necessary app configuration.
public final static void initAndroid(MobihelpConfig config) {
if ("and".equals([Link]().getPlatformName())) {
init(config);
}
}
659
private static void init(MobihelpConfig config) {
MobihelpNative peer = (MobihelpNative) [Link]([Link]);
peer.config_setAppId([Link]());
peer.config_setAppSecret([Link]());
peer.config_setAutoReplyEnabled([Link]());
peer.config_setDomain([Link]());
peer.config_setEnhancedPrivacyModeEnabled([Link]());
if ([Link]() != null) {
peer.config_setFeedbackType([Link]().ordinal());
}
peer.config_setLaunchCountForReviewPrompt([Link]());
peer.config_setPrefetchSolutions([Link]());
[Link]();
}
}
For some other methods, the public API needs to break apart the parameters into a form that the
native interface can accept. E.g. the init() method, shown above, takes a MobihelpConfig object as a
parameter, but it passed the properties of the config object individually into the native interface.
The only other non-trivial wrapper is the getUnreadCountAsync() method that we discussed before:
Now that we have set up our public API and our native interface, it is time to work on the native
side of things. You can generate stubs for all platforms in your IDE (Netbeans in my case), by right
clicking on the MobihelpNative class in the project explorer and selecting "Generate Native Access".
660
Figure 461. Generate Native Access Menu Item
This will generate a separate directory for each platform inside your project’s native directory:
Our implementation will be a thin wrapper around the native Android SDK. See the source here
[[Link]
freshdesk/[Link]].
Some highlights:
1. Context : The native API requires us to pass a context object as a parameter on many methods.
This should be the context for the current activity. It will allow the FreshDesk API to know
where to return to after it has done its thing. Codename One provides a class called
AndroidNativeUtil that allows us to retrieve the app’s Activity (which includes the Context). We’ll
wrap this with a convenience method in our class as follows:
This will enable us to easily wrap the freshdesk native API. E.g.:
2. runOnUiThread() - Many of the calls to the FreshDesk API may have been made from the
Codename One EDT. However, Android has its own event dispatch thread that should be used
for interacting with native Android UI. Therefore, any API calls that look like they initiate some
sort of native Android UI process should be wrapped inside Android’s runOnUiThread() method
which is similar to Codename One’s [Link]() method. E.g. see the showSolutions()
method:
661
public void clearUserData() {
[Link](context());
}
(Note here that the activity() method is another convenience method to retrieve the app’s
current Activity from the AndroidNativeUtil class).
3. Callbacks. We discussed, in detail, the mechanisms we put in place to enable our native code to
perform callbacks into Codename One. You can see the native side of this by viewing the
getUnreadCountAsync() method implementation:
The last step (at least on the Android side) is to bundle the FreshDesk SDK. For Android, there are a
few different scenarios you’ll run into for embedding SDKs:
1. The SDK includes only Java classes - NO XML UI files, assets, or resources that aren’t included
inside a simple .jar file. In this case, you can just place the .jar file inside your project’s
native/android directory.
2. The SDK includes some XML UI files, resources, and assets. In this case, the SDK is generally
distributed as an Android project folder that can be imported into an Eclipse or Android studio
workspace. In general, in this case, you would need to zip the entire directory and change the
extension of the resulting .zip file to ".andlib", and place this in your project’s native/android
directory.
3. The SDK is distributed as an .aar file - In this case you can just copy the .aar file into your
native/android directory.
The FreshDesk (aka Mobihelp) SDK is distributed as a project folder (i.e. scenario 2 from the above
list). Therefore, our procedure is to download the SDK (download link [[Link]
[Link]/sdk/mobihelp_sdk_android.zip]), and rename it from mobihelp_sdk_android.zip
to mobihelp_sdk_android.andlib, and copy it into our native/android directory.
Dependencies
Unfortunately, in this case there’s a catch. The Mobihelp SDK includes a dependency:
662
Mobihelp SDK depends on AppCompat-v7 (Revision 19.0+) Library. You will
need to update [Link] to point to the Appcompat library.
If we look inside the [Link] file (inside the Mobihelp SDK directory--- i.e. you’d need to
extract it from the zip to view its contents), you’ll see the dependency listed:
[Link].1=../appcompat_v7
I.e. it is expecting to find the appcompat_v7 library located in the same parent directory as the
Mobihelp SDK project. After a little bit of research (if you’re not yet familiar with the Android
AppCompat support library), we find that the AppCompat_v7 library is part of the Android Support
library, which can can installed into your local Android SDK using Android SDK Manager.
Installation processed specified here [[Link]
After installing the support library, you need to retrieve it from your Android SDK. You can find
that .aar file inside the
ANDROID_HOME/sdk/extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/ directory
(for version 19.1.0). The contents of that directory on my system are:
[Link] [Link]
[Link].md5 [Link].md5
[Link].sha1 [Link].sha1
1. [Link] - This is the actual library that we need to include in our project to
satisfy the Mobisdk dependency.
2. [Link] - This is the Maven XML file for the library. It will show us any
dependencies that the appcompat library has. We will also need to include these dependencies:
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>support-v4</artifactId>
<version>19.1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
i.e. We need to include the support-v4 library version 19.1.0 in our project. This is also part of
the Android Support library. If we back up a couple of directories to:
ANDROID_HOME/sdk/extras/android/m2repository/com/android/support, we’ll see it listed there:
appcompat-v7 palette-v7
cardview-v7 recyclerview-v7
663
gridlayout-v7 support-annotations
leanback-v17 support-v13
mediarouter-v7 support-v4
multidex test
multidex-instrumentation
[Link] [Link]
[Link].md5 [Link].md5
[Link].sha1 [Link].sha1
[Link] [Link]
[Link].md5 [Link].md5
[Link].sha1 [Link].sha1
Looks like this library is pure Java classes, so we only need to include the [Link]
file into our project. Checking the .pom file we see that there are no additional dependencies we
need to add.
So, to summarize our findings, we need to include the following files in our native/android
directory:
1. [Link]
2. [Link]
And since our Mobihelp SDK lists the appcompat_v7 dependency path as "../appcompat_v7" in its
[Link] file, we are going to rename [Link] to appcompat_v7.aar.
When all is said and done, our native/android directory should contain the following:
appcompat_v7.aar [Link]
com [Link]
The final step on the Android side is to inject necessary permissions and services into the project’s
[Link] file.
We can find the manifest file injections required by opening the [Link] file from the
MobiHelp SDK project. Its contents are as follows:
664
<uses-sdk
android:minSdkVersion="10" />
<application>
<activity
android:name="[Link]"
android:configChanges="orientation|screenSize"
android:theme="@style/[Link]"
android:windowSoftInputMode="adjustPan" >
</activity>
<activity
android:name="[Link]"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/[Link]"
android:windowSoftInputMode="adjustResize|stateVisible" >
</activity>
<activity
android:name="[Link]"
android:configChanges="orientation|screenSize"
android:theme="@style/[Link]">
</activity>
<activity
android:name="[Link]"
android:parentActivityName="[Link]"
android:theme="@style/[Link]" >
665
android:value="[Link]" />
</activity>
<activity
android:name="[Link]"
android:configChanges="keyboardHidden|orientation|screenSize"
android:parentActivityName="[Link]"
android:theme="@style/[Link]" >
</manifest>
We’ll need to add the <uses-permission> tags and all of the contents of the <application> tag to our
manifest file. Codename One provides the following build hints for these:
1. [Link] - For your <uses-permission> directives. Add a build hint with name
[Link], and for the value, paste the actual <uses-permission> XML tag.
Proguard Config
For the release build, we’ll also need to inject some proguard configuration so that important
classes don’t get stripped out at build time. The FreshDesk SDK instructions state:
If you use Proguard, please make sure you have the following included in
your project’s [Link]
In addition, if you look at the [Link] file inside the Mobihelp SDK, you’ll see the rules:
666
-keepclassmembers class * implements [Link] {
public static final [Link]$Creator *;
}
We’ll want to merge this and then paste them into the build hint [Link] of our
project.
If, after doing all this, your project fails to build, you can enable the "Include Source" option of the
build server, then download the source project, open it in Eclipse or Android Studio, and debug
from there.
After selecting "Generate Native Interfaces" for our "MobihelpNative" class, you’ll find a native/ios
directory in your project with the following files:
1. com_codename1_freshdesk_MobihelpNativeImpl.h [[Link]
cn1-freshdesk-demo/native/ios/com_codename1_freshdesk_MobihelpNativeImpl.h]
2. com_codename1_freshdesk_MobihelpNativeImpl.m [[Link]
cn1-freshdesk-demo/native/ios/com_codename1_freshdesk_MobihelpNativeImpl.m]
We make use of the API docs [[Link] to see how the native SDK
needs to be wrapped. The method names aren’t the same. E.g. instead of a method showFeedback(), it
has a message -presentFeedback:
#import "Mobihelp.h"
2. Similar to our use of runOnUiThread() in Android, we will wrap all of our API calls in either
dispatch_async() or dispatch_sync() calls to ensure that we are interacting with the Mobihelp
API on the app’s main thread rather than the Codename One EDT.
667
#import "CodenameOne_GLViewController.h"
-(void)showFeedback{
dispatch_async(dispatch_get_main_queue(), ^{
[[Mobihelp sharedInstance] presentFeedback:[CodenameOne_GLViewController instance]];
});
}
We described earlier how we created a static method on the MobihelpNativeCallback class so that
native code could easily fire a callback method. Now let’s take a look at how this looks from the iOS
side of the fence. Here is the implementation of getUnreadCountAsync():
-(void)getUnreadCountAsync:(int)param{
dispatch_async(dispatch_get_main_queue(), ^{
[[Mobihelp sharedInstance]
unreadCountWithCompletion:^(NSInteger count){
com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(
CN1_THREAD_GET_STATE_PASS_ARG param, 3 /*SUCCESS*/, count);
}];
});
}
In our case the iOS SDK version of this method is +unreadCountWithCompletion: which takes a block
(which is like an anonymous function) as a parameter.
com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(
CN1_THREAD_GET_STATE_PASS_ARG param, 3 /*SUCCESS*/, count);
#import "com_codename1_freshdesk_MobihelpNativeCallback.h"
668
17.12.2. Bundling Native iOS SDK
Now that we have implemented our iOS native interface, we need to bundle the Mobihelp iOS SDK
into our project. There are a few different scenarios you may face when looking to include a native
SDK:
1. The SDK includes .bundle resource files. In this case, just copy the .bundle file(s) into your
native/ios directory.
2. The SDK includes .h header files. In this case, just copy the .h file(s) into your native/ios
directory.
3. The SDK includes .a files. In this case, just copy the .a file(s) into your native/ios directory.
4. The SDK includes .framework files. In this case, you’ll need to zip up the framework, and copy it
into your native/ios directory. E.g. If the framework is named, [Link], then
the zip file should be named [Link], and should be located at
native/ios/[Link].
The FreshDesk SDK doesn’t include any .framework files, so we don’t need to worry about that last
scenario. We simply download the iOS SDK [[Link]
mobihelp_sdk_ios.zip], copy the libFDMobihelpSDK.a, Mobihelp.h. [Link], [Link],
and MHLocalization/[Link]/[Link] into native/ios.
If you run into problems with the build, you can select "Include Sources" in the build server to
download the resulting Xcode Project. You can then debug the Xcode project locally, make changes
to your iOS native implementation files, and copy them back into your project once it is building
properly.
The iOS integration guide for the FreshDesk SDK lists the following core frameworks as
dependencies:
We can add these dependencies to our project using the ios.add_libs build hint. E.g.
I.e. we just list the framework names separated by semicolons. Notice that my list in the above
image doesn’t include all of the frameworks that they list because many of the frameworks are
already included by default (I obtained the default list by simply building the project with "include
sources" checked, then looked at the frameworks that were included).
669
17.13. Part 3 : Packaging as a cn1lib
During the initial development, I generally find it easier to use a regular Codename One project so
that I can run and test as I go. But once it is stabilized, and I want to distribute the library to other
developers, I will transfer it over to a Codename One library project. This general process involves:
2. Copy the .java files from my original project into the library project.
3. Copy the native directory from the original project into the library project.
4. Copy the relevant build hints from the original project’s codenameone_settings.properties file
into the library project’s codenameone_library_appended.properties file.
In the case of the FreshDesk .cn1lib, I modified the original project’s build script to generate and
build a library project automatically. But that is beyond the scope of this tutorial.
When we build the layout we need to take margin into consideration and make sure to add it into
the position/size calculations. Building a layout manager involves two simple methods:
layoutContainer & getPreferredSize.
layoutContainer is invoked whenever Codename One decides the container needs rearranging,
Codename One tries to avoid calling this method and only invokes it at the last possible moment.
Since this method is generally very expensive (imagine the recursion with nested layouts).
Codename One just marks a flag indicating layout is "dirty" when something important changes
and tries to avoid "reflows".
getPreferredSize allows the layout to determine the size desired for the container. This might be a
difficult call to make for some layout managers that try to provide both flexibility and simplicity.
Most of FlowLayout bugs stem from the fact that this method is just impossible to implement
correctly & efficiently for all the use cases of a deeply nested FlowLayout. The size of the final layout
won’t necessarily match the requested size (it probably won’t) but the requested size is taken into
consideration, especially when scrolling and also when sizing parent containers.
This is a layout manager that just arranges components in a center column aligned to the middle.
We then show the proper usage of margin to create a stair like effect with this layout manager:
670
[Link](callbackId, [Link](), count);
}
});
}
});
1. Codename One doesn’t have Insets, we added some support for them in order to port GridBag
but components in Codename One have a margin they need to consider instead of the Insets
(the padding is in the preferred size and is thus hidden from the layout manager).
2. AWT layout managers also synchronize a lot on the AWT thread. This is no longer necessary
since Codename One is single threaded, like Swing.
3. AWT considers the top left position of the Container to be 0,0 whereas Codename One considers
the position based on its parent Container. The top left position in Codename One is getX(),
getY().
Other than those things it’s mostly just fixing method and import statements, which are slightly
different. Pretty trivial stuff.
A JVM Language is any programming language that can be compiled to byte-codes that will run on
the JVM (Java Virtual Machine). Java was the original JVM language, but many others have sprung
671
up over the years. Kotlin [[Link] Scala [[Link] Groovy [[Link]
[Link]/], and JRuby [[Link] come to mind as well-established and mature languages, but
there are many others [[Link]
The difficulty of porting a particular language to Codename One will vary depending on such
factors as:
a. How complex is the runtime library? (E.g. Does it require classes that aren’t currently
offered in Codename One’s subset of the java standard libraries?)
a. Codename One doesn’t support reflection because it would result in a very large application
size. If a JVM language requires reflection just to get off the ground then adding it to
Codename one would be tricky.
The more similar a language, and its build outputs are to Java, the easier it will be to port
(probably). Most JVM languages have two parts:
1. A compiler, which compiles source files to JVM byte-code (usually as .class files).
2. A runtime library.
Currently I’m only aware of one language (other than Java) that doesn’t require a runtime library,
and that is Mirah [[Link]
The first thing I do is take a look at the byte-code that is produced by the compiler. I use javap to
print out a nice version.
package com.codename1.hellokotlin2
import [Link]
import [Link]
import [Link]
import [Link]
672
/**
* Created by shannah on 2017-07-10.
*/
class KotlinForm : Form {
add(label).add(clickMe);
Let’s take a look at the bytecode that Kotlin produced for this class:
$ javap -v com/codename1/hellokotlin2/[Link]
673
#19 = Methodref #4.#18 //
com/codename1/ui/Form."<init>":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V
#20 = Utf8 com/codename1/ui/Label
#21 = Class #20 // com/codename1/ui/Label
#22 = Utf8 (Ljava/lang/String;)V
#23 = NameAndType #5:#22 // "<init>":(Ljava/lang/String;)V
#24 = Methodref #21.#23 // com/codename1/ui/Label."<init>":(Ljava/lang/String;)V
#25 = Utf8 com/codename1/ui/Button
#26 = Class #25 // com/codename1/ui/Button
#27 = Utf8 Click Me
#28 = String #27 // Click Me
#29 = Methodref #26.#23 // com/codename1/ui/Button."<init>":(Ljava/lang/String;)V
#30 = Utf8 com/codename1/hellokotlin2/KotlinForm$1
#31 = Class #30 // com/codename1/hellokotlin2/KotlinForm$1
#32 = Utf8 (Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V
#33 = NameAndType #5:#32 // "<init>":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V
#34 = Methodref #31.#33 //
com/codename1/hellokotlin2/KotlinForm$1."<init>":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V
#35 = Utf8 com/codename1/ui/events/ActionListener
#36 = Class #35 // com/codename1/ui/events/ActionListener
#37 = Utf8 addActionListener
#38 = Utf8 (Lcom/codename1/ui/events/ActionListener;)V
#39 = NameAndType #37:#38 // addActionListener:(Lcom/codename1/ui/events/ActionListener;)V
#40 = Methodref #26.#39 //
com/codename1/ui/[Link]:(Lcom/codename1/ui/events/ActionListener;)V
#41 = Utf8 com/codename1/ui/Component
#42 = Class #41 // com/codename1/ui/Component
#43 = Utf8 add
#44 = Utf8 (Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
#45 = NameAndType #43:#44 // add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
#46 = Methodref #2.#45 //
com/codename1/hellokotlin2/[Link]:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
#47 = Utf8 com/codename1/ui/Container
#48 = Class #47 // com/codename1/ui/Container
#49 = Methodref #48.#45 //
com/codename1/ui/[Link]:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
#50 = Utf8 clickMe
#51 = Utf8 Lcom/codename1/ui/Button;
#52 = Utf8 label
#53 = Utf8 Lcom/codename1/ui/Label;
#54 = Utf8 this
#55 = Utf8 Lcom/codename1/hellokotlin2/KotlinForm;
#56 = Utf8 Lkotlin/Metadata;
#57 = Utf8 mv
#58 = Integer 1
#59 = Integer 6
#60 = Utf8 bv
#61 = Integer 0
#62 = Utf8 k
#63 = Utf8 d1
#64 = Utf8
\n\n\20¢¨
#65 = Utf8 d2
#66 = Utf8 Lcom/codename1/ui/Form;
#67 = Utf8 HelloKotlin2
#68 = Utf8 [Link]
#69 = Utf8 Code
#70 = Utf8 LocalVariableTable
#71 = Utf8 LineNumberTable
#72 = Utf8 SourceFile
#73 = Utf8 InnerClasses
#74 = Utf8 RuntimeVisibleAnnotations
{
public [Link]();
descriptor: ()V
flags: ACC_PUBLIC
674
Code:
stack=5, locals=3, args_size=1
0: aload_0
1: ldc #8 // String Hello Kotlin
3: invokestatic #14 // Method
com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout;
6: checkcast #16 // class com/codename1/ui/layouts/Layout
9: invokespecial #19 // Method
com/codename1/ui/Form."<init>":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V
12: new #21 // class com/codename1/ui/Label
15: dup
16: ldc #8 // String Hello Kotlin
18: invokespecial #24 // Method com/codename1/ui/Label."<init>":(Ljava/lang/String;)V
21: astore_1
22: new #26 // class com/codename1/ui/Button
25: dup
26: ldc #28 // String Click Me
28: invokespecial #29 // Method com/codename1/ui/Button."<init>":(Ljava/lang/String;)V
31: astore_2
32: aload_2
33: new #31 // class com/codename1/hellokotlin2/KotlinForm$1
36: dup
37: aload_0
38: aload_1
39: invokespecial #34 // Method
com/codename1/hellokotlin2/KotlinForm$1."<init>":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V
42: checkcast #36 // class com/codename1/ui/events/ActionListener
45: invokevirtual #40 // Method
com/codename1/ui/[Link]:(Lcom/codename1/ui/events/ActionListener;)V
48: aload_0
49: aload_1
50: checkcast #42 // class com/codename1/ui/Component
53: invokevirtual #46 // Method add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
56: aload_2
57: checkcast #42 // class com/codename1/ui/Component
60: invokevirtual #49 // Method
com/codename1/ui/[Link]:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container;
63: pop
64: return
LocalVariableTable:
Start Length Slot Name Signature
32 32 2 clickMe Lcom/codename1/ui/Button;
22 42 1 label Lcom/codename1/ui/Label;
0 65 0 this Lcom/codename1/hellokotlin2/KotlinForm;
LineNumberTable:
line 13: 0
line 14: 12
line 15: 22
line 16: 32
line 21: 48
}
That’s a big mess of stuff, but it’s pretty easy to pick through it when you know what you’re looking
for. The layout of this output is pretty straight forward. The beginning shows that this is a class
definition:
675
boolean rtl = [Link]();
for (int iter = 0; iter < components; iter++) {
Component current = [Link](iter);
Dimension d = [Link]();
Style currentStyle = [Link]();
int marginRight = [Link](rtl);
int marginLeft = [Link](rtl);
int marginTop = [Link]();
int marginBottom = [Link]();
[Link](d);
int actualWidth = [Link]() + marginLeft + marginRight;
[Link](centerPos - actualWidth / 2 + marginLeft);
y += marginTop;
[Link](y);
y += [Link]() + marginBottom;
}
}
Even just comparing this line with the class definition from the source file we have learned
something about the Kotlin compiler. It has made the class final by default. That observation
shouldn’t affect our assessment here, but it is kind of interesting.
676
After the class definition, it shows the internal classes:
InnerClasses:
static final #31; //class com/codename1/hellokotlin2/KotlinForm$1
Constant pool:
#1 = Utf8 com/codename1/hellokotlin2/KotlinForm
#2 = Class #1 // com/codename1/hellokotlin2/KotlinForm
#3 = Utf8 com/codename1/ui/Form
#4 = Class #3 // com/codename1/ui/Form
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Hello Kotlin
#8 = String #7 // Hello Kotlin
#9 = Utf8 com/codename1/ui/layouts/BoxLayout
... etc...
The constant pool will consist of class names, and strings mostly. You’ll want to peruse this list to
see if the compiler has added any classes that aren’t in the source code. In the example above, it
looks like Kotlin is pretty faithful to the original source’s dependencies. It didn’t inject any classes
that aren’t in the original source.
Even if the compiler does inject other dependencies into the bytecode, it might not be a problem. It
is only a problem if those classes aren’t supported by Codename One. Keep your eyes peeled for
anything in the [Link] package or unsolicited use of [Link], [Link], or any other
package that aren’t part of the Codename One standard library. If you’re not sure if a class or
package is available in the Codename One standard library, check the javadocs
[[Link]
After the constant pool, we see each of the methods of the class written out as a list of bytecode
instructions. E.g.
public [Link]();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=5, locals=3, args_size=1
0: aload_0
1: ldc #8 // String Hello Kotlin
3: invokestatic #14 // Method
com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout;
6: checkcast #16 // class com/codename1/ui/layouts/Layout
9: invokespecial #19 // Method
com/codename1/ui/Form."<init>":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V
12: new #21 // class com/codename1/ui/Label
677
15: dup
16: ldc #8 // String Hello Kotlin
etc...
In the above snippet, the first instruction is aload_0 (which adds this to the stack). The 2nd
instruction is ldc, (which loads constant #8 — the string "Hello Kotlin" to the stack). The 3rd
instruction is invokestatic which calls the static method define by Constant #14 from the constant
pool, with the two parameters that had just been added to the stack.
You don’t need to understand what all of these instructions do. You just need to
look for instructions that may be problematic.
The only instruction that I think might be problematic is "invokedynamic". All other instructions
should work find in Codename One. (I don’t know for a fact that invokedynmic won’t work - I just
suspect it might not work on some platforms).
So to summarize, the byte-code assessment phase, we’re basically just looking to make sure that the
compiler doesn’t tend to add dependencies to parts of the JDK that Codename One doesn’t currently
support. And we want to make sure that it doesn’t use invokedynamic.
If you find that the compiler does use invokedynamic or add references to classes that Codename
One doesn’t support, don’t give up just yet. You might be able to create your own "porting" runtime
library that will provide these dependencies at runtime.
The process for assessing the runtime library is pretty similar to the process for the bytecodes.
You’ll want to get your hands on the language’s runtime library, and use javap to inspect the .class
files. You’re looking for the same things as you were looking for in the compiler’s output:
"invokedynamic" and classes that aren’t supported in Codename One.
Once you have assessed the language and are optimistic that it is a good candidate for porting, you
can proceed to port the runtime library into Codename One. Usually that language’s runtime
library will be distributed in .jar format. You need to convert this into a cn1lib so that it can be used
in a Codename One project. If you can get your hands on the source code for the runtime library
then the best approach is to paste the source files into a Codename One Library project, and try to
build it. This has the advantage that it will validate the source during compile to ensure that it
doesn’t depend on any classes that Codename One doesn’t support.
If you can’t find the sources of the runtime library or they don’t seem to be easily "buildable", then
the next best thing is to just get the binary distribution’s jar file and convert it to a cn1lib. This is
what we did for the Kotlin runtime library [[Link]
This procedure exploits the fact that a cn1lib file is just a zip file with a specific file structure inside
it. The cross-platform Java .class files are all contained inside a file named "[Link]", inside the zip
file. This is the only mandatory file that must be inside a cn1lib.
678
To make the library easier to use the cn1lib file can also contain a file named "[Link]" which
includes stubs of the Java sources. When you build a cn1lib using a Codename One Library project,
it will automatically generate stubs of the source so that the IDE will have access to nice things like
Javadoc when using the library. The kotlin distribution includes a separate jar file with the runtime
sources, named "[Link]", so we used this as the "stubs". It contains full sources,
which isn’t necessary, but it also doesn’t hurt.
So now that we had my two jar files: [Link] and [Link], I created a
new empty directory, and copied them inside. I renamed the jars "[Link]" and "[Link]"
respectively. Then I zipped up the directory and renamed the zip file "kotlin-runtime.cn1lib".
Building cn1libs manually in this way is a very bad habit, as it bypasses the API
verification step that normally occurs when building a library project. It is
possible, even likely, that the jar files that you convert depend on classes that
aren’t in the Codename One library, so your library will fail at runtime in
unexpected ways. The only reason we could do this with kotlin’s runtime (with
some confidence) is because I already analyzed the bytecodes to ensure that they
didn’t include anything problematic.
For our "Hello World" test we will need to create a separate project in our JVM language and
produce class files that we will manually copy into an appropriate location of our project. We’ll
want to use the normal tools for the language and not worry about how it integrates with
Codename One. For Kotlin, I just followed the getting started tutorial on the Kotlin site to create a
new Kotlin project in IntelliJ. When Steve ported Mirah, he just used a text editor and the mirahc
command-line compiler to create my Hello World class. The tools and process will depend on the
language.
package [Link]
class HelloKotlin {
fun hello() {
[Link]("Hello from Kotlin");
}
}
The easiest way to integrate external code into a Codename One project, is just to wrap it as a cn1lib
file and place it into my Codename One project’s lib directory. That way you don’t have to mess
with any of the build files. So, using roughly the same procedure as we used to create the kotlin-
runtime.cn1lib, I wrap my [Link] as a cn1lib to produce "hellokotlin.cn1lib" and copy it to
679
the "lib" directory of a Codename One project.
Remember to select "Codename One" → "Refresh CN1Libs" after placing the cn1lib
in your lib directory or it won’t get picked up.
If we run this in the Simulator, it should print "Hello from Kotlin" in the output console. If we get an
error, then we can dig in and try to figure out what went wrong using my standard debugging
techniques. EXPECT an error on the first run. Hopefully it will just be a missing import or
something simple.
In the case of Kotlin, the hello world example app would actually run without the runtime library
because it was so simple. So it was necessary to add a more complex example to prove the need for
the runtime library. It doesn’t matter what you do with your more complex example, as long as it
doesn’t require classes that aren’t in Codename One.
If you want to use the Codename One inside your project, you should add the [Link]
(found inside any Codename One project) to your classpath so that it will compile.
At this point we already have a manual process for incorporating files built with our alternate
language into a Codename One project. The process looks like:
1. Use standard tools for your JVM language to write your code.
2. Use the JVM language’s standard build tools (e.g. command-line compiler, etc..) to compile your
code so that you have .class files (and optionally a .jar file).
When Steve first developed Mirah support he automated this process using an ANT script
[[Link] He also
automatically generated some bootstrap code so that he could develop the whole app in Mirah and
he woudn’t have to write any Java. However, this level of integration has limitations.
For example, with this approach alone, you couldn’t have two-way dependencies between Java
source and Mirah source. Yes, Mirah code could use Java libraries (and it did depend on
[Link]), and my Java code could use my Mirah code. However, Mirah source code could
not depend on the Java source code in my project. This has to do with the order in which code is
680
compiled. It’s a bit of a chicken and egg issue. If we are building a project that has Java source code
and Mirah source code, we are using two different compilers: mirahc to compile the Mirah files,
and javac to compile the Java files. If we are starting from a clean build, and we run mirahc first,
then the .java files haven’t yet been compiled to .class files - and thus mirahc can’t reference them -
and any mirah code that depends on those uncompiled Java classes will fail. If we compile the .java
files first, then we have the opposite problem.
Steve worked around this problem in Mirah by writing my own pseudo-compiler [[Link]
shannah/mirah-ant/blob/master/src/ca/weblite/asm/[Link]] that produced stub class
files for the java source that would be referenced by mirahc when compiling the Mirah files. In this
way he was able to have two-way dependencies between Java and Mirah in the same project.
For both the Kotlin and Mirah support, we wanted integration to be seamless. We didn’t want users
to have to create a separate project for their Kotlin/Mirah code. We wanted them to simply add a
Kotlin/Mirah file into their project and have it just work. Achieving this level of integration in
Kotlin was quite easy, since they provide an ANT plugin [[Link]
[Link]] that essentially allowed me to just add one tag inside my <javac/> tags:
<withKotlin/>
And it would automatically handle Kotlin and Java files together: Seamlessly. There are a few places
in a Codename One’s [Link] file where we call "javac" so we just needed to inject these tags in
those places. This injection is performed automatically by the Codename One IntelliJ plugin.
For Mirah, Steve developed his own ANT plugins [[Link] and Netbeans
module [[Link] that do something similar in Netbeans.
• Download once - if you have multiple projects the library will only download once to the
.codenameone directory. All the projects will update from local storage
• Skins update automatically - this is hugely important. When we change a theme we need to
update it in the skins and if you don’t update the skin you might see a difference between the
simulator and the device
• Update of settings/designer without IDE plugin update - The IDE plugin update process is slow
681
and tedious. This way we can push out a bug fix for the GUI builder without going through the
process of releasing a new plugin version
For the most part this framework should be seamless. You should no longer see the "downloading"
message whenever we push an update after your build client is updated. Your system would just
poll for a new version daily and update when new updates are available.
You can also use the usual method of Codename One Settings → Basic → Update Client Libs which
will force an update check. Notice that the UI will look a bit different after this update.
You can see the full code here [[Link] the gist of it is very
simple. We create a jar called [Link] under ~/.codenameone (~ represents the users
home directory).
An update happens by running this tool with a path to a Codename One project e.g.:
E.g.:
Notice that no download happened since the files were up to date. You can also force a check
against the server by adding the force argument as such:
The way this works under the hood is thought a [Link] within your directory that lists
the versions of local files. That way we know what should be updated.
Under the ~/.codenameone directory we have a more detailed [Link] file that
682
includes versions of the locally downloaded files. Notice you can delete this file and it will be
recreated as all the jars get downloaded over again.
You will notice 3 big things that aren’t covered in this unified framework:
• We don’t update cn1libs - I’m not sure if this is something we would like to update automatically
• Versioned builds - there is a lot of complexity in the versioned build system. This might be
something we address in the future but for now I wanted to keep the framework simple.
683
684
Chapter 18. Security
Security is a "big word". It implies many things and that is also true for the mobile app development
so before we get started lets try to define the scope of security.
We will deal only with application security and its communication mechanisms while ignoring
everything beyond that scope. Let’s start with a simple fact:
Codename One applications are secure on the devices by the nature of the
mobile OS security.
For most intents and purposes this will be enough, unless you are specifically concerned about
security this section isn’t for you. Mobile OS’s isolate applications from one another so it’s hard for
an application to damage the OS or even damage/spy on a different app.
The restrictions laid on apps are here to make them extra secure and on top of that Codename One
lays a few big advantages in terms of security:
• We compile the UI to native code too which means typical reverse engineering code will have a
harder time following
• We disable debug flags so a hacker won’t be able to debug your production app on the device
Despite that you still need to keep in mind that the binary could still be reverse engineered and so it
is important to avoid storing keys in the client side code. E.g. if you have an API key to access a
service (e.g. Google Cloud key) it needs to be stored in your server and not as a constant in your
app!
Each section below discusses some attack vectors against applications and how they can be
stopped. Pretty much all of these attacks require a very sophisticated attacker which will only exist
for high value targets (e.g. bank apps, government etc.).
It won’t lead directly to a hack or exploit but it will show you the approximate area of the code
where we should look and it makes the first step that much easier. Obfuscation helps as it removes
descriptive method names but it can’t hide the Strings we use in constants. If an app has a secret
key within obfuscating it can make a difference (albeit a slight difference).
Notice that this is a temporary roadblock as any savvy hacker would compile the app and connect a
debugger eventually (although this is blocked in release builds) and would be able to inspect values
of variables/flow. But the road to reverse engineering the app would be harder even with a simple
685
xor obfuscation.
We’re not calling this encoding or encryption since it’s neither. It’s a simple
obfuscation of the data
They use a simple xor based obfuscation to make a String less readable. E.g. if you have code like
this:
private static final String SECRET = "Don't let anyone see this....";
You might be concerned about the secret, then this would make it slightly harder to find out:
Notice that this is not secure, if you have a crucial value that must not be found you need to store it
in the server. There is no alternative as everything that is sent to the client can be compromised by
a determined hacker
Use the comment to help you find the string in the code
Our builtin user specific constants are obfuscated with this method, e.g. normally an app built with
Codename One carries some internal data such as the user who built the app etc. This is obfuscated
now. We built this small app to encode strings easily so we can copy and paste them into our app
easily:
[Link]();
686
This allows you to type in the first text field and the second text area shows the encoded result. We
used a text area so copy/paste would be easy.
Codename One supports full encryption of the Storage (notice the distinction, Storage is not
FileSystemStorage). This is available by installing the bouncy castle cn1lib from the extensions
menu then using one line of code
[Link]("your-pass-encryption-key");
Normally you would want that code within your init(Object) method
Notice that you can’t use storage or preferences to store this data as it would be encrypted
(Preferences uses Storage internally). You can use a password for this key and it would make it way
more secure but if a user changes his password you might have a problem. In that case you might
need the old password to migrate to a new password.
This works thru a new mechanism in storage where you can replace the storage instance with
another instance using:
[Link](new MyCustomStorageSubclass());
We can leverage that knowledge to change the encryption password on the encryption storage
using pseudo code like this:
[Link](oldKey);
InputStream is = [Link]().createInputStream(storageFileName);
byte[] data = [Link](is);
[Link](newKey);
OutputStream o = [Link]().createOutputStream("TestEncryption");
[Link](data);
[Link]();
It’s not a good idea to replace storage objects when an app is running so this is
purely for this special case…
If you use preferences it might be a good idea to set their builtin location to a different path using
something like [Link]("EncryptedPreferences");.
687
This is useful as it prevents the encrypted preferences from colliding with the regular preferences.
Blocking screenshots is implemented by classifying the app window as secure on Android. You can
enable this via the build hint [Link]=true. Once that is added screenshots
should no longer work for the app, this might impact other things as well such as the task view
which will no longer show the screenshot either.
On iOS, you cannot prevent a user from taking a static screenshot, but you can block screen
capture/recording while it is active. This uses [Link] and its change notification to
hide the app contents while a capture session is active. Enable this behavior with the build hint
[Link]=true.
You can block copy & paste on Android & iOS. Blocking of copy & paste can be implemented globally
or on a specific field.
[Link]().setProperty("blockCopyPaste", "true");
[Link]("blockCopyPaste", [Link]);
Notice that the inverse of using false might not work as expected
This isn’t true for jailbroken or rooted devices. In these devices security has been compromised
688
often with good intentions (opening up the ecosystem) but it can also be used as a step in a serious
attack on an application!
For obvious reasons it’s really hard to accurately detect a jailbroken or rooted device but when
possible if you have a high security app you might want to block the functionality or even raise a
"silent alarm" in such a case. To detect this you can use the isJailbrokenDevice method as such:
if([Link]().isJailbrokenDevice()) {
// probably jailbroken or rooted
} else {
// probably not
}
Notice that this isn’t accurate, we can’t be 100% sure as there are no official ways to detect
jailbreak. That is why it’s crucial to encrypt everything and assume the device was compromised to
begin with when dealing with very sensitive data. Still it’s worthwhile to use these API’s to make the
life of an attacker just a little bit harder.
APK’s are signed as part of the build process when we upload an app to the Google Play Store. This
process seems redundant as we generate the signature/certificate ourselves (unlike Apple which
generates it for us). However, this is a crucial step as it allows the device to verify upgrades and
make sure a new update is from the same original author!
This means that if a hacker takes over your account on Google Play, he still won’t be able to ship
fake updates to your apps without your certificate. That’s important since if a hacker would have
access to your certificate he could create an app update that would just send him all the users
private information e.g. if you are a bank this could be a disaster.
Android launched with RSA1024/SHA1 as the signing certificates. This was good enough at the time
and is still pretty secure. However, these algorithms are slowly eroding and it is conceivable that
within the 10-15 year lifetime of an app they might be compromised using powerful hardware. That
is why Google introduced support for stronger cryptographic signing into newer versions of
Android and you can use that.
There is a downside…
Google only introduced that capability in Android 4.3 so using these new keys will break
compatibility with older devices. If you are building a highly secure app this is probably a tradeoff
you should accept. If not this might not be worth it for some theoretical benefit.
689
Furthermore, if your app is already shipping you are out of luck. Due to the obvious security
implications once you shipped an app the certificate is final. Google doesn’t provide a way to
update the certificate of a shipping app. Thus this feature only applies to apps that aren’t yet in the
play store.
If you are building a new app this is pretty easy to integrate and requires no changes on your part.
Just a new certificate. You can generate the new secure key using instructions in articles like this
one [[Link]
If you are using Codename One Setting you can check the box to generate an SHA512 key which will
harden the security for the APK.
What if the coffee shop was hacked and the router is listening in on everything?
So HTTPS is encrypted and the way encryption works is thru the certificate. The server sends me a
certificate and we can use that to send encrypted data to it.
What if the router grabs the servers certificate and communicates with Google in
my name?
This won’t work since the data we send to the server is encrypted with the certificate from the
server.
That won’t work either. All certificates are signed by a "certificate authority" indicating that a
[Link] certificate is valid.
That’s a problem!
It’s obviously hard to do but if someone was able to do this he could execute a "man in the middle"
attack as described above. People were able to fool certificate authorities in the past and gain fake
certificates using various methods so this is possible and probably doable for any government level
attacker.
690
18.7.1. Certificate Pinning
This is the attack certificate pinning (or SSL pinning) aims to prevent. We code into our app the
"fingerprint" of the certificate that is "good" and thus prevent the app from working when the
certificate is changed. This might break the app if we replace the certificate at some point but that
might be reasonable in such a case.
if([Link]()) {
String f = [Link](myHttpsURL);
if([Link](f)) {
// OK it's a good certificate proceed
} else {
if([Link]("Security Warning", "WARNING: it is possible your commmunications are being tampered! We suggest
quitting the app at once!", "Quit", "Continue")) {
[Link]().exitApplication();
}
}
} else {
// certificate fingerprint checking isn't supported on this platform... It's your decision whether to proceed or not
}
Notice that once connection is established you don’t need to verify again for the current application
run.
691
692
Chapter 19. Signing, Certificates &
Provisioning
In this section we attempt to explain how to acquire certificates for the various platforms and how
to set them up.
The good news is that this is usually a "one time issue" and once it’s done the work becomes easier
(except for the case of iOS where a provisioning profile should be maintained).
Certificates use cryptographic principals to "sign" data (e.g. an application). Think of them as you
would think of a company stamp, you use them to sign an app so users know who it’s from.
Provisioning provides the hints/guidelines for the application install. E.g. if an application needs
some service from the OS such as push it can usually request that with provisioning.
In iOS provisioning is separate from the app and you need to also define the devices supported by
the app during development.
Normally certificates are issued by a signing authority which is a body that certifies that you are
who you say you are. Apple issues certificates for iOS and is in effect a signing authority.
Android uses self signed certificates which don’t use a signing authority so anyone can ship an
Android app.
The logic with the Android approach is that a signature indicates that you are the same person who
previously shipped the app. Hence an app will be updated only with the exact same certificate.
The UDID is the Universal Device Identifier. It identifies mobile devices uniquely, notice that some
operating systems e.g. iOS block access to this value due to privacy concerns.
You need the iOS device UDID value during development to add the device into the list of allowed
devices.
693
Don’t use an app to get the UDID!
Most return the wrong value. Use the Finder device summary (macOS), Apple
Configurator, or a trusted service such as [Link] which seems to work
rather well
We would recommend it for all platforms for simplicity but some developers prefer creating per-
application certificates for Android. The advantage here is that you can transfer ownership of the
application later on without giving away what is effectively "you house keys".
To generate your certificates and profiles, open project’s properties and click on "iOS" in the left
menu. This will show the "iOS Signing" panel that includes fields to select your certificates and
mobile provisioning profiles.
If you already have valid certificates and profiles, you can just enter their locations here. If you
don’t, then you can use the wizard by clicking the Generate button in the lower part of the form.
After clicking Generate you’ll be shown a login form. Log into this form using the App Store
Connect Apple ID that is registered on your Apple Developer Program team. NOT YOUR
CODENAME ONE LOGIN.
694
Figure 467. Wizard login form
Once you are logged in you will be shown a list of all of the devices that you currently have
registered on your Apple developer account.
Select the ones that you want to include in your provisioning profile and click next.
Apple currently allows up to 100 devices per device type (iPhone, iPad, Apple
Watch, Apple TV, and Apple Vision) for testing purposes in each membership year.
Make sure every device you intend to install builds on is registered here before
you generate a new provisioning profile.
695
If you don’t have any devices registered yet, you can click the "Add New Device" button, which will
prompt you to enter the UDID for your device.
If you already have iOS P12 development/distribution certificates you should reuse
them for all your apps from that account and you shouldn’t regenerate them
After you click Next on the device form, the wizard checks to see if you already have a valid
certificate. If your project already has a valid certificate and it matches the one that is currently
active in your apple developer account, then it will just use the same certificate. If the certificate
doesn’t match the currently-active one, or you haven’t provided a certificate, you will be prompted
to overwrite the old certificate with a new one.
The same decision need to be made twice: Once for the development certificate, and once for the
App Store distribution certificate.
Each Apple Developer account can only have three active iOS Development
certificates and three active iOS Distribution certificates at a time. Reuse existing
certificates whenever possible to avoid hitting this limit.
You can revoke a certificate when you have an application in the store shipping
with said certificate!
This won’t affect the shipping app see this [[Link]
696
i-revoke-an-existing-distribution-certificate-will-it-mess-up-anything-with].
• Development - this is used during development and can’t be shipped to 3rd parties. An
application signed with this certificate can only be installed on one of the up to 100
devices listed above.
• Distribution - this is used when you are ready to upload your app to App Store Connect
whether for final shipping or beta testing. Notice that you can’t install a distribution build
on your own device. You need to upload it to App Store Connect.
• There are two push certificates, they are separate from the signing certificates. Don’t
confuse them!
They are used to authenticate with Apple when sending push messages.
The next form in the wizard asks for your app’s bundle ID. This should have been pre-filled, but
you can change the app ID to a wildcard ID if you prefer.
Wildcard ids such as [Link].\* or even \* allow you to create one generic certificate
to use with all applications. This is remarkably useful for the global settings dialog and allows
you to create an app without launching the wizard. Notice that wildcards apps can’t use
features such as push etc.
You can set the global defaults for the IDE by going to IDE settings/preferences and setting
default values e.g.:
697
Figure 473. Setting the development certificate and a global \* provisioning profile allows you to
create a new app and built it to device without running the certificate wizard. Notice that you will
need to run it when going into production
Once the wizard is finished generating your provisioning profiles, you should click "Install Locally",
which will open a file dialog for you to navigate to a folder in which to store the generated files.
698
Figure 476. Final Done Message
You can see the password for the P12 files in the codenameone_settings.properties
file. You can use the values defined there when creating a new application
After selecting your local install location, and closing the wizard, you should see the fields of the
"iOS Signing" properties panel filled in correctly. You should now be able to send iOS debug or App
Store builds without the usual hassles.
iOS signing has two distinct modes: App Store signing which is only valid for distribution via App
Store Connect (you won’t be able to run the resulting application without submitting it to Apple)
and development mode signing.
2. Provisioning Profile - details about the application and who is allowed to execute it
You need two versions of each file (4 total files) one pair is for development and the other pair is for
699
uploading to App Store Connect.
You need to use a Mac in order to create a certificate file for iOS
The first step you need to accomplish is signing up as a developer to Apple’s iOS development
program [[Link] even for testing on a device this is required!
This step requires that you pay Apple on an annual basis.
The Apple website will guide you through the process of applying for a certificate at the end of this
process you should have a distribution and development certificate pair. After that point you can
login to the iOS provisioning portal [[Link] where
there are plenty of videos and tutorials to guide you through the process. Within the iOS
provisioning portal you need to create an application ID and register your development devices.
You can then create a provisioning profile which comes in two flavors:
• Development - the development provisioning profile needs to contain the devices on which you
want to test.
You can then configure the 4 files in the IDE and start sending builds to the Codename One cloud.
In the certificates section you can download your development and distribution certificates.
700
Figure 480. Download distribution provisioning profile
In the devices section add device ids for the development devices you want to support. Notice no
more than 100 devices are supported!
Create an application id; it should match the package identifier of your application perfectly!
Create a provisioning profile for development, make sure to select the right app and make sure to
add the devices you want to use during debug.
Refresh the screen to see the profile you just created and press the download button to download
your development provisioning profile.
701
Figure 485. Create provisioning profile step 3
Create a distribution provisioning profile; it will be used when uploading to the App Store. There is
no need to specify devices here.
We can now import the cer files into the key chain tool on a Mac by double clicking the file, on
Windows the process is slightly more elaborate
We can export the p12 files for the distribution and development profiles through the keychain tool
In the IDE we enter the project settings, configure our provisioning profile, the password we typed
when exporting and the p12 certificates. It is now possible to send the build to the server.
702
Figure 490. IOS Project Settings
The Codename One build servers read signing assets from codenameone_settings.properties. When
you bypass the signing wizard, populate the following keys manually so the packaging process can
locate your certificates, provisioning profiles, and keystores:
iOS
• [Link] / [Link] /
[Link] – Development P12 and provisioning profile for device/debug
builds.
• [Link] / [Link] /
[Link] – Distribution P12 and provisioning profile for App
Store/TestFlight builds.
Paths may be absolute or relative to the project directory. The passwords must match the values
you used when exporting the P12 files from Keychain Access.
Android
If the keystore fields are empty the Maven and Ant builders will create a default keystore, but
production apps should commit the real paths and credentials so reproducible builds use your
long-lived signing key.
Below is a list of common issues when singing and a set of suggestions for things to check. Notice
that some of these signing failures will sometimes manifest themselves during build and sometimes
will manifest during the install of the application.
Most of these issues aren’t applicable when using the wizard e.g. a Mac isn’t
required for the certificate wizard as it uses the Codename One cloud
703
• You must use a Mac to generate P12 certificates manually. The only workaround we found is
the certificate wizard!
Notice that this is something you need to do once a year (generate P12), you will also need a Mac
to upload your final app to the store at this time.
• When exporting the P12 certificate make sure that you selected BOTH the public and the
private keys as illustrated here. If you only see one entry (no private key) then you created the
CSR (singing request) on a different machine than the one where you imported the resulting
CER file.
• Make sure the package matches between the main preferences screen in the IDE and the iOS
settings screen.
• Make sure the prefix for the app id in the iOS section of the preferences matches the one you
have from Apple
• Make sure your provisioning profile’s app id matches your package name or is a * provisioning
profile. Both are sampled in the pictures below, notice that you would need an actual package
name for push/in-app-purchase support as well as for app store distribution.
704
Figure 495. Provisioning Profile with app id
• Make sure the certificate and provisioning profile are from the same source (if you work with
multiple accounts), notice that provisioning profiles and certificates expire so you will need to
regenerate provisioning when your certificate expires or is revoked.
• If you declare push in the provisioning profile then [Link] (in the build arguments)
MUST be set to true, otherwise it MUST be set to false (see pictures below). Notice that this
should be configured via the UI in the iOS section. The build server automatically enables the
notification entitlement when your project uses LocalNotification, even if [Link] is
false, so do not try to disable the entitlement manually to "fix" warning messages from App
Store Connect.
19.5. Android
Signing Android applications is trivial when compared to the pain of iOS signing.
The NetBeans and Eclipse plugins have a simple wizard to generate the certificate that you can
launch by pressing this button:
705
Figure 498. UI for Android certificate details
This will seamlessly generate a certificate for your project, you can reuse it for other projects as
well.
You need the JDK’s keytool executable (it should be under the JDK’s bin directory) and execute the
following command:
keytool -genkey -keystore [Link] -alias [alias_name] -keyalg RSA -keysize 2048 -validity 15000 -dname "CN=[full
name], OU=[ou], O=[comp], L=[City], S=[State], C=[Country Code]" -storepass [password] -keypass [password]
Executing the command will produce a [Link] file in that directory which you need to keep
since if you lose it you will no longer be able to upgrade your applications! Fill in the appropriate
details in the project properties or in the CodenameOne section in the Netbeans preferences dialog.
19.6. RIM/BlackBerry
You can now get signing keys for free from Blackberry by going here [[Link]
SignedKeys/]. Once you obtain the certificates you need to install them on your machine (you will
need the Blackberry development environment for this). You will have two files: [Link] and
[Link] on your machine (within the JDE directory hierarchy). We need them and their
associated password to perform the signed build for Blackberry application.
706
19.7. J2ME
Currently signing J2ME applications isn’t supported. You can use tools such as the Sprint WTK to
sign the resulting jad/jar produced by Codename One.
707
708
Chapter 20. Working with iOS
20.1. Troubleshooting iOS Debug Build installs
If you have access to a Mac you can connect a cable and open Xcode where you can use the device
explorer console to look at messages which sometimes give a clue about what went wrong. If not,
here is a laundry list of a few things that might fail:
• Make sure you built the debug version and not the appstore version. The appstore version won’t
install on the device and can only be distributed via Apple’s store or testflight
• Check the the UDID is correct - if you got the UDID from an app then it’s probably wrong as apps
don’t have access to the device UDID anymore. The way to get the UDID is either thru iOS
Settings app or itunes
• Make sure the device isn’t locked for installing 3rd party apps. I’ve had this when trying to
install on my kids tablet which I configured to be child safe. This is configured in the settings as
parental controls
• Check that you "own" the package name. E.g. if you previously installed an app with the same
package name but a different certificate a new install will fail (this is true for Android too). So if
you installed the kitchen sink from the store then built one of your own and installed it there
will be a collision.
Notice that this might be problematic if you use overly generic package names as someone else
might have used them which is why you must always use your own domain
• Make sure the device has a modern enough version of iOS for the dependencies. As of 2024,
Codename One builds target iOS 12.0 or newer by default. You can raise the requirement with
the ios.deployment_target build hint if a library needs a newer version.
• Verify that you are using Safari when installing on the device (if you tried via cable that’s not a
problem), some developers had issues with firefox not launching the install process
• Check that the build hint [Link] is set in a way that matches your iOS provisioning. So
it must be false if you don’t have push within the provisioning profile
The build server provides a minimal launch storyboard automatically. Customise it by adding any
of the following files under your project’s native/ios directory:
1. [Link] - Shown in the centre of the screen instead of your app icon.
709
2. [Link] - Drawn behind the content to provide a colour or illustration.
3. [Link] - A custom storyboard created in Xcode that replaces the default layout
entirely.
Make sure to add the [Link]=true build hint, or your launch storyboard
will not be used.
Keep the launch storyboard simple and static. The layout is rendered before your app code runs, so
avoid views that depend on live data or animation. Follow these guidelines when editing
[Link] in Xcode:
• Use Auto Layout constraints and safe-area guides so the design scales to every device, including
split view on iPad.
• Prefer system colours or vector/PDF assets for logos so the result stays crisp on high-density
screens and supports Dark Mode.
• Reserve text for short taglines or status messages that do not need localisation at launch.
Dynamic localisation is not available.
• Avoid referencing application delegate outlets or custom classes. Only design-time UIKit
elements are supported.
The default storyboard expects PNG assets with the following characteristics. All sizes are specified
in points (pt); supply @2x and @3x variants for Retina displays when possible.
710
Asset Purpose Suggested 1x Notes
dimensions
[Link] Full-screen backdrop 1024×1024 Supply complementary
[Link]@2x.p
ng (2048×2048) and
[Link]@3x.p
ng (3072×3072) if you
rely on artwork instead
of a flat colour. Keep
file sizes small (<2 MB)
to avoid slowing
startup.
[Link] Complete custom N/A Target iOS 12.0 and
d layout later, enable Auto
Layout, and include
constraints for every
view. Avoid timers or
code connections.
Changes to the launch screen will not take effect until the device has been
restarted. I.e. If you install your app on a device, then you make changes to the
launch screen and update the app, the launch screen won’t change until the device
is restarted.
When iterating locally with a Mac, open the generated Xcode project and run it on device or
Simulator to verify that the layout adapts correctly. On Windows or Linux, submit a TestFlight or Ad
Hoc build and validate on hardware before shipping.
711
[Link]] object with the information you want to send in the notification.
[Link]().scheduleLocalNotification(
n,
[Link]() + 10 * 1000, // fire date/time
LocalNotification.REPEAT_MINUTE // Whether to repeat and what frequency
);
The API for receiving/handling local notifications is also similar to push. Your application’s main
lifecycle class needs to implement the [Link]
interface which includes a single method:
The notificationId parameter will match the id value of the notification as set using
[Link]().
712
Example Receiving Notification
Repeating notifications will continue until they are canceled by the app. You can cancel a single
notification by calling:
[Link]().cancelLocalNotification(notificationId);
Where notificationId is the string id that was set for the notification using
[Link]().
713
This is supported for pro users as part of the crash protection feature.
To take advantage of that capability use the build hint [Link]=true and then submit the app
to the store for beta testing. Make sure to use a release build target.
You can disable the strict URL checks from Apple by using the venerable [Link] build hint
and setting it to:
<key>NSAppTransportSecurity</key><dict><key>NSAllowsArbitraryLoads</key><true/></dict>
However, it seems that Apple will reject your app if you just include that and don’t have a good
reason.
Examples
[Link]=AFNetworking
For full versioning syntax specifying pods see the Podfile spec for the "pod" directive
[[Link]
714
20.6.1. Including Multiple Pods
Multiple pods can be separated by either commas or semi-colons in the value of the [Link] build
hint. E.g. To include GoogleMaps and AFNetworking, you could:
[Link]=GoogleMaps,AFNetworking
Or specifying versions:
[Link] : Some pods require that you specify a URL for the source of the pod spec. This
may be optional if the spec is hosted in the central CocoaPods source ([Link]
CocoaPods/[Link]).
Most documentation for Cocoapods "pods" provide instructions on what you need to add to your
Xcode project’s PodFile. Here is an example from the GoogleMaps cocoapod to show you how a
PodFile can be converted into equivalent build hints in a Codename One project.
The GoogleMaps cocoapod directs you to add the following to your PodFile:
source '[Link]
platform :ios, '7.0'
pod 'GoogleMaps'
This would translate to the following build hints in your Codename One project:
[Link]=[Link]
[Link]=7.0
[Link]=GoogleMaps
715
and copying it to your native/ios directory.
e.g. native/ios/[Link]
There are no build hints necessary for this approach. The build server will automatically detect the
framework and link it into your app.
716
Chapter 21. Working with JavaScript
This section covers the Codename One Javascript port, which allows you to compile your app as
native javascript and run it inside a browser. This is different from the BrowserComponent
[[Link] and other methods of
displaying HTML/Javascript inside a Codename One app.
This section pertains to Codename One 3.6 and older. Newer versions of Codename
One support multithreaded code inside static initializers now.
Codename One’s Javascript port uses TeaVM [[Link] to compile your application directly to
Javascript so that it can run inside modern web browsers without the need for any plugins (i.e. NOT
as an applet). One of the revolutionary features that TeaVM provides is the ability to run multi-
threaded code (i.e. it has full support for [Link](), [Link](), [Link](), and the
synchronized keyword). The one caveat to be aware of is that you cannot use any threading
primitives inside static initializers. This is due to technical limitations in the browser
environment and the way that TeaVM compiles class definitions into Javascript. The workaround
for this issue is to do lazy initialization in cases where you need to use multithreaded code.
Example
The following code will result in a build error when deploying a Javascript build:
[Link]
import [Link];
class Class1 {
public static int getValue() {
Log.p("Hello world");
return 1;
}
}
[Link]
class Class2 {
public static int value = [Link]();
This fails because Class2 calls [Link]() in its static initializer, and getValue() calls Log.p(),
which, underneath the covers, writes to Storage - which involves some synchronous network access
717
in the Javascript port (i.e. it uses wait() and notify() under the hood.
But How do we Know if A method includes wait()/notify somewhere along the line?
When you try to build your app as a Javascript app, it will fail (if code in your static initializers uses
wait()/notify() somewhere along the line).
Use lazy initialization wherever you can. You don’t need to worry about this for setting static
variables to literal values. E.g.: static int someVal = 20; will always be fine. But static int someVal
= [Link](); may or may not be fine, because you don’t know whether
calculateSomeVal() uses a wait/notify. So instead of initializing someVal in the static initializer,
create a static accessor that lazily initializes it. Or initialize it inside your app’s init() method. Or
initialize it inside the class constructor.
This error will occur if you have static initializers that use multithreaded code (e.g.
wait/notify/sleep, etc…). See Static Initializers for information about troubleshooting this error.
In some cases TeaVM may give a false-positive here (i.e. it thinks you are doing some
multithreaded stuff, but you’re really not), then you can force the build to "succeed" by adding
the [Link]=false build hint.
TeaVM uses its own Java runtime library. It is mostly complete, but you may occasionally run
into methods that haven’t been implemented. If you run into errors saying that certain classes
or methods were not found, please post them to the Codename One issue tracker
[[Link] You can also work around these by changing
your own code to not use such functions. If this missing method doesn’t fall on a critical path on
your app, you can also force the app to still build despite this error by adding the
[Link]=false build hint.
718
21.3. ZIP, WAR, or Preview. What’s the difference?
The Javascript build target will result in up to three different bundles being generated:
1. [Link]
2. [Link]
3. [Link]
[Link] is a self contained application bundle that can be installed in any JavaEE servlet
container. If you haven’t customized any proxy settings, then the application will be configured to
use a proxy servlet that is embedded into the .war file.
719
Some things to note in this file listing:
2. [Link] is the proxy servlet for making network requests to other domains.
3. The assets directory contains all of your application’s jar resources. All resource files in your
app will end up in this directory.
4. The teavm directory contains all of the generated javascript for your application. Notice that
there are some debugging files generated ([Link] and [Link]). These are
not normally loaded by the browser when your app is run, but they can be used by Chrome
when you are doing debugging.
5. The jar files in the WEB-INF/lib directory are dependencies of the proxy servlet. They are not
required for your app to run - unless you are using the proxy.
[Link] is appropriate for deploying the application on any web server. It contains all of
the same files as the .war file, excluding the WEB-INF directory (i.e. it doesn’t include any servlets,
class files, or Java libraries - it contains purely client-side javascript files and HTML).
As an example, this is a listing of the files in the zip distribution of the PropertyCross demo:
You’ll notice that it has many of the same files as the .war distribution. It is just missing the the
proxy servlet and dependencies.
720
probably better to use the ZIP or WAR distribution instead as some mobile devices have file size
limitations that may cause problems for the "one large single file" approach. If you do decide to use
this file for your production app (i.e. copy the file to your own web server), you will need to change
the proxy settings, as it is configured to use the proxy on the Codename One build server - which
won’t be available when the app is hosted on a different server.
The HTTP standard does support cross-origin requests in the browser via the
Access-Control-Allow-Origin HTTP header. Some web services supply this header
when serving resources, but not all. The only way to be make network requests to
arbitrary resources is to do it through a proxy.
Luckily there is a solution. The .war javascript distribution includes an embedded proxy servlet,
and your application is configured, by default, to use this servlet. If you intend to use the .war
distribution, then it should just work. You shouldn’t need to do anything to configure the proxy.
If, however, you are using the .zip distribution or the single-file preview, you will need to set up a
Proxy servlet and configure your application to use it for its network requests.
This section is only relevant if you are using the .zip or single-file distributions of
your app. You shouldn’t need to set up a proxy for the .war distribution since it
includes a proxy built-in.
The easiest way to set up a proxy is to use the Codename One cors-proxy [[Link]
cors-proxy] project. This is the open-source project from which the proxy in the .war distribution is
derived. Simply download and install the cors-proxy .war file in your JavaEE compatible servlet
container.
If you don’t want to install the .war file, but would rather just copy the proxy servlet into an
existing web project, you can do that also. See the cors-proxy wiki for more information about this
[[Link]
721
21.4.2. Step 2: Configuring your Application to use the Proxy
There are three ways to configure your application to use your proxy.
E.g.:
[Link]=[Link]
E.g.:
<script type="text/javascript">
window.cn1CORSProxyURL='[Link]
</script>
3. By setting the [Link] property in your Java source. Generally you would do this
inside your init() method, but it just has to be executed before you make a network request
that requires the proxy.
[Link]().setProperty(
"[Link]",
"[Link]
);
The method you choose will depend on the workflow that you prefer. Options #1 and #3 will almost
always result in fewer changes than #2 because you only have to set them up once, and the builds
will retain the settings each time you build your project.
[Link]().setProperty("[Link]", "true");
The browser shields some HTTP headers (e.g. "Set-Cookie") from Javascript so that your app cannot
access them. Going through the proxy works around this limitation by copying and encoding such
headers in a format that the browser will allow, and then decoding them client-side to make them
722
available to your app seamlessly.
If you are hosting your application on an Apache 2 web server with mod_proxy installed, and you
only need to make CORS requests to a single domain (or a limited set of domains), you can use
Apache to serve as your proxy. One sample configuration (which you would place either in your
VirtualHost definition or your .htaccess file is as follows:
SSLProxyEngine on
ProxyPass /app [Link]
ProxyPassReverse /app [Link]
This tells Apache to proxy all requests for '/app' to the domain [Link]
You would then need to set your CORS proxy URL in your CN1 app to "/app/".
The syntax is the same if you have multiple domains, but keep attention to the order of the lines to
make the proxy working correctly. For example:
SSLProxyEngine on
ProxyPass /app [Link]
ProxyPassReverse /app [Link]
ProxyPass /storage [Link]
ProxyPassReverse /storage [Link]
This tells Apache to proxy all requests for '/app' to the domain
[Link] and all requests for '/storage' to the domain
[Link]
You can customize this splash screen by simply modifying the HTML source inside the cn1-splash
div tag of your app’s [Link] file:
<div id="cn1-splash">
<img class="icon" src="[Link]"/>
723
21.7. Debugging
If you run into problems with your app that only occur in the Javascript version, you may need to
do a little bit of debugging. There are many debugging tools for Javascript, but the preferred tool for
debugging Codename One apps is Chrome’s debugger.
If your application crashes and you don’t have a clue where to begin, follow these steps:
3. Enable the "Pause on Exceptions" feature, then click the "Refresh" button to reload your app.
4. Step through each exception until you reach the one you are interested in. Chrome will then
show you a stack trace that includes the name of the Java source file and line numbers.
A resource is a file whose contents can be loaded by your application at runtime using
[Link]().getResourceAsStream(). In a typical Java environment, resources would be
724
stored on the application’s classpath (usually inside a Jar file). On iOS, resources are packaged
inside the application bundle. In the Javascript port, resources are stored inside the APP_ROOT/assets
directory. Historically, javascript files have always been treated as resources in Codename One, and
many apps include HTML and Javascript files for use inside the BrowserComponent
[[Link]
With the Javascript port, it isn’t quite so clear whether a Javascript file is meant to be a resource or
a library that the application itself uses. Most of the time you probably want Javascript files to be
used as libraries, but you might also have Javascript files in your app that are meant to be loaded at
runtime and displayed inside a Web View - these would be considered resources.
In order to differentiate libraries from resources, you should provide a [Link] file inside your
native/javascript directory that specifies any files or directories that should be treated as libraries.
This file can be named anything you like, as long as its name ends with [Link]. Any files or
directories that you list in this manifest file will be packaged inside your app’s includes directory
instead of the assets directory. Additionally it add appropriate <script> tags to include your
libraries as part of the [Link] page of your app.
If you include the [Link] file in your project’s src directory it could potentially
be used to add configuration parameters to platform’s other than Javascript
(although currently no other platforms use this feature). If you place it inside your
native/javascript directory, then only the Javascript port will use the
configuration contained therein.
{
"javascript" : {
"libs" : [
"[Link]"
]
}
}
I.e. It contains a object with key libs whose value is a list of files that should be treated as libraries.
In the above example, we are declaring that the file native/javascript/[Link] should be treated
as a library. This will result in the following <script> tag being added to the [Link] file:
<script src="includes/[Link]"></script>
This also caused the [Link] file to be packaged inside the includes directory
instead of the assets directory.
A project may contain more than one manifest file. This allows you to include
725
manifest files with your cn1libs also. You just need to make sure that each manifest
file has a different name.
In some cases you may want a Javascript file to be treated as a library (i.e. packaged in the includes
directory) but not automatically included in the [Link] page. Rather than simply specifying the
name of the file in the libs list, you can provide a structure with multiple options about the file. E.g.
{
"javascript" : {
"libs" : [
"[Link]",
{
"file" : "[Link]",
"include" : false
}
]
}
}
In the above example, the [Link] file will be packaged inside the includes directory, but the build
server won’t insert its <script> tag in the [Link] page.
Library Directories
You can also specify directories in the manifest file. In this case, the entire directory will be
packaged inside the includes directory of your app.
If you are including Javascript files in your app that are contained inside a
directory hierarchy, you should specify the root directory of the hierarchy in your
manifest file and use the sub "includes" property of the directory entry to specify
which files should be included with <script> tags. Specifying the file directly inside
the "libs" list will result in the file being packed directly in the your app’s includes
directory. This may or may not be what you want.
E.g.
{
"javascript" : {
"libs" : [
"[Link]",
{
"file" : "[Link]",
"include" : false
},
{
"file" : "mydir1",
726
"includes" : ["[Link]", "[Link]"]
}
]
}
}
In this example the entire mydir1 directory would be packed inside the app’s includes directory, and
the following script tags would be inserted into the [Link] file:
<script src="includes/mydir1/[Link]"></script>
<script src="includes/mydir1/[Link]"></script>
Libraries included from a directory hierarchy may not work correctly with the
single file preview that the build server generates. For that version, it will embed
the contents of each included Javascript file inside the [Link] file, but the rest
of the directory contents will be omitted. If your the library depends on the
directory hierarchy and supporting files and you require the single-file preview to
work, then you may consider hosting the library on a separate server, and
including the library directly from there, rather than embedding it inside your
project’s "native/javascript" directory.
The examples so far have only demonstrated the inclusion of libraries that are part of the app
bundle. However, you can also include libraries over the network by specifying the URL to the
library directly. This is handy for including common libraries that are hosted by a CDN.
E.g. The Google Maps library requires the Google maps API to be included. This is accomplished
with the following manifest file contents:
{
"javascript" : {
"libs" : [
"//[Link]/maps/api/js?v=[Link]"
]
}
}
This example uses the "//" prefix for the URL instead of specifying the protocol
directly. This allow the library to work for both http and https hosting. You could
however specify the protocol as well:
{
"javascript" : {
727
"libs" : [
"[Link]
]
}
}
CSS files can be included using the same mechanism as is used for Javascript files. If the file name
ends with ".css", then it will be treated as a CSS file (and included with a <link> tag instead of a
<script> tag. E.g.
{
"javascript" : {
"libs" : [
"[Link]"
]
}
}
or
{
"javascript" : {
"libs" : [
"[Link]
]
}
}
In some cases the URL for a library may depend on the values of some build hints in the project. For
example, in the Google Maps cn1lib, the API key must be appended to the URL for the API as a GET
parameter. E.g. [Link] but the
developer of the library doesn’t want to put his own API key in the manifest file for the library. It
would be better for the API key to be supplied by the developer of the actual app that uses the
library and not the library itself.
The solution for this is to add a variable into the URL as follows:
{
"javascript" : {
"libs" : [
"//[Link]/maps/api/js?v=[Link]&key={{[Link]}}"
]
}
728
}
<script src="//[Link]/maps/api/js?v=[Link]&key=XYZ"></script>
Name Description
[Link] A String, representing the entire URL of the
page, including the protocol (like [Link]
[Link] A String, representing the querystring part of a
URL, including the question mark (?)
[Link] A String, representing the domain name and
port number, or the IP address of a URL
[Link] A String, representing the anchor part of the
URL, including the hash sign (#)
[Link] A String, representing the protocol (including
://), the domain name (or IP address) and port
number (including the colon sign (:) of the URL.
For URL’s using the "file:" protocol, the return
value differs between browsers
[Link] A String, representing the pathname
[Link] A String, representing the protocol of the
current URL, including the colon (:)
[Link] A String, representing the port number of a URL.
+ Note: If the port number is not specified or if it
is the scheme’s default port (like 80 or 443), an
empty string is returned
[Link] A String, representing the domain name, or the
IP address of a URL
User-Agent The User-agent string identifying the browser,
version etc..
729
Name Description
[Link] The language code that the browser is currently
set to. (e.g. en-US)
[Link] the name of the browser as a string.
Platform a string that must be an empty string or a string
representing the platform on which the browser
is executing. + For example: "MacIntel", "Win32",
"FreeBSD i386", "WebTV OS"
[Link] the internal name of the browser
[Link] the version number of the browser
[Link] Specifies the deployment type of the app. This
will be "file" for the single-file preview,
"directory" for the zip distribution, and "war" for
the war distribution.
You can override this behavior dynamically by setting the [Link] Display
[[Link] property to a theme that you have
included in your app. All of the native themes are available on GitHub, so you can easily copy these
into your application. The best place to add the theme is in your native/javascript directory - so
that they won’t be included for other platforms.
As of Codename One 6.0, apps will automatically use the Android theme when run
on an Android device, so this example is not necessary. However the technique of
changing the native theme at runtime is still applicable.
Display d = [Link]();
if ([Link]("User-Agent", "Unknown").indexOf("Android") != -1) {
[Link]("[Link]", "/[Link]");
}
730
21.11. Disabling the 'OnBeforeUnload' Handler
By default, apps will display warning/confirm dialog when the user attempts to leave the page.
Some browsers don’t allow you to specify the message that is displayed in this
dialog. In those browsers, this property can be viewed as boolean: A null value will
result in no prompt being shown, and a non-null value will result in a prompt
being shown.
Below is a screenshot from Chrome for Android where the browser is prompting the user to add
the app to their home screen.
731
Figure 501. Add app to homescreen banner
If the app is available as a native app, in the Play store, you can indicate this using the
[Link].related_applications and [Link].prefer_related_applications
build hints. Then, instead of prompting the user to add the web app to their home screen, they’ll be
prompted to install the native app from the Play store, as shown below.
The PWA standard requires that you host your app on over HTTPS. For testing
purposes, it will also work when accessed at a localhost address. You can use the
Lighthoust PWA analysis tool [[Link]
analysis-tool] to ensure compliance.
For more information about Progressive Web Apps see Google’s introduction to the subject
[[Link]
At the heart of a progressive web app is the web app manifest [[Link]
Web/Manifest]. It specifies things like the app’s name, icons, description, preferred orientation,
display mode (e.g. whether to display browser navigation or to open with the full screen like a
native app), associated native apps, etc.. The Codename One build server will automatically
generate a manifest file for your app but you can (and should) customize this file via build hints.
Build hints of the form [Link] will be injected into the app manifest. E.g. To set the
732
app’s description, you could add the build hint:
You can find a full list of available manifest keys here [[Link]
Manifest]. The build server will automatically generate all of the icons so you don’t need to worry
about those. The "name" and "short_name" properties will default to the app’s display name, but
they can be overridden via the [Link] and [Link].short_name build
hints respectively.
One nice feature (discussed above) of progressive web apps, is the ability to specify related
applications in the app manifest. Browsers that support the PWA standard use some heuristics to
"offer" the user to install the associated native app when it is clear that the user is using the app on
a regular basis. Use the [Link].related_applications build hint to specify the location
of the native version of your app. E.g.
[Link].related_applications=[{"platform":"play", "id":"[Link]"}]
You can declare that the native app is the preferred way to use the app by setting the
[Link].prefer_related_applications build hint to "true".
Chrome and Firefox both support PWAs on desktop and on Android. iOS doesn’t support the PWA
standard, however, many aspects of it are supported. E.g. On iOS you can add the app to your home
screen, after which time it will appear and behave like a native app - and it will continue to work
while offline. However, many other nice features of PWA like "Install this app on your home
screen" banners, push notifications, and invitations to install the native version of the app, are not
supported. It is unclear when, or even, whether Apple will ever add full support; but most experts
predict that they will join the rest of the civilized world and add PWA support in the near future.
On the desktop, Chrome provides an analogous feature to "add to your homescreen": "Add to shelf".
If it looks like the user is using the app on a regular basis, and it isn’t yet installed, it will show a
banner at the top of the page asking the user if they want to add to their shelf.
733
Figure 503. Add to shelf banner
Clicking the "Add button" prompts the user for the name they wish the app to appear as:
Upon submission, Chrome will generate a real application (on Mac, it will be a ".app", on Windows,
an "exe", etc..) which the user can double click to open the app directly in the Chrome. And,
importantly, the app will still work when the user is offline.
The app will also appear in their "Shelf" which you can always access at chrome://apps, or by
opening the "Chrome App Launcher" app (on OS X this is located in "~/Applications/Chrome
Apps/Application Launcher").
The Chrome App Launcher lists apps installed both via the Chrome Web Store and
via the "Add to Shelf" feature that we discuss here. The features we describe in this
article are orthogonal to the Chrome Web Store and will not be affected by its
closure.
If your app needs to play media (e.g. [Link]()), or open a link (e.g. [Link]("…"))
without the user actually interacting physically (e.g. key press or pointer press), then it will display
a popup dialog confirming that the user actually wants to perform this action.
734
In some cases this dialog may affect the utility of the app. For example, suppose you want to play a
video in response to a voice command. Having to press an "OK" button after the command, may be
annoying. For such cases, you can use the [Link] property
to poll for media play requests on an authorized event.
For example:
[Link]("[Link]", "1000");
Do not abuse this feature. You should enable this polling only when necessary. E.g.
If your app enables the user to listen for voice commands, only enable polling for
the period of time that it is listening. When the user wants to stop listening, you
should also stop the polling by setting the interval to "0".
735
736
Chapter 22. Working with Mac OS X
22.1. Mac OS Desktop Build Options
You can configure Desktop Mac OS build settings, by opening Codename One Settings, and clicking
the "Mac Desktop Settings" button:
Here you can provide your certificate(s) as a .p12 file, and select a bundle type.
There are three bundle types which dictate what the build server produces for you when you build
your project as a Desktop Mac OS App.
1. DMG - Produces a .DMG disk image with your app. This is the preferred format for distributing
your app outside of the Mac Appstore. If you provide a Developer ID Application certificate (see
"Understanding Certificates" below), this the app will be signed so that users won’t receive
warnings about "Unidentified developer" when they install your app.
2. Sandboxed DMG - Same as DMG bundle type except that your app is set up to use the app
sandbox. Generally this would be used to test an app that is being distributed via the Appstore,
since Appstore apps must use the sandbox. If you select this bundle type, you are required to
provide a Mac App Distribution Certificate, and you should additionally specify entitlements
required for your app to function properly. For more information about the app sandbox, see
Apple’s documentation on the subject [[Link]
3. Mac Appstore Upload (PKG) - Produces a .PKG file that you can upload to the Mac appstore.
This requires that you provide both a Mac App Distribution certificate, and a Mac App Installer
certificate (see "Understanding Certificates" below). Both of these certificates should be
embedded into a single .p12 file (See "Exporting Certificates as p12" below).
Currently (as of Jan. 5, 2020) the [Link]-vm build hint is ignored if you include
a certificate with your app. This limitation should be fixed soon.
For the purposes of Mac application distribution, there are 3 types of certificates that we will be
737
interested in. The type(s) of certificate required will depend on the type of bundle you generate.
The certificate types are:
This type of certificate is used to sign an app to be distributed outside of the Mac Appstore as a
DMG image. This corresponds to the "DMG" bundle type in Codename one settings. You can
easily identity this kind of certificate because its identity will be of the form "Developer ID
Application: YOUR COMPANY NAME (SOMECODE)". E.g. Developer ID Application: Acme
Widgets Corp. (XYSD5YF).
This type of certificate is used to sign the .app bundle for an app that is to be distributed in the
Mac Appstore. This certificate is required for both the "Sandboxed DMG", and "Mac Appstore
Upload (PKG)" bundle types. You can easily identify this kind of certificate because its identity
will be of the form "3rd Party Mac Developer Application: YOUR COMPANY NAME
(SOMECODE)". E.g. 3rd Party Mac Developer Application: Acme Widgets Corp. (XYSD5YF).
This type of certificate is used to sign the .pkg installer for an app that is being submitted to the
Mac Appstore. This certificate is required for the "Mac Appstore Upload (PKG)" bundle type only.
You can easily identify this kind of certificate because its identity will be of the form "3rd Party
Mac Developer Installer: YOUR COMPANY NAME (SOMECODE)". E.g. 3rd Party Mac Developer
Installer: Acme Widgets Corp. (XYXD5YF).
If you have an Apple developer account, you can manage your certificates here
[[Link]
The screenshot above shows an account that already has the three kinds of certificates we will
require:
2. Mac App Distribution - Used for the Sandboxed DMG and Mac Appstore Upload (PKG) bundle
738
types.
3. Mac App Installer - Used for the Mac Appstore Upload (PKG) bundle type.
If your account doesn’t yet have a certificate of the required type, you should begin by pressing the
"+" button in the upper right. This will bring you to a page asking "What type of Certificate do you
need?". There are only two options on this page that we’ll be interested in:
1. Production > Mac App Store - For both the Mac App Distribution and Mac App Installer
certificates.
Select the option corresponding to the certificate you wish to generate. In either case, you’ll be
taken to a form to select whether you want an "Installer" certificate or an "Application" certificate.
Select the appropriate type.
You will then be prompted to upload a Certificate Signing Request (CSR) file, and it will provide
instructions on how to do this via the Keychain app.
You can reuse the same CSR file for generating all 3 certificates.
After generating the certificates, you should download them to your Mac, and import them into
your keychain. You should be able to accomplish this by simply double-clicking the downloaded
".cer" file, and following the prompts.
The following section requires access to a Mac, and assumes that you have already
generated your 3 certificates
Notice that Mac apps may require three different kinds of certificates, yet the settings page only
provides space for a single certificate (P12) file. This is not a mistake. P12 files may contain more
than one certificate, and you are expected to include all of the certificates that the build server may
739
require inside a single P12. The build server will automatically extract the certificates it needs
according to the bundle type.
When building the "DMG" bundle type, the build server will look for a "Developer ID Application
Certificate" inside the P12. If one is found, it will be used to sign the app bundle.
The "Sandboxed DMG" target will look for a "Mac App Distribution Certificate" certificate in the P12.
The "Mac Appstore Upload (PKG)" target will require both a "Mac App Distribution Certificate" and
a "Mac App Installer Certificate" to be included in the P12.
The easiest way to produce a P12 that includes all 3 kinds of certificates is to export them from the
Keychain Access app (Requires a Mac). Select all 3 certificates at once (using CMD-click), then right
click and select "Export 3 Items…"
You will then be prompted to select a location to save the .p12 file, as well as selecting a password
for the file.
22.1.5. Entitlements
When distributing apps in the Mac Appstore, or when using the "Sandboxed DMG" bundle type,
your app is run inside a sandboxed environment, meaning that it doesn’t have access to the outside
world. It is provided its own "sandboxed" container for file system access, and it doesn’t get any
network access. If your app requires access to the "outside world", you need to request entitlements
for that access. If you select a bundle type that uses the sandbox, you you will be shown a list of all
of the available entitlements from which you can "check" the ones that you wish to include.
For more information about the app sandbox, and a full list of entitlements, see Apple’s
documentation on the subject [[Link]
Reference/EntitlementKeyReference/Chapters/[Link]].
740
[Link]=true
[Link]
Injects the entry with key KEYNAME into the [Link] file. E.g.
[Link]=[Link]
741
742
Chapter 23. Working with Codename One
Sources
The Codename One SDK is published as a Maven multi-module project. Building the aggregator in
the /maven directory compiles every module, installs the artifacts into your local Maven repository,
and lets you depend on the exact snapshot you cloned from GitHub.
23.1. Prerequisites
Codename One uses multiple JDKs and Maven profiles during the build. Make sure your
development machine includes the following tooling (see [Link] for platform-specific
installation tips):
• macOS with Xcode (only if you plan to build or test the iOS port)
The default build runs every module and its unit tests. Append -DskipTests if you want to skip the
test phases to speed up local builds.
The version printed by Maven will usually end with -SNAPSHOT when you build from the master
branch. Release builds from Maven Central omit the suffix.
743
23.4. Running tests selectively
The Maven aggregator exposes modules for the different automated test suites. Run any of these
from the CodenameOne/maven directory:
Refer to the READMEs in maven/core-unittests and maven/tests for additional configuration flags,
platform requirements, and environment variables.
<properties>
<!-- Replace with the snapshot printed during mvn install -->
<[Link]>{cn1-snapshot-version}</[Link]>
<[Link]>{cn1-snapshot-version}</[Link]>
</properties>
Open the project in your IDE and build or run it. Maven will resolve the local snapshot instead of
downloading the latest release from Maven Central.
• Immediate access to fixes and features before they reach Maven Central.
• The ability to inspect, debug, and modify the framework when you need custom behavior.
Once you are comfortable with the baseline build, continue with the scripts in scripts/ or the
[Link] guide to compile specific ports (Android or iOS) or to automate CI workflows.
744
Appendix A: Project Archetypes
A.1. Codename One Application Project Archetype
(cn1app-archetype)
The cn1app-archetype is the basis for all maven Codename One application projects. It provides a
multimodule project with the following modules:
See Getting Started with the Bare-Bones Java App Template [[Link]
archetypes/cn1app-archetype-tutorial/[Link]] for details on using this archetype.
Also see Section [Link].1, “Bare-bones Java Project” for an example using this archetype from the
command-line to create a bare-bones Java project.
745
746
Appendix B: Maven Goals
B.1. Codename One Build (build)
The build goal is used to send builds to the Codename One build server. It also supports a few local
build targets, such as ios-source, which generates an Xcode project, and android-source which
generates an Android gradle project.
This goal is bound to the package phase of the Section A.1, “Codename One Application Project
Archetype (cn1app-archetype)”, so you generally don’t need to run this directly.
B.1.1. Example
Listing 32. Submitting a Mac Desktop build directly using this goal.
B.1.2. Properties
[Link]
Specifies the platform to build for. Values include javase, android, ios, javascript, and win.
[Link]
The build target. Different platforms support different build sets of build targets.
747
Build Target Platform Description
automated
Set to true to submit build as an automated build. When using this flag, the goal will wait for the
build server to complete the build, then download it and save it in the target directory (of the
associated module) using standard maven artifact file naming conventions. This allows you to
set up automated CI workflows more easily.
open
If set to true, this will automatically open the generated gradle or xcode project in Android
studio or Xcode. Only applicable to the ios-source and android-source buildTargets.
Default is false
This will output the cn1lib file inside the common/target directory of the root multimodule maven
project.
This goal is bound to the process-classes phase of both Section A.1, “Codename One Application
Project Archetype (cn1app-archetype)” projects and Section A.2, “Codename One Library Project
Archetype (cn1lib-archetype)” projects, so you generally shouldn’t ever need to run this goal
directly.
This goal is bound to the process-classes phase of Section A.1, “Codename One Application Project
Archetype (cn1app-archetype)” projects, so you generally shouldn’t need to run this goal directly.
748
B.5. Generate App Project (generate-app-project)
Generates a Maven project using the Section A.1, “Codename One Application Project Archetype
(cn1app-archetype)” as a basis, and applying an optional project template. This goal can also be
used to migrate legacy Codename One Ant-based Application projects into maven.
This goal should not be run inside an existing Maven project directory. It will output a project into a
directory named after the artifactId parameter.
Because there is no existing project, you will need to provide the full maven path to the goal.
mvn [Link]:codenameone-maven-plugin:$CN1VERSION:generate-app-project \
-DsourceProject=/path/to/my/ProjectTemplate \
-DgroupId=[Link] \
-DartifactId=myapp \
-Dcn1Version=$CN1VERSION
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
In the above example, assuming all went well, it would output your project into a directory named
myapp.
B.5.2. Parameters
cn1Version
The Codename One version that you want to use for the project. This will be manifested as the
[Link] and [Link] properties in the common/[Link] file of the generated
project. If omitted it will default to the cn1Version that is hard-coded in the cn1app-archetype
artifact.
sourceProject
The path to an optional project template to use. This may be either a legacy Ant project, or a
Maven project that follows the structure of Section A.1, “Codename One Application Project
Archetype (cn1app-archetype)”.
749
artifactId
The artifactId to use for the new project.
groupId
The groupId to use for the new project.
version
The version to use for the new project. Optional.
packageName
The package name to use for the new project.
This is only necessary if the sourceProject property is a Maven project. If the sourceProject is a
legacy Ant project, then this property is ignored.
mainName
The main class name to use for the new project.
This is only necessary if the sourceProject property is a Maven project. If the sourceProject is a
legacy Ant project, then this property is ignored.
When providing an Ant-based Codename One application project as the sourceProject parameter,
this goal will simply generate an equivalent Maven project to the Ant project, with the same settings
and sources.
See Section 1.2.2, “Migrating an Existing Project” for examples using this goal to migrate an existing
project into Maven.
When using a Section A.1, “Codename One Application Project Archetype (cn1app-archetype)”
maven project as the sourceProject parameter, the project will be treated project template template,
and perform some basic processing of the source files as necessary convert the template into a real
project. This includes replacing all occurrences of ${mainName} and ${packageName} in project sources
with the value of the mainName and packageName parameters provided on the command-line.
Additionally any occurrences of mainName and packagePath in file or directory names will be
swapped with the values of mainName and packagePath (which is automatically derived from
packageName by substituting '.' with file separators).
The (optional) secret sauce that differentiates a regular Maven project from a Maven project
template is the existence of a [Link] file in the root project directory. This file is
in rich property file format, and allows you to define a minimal amount of configuration details
750
that the generate-app-project goal needs to convert the template into a real project.
[Link]
The name of the main class that is currently used in this project. This property is not required if
the project is already using the mainName placeholder in the file name that contains the main
class, and the ${mainName} placeholder in any source code referring to the main class.
Often times it is just easier to specify this property here rather than injecting placeholders into
the template source base, because that way the template itself can be used as a valid project.
[Link]
The package name for the app. This property is not required if the project is already using the
packagePath placeholder in directories containing your main package files, and the
${packageName} placeholder in any source code that refers to the main package.
Often times it is just easier to specify this property here rather than injecting placeholders into
the template source base, because that way the template itself can be used as a valid project.
[Link]
Either maven or ant, depending on the project type.
dependencies
An XML snippet containing any additional Maven dependencies that should be added to the
project. This is handy of the project template relies on other cn1libs that are on Maven central.
[Link]=MyApp
[Link]=[Link]
[Link]=maven
[dependencies]
====
<dependency>
<groupId>[Link]</groupId>
<artifactId>googlemaps-lib</artifactId>
<version>1.0.1</version>
<type>pom</type>
</dependency>
====
751
This is the template that is used in Codename One initializr [[Link] for the
Bare-bones Kotlin project.
mvn cn1:clone \
-DgroupId=[Link] \
-DartifactId=newapp \
This command is formatted for the bash prompt (e.g. Linux or Mac). It will work
on Windows also if you use bash. If you are on Windows and are using PowerShell
or the regular command prompt, then you’ll need to modiy the command slightly.
In particular, the entire command would need to be on a single line. (Remove the
'\' at the end of each line, and merge lines together, with space between the
command-line flags)
In the above example, assuming all went well, it would output your project into a directory named
myapp.
B.6.2. Parameters
artifactId
The artifactId to use for the cloned project.
groupId
The groupId to use for the cloned project.
The above example will generate a GUIBuilder form with the provided class name. It effectively
generates two files:
1. common/src/main/guibuilder/com/example/[Link]
752
2. common/src/main/java/com/example/[Link]
The paths above assume that this goal was run from the root multimodule maven
project. If it were run inside the "common" submodule, then the files would simply
be generated in the "src/main/…" directory of that module. (It would try to create
yet another "common" submodule). See Project Structure [[Link]
cn1-maven-archetypes/cn1app-archetype-tutorial/[Link]#_project_structure].
You can then open the GUI builder to edit this form using the cn1:guibuilder goal.
B.7.1. Parmeters
className
(Required) The class name of the form that you wish to generate. E.g [Link].
guiType
The type of GUI component to generate. Supports "Form", "Dialog", and "Container". Default
value is "Form"
autoLayout
Whether to use autolayout. This is boolean (true/false), and the default value is true.
This is to assist in migrating Ant projects to Maven projects. This won’t make any changes to the
source Ant project. It simply generates a new project using the Section A.2, “Codename One Library
Project Archetype (cn1lib-archetype)” and copies all of files and configuration from the source
project, into the new project.
This goal is not run in an existing maven project directory, therefore we need to
include the absolute Maven coordinates for the goal.
mvn [Link]:codenameone-maven-plugin:$CN1VERSION:generate-cn1lib-project \
753
-DsourceProject=/path/to/MyLegacyAntLibraryProject \
-DgroupId=[Link] \
-DartifactId=my-maven-lib \
-Dversion=1.0-SNAPSHOT \
-U
1. This command is formatted for Unix/Mac on multiple lines, using the \ character to escape the
new-lines. On windows the command will need to be all one one line, and you should omit
those \ escape characters.
2. The -U flag tells Maven to update its catalogs to ensure that it can find the $CN1VERSION that you
specify.
If all goes well, you should find a new maven project generated in the "my-maven-lib" directory
(named after the artifactId that you specified).
To test that the project was generated successfully, try opening the resulting project in your IDE or
simply run its "install" goal on the command-line.
E.g.
cd my-maven-lib
mvn install
After running the "install" command, you should be able to add your library as a dependency to a
Section A.1, “Codename One Application Project Archetype (cn1app-archetype)” project using the
following dependency:
<dependency>
<groupId>[Link]</groupId>
<artifactId>my-maven-lib-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
Notice that the artifactId has an extra "-lib" appended. I.e. it is <artifactId>my-
maven-lib-lib</artifactId> and not <artifactId>my-maven-lib</artifactId>. This is
because the artifactId that you specify in the generate-cn1lib-project goal is used
for the "root" module of the multimodule maven project. The actual "lib" project
that you can use as a Maven dependency is the "lib" submodule, which uses the
specified artifactId with a "-lib" suffix.
754
See Section 1.5, “Codename One Libraries” for more information about the resulting maven library
project.
B.8.2. Properties
sourceProject
The path to the legacy Ant project that you want to convert to a Maven project.
groupId
The maven groupId to use for the resulting project.
artifactId
The maven artifactId to use for the resulting project.
version
The maven version to use for the resulting project. Default "1.0-SNAPSHOT"
You should run this goal explicitly after you create a native interface in your class.
See the Codename One Developer guide section on native interfaces [[Link]
[Link]#_native_interfaces] for more information on creating native interfaces.
755
_native_interfaces].
After creating this (and possibly other) native interfaces in our project, run the generate-native-
interfaces Maven goal as follows:
mvn cn1:generate-native-interfaces
This will generate the following files (if they don’t exist yet).
javase
javase/src/main/java/com/mycompany/myapp/[Link]
ios
1. ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.h
2. ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.m
android
android/src/main/java/com/mycompany/myapp/[Link]
windows (uwp)
win/src/main/csharp/com/mycompany/myapp/[Link]
javascript
javascript/src/main/javascript/com_mycompany_myapp_MyNativeImpl.js
Open and edit these files to implement your native interface methods as desired.
B.12.1. Usage
This will open the gui builder to edit the form whose class is [Link].
B.12.2. Parameters
className
The fully-qualified name to the form class that you wish to edit. This must have been previously
generated using Section B.7, “Create GUI Form (create-gui-form)”.
756
simplifying the maintenance of multiple application archetypes, but this approach
was later abandoned in favor of application project templates.
The generate-archetype mojo will generate a new archetype maven project based on an existing
archetype project. It is designed to facilitate the maintenance of several similar archetypes which
differe only in some dependencies or default project source code. It takes as input a template file
which references the base archetype project and specifies what to customize.
Example Usage
B.13.1. Parameters
template
Required. The path to a template file that should be used to generate the archetype project.
outputDir
Optional. The output directory where the archetype project should be written to. The project will
be created at outputDir/artifactId, where the artifactId is as specified in the [archetype] section
of the template. By default this will be the current working directory.
This goal does not require a project in order to run. You can run it directly using
the full goal coordinates:
mvn [Link]:codenameone-maven-plugin:7.0-SNAPSHOT:generate-archetype \
-Dtemplate=/path/to/[Link]
[dependencies] ③
----
<dependency>
<groupId>[Link]</groupId>
757
<artifactId>filechooser-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
----
*/
import static [Link].*;
import [Link].*;
import [Link].*;
import [Link].*;
import [Link].*;
import [Link];
/**
* This file was generated by <a href="[Link] One</a> for the purpose
* of building native mobile applications using Java.
*/
public class ${mainName} {
theme = [Link]("/theme");
addNetworkErrorListener(err -> {
// prevent the event from propagating
[Link]();
if([Link]() != null) {
Log.e([Link]());
}
[Link]();
[Link]("Connection Error", "There was a networking error in the connection to " +
[Link]().getUrl(), "OK", null);
});
}
[Link]();
}
758
public void destroy() {
}
① The [archetype] section specifies details about the archetype project to be generated. E.g. the
groupId, artifactId, etc…
② The extends property in the [archetype] section is required, and points to the location of the
archetype project that this template is based on. Path is relative to the location of the template
file.
③ The [dependencies] section includes content that should be injected into the <dependencies>
section of the [Link] file. Note that this is not the pom file for the archetype project itself. It is
the [Link] file for the project that the archetype project is to generate.
Notice that the above template is velocity template for a java file. It will be used as the source code
for the main class in the resulting project. It is a velocity template because maven’s
archetype:generate goal will process it to replace properties such as package and the main class
name.
Template Sections
archetype
This is a required section and specifies both the location of the base archetype project from
which this project is to be derived, and the the coordinates of the output archetype project, such
as groupId, archetypeId, and version.
Example
[archetype]
----
extends=../cn1app-archetype
groupId=[Link]
artifactId=helloworld2-archetype
version=7.0-SNAPSHOT
----
Properties
extends
Required. The path to the archetype project that this is based on. This should usually be
the cn1app-archetype project as it includes the placeholders that this generator relies on
for injecting content into the [Link] file and its project structure was used as a basis
for developing this mojo.
groupId
The groupID of the resulting archetype project. You can alternatively use the id property
to specify groupId, artifactId and version in a single string.
759
artifactId
The artifactId of the resulting archetype project. You can alternatively use the id
property to specify groupId, artifactId and version in a single string.
version
The version of the resulting archetype project. You can alternatively use the id property
to specify groupId, artifactId and version in a single string.
id
A colon-separated string in the format groupId:artifactId:version that can be used as an
alternative to groupId, artifactId, and version.
parentGroupId
If the output archetype project should be part of a multi-module project, then this will
specify the parent groupId for the <parent> tag in the [Link] file.
parentArtifactId
If the output archetype project should be part of a multi-module project, then this will
specify the parent archetypeId for the <parent> tag in the [Link] file.
parentVersion
If the output archetype project should be part of a multi-module project, then this will
specify the parent archetypeId for the <parent> tag in the [Link] file.
parent
A colon-delimited string in the format parentGroupId:parentArtifactId:parentVersion
that can be used as an alternative to the parentGroupId, parentArtifactId, and
parentVersion properties separately.
dependencies
Specify additional dependencies that should be injected into the <dependencies> section of the
[Link] file for the common module of the maven project that the archetype will generate. The
content of this section will be injected into the src/main/resources/archetype-
resources/common/[Link] of the archetype project.
Example
[dependencies]
---
<dependency>
<groupId>[Link]</groupId>
<artifactId>filechooser-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
---
760
css
CSS content that should be injected into the [Link] file of the project. This CSS will be injected
into the src/main/resources/archetype-resources/common/src/main/css/[Link] file of the
archetype project.
Example
[css]
---
#Constants {
includeNativeBool: true;
}
Button {
color:green;
border:1px solid green;
border-radius: 2mm;
margin: 5mm;
}
---
properties
Properties that should be appended to the codenameone_settings.properties file. These will be
added to the src/main/resources/archetype_resources/common/codenameone_settings.properties
file of the archetype project.
Example
[properties]
---
[Link]-vm=zuluFx8-32bit
[Link]=true
[Link]=true
[Link]=true
---
files
Files contains a list of files that should be created in the archetype project. All file paths are
relative to the "common" project root directory. Each path should have a corresponding section
with the heading [file:path/to/file].
Example
[files]
761
----
src/main/guibuilder/__mainName__MainForm.gui
src/main/java/__mainName__MainForm.java
----
[file:src/main/guibuilder/__mainName__MainForm.gui]
----
<?xml version="1.0" encoding="UTF-8"?>
[file:src/main/java/__mainName__MainForm.java]
----
package ${package};
public class ${mainName}MainForm extends [Link] {
public ${mainName}MainForm() {
this([Link]());
}
762
B.15. Install Legacy Cn1lib (install-cn1lib)
Installs a legacy cn1lib file as a dependency in this application project.
See Section 1.2.3, “Adding Project Dependencies” for more complete coverage of project
dependencies.
Generally using this goal explicitly is a last resort. The best solution for installing
add-ons into your project is Section [Link], “Managing Add-Ons in Control Center”.
This will generate a Maven pom project for this lib inside the "cn1libs" directory, and it will add a
dependency inside the common/[Link] file.
If you need to remove the cn1lib later (i.e. revert an installation), you can simply remove the
<dependency> tag that was added to the common/[Link] file for the cn1lib.
You can also remove the directory that was created inside the cn1libs folder for this cn1lib - but this
step isn’t strictly necessary.
B.15.3. Parameters
file
Path to the cn1lib file you want to install.
groupId
The groupId to use for the generated pom project. Optional. If not specified, it will use the same
groupId as the root project.
artifactId
The artifactId to use for the generated pom project. Optional. If not provided, it will generate an
artifactId derived from the project’s artifactId.
version
The version to use for the generated pom project. Optional. If not provided, it will use the project
version.
763
updatePom
A boolean flag indicating whether it should automatically update the [Link] file with the
dependency.
Default is true
overwrite
A boolean flag indicating whether it should overwrite an existing project of the same name.
Default false.
Default is false
If your project is using CSS it may not have a [Link] file, thus this goal will fail
in this case.
mvn cn1:designer
This goal is bound to the initialize phase of the javase module of the Section A.1, “Codename One
Application Project Archetype (cn1app-archetype)” and should not be executed directly.
This goal is bound to the test phase of Section A.1, “Codename One Application Project Archetype
(cn1app-archetype)” projects, so that running it directly is not necessary. If you build a cn1app-
764
archetype project using:
mvn install
or
mvn package
Prevent tests from running using the skipTests flag. E.g. mvn install -DskipTests
mvn cn1:update
Parameters
newVersion
(Optional) The version to update to. This should be a version number available on Maven
central. Will accept a value of "LATEST" to cause it to resolve to the latest version available on
Maven central.
If this parameter is omitted, then it will be implicitly set to "LATEST", but it will not update the
[Link] or [Link] properties if they are currently set to a SNAPSHOT version.
See Section 1.3, “Updating Codename One” for more information about updating Codename One.
765
766
Appendix C: API
This section is not a comprehensive treatment of the Codename One API. For a
more in-depth discussion of Codename One and it’s supported APIs, refer to the
Developer Guide [[Link]
Codename One apps support a subset of the JavaSE 8 API as well as its own light-weight runtime
and UI component library that includes support for everything that would expect in a mobile
application platform.
See the JavaDocs [[Link] for a full list of supported classes, and check
out the source in the git repository [[Link]
The Codename One source is open source. Released under GPLv2 with Classpath
Exception.
Codename One is much more than just an API library. It provides a full tool-chain and eco-system
for developing beautiful, performant native mobile apps with a single codebase in Java and Kotlin.
Please see the introduction in the Developer guide [[Link]
_introduction] for a proper overview of Codename One.
C.1. Limitations
No reflection
Codename One apps do not support reflection because reflection makes it impossible to keep
app-size down.
Codename One supports its own library format (cn1lib) which sort of "certifies" that it is compatible
with Codename One. You can browse the growing catalog of available cn1libs inside Codename One
Settings.
For more information about cn1libs, see the cn1libs section [[Link]
[Link]#_libraries_cn1lib] of the developer guide.
767
768
Appendix D: Codename One Settings
The Codename One Settings app (aka Codename One Preferences, aka Control Center) allows you to
configure many aspects of your application. This is where you can generate certificates,
browse/install add-ons, monitor the status of your cloud builds, configure build hints, and more.
Use the [Link] (or [Link], if on Windows) to open Codename One settings:
./[Link] settings
Click on the "Configuration" menu in the upper right of the toolbar, and select "Tools" > "Codename
One Settings" as shown below.
Right-click on the project in the project inspector, and select "Maven" > "Open Control Center" as
shown below:
769
D.1.4. Opening Codename One Settings from Eclipse
Press the button, and select the "My Project Settings" option. (Where My Project is the name of
your project). E.g.
770
771
772
Appendix E: Rich Properties File (rpf)
Format
The rich properties file (rpf) format is used to store configuration for the generate-app-project goal.
The format is the same as a regular properties file except that it can more easily accommodate
properties whose values are "rich" and lengthy.
[keyname] ①
=== ②
Key value ③
=== ④
④ The separator with the same number of equals signs on a line of its own.
firstName=Bob
lastName=Smith
[bio]
====
Bob is a hard worker.
He attended Harvard and is looking for opportunities in the fast food industry.
====
age=23
favoriteColor=Brown
Many of the properties of this file are just regular properties. There are two rich properties: bio and
xmldata.
773
774
Historical Reference
775
776
Chapter 24. Working with UWP
UWP Apps may distributed in 2 different ways:
1. In the Windows App Store. (This should be used for deployment of any production app).
2. Outside of the Windows App Store via sideloading directly onto a device. This should only be
used for development.
Before you can side-load apps onto your phone, you’ll need to set up your phone for development.
777
2. Select "Developer mode"
3. Under "Device Discovery", make sure that the "Make your device visible to USB connections and
your local network" is set to "On".
5. When you switch "Device Portal" to "On" it should show you an address that you can access the
Phone at via wifi. (E.g. [Link] Remember this address, you’re going to use it to install
all of your apps onto the device.
This will be a local address within your local network. It won’t be available to
the outside world.
778
At this point, your phone should be ready to receive "Side-loaded" apps. This was a one-time setup,
so you shouldn’t have to do it again, until you set up another device.
Now that your device is set up for development, you can proceed to build your app.
1. Select the "Mobile Debug Build" option in the UWP Codename One Settings.
2. Select the "Send Windows UWP Build" option in the Codename One menu of your IDE. This will
initiate the build on the Codename One build server.
3. Log into the Codename One dashboard to watch the build progress. When it is complete, you’ll
be able to download the ".appxbundle" file to your desktop.
You cannot simply download the .appxbundle file directly to your Windows
Phone 10 mobile device and install it. It will indeed allow you to download it,
and will give you an option to install it, but the install will silently fail.
1. Point your computer’s web browser to the address for your mobile device. (This is the address
listed when you turned on the "Device Portal" in the "Enabling Developer Mode on Device"
section above. This will open the App Manager page.
779
3. If this is the first time installing a UWP (debug) app on your device, you will need to install the
dependencies. You can find the dependencies for mobile/ARM apps here [[Link]
codenameone/cn1-binaries/tree/master/uwp/Dependencies/ARM]. You’ll need to install both
[Link] and [Link]. If this is not
the first time installing a UWP app, you can skip to the next step.
a. Under the "Install App" section, click the "Choose File" button and navigate through the file
chooser to select the "[Link]" file. Then click "Go".
4. Under the "Install App" section, click the "Choose File" button and navigate through the file
chooser to select the .appxbundle file for your app.
5. Once you have the appxbundle selected, you should press "Go" under the "Deploy" subheading.
This will install the app and, if all went well, your app will appear in the "Recently Added"
section in the apps list of the phone.
The easiest way to be able to run your development apps on a Windows 10 PC is to enable
developer mode. This will allow you to install any app even if it is just "self-signed".
To enable developer mode, open "Settings", then select "Updates an Security". Under the "For
Developers" menu item, select "Developer Mode" as shown below:
780
Building the App
Before building the app, you’ll need to ensure that the build target is set to "Debug Desktop" in the
Codename One Settings panel for Windows apps.
Steps:
1. Open Codename One Settings (steps vary by IDE). On Netbeans you will find "Codename One
Settings" by right clicking your project’s node in the project explorer, and look in the
"Codename One" submenu:
3. Under "Build Type", make sure that "Desktop Debug Build" is selected, as shown below:
4. Save the changes by clicking the "Disk" icon in the upper right:
Now you can proceed to send the build to the build server.
1. Select the "Send Windows UWP Build" option in the Codename One menu of your IDE. This will
initiate the build on the Codename One build server.
781
2. Log into the Codename One dashboard to watch the build progress. When it is complete, you’ll
be able to download the ".zip" file to the Windows 10 PC on which you wish to install the app.
Start by extracting the .zip file. (Navigate to the folder where the zip was downloaded, right click it,
and select "Extract all" as shown below:
After extraction, open the resulting directory. You should see contents similar to the following:
Downloading Dependencies
If this is your first time installing a UWP app on this PC, you may need to add the dependencies
before you can install. You can download the dependencies here [[Link]
binaries/raw/master/uwp/[Link]]. Extract "[Link]" and copy the resulting
"Dependencies" directory into the app install directory. Your app install directory should now look
like:
782
We are finally at the point where we can run the installer.
You may be prompted that you need to change the execution policy, in Powershell:
If all goes well, you should see a message saying that the app as successfully installed.
And if you look in your "Windows Menu" under "All Apps", you should see your app listed there:
If you want to be able to distribute your app to the public, the Windows Store is your best channel.
Building for the Windows store involves roughly 3 steps:
783
1. Reserve a name for your app in the Windows Store
2. Build your app using the "Windows Store Upload" build type.
If you don’t already have an account, sign up for one. Then log in. Once logged in, you can click the
"Dashboard" link on the toolbar.
Under the "Your apps" section (on the left in the above screenshot), click the "Create new app"
button.
Enter a name for your app, and click "Reserve app name".
784
If the name was available, it should take you to the app overview page for your new app. There’s
quite a few options there to play with, but we’re not going to worry about any of them for now. All
we need to know is:
1. Your App’s ID
You can get this information by scrolling down to the bottom of the "App overview" page and
clicking the "View app identity details" under the "App Management" > "App identity" section:
The next step is to copy this information into your Codename One project.
Open up the Codename One Settings for your project and go to the "Windows Settings" section.
It is important that your App ID and Publisher Display Name match exactly what
you have in the store, or your app will fail at the validation stage when you try to
upload your app to the store.
This will open a dialog titled "Certificate Generator". Paste the value from the
"Package/Identity/Publisher" listed in the Windows Store into the Publisher ID field as shown
below:
785
Then click OK. This will generate a .pfx file inside your project folder.
The "Display Name" must also match that app name in the store.
Finally, make sure that "Windows Store Upload" is selected in the "Build Type" field. For the
example above, my settings form looks like the following screenshot when I am done.
When you are done, hit the "Save" icon in the upper right corner of the window to save your
changes.
Finally, select "Codename One" > "Send Windows UWP Build" in your IDE’s project explorer.
This will produce an .appxupload file that you can upload to the Windows Store.
The best way to debug apps on device is to enable crash protection in your app. This can be enabled
by adding the following to your app’s init() method:
[Link](true);
With crash protection enabled, you’ll receive an email whenever an exception is thrown that isn’t
caught in your application code. The email will include the stack trace of the error along with any
output you had previously provided using the [Link] class (e.g. Log.p() and Log.e()).
One major annoyance of UWP is that it doesn’t provide line numbers in its stack traces. Here is
what you can expect to see in a stack trace:
786
[EDT] 0:0:7,830 - OS win
[EDT] 0:0:7,836 - Error [Link]
[EDT] 0:0:7,836 - Current Form null
[EDT] 0:0:7,836 - Exception: [Link] - null
at [Link](Exception e, Boolean needFileInfo)
at [Link].get_StackTrace()
at [Link]()
at [Link]()
at [Link]..ctor()
at [Link]..ctor()
at [Link]..ctor()
at [Link]..ctor()
at [Link].__mapImpl(Exception )
at [Link][T](Exception x, Boolean remap, Boolean unused)
at [Link][T](Exception x, MapFlags mode)
at [Link](Runnable r, Boolean dropEvents)
at [Link](Runnable r)
at [Link](Component n1, Int32 n2, Int32 n3, String n4, Int32 n5)
at [Link](Component cmp, Int32 maxSize, Int32 constraint, String
text, Int32 initiatingKeycode)
at [Link](Component cmp, Int32 maxSize, Int32 constraint, String text, Int32
initiatingKeycode)
at [Link](Component cmp, Int32 maxSize, Int32 constraint, String text)
at [Link]()
at [Link](Int32 x, Int32 y)
at [Link](Int32 x, Int32 y)
at [Link](Int32 x, Int32 y)
at [Link](Int32[] x, Int32[] y)
at [Link](Int32 offset)
at [Link]()
at [Link]()
at [Link]()
at [Link]()
at [Link].threadProc2()
at [Link]()
at [Link]()
at [Link].<>c__DisplayClass6_0.<init>b__0()
at [Link]()
at [Link]()
at [Link](Object obj)
at [Link](ExecutionContext executionContext, ContextCallback callback, Object state)
at [Link](Task& currentTaskSlot)
at [Link](Boolean bPreventDoubleExecution)
at [Link](Object obj)
at [Link].ThreadStart_Context(Object state)
at [Link](ExecutionContext executionContext, ContextCallback callback, Object state)
at [Link](Object obj)
Originating from:
Message=Object reference not set to an instance of an object.
at [Link]()
at [Link]()
at [Link]()
at [Link](Runnable r, Boolean dropEvents)
It will show you the call stack with the names of the methods. But it won’t show you the line
numbers. If the stack trace isn’t specific enough, you can add Log.p() statements in various
positions in my code to help narrow down the source of the exception.
787
opacity are set using the unselected style of the form being shown.
You can override these colors application-wide using the following display properties:
e.g.
Display d = [Link]();
[Link]("[Link]", [Link](0xff0000)); // red
[Link]("[Link]", [Link](0xffffff)); // white
[Link]("[Link]", [Link](255)); // fully opaque
The following value would associate the app with the file extension ".alsdk". This example is taken
from this MSDN document [[Link]
activation?f=255&MSPPError=-2147217396].
<uap:Extension Category="[Link]">
<uap:FileTypeAssociation Name="alsdk">
<uap:Logo>images\[Link]</uap:Logo>
<uap:SupportedFileTypes>
<uap:FileType>.alsdk</uap:FileType>
</uap:SupportedFileTypes>
</uap:FileTypeAssociation>
</uap:Extension>
<uap:Extension Category="[Link]">
<uap:FileTypeAssociation Name="pdf">
<uap:Logo>images\[Link]</uap:Logo>
<uap:SupportedFileTypes>
<uap:FileType ContentType="application/pdf">.pdf</uap:FileType>
</uap:SupportedFileTypes>
788
</uap:FileTypeAssociation>
</uap:Extension>
For more information about using the "AppArg" property, see this blog post
[[Link] which describes its usage on iOS
and Android for intercepting URL types.
789