Sending arguments to the build server

When you send a build to the server, you can provide more parameters. The server incorporates those parameters into the build process as build hints.

These hints are often called "build hints" or "build arguments." They behave like compiler flags that tune the build server’s behavior. That makes them useful for fast iteration on new functionality without rebuilding plugin UI for every change. They’re also useful when you need to expose low-level behavior such as customizing the Android manifest XML or the iOS plist.

Launch Codename One Settings from the Maven project root, then select Hints in the left navigation rail. The hints use the key=value style.

mvn cn1:settings
The build hints UI in Codename One Settings
Figure 313. The build hints UI in Codename One Settings

You can also set the build hints directly in the codenameone_settings.properties file. When you do that, each setting must start with the codename1.arg. prefix. For example, android.debug=true becomes codename1.arg.android.debug=true.

Application code can read a custom build argument through Display.getProperty():

class AppArgSnippet {
    public void readArgument() {
        String arg = Display.getInstance().getProperty("AppArg", null);
    }
}

Here is the current list of supported arguments, rendered from the annotations and the catalog that declare them:

Most of the commonly used hints also have a compiler-checked form: an annotation in com.codename1.annotations.buildhints that you put on the application’s main class. Written that way a misspelled name is an unknown symbol and an unsupported value is an unknown enum constant, instead of a properties line that is accepted, never read, and has no effect. The Annotation column below names that form where one exists. Setting the same hint both ways fails the build.

Each annotation groups the hints of one platform, and the attribute names match the part of the hint name after the prefix, so codename1.arg.ios.pods is @Ios(pods = …​):

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN,
     newStorageLocation = Toggle.ON,
     pods = {"Intercom", "AFNetworking"})
public class BuildHintAnnotationSnippet {
}

That’s the same as writing these lines in codenameone_settings.properties:

codename1.arg.and.themeMode=modern
codename1.arg.android.min_sdk_version=24
codename1.arg.nativeTheme=modern
codename1.arg.desktop.titleBar=native
codename1.arg.desktop.width=1280
codename1.arg.desktop.height=800
codename1.arg.ios.themeMode=modern
codename1.arg.ios.newStorageLocation=true
codename1.arg.ios.pods=Intercom,AFNetworking

Three things in that example are worth calling out.

A hint whose value is a list takes a Java array, and the build joins it with the separator that hint uses — a comma for ios.pods, so you never have to remember which hint wants which delimiter.

A boolean hint takes Toggle, not boolean. Toggle.ON and Toggle.OFF say what you want, and leaving the attribute out entirely means you have said nothing, so the build service applies whatever it applies today. That’s why the Default column reads (set by the build) for these hints: the annotation records no default of its own, by design: a value compiled into your app can’t follow the build service when the service changes it.

A hint with a closed set of values takes an enum, so @Ios(themeMode = …​) offers you the four themes that exist and rejects anything else at compile time. Hints whose accepted values are open-ended stay String.

The long tail of hints has no annotation, and neither do the open-ended families such as android.permission.<NAME>, because a Java annotation can’t express a map. Set those in codenameone_settings.properties exactly as before. You can mix the two forms in one project, as long as no single hint is declared in both.

Build hints are the one part of Codename One that doesn’t promise source compatibility, and that’s deliberate.

Everywhere else, code that compiles against one release keeps compiling against the next. Here, a hint that changes its name, its type or its accepted values is meant to stop compiling, so you find out at your desk. The alternative is what the properties file has always done: accept the line, never read it, and leave you with a build that succeeds and an app that doesn’t do what you asked.

The failure is always on the client side, never on the build service. A hint reaches the service as a name and a string, and the service goes on accepting the strings it always did — an app already built keeps building, and an older Codename One keeps talking to a newer service. A changed hint breaks a compile in your project, with the attribute named.

If a release does take an annotation away, the properties form of the same hint still works, so the upgrade costs a line in codenameone_settings.properties rather than being one you can’t take.

Table 18. Build hints
NameTypeDefaultAnnotationDescription

and.captureRecord

string

(none)

(none)

Override alias of android.captureRecord, read after it and winning when set.

and.facebook_permissions

string

(none)

(none)

Override alias of android.facebook_permissions, read after it and winning when set. IPhoneBuilder also falls back to it when ios.facebook_permissions is unset.

and.themeMode

auto, modern, hololight, legacy

(set by the build)

@Android(themeMode)

auto, modern / material, hololight (default for existing apps), legacy. auto and modern / material opt in to the CSS-generated Android Material 3 theme from native-themes/android-material/theme.css. hololight is Android Holo Light (what the framework shipped on API 14+ before this refactor). legacy loads the pre-Holo Android theme. The legacy alias cn1.androidTheme is still accepted, and and.hololight=true still maps to hololight. The default stays on hololight for existing apps until you flip in a future release.

android.NotificationChannel.description

string

Remote notifications

(none)

android.NotificationChannel.enableLights

boolean

true

(none)

android.NotificationChannel.enableVibration

boolean

false

(none)

android.NotificationChannel.id

string

cn1-channel

(none)

android.NotificationChannel.importance

int

2

(none)

android.NotificationChannel.lightColor

string

(none)

(none)

android.NotificationChannel.name

string

Notifications

(none)

android.NotificationChannel.vibrationPattern

string

(none)

(none)

android.accessibilityGuard

boolean

false

(none)

android.accessibilityGuard.allow

string

(none)

(none)

android.accessibilityGuard.mode

string

exit

(none)

android.activity.launchMode

string

(set by the build)

@Android(activityLaunchMode)

Allows explicitly setting the android:launchMode attribute of the main activity in android. Default is "singleTop," but for some applications you may need to change this behaviour. In particular, apps that are meant to open a file type will need to set this to "singleTask." See Android docs for the activity element for more information about the android:launchMode attribute.

android.activityClassBody

string

(none)

(none)

android.activityClassImports

string

(none)

(none)

android.adaptiveIconBackground

string

#ffffff

(none)

Background color to use for adaptive icons when android.enableAdaptiveIcons=true and no background image is supplied. Defaults to #ffffff and is written as @color/ic_launcher_background.

android.adaptiveIconBackgroundImage

string

(none)

(none)

Optional path (relative to the root of the native Android project) to an image file to use as the adaptive icon background when android.enableAdaptiveIcons=true. If this property is set, it overrides android.adaptiveIconBackground.

android.allowBackup

boolean

true

(none)

android.androidAuto.messaging

boolean

false

(none)

android.androidAuto.minCarApiLevel

int

1

(none)

android.androidAuto.navigation

boolean

false

(none)

android.androidAuto.poi

boolean

false

(none)

android.anyDensity

boolean

true

(none)

android.apacheLegacy

boolean

false

(none)

android.appBundle

boolean

(set by the build)

@Android(appBundle)

Produces an Android App Bundle (.aab) rather than an APK. Required for new Play Store submissions.

android.appReview.version

version

2.0.1

(none)

android.ar.required

boolean

false

(none)

android.arrcompile

string

(none)

(none)

android.arrimplementation

string

(none)

(none)

android.asyncPaint

boolean

true

(none)

Boolean true/false defaults to true. Toggles the Android pipeline between the legacy pipeline (false) and new pipeline (true)

android.background_push_handling

boolean

false

(none)

android.billingclient.version

version

8.0.0

(none)

The Play Billing Library version an in-app-purchase app is built against, defaulting to 8.0.0. Codename One’s billing implementation uses the ProductDetails API, so 8.0.0 is also the minimum: Play Billing removed the older SkuDetails API, and a build set lower is refused with an explanation rather than a page of compiler errors. The default is the lowest version Google still accepts for new apps and updates, and the only one at that level whose library is content with minSdkVersion 21; every later release requires 23. Setting a newer version raises android.min_sdk_version to match, because the library’s own manifest would otherwise fail the merge.

android.blockExternalStoragePermission

boolean

false

(none)

Boolean true/false defaults to false. Disables the external storage (SD card) permission

android.blockLabel

boolean

false

(none)

Boolean true/false defaults to false. Leaves android:label off the generated <application> tag so a label set through android.xapplication_attr or a merged manifest is the one that survives. Honoured by the wear module’s tag as well as the phone’s.

android.blockReadMediaPermissions

boolean

(none)

(none)

Boolean true/false, defaults to the value of android.blockExternalStoragePermission. Suppresses the READ_MEDIA_VIDEO and READ_MEDIA_AUDIO permissions that playing a URI adds on API 33 and above

android.bluetooth.neverForLocation

boolean

true

(none)

android.bluetooth.required

boolean

false

(none)

android.buildToolsVersion

version

(set by the build)

@Android(buildToolsVersion)

Android build-tools version. It also selects the compile SDK, so there is no separate compile-SDK hint.

android.call.video

boolean

false

(none)

Whether the manifest declares CAMERA, which video calls need. Overrides call.video on Android. CallConfiguration.videoSupported(true) is a runtime decision no scanner can see, so the build has to be told here as well; without it Calls.getCapabilities() omits CAPABILITY_VIDEO.

android.captureRecord

string

(set by the build)

@Android(captureRecord)

Indicates whether the RECORD_AUDIO permission should be requested. Can be enabled or any other value to disable this option

android.carAppVersion

version

1.4.0

(none)

android.credentialsPlayServicesVersion

string

(none)

(none)

android.credentialsVersion

version

1.3.0

(none)

android.cusom_layout

string

(none)

(none)

android.cusom_layout*

string

(none)

(properties file only)

Numbered custom layout resources: android.cusom_layout1, android.cusom_layout2 and upward. The misspelling is load-bearing: it’s the key the builder actually reads, so correcting it drops the layout with no warning.

android.cusom_layout1

string

(none)

(none)

Applies to any number of layouts as long as they’re in sequence (for example, android.cusom_layout2, android.cusom_layout3 etc.). Will write the content of the argument as a layout XML file and give it the name cusom_layout1.xml onwards. This can be used by native code to work with XML files

android.customActivity

string

CodenameOneActivity

(none)

android.customTabsVersion

version

1.8.0

(none)

android.debug

boolean

(set by the build)

@Android(debug)

Whether to include the debug version in the build. Left alone, this follows [#release] rather than a fixed value, so a build that selects neither still produces something installable.

android.decouplePlayServiceVersions

string

(none)

(none)

android.delayPushCompletion

boolean

false

(none)

android.disableR8

boolean

(set by the build)

@Android(disableR8)

Turns off R8, falling back to the older shrinker. Note that hardening requires R8, so this conflicts with harden.level.

android.disableR8FullMode

boolean

true

(none)

android.disableScreenshots

boolean

false

(none)

android.documentProvider.enabled

boolean

false

(none)

The Android counterpart of ios.documentProvider.enabled, and the supported way to declare the feature when the code that publishes lives in a cn1lib rather than in the app: the build’s usage scan reads the application’s own classes, so an app that only calls a library which publishes documents goes undetected. Falls back to ios.documentProvider.enabled when unset, so a project that sets one gets both.

android.enableAdaptiveIcons

boolean

false

(none)

Boolean true/false defaults to false. Enables Android adaptive icon generation in Android Gradle builds. When enabled, Codename One generates mipmap launcher resources (ic_launcher, ic_launcher_foreground, and adaptive XML in mipmap-anydpi-v26) and uses them in the application manifest (android:icon and android:roundIcon).

android.enableProguard

boolean

(set by the build)

@Android(enableProguard)

Boolean true/false defaults to true. Allows disabling the proguard obfuscation even on release builds, notice that this isn’t recommended

android.excludeBolts

boolean

false

(none)

android.extendAppCompatActivity

boolean

false

(none)

android.facebookSdkVersion

version

16.2.0

(none)

android.facebook_permissions

string

"public_profile","email","user_friends"

(none)

Permissions for Facebook used in the Android build target, applicable only if Facebook native integration is used.

android.file_paths

string

` <files-path name="app_files" path="." /><external-files-path name="app_external_files" path="." /><external-cache-path name="app_external_cache" path="." /><external-path name="external" path="." />`

(none)

The FileProvider roots written into file_paths.xml, besides the cache/intent_files one the framework always needs. A file has to be under one of these for the application to hand it to another application — when sharing, or when dragging it out. Setting this replaces the default rather than adding to it.

android.firebaseAnalytics

boolean

false

(none)

android.firebaseAnalyticsVersion

version

21.5.0

(none)

android.firebaseCoreVersion

string

(none)

(none)

android.firebaseMessagingVersion

string

(none)

(none)

android.foldableSupport

boolean

false

(none)

android.forceJava8Builder

boolean

false

(none)

android.foregroundServiceType

string

dataSync

(none)

android.fridaDebugLogging

boolean

(none)

(none)

Boolean true/false defaults to false. If true, it will add verbose debug logs during frida detection to show which check if fails on.

android.fridaDetection

boolean

false

(none)

Boolean true/false defaults to false. Indicates whether the app should check for the presence of the Frida dynamic instrumentation toolkit on the device. If Frida is detected, the app will exit. This uses the [frida-blocker](https://github.com/shannah/frida-blocker) library to perform the frida detection.

android.fridaVersion

string

(none)

(none)

x.y.z The version of [frida-blocker](https://github.com/shannah/frida-blocker) to use to perform frida detection. This is only relevant if android.fridaDetection=true. If omitted, it will use the latest tested version in the build server.

android.fullScreenIntent

boolean

false

(none)

android.googleAdUnitId

string

(none)

(none)

Allows integrating admob/google play ads, this is effectively identical to google.adUnitId but only applies to Android

android.googleAdUnitTestDevice

string

C6783E2486F0931D9D09FABC65094FDF

(none)

Device key used to mark a specific Android device as a test device for Google Play ads defaults to C6783E2486F0931D9D09FABC65094FDF

android.gpsPermission

boolean

false

(none)

Indicates whether the GPS permission should be requested, it’s autodetected by default if you use the location API. But, some code might want to explicitly define it

android.gradle.androidx

list (newline delimited)

(none)

(none)

android.gradleDep

list (; delimited)

(set by the build)

@Android(gradleDep)

Gradle dependency statements to add to the app module, such as implementation 'com.example:lib:1.0'.

android.gradlePlugin

list (newline delimited)

(none)

(none)

android.hce

boolean

false

(none)

android.hceAids

string

F0010203040506

(none)

android.hceCategory

string

other

(none)

android.hceDescription

string

(none)

(none)

android.hceRequireUnlock

boolean

false

(none)

android.headphoneCallback

boolean

false

(none)

Boolean true/false defaults to false. When set to true it assumes the main class has two methods: headphonesConnected & headphonesDisconnected which it invokes appropriately as needed

android.health.background

boolean

false

(none)

android.health.connectVersion

string

1.1.0-alpha07

(none)

android.health.history

boolean

false

(none)

android.health.privacyPolicyUrl

string

(none)

(none)

android.health.read

string

(none)

(none)

android.health.write

string

(none)

(none)

android.hideOverlayWindows

boolean

false

(none)

Boolean true/false defaults to false. Declares the android.permission.HIDE_OVERLAY_WINDOWS permission needed by DeviceIntegrity.setHideOverlayWindows() on Android 12+, for apps that call the runtime API without enabling android.tapjackingGuard. A normal install-time permission, so the user sees no prompt.

android.hideStatusBar

boolean

(set by the build)

@Android(hideStatusBar)

Hides the Android status bar.

android.hms.pushVersion

string

6.3.0.302

(none)

android.home.playServicesVersion

string

16.0.0-beta1

(none)

android.includeGPlayServices

boolean

true

(none)

Deprecated, please android.playService.*! Indicates whether Google Play Services should be included into the build, defaults to false but that might change based on the functionality of the application and other build hints. Adding Google Play Services support allows you to use a more refined location implementation and invoke some Google specific functionality from native code.

android.includeMavenCentral

boolean

false

(none)

android.installLocation

auto, internalOnly, preferExternal

(set by the build)

@Android(installLocation)

Maps to android:installLocation manifest entry defaults to auto. Can also be set to internalOnly or preferExternal.

android.invite.appLinks

boolean

true

(none)

Whether the build injects the android:autoVerify intent filter for the invite link domain into the main activity. Set it to false only when declaring the filter yourself through android.xintent_filter; with no filter at all an invite link opens the browser instead of the app.

android.invite.signingFingerprint

list (, delimited)

(none)

(none)

Comma separated SHA-256 signing certificate fingerprints, in colon separated hex, enrolled in the shared assetlinks.json alongside the one derived from the build’s keystore. Apps distributed through Play App Signing must add the app signing certificate fingerprint from the Play Console here: Google re-signs the app, so the upload key the build holds isn’t the certificate Android verifies against. Without it, App Links verification fails on every Play install and nothing reports an error.

android.java8

string

(none)

(none)

Accepted and ignored. A Java 6 source level was produced by running retrolambda over the compiled classes; retrolambda has been removed, so Android builds always use a Java 8 source level. Setting this to false logs a notice and changes nothing.

android.keyboardOpen

boolean

true

(none)

Boolean true/false defaults to true. Toggles the new async keyboard mode that leaves the keyboard open while you move between text components

android.kotlinStdlibAlignment

boolean

true

(none)

Boolean true/false defaults to true. Kotlin 1.8.0 moved the contents of kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 into kotlin-stdlib and left the two shims empty. A build that reaches kotlin-stdlib 1.8 or newer through one dependency and an older kotlin-stdlib-jdk8 through another then carries the same classes twice and fails in checkReleaseDuplicateClasses, naming Kotlin artifacts you never asked for. The 1.8.x line ships no Gradle module metadata to say the two overlap; from 1.9.22 JetBrains ships it. This adds that missing statement, as a Gradle capability: from 1.8.0 up, kotlin-stdlib provides what the shims provide, so Gradle drops the redundant shim. It moves no version, which is what keeps it out of your way — a version pin, a force, an enforced BOM, a range or a Kotlin compiler older than 1.8 all resolve exactly as they did without it. Below 1.8.0 nothing happens at all, because there the shims still hold the only copy of their classes. Set to false to manage these coordinates yourself.

android.largeScreens

boolean

true

(none)

android.licenseKey

string

(set by the build)

@Android(licenseKey)

The license key for the Android app, this is required if you use in-app purchase on Android

android.locales

string

(none)

(none)

android.locationButton.exclusive

string

auto

(none)

Whether ACCESS_FINE_LOCATION is declared onlyForLocationButton, which means the system grants precise location through the location button and never any other way. Defaults to auto: the build infers it, declaring the flag when the application uses LocationButton and nothing else from the location or maps packages. Set false when the application reaches precise location through native Android code, which the class scan can’t see because Gradle compiles it later, and true to declare the flag whenever the build declares the permission at all, whatever the inference would have chosen. Neither value makes the build declare a permission it otherwise would not: an application whose only location use is invisible to the class scan gets no declaration to flag, and reaches precise location through its own android.xpermissions entry.

android.manifest.queries

string

(none)

(none)

Embeds XML content into the <queries> section of the Android manifest file. This is required in Android 11 for package visibility. See queries element Android documentation.

android.maps.provider

string

(none)

(none)

Android’s own native map provider, overriding maps.provider.

android.messagingService

string

(none)

(none)

android.migrateToAndroidX

boolean

true

(none)

android.min_sdk_version

19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36

(set by the build)

@Android(minSdkVersion)

The least SDK required to run this app, the default value changes based on functionality but can be as low as 7. This corresponds to the XML attribute android:minSdkVersion.

android.mockLocation

boolean

true

(none)

Boolean true/false defaults to true. Toggles the mock location permission which is on by default, this allows easier debugging of Android device location based services

android.mopubId

string

(none)

(none)

android.multidex

boolean

(set by the build)

@Android(multidex)

Multidex lets an Android binary reference more than 65536 methods. Set [Toggle#OFF] to opt out, which builds a little faster and reinstates the limit.

android.nearby.computerProfile

boolean

false

(none)

Offers the computer device profile in the companion-device chooser. Only read when the app uses nearby ranging, transport or companion association.

android.nearby.glassesProfile

boolean

false

(none)

Offers the glasses device profile in the companion-device chooser, on the same terms as android.nearby.computerProfile.

android.nearby.watchProfile

boolean

false

(none)

Offers the watch device profile in the companion-device chooser, on the same terms as android.nearby.computerProfile.

android.newFirebaseMessaging

boolean

(set by the build)

@Android(newFirebaseMessaging)

Uses the current Firebase Cloud Messaging integration. Requires AndroidX and Gradle 8.13 or newer.

android.nonconsumable

string

(none)

(none)

Comma delimited string of items that are non-consumable in the in-app purchase API

android.normalScreens

boolean

true

(none)

android.onCreate

string

(none)

(none)

android.onDeviceDebug

boolean

(set by the build)

@OnDeviceDebug(android)

Boolean true/false defaults to false. When true, the generated AndroidManifest.xml is marked android:debuggable="true", R8/proguard is disabled, and the build is pinned to debug-only (android.release is forced off and android.debug is forced on) so a stray hint can’t ship a release-signed APK that’s debuggable="true". Pair with the cn1:android-on-device-debugging Maven goal (or the bundled IntelliJ run configs) to install, launch, forward JDWP, and stream logcat through adb. Has no effect on builds that don’t carry it — release builds are unaffected. See the On-Device Debugging (Android) chapter for the full flow.

android.permission.*

string

(none)

(properties file only)

true/false. Whether to include a particular permission. Preferred over android.xpermissions because it avoids conflicts with libraries. See Android’s Manifest.permission documentation for the full list. The optional .maxSdkVersion suffix becomes the maxSdkVersion attribute of the generated <uses-permission> tag, and .required marks the permission required.

android.playIntegrity

boolean

false

(none)

android.playIntegrity.verifyUrl

string

(none)

(none)

android.playIntegrityVersion

version

1.4.0

(none)

android.playService.*

string

(none)

(properties file only)

Opts a single Google Play service in or out. The sibling <name>.minPlayServicesVersion pins its version.

android.playService.ads

boolean

false

(none)

android.playService.analytics

string

(none)

(none)

android.playService.appInvite

boolean

false

(none)

android.playService.auth

string

(none)

(none)

android.playService.base

string

(none)

(none)

android.playService.cast

boolean

false

(none)

android.playService.drive

boolean

false

(none)

android.playService.fitness

boolean

false

(none)

android.playService.games

boolean

false

(none)

android.playService.gcm

string

(none)

(none)

android.playService.identity

boolean

false

(none)

android.playService.indexing

boolean

false

(none)

android.playService.location

string

(none)

(none)

android.playService.maps

string

(none)

(none)

android.playService.nearby

boolean

false

(none)

android.playService.panorama

boolean

false

(none)

android.playService.plus

boolean

false

(none)

android.playService.safetynet

boolean

false

(none)

android.playService.vision

boolean

false

(none)

android.playService.wallet

boolean

false

(none)

android.playService.wearable

boolean

false

(none)

android.playServicesVersion

string

(none)

(none)

The version number of play services to build against. Experimental. Use with caution as building against versions other than the server default may introduce incompatibilities with some Codename One APIs.

android.proguardKeep

list (newline delimited)

(set by the build)

@Android(proguardKeep)

Arguments for the keep option in proguard allowing you to keep a pattern of files for example, -keep class com.mypackage.ProblemClass { *; }

android.proguardKeepOverride

string

Exceptions, InnerClasses, Signature, Deprecated, SourceFile, LineNumberTable, Annotation, EnclosingMethod

(none)

android.pushSound

string

(none)

(none)

android.pushVibratePattern

string

(none)

(none)

Comma delimited long values to describe the push pattern of vibrate used for the setVibrate native method

android.release

boolean

(set by the build)

@Android(release)

true/false defaults to true - indicates whether to include the release version in the build

android.removeBasePermissions

boolean

false

(none)

Boolean true/false defaults to false. Disables the built-in permissions specifically INTERNET permission (that is, no networking…​)

android.repositories

list (newline delimited)

(set by the build)

@Android(repositories)

Extra Gradle repositories to resolve dependencies from.

android.requestReadMediaPermissions

boolean

false

(none)

Boolean true/false defaults to false. Declares READ_MEDIA_IMAGES, READ_MEDIA_VIDEO and READ_MEDIA_AUDIO on API 33 and above even when the build detected no media playback. READ_MEDIA_IMAGES is only ever added by this hint

android.rootCheck

boolean

false

(none)

Boolean true/false defaults to false. Indicates whether the app should check for root access on the device. If root access is detected, the app will exit.

android.rootbeerVersion

version

0.1.0

(none)

android.shareFilter

string

(none)

(none)

android.sharedUserId

string

(none)

(none)

Allows adding a manifest attribute for the sharedUserId option

android.sharedUserLabel

string

(none)

(none)

Allows adding a manifest attribute for the sharedUserLabel option

android.shrinkResources

boolean

false

(none)

Boolean true/false defaults to false. Used only in conjunction with android.enableProguard. Strips out unused resources to reduce apk size. Since 7.0

android.signingV1

boolean

(none)

(none)

true/false Default true. See https://source.android.com/docs/security/features/apksigning

android.signingV2

boolean

(none)

(none)

true/false Default true. See https://source.android.com/docs/security/features/apksigning

android.signingV3

boolean

(none)

(none)

true/false Default true. See https://source.android.com/docs/security/features/apksigning

android.signingV4

boolean

(none)

(none)

true/false Default true. See https://source.android.com/docs/security/features/apksigning

android.smallScreens

boolean

true

(none)

Boolean true/false defaults to true. Corresponds to the android:smallScreens XML attribute and allows disabling the support for small phones

android.stack_size

string

(none)

(none)

Size in bytes for the Android stack thread

android.statusbar_hidden

boolean

false

(none)

true/false defaults to false. When set to true hides the status bar on Android devices.

android.store_ids

string

(none)

(none)

android.streamMode

string

(none)

(none)

The mode in which the volume key should behave, defaults to OS default. Allows setting it to music for music playback apps

android.stringsXml

string

(none)

(none)

Allows injecting more entries into the strings.xml file using a value that includes something like this <string name="key1">value1</string><string name="key2">value2</string>

android.style

string

(none)

(none)

Allows injecting more data into the styles.xml file right before the closing resources tag

android.supportScreens

string

(none)

(none)

android.supportV4

boolean

(none)

(none)

Boolean true/false defaults to false but that can change based on usage (for example, push implicitly activates this). Indicates whether the android support v4 library should be included in the build

android.supportv4Dep

list (newline delimited)

(none)

(none)

android.surfaces.complicationUpdateSeconds

int

0

(none)

UPDATE_PERIOD_SECONDS on the generated complication service. Zero, the default, means the system never polls on a timer and the complication updates only when the app pushes new data.

android.surfaces.exactAlarms

boolean

false

(none)

android.tapjackingGuard

boolean

false

(none)

Boolean true/false defaults to false. Switches on tapjacking / screen-overlay protection at launch, so touches that arrive while another app’s window covers this one are detected and dropped. See the security chapter.

android.tapjackingGuard.hideOverlays

boolean

true

(none)

Boolean true/false defaults to true. Also asks Android 12+ to hide overlay windows drawn over the app, which is the only mitigation that covers native peer components, and declares the HIDE_OVERLAY_WINDOWS permission it requires. Only relevant if android.tapjackingGuard=true.

android.tapjackingGuard.mode

string

block

(none)

block (default), strict, report or off. block drops gestures that start on a fully obscured window, report only observes, strict also drops touches where only part of the window is covered (which benign system UI can trigger). Only relevant if android.tapjackingGuard=true.

android.targetSDKVersion

int

(set by the build)

@Android(targetSDKVersion)

The Android SDK the build compiles against. Unset, the build server uses the highest platform it has installed, so leaving this alone tracks the server rather than pinning a number. Not every target works: the source may have limitations, and not all SDK targets are installed.

android.textureView

boolean

false

(none)

android.theme

string

Light

(none)

Light or Dark defaults to Light. On Android 4+ the default Holo theme is used to render the native widgets sometimes and this indicates whether holo light or holo dark is used. This doesn’t affect the Codename One theme but that might change in the future.

android.topDependency

list (newline delimited)

(set by the build)

@Android(topDependency)

Statements added to the top-level Gradle build file rather than the app module.

android.tv

boolean

false

(none)

true/false (defaults to false). Marks the build as an Android TV / Google TV app. Adds the LEANBACK_LAUNCHER intent category to the launcher activity (so the app appears on the TV home screen), declares the android.software.leanback feature, makes android.hardware.touchscreen optional (so it installs on touchless TVs), and generates a 320×180 launcher banner (@drawable/tv_banner) from the app icon. The same APK still installs and runs on phones and tablets, and CN.isTV() returns true at runtime on a TV.

android.useAndroidX

boolean

(set by the build)

@Android(useAndroidX)

Use Android X instead of support libraries. This will also run a find/replace on all source files to replace support libraries and artifacts with AndroidX equivalents.

android.useGradle8

string

(none)

(none)

android.uses_feature.*

string

(none)

(properties file only)

Adds a <uses-feature> element named by the suffix.

android.uses_permission.*

string

(none)

(properties file only)

Adds a <uses-permission> element named by the suffix.

android.versionCode

string

(none)

(none)

Allows overriding the auto generated version number with a custom internal version number specifically used for the XML attribute android:versionCode

android.watchModule

boolean

true

(none)

Boolean true/false defaults to true. Set to false to build the phone app alone in a companion build: the wearable link stays, no watch module is generated, and the phone output matches what it was before the watch app existed.

android.watchVersionCode

int

(none)

(none)

The wear module’s version code, stated outright. Play requires it to be higher than the phone’s, so a value other than a whole number above android.versionCode fails the build rather than being replaced without a word. Leave it unset to derive the value from android.watchVersionCodeOffset.

android.watchVersionCodeOffset

int

100000000

(none)

How far above the phone’s version code the wear module’s sits when android.watchVersionCode is unset. The default leaves room for the phone app to keep incrementing without ever catching up.

android.wear

boolean

false

(none)

android.wear.complicationsVersion

string

1.2.1

(none)

Version of androidx.wear.watchface:watchface-complications-data-source added to the wear module. Kept out of android.gradleDependencies because that hint feeds the phone module too, and these libraries declare minSdk 26.

android.wear.guavaVersion

string

31.1-android

(none)

Version of com.google.guava:guava added to the wear module alongside the tiles and complications libraries, which need it at runtime.

android.wear.protoLayoutVersion

string

1.2.1

(none)

Version of the androidx.wear.protolayout libraries the generated tile service builds its layout with.

android.wear.standalone

string

(none)

(none)

android.wear.tilesVersion

string

1.4.1

(none)

Version of androidx.wear.tiles added to the wear module when the app declares a tile.

android.web_loading_hidden

boolean

false

(none)

true/false defaults to false - set to true to hide the progress indicator that appears when loading a web page on Android.

android.windowVersion

version

1.3.0

(none)

android.xactivity

xml

(none)

(none)

Allows injecting more attributes into the activity tag in the Android XML

android.xapplication

xml

(set by the build)

@Android(xapplication)

defaults to an empty string. Allows developers of native Android code to add text within the application block to define things such as widgets, services etc.

android.xapplication_attr

xml

(none)

(none)

Allows injecting more attributes into the application` tag in the Android XML

android.xgradle

list (newline delimited)

(set by the build)

@Android(xgradle)

Arbitrary text spliced into the generated app-module Gradle file.

android.xgradle_default_config

list (newline delimited)

(none)

(none)

android.xintent_filter

xml

(none)

(none)

Allows adding an intent filter to the main android activity

android.xlargeScreens

boolean

true

(none)

android.xlayout_attr

string

(none)

(none)

android.xmanifest

xml

(none)

(none)

android.xpermissions

xml

(set by the build)

@Android(xpermissions)

more permissions for the Android manifest

desktop.adaptToRetina

boolean

(set by the build)

@DesktopBuild(adaptToRetina)

Boolean true/false defaults to true. When set to true some values will ve implicitly doubled to deal with retina displays and icons etc. Will use higher DPI’s

desktop.fontSizes

string

(none)

(none)

Indicates the sizes in pixels for the system fonts as a comma delimited string containing 3 numbers for small,medium,large fonts.

desktop.fullscreen

boolean

(set by the build)

@DesktopBuild(fullscreen)

Starts the desktop build in full-screen mode.

desktop.height

int

(set by the build)

@DesktopBuild(height)

Height in pixels for the form in desktop builds, will be doubled for retina grade displays. Defaults to 600.

desktop.interactiveScrollbars

boolean

(set by the build)

@DesktopBuild(interactiveScrollbars)

Enables grab-able, click-to-page desktop scrollbars.

desktop.resizable

boolean

(set by the build)

@DesktopBuild(resizable)

Boolean true/false defaults to true. Indicates whether the UI in the desktop build is resizable

desktop.theme

string

(none)

(none)

Name of the theme res file (without the ".res" extension) to use as the "native" theme. By default this is native indicating iOS theme on Mac and Windows Metro on Windows. If its something else then the app will try to load the file /themeName.res (placed in native/Java SE directory).

desktop.themeMac

string

(none)

(none)

Same as desktop.theme but specific to macOS

desktop.themeMode

string

(set by the build)

@DesktopBuild(themeMode)

Which native theme a desktop build installs, and the one hint that decides whether a desktop application looks like the platform it’s running on. One desktop binary runs on Windows, macOS and Linux, so the value is resolved against the machine the application starts on rather than at build time. auto, native and modern are one value under three spellings and select the host’s own look: Fluent, Aqua or Adwaita. Naming a theme outright with fluent, aqua or adwaita pins that one look on every machine instead, which is what an application with a deliberate cross-platform identity wants. legacy, which is also the default, keeps whatever the application was built and tested against before these themes existed, and custom installs no framework theme at all so the application’s own is the only one loaded. The per-value and per-platform tables, and how this relates to the iOS, Android, macOS and cross-platform theme hints, are on the @DesktopBuild annotation itself. Read by the JavaSE port at runtime rather than by a builder, so unlike most hints here it changes what the running application does rather than what’s produced for it.

desktop.themeWin

string

(none)

(none)

Same as desktop.theme but specific to Windows

desktop.title

string

(none)

(none)

desktop.titleBar

native, custom, toolbar

(set by the build)

@DesktopBuild(titleBar)

How the desktop window is framed: native for the OS title bar and menu bar, custom for an undecorated window with a Codename One drawn title bar, or toolbar for the legacy in-app Toolbar. An unrecognized value falls back to native with a warning.

desktop.width

int

(set by the build)

@DesktopBuild(width)

Width in pixels for the form in desktop builds, will be doubled for retina grade displays. Defaults to 800.

desktop.win.cef

boolean

(none)

(none)

Whether to use CEF for media and BrowserComponent instead of JavaFX in windows desktop builds. true/false. Default value is false (Jan 2021), but this will be changed to true in a future version.

desktop.windowsOutput

string

(none)

(none)

Can be exe or msi depending on desired results

KeepScreenOn

boolean

false

(none)

androidx.appcompat.version

string

(none)

(none)

block_server_registration

boolean

(none)

(none)

true/false flag defaults to false. By default Codename One applications register with the Codename One server. Setting this to true blocks them from sending information to the Codename One cloud, which is kept for statistical purposes and may be used to provide more installation stats in the future.

build.cn1Version

string

(none)

(none)

Pro/Enterprise only. Pins the cloud build to a specific released Codename One version using the Maven release scheme (for example 7.0.182), or to master to build against the current development head. The build server fetches that version’s framework artifacts. Pro accounts can target versions published within the last two months; Enterprise within the last six months. Requesting an older version, a version that was never published, or using this hint without a Pro/Enterprise subscription fails the build with an explanatory error. See Versioned builds.

build.incSources

string

(none)

(none)

build.testReporter

string

(none)

(none)

build.unitTest

string

(none)

(none)

call.video

boolean

false

(none)

Whether video calls are offered, on both platforms. ios.call.video and android.call.video override it per platform.

cn1.androidTheme

string

(none)

(none)

Deprecated alias for and.themeMode (AndroidGradleBuilder.java:4097). Both names configure one setting, so declaring this alongside @Android(themeMode) is a conflict.

cn1.buildKey

string

(none)

(none)

cn1.entitled

boolean

true

(none)

cn1.harden.forceOff

string

(none)

(none)

cn1.hardenLevel

string

off

(none)

cn1.hardened

boolean

false

(none)

cn1.hardening.libraryJars

string

(none)

(none)

cn1.mappingId

string

(none)

(none)

cn1.nativeTheme

string

(none)

(none)

Deprecated alias for nativeTheme (AndroidGradleBuilder.java:4099, IPhoneBuilder.java:947). Both names configure one setting, so declaring this alongside @Build(nativeTheme) is a conflict.

codename1.mac.appid

string

(none)

(none)

Mac Native cloud builds only. The Mac bundle identifier registered in App Store Connect / Apple Developer. Distinct from codename1.ios.appid because Apple treats the iOS and Mac App Store records as separate products. Required for cloud Mac builds.

codename1.mac.certificate

string

(none)

(none)

Mac Native cloud builds only. Path to the .p12 file containing the Mac signing certificate(s) — Mac App Distribution (3rd Party Mac Developer Application) for App Store builds, Developer ID Application for Developer ID builds, or both bundled into the same P12 when macNative.distribution=both. Not interchangeable with the iOS distribution certificate. Required for cloud Mac builds.

codename1.mac.certificatePassword

secret

(none)

(none)

Mac Native cloud builds only. Password to unlock the P12 referenced by codename1.mac.certificate. Required for cloud Mac builds.

codename1.mac.provision

string

(none)

(none)

Mac Native cloud builds only. Path to the Mac provisioning profile (.provisionprofile). Apple issues distinct provisioning profiles for Mac App Store and Developer ID distribution — pass the one that matches the chosen channel.

db.legacy

string

(none)

(none)

delayPushCompletion

boolean

false

(none)

facebook.appId

string

(set by the build)

@Build(facebookAppId)

The application ID for an app that requires native Facebook login integration, this defaults to null which means native Facebook support shouldn’t be in the app

facebook.clientToken

secret

(none)

(none)

The client token for an app that requires native Facebook login integration, this is required if the facebook.appId is set.

gcm.sender_id

string

(set by the build)

@Build(gcmSenderId)

The Android/chrome push identifier, see the push section for more details

google.adUnitId

string

(none)

(none)

Allows integrating Admob/Google Play ads into the application see this

gradleDependencies

list (newline delimited)

(none)

(none)

harden.*

string

(none)

(properties file only)

The whole hardening namespace is swept into the hardening engine’s configuration, so a hint added there reaches it without a dedicated reader.

harden.*.enabled

string

(none)

(properties file only)

Enables or disables hardening for one platform slice.

harden.allowUnhardenedLocalBuild

boolean

(set by the build)

@Hardening(allowUnhardenedLocalBuild)

Permits a local or source build to run with hardening requested but not applied. Without it such a build is refused, so a hardened app is never shipped from a target that can’t actually harden it.

harden.controlFlow

off, on

(set by the build)

@Hardening(controlFlow)

Overrides control-flow obfuscation independently of harden.level.

harden.ios.enabled

boolean

true

(none)

harden.keep

text_block

(set by the build)

@Hardening(keep)

Keep rules in ProGuard syntax, one per line, for classes that are resolved by name at runtime and so can’t be found by the automatic analysis. Same syntax as android.proguardKeep, so existing rules port directly. Rules are separated by newlines only, because a semicolon is legal inside a rule body such as { *; }.

harden.level

off, standard, aggressive, paranoid

(set by the build)

@Hardening(level)

Master switch for app hardening: off, standard, aggressive or paranoid. An unrecognized value fails the build rather than being treated as off.

harden.mac.enabled

boolean

true

(none)

harden.rename

boolean

(set by the build)

@Hardening(rename)

Overrides symbol renaming independently of harden.level.

harden.strings

off, constants, all

(set by the build)

@Hardening(strings)

Overrides string obfuscation independently of harden.level: off, constants or all.

harden.tv.enabled

boolean

true

(none)

harden.watch.enabled

boolean

true

(none)

invite.domain

string

cloud.codenameone.com

(none)

The host that serves Codename One invite links (https://<host>/i/<slug>/<code>;). Changing it points the generated Android App Link intent filter and the iOS associated domain at a different link service; the matching apple-app-site-association and assetlinks.json must be served from that host.

invite.slug

string

(none)

(none)

This app’s path segment in its invite links (https://<host>/i/<slug>/<code>;), shown in the invite settings of the build console. Set it so the generated Android App Link filter matches only this app’s own links. The link domain is shared by every invite-enabled app, so when this is unset the filter matches every invite link on the domain and a device with two such apps installed may show a chooser or open the other one.

java.version

int

8

(none)

Valid values include 5 or 8. Indicates the JVM version that should be used for server compilation, this is defined by default for newly created apps based on the Java 8 mode selection

mac.desktop-vm

string

(none)

(none)

The JVM the should be bundled with Mac desktop build. Mac desktop builds only. Supported values: zuluFx8, zulu11, zuluFx11

maps.provider

string

(none)

(none)

Selects the native map provider. android.maps.provider and ios.maps.provider override it for one platform.

nativeTheme

modern, native, legacy, custom

(set by the build)

@Build(nativeTheme)

native, modern, legacy, custom (default unset). Cross-platform override that sets ios.themeMode and and.themeMode together when those aren’t set explicitly. modern = liquid glass + Material 3, legacy = iOS 7 flat + Holo Light, custom disables the framework native theme entirely. The legacy alias cn1.nativeTheme is still accepted. native is modern plus the desktop: it additionally selects the host’s own desktop theme — Fluent, Aqua or Adwaita — the way desktop.themeMode = auto does. modern stops short of the desktop on purpose, because it predates the desktop themes by years and an application that set it for its phone builds never asked for its desktop screens to be redrawn. desktop.themeMode overrides this hint either way, and the full per-platform table is on the @DesktopBuild annotation.

nativeVerify

string

(none)

(none)

strict or warn turns on ParparVM’s native signature check for this build; anything else leaves it off, which is the default. ParparVM encodes the whole Java signature in the C function name, so a native spelled even slightly differently never reaches the linker as an error: the correctly named symbol is simply absent, the dead-code pass reads that as unused, and the feature ships inert. ios.nativeVerify, linux.nativeVerify and windows.nativeVerify override it for one platform.

noExtraResources

boolean

(set by the build)

@Build(noExtraResources)

true/false (defaults to false). Blocks codename one from injecting its own resources when set to true, the only effect this has is in slightly reducing archive size. This might have adverse effects on some features of Codename One so it isn’t recommended.

requireKotlinStdlib

string

(none)

(none)

tvMain

string

(none)

(none)

var.*

string

(none)

(properties file only)

Defines a variable that any other hint can interpolate as ${var.name}, with ${var.name:default} for a fallback.

vserv.allowSkipping

boolean

true

(none)

vserv.category

int

29

(none)

vserv.countryCode

string

null

(none)

vserv.locale

string

en_US

(none)

vserv.networkCode

string

null

(none)

vserv.scaleMode

boolean

false

(none)

vserv.transition

int

300000

(none)

vserv.zone

string

(none)

(none)

watchMain

string

(none)

(none)

watchStandalone

boolean

false

(none)

xxx.minPlayServicesVersion

string

(none)

(none)

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 android.minPlayServicesVersion with the exception that the "highest version wins." That way if your cn1lib requires play services 9+ and uses: myLib.minPlayServicesVersion=9.0.0 and another library has otherLib.minPlayServicesVersion=10.0.0 then play services will be 10.0.0

ios..appext.

string

(none)

(properties file only)

Per-app-extension signing. ios.debug.appext.<Name>.* and ios.release.appext.<Name>.* are collapsed to unqualified keys before the request is sent.

ios.NFCReaderUsageDescription

string

(none)

(none)

ios.NS*UsageDescription

string

(none)

(properties file only)

Info.plist privacy strings. The commonly used keys are catalogued individually and exposed through @IosPrivacy; this entry covers the open tail that the builder sweeps by prefix.

ios.NSBluetoothAlwaysUsageDescription

string

(set by the build)

@IosPrivacy(bluetoothAlwaysUsageDescription)

Why the app uses Bluetooth. Supplied automatically when the app references com.codename1.bluetooth; set it to say something more specific than the default.

ios.NSBluetoothPeripheralUsageDescription

string

(set by the build)

@IosPrivacy(bluetoothPeripheralUsageDescription)

The pre-iOS 13 spelling of the Bluetooth usage description, supplied and overridable on the same terms.

ios.NSBonjourServices

string

(none)

(none)

ios.NSCalendarsFullAccessUsageDescription

string

(set by the build)

@IosPrivacy(calendarsFullAccessUsageDescription)

The text iOS shows when the app first asks for the calendars full access. It becomes the NSCalendarsFullAccessUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSCalendarsUsageDescription

string

(set by the build)

@IosPrivacy(calendarsUsageDescription)

The text iOS shows when the app first asks for the calendars. It becomes the NSCalendarsUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSCalendarsWriteOnlyAccessUsageDescription

string

(set by the build)

@IosPrivacy(calendarsWriteOnlyAccessUsageDescription)

The text iOS shows when the app first asks for the calendars write only access. It becomes the NSCalendarsWriteOnlyAccessUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSCameraUsageDescription

string

(set by the build)

@IosPrivacy(cameraUsageDescription)

The text iOS shows when the app first asks for the camera. It becomes the NSCameraUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSHealthShareUsageDescription

string

(set by the build)

@IosPrivacy(healthShareUsageDescription)

The text iOS shows when the app first asks for the health share. It becomes the NSHealthShareUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSHealthUpdateUsageDescription

string

(set by the build)

@IosPrivacy(healthUpdateUsageDescription)

The text iOS shows when the app first asks for the health update. It becomes the NSHealthUpdateUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSLocalNetworkUsageDescription

string

(set by the build)

@IosPrivacy(localNetworkUsageDescription)

The text iOS shows when the app first asks for the local network. It becomes the NSLocalNetworkUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSLocationAlwaysAndWhenInUseUsageDescription

string

(set by the build)

@IosPrivacy(locationAlwaysAndWhenInUseUsageDescription)

The text iOS shows when the app first asks for the location always and when in use. It becomes the NSLocationAlwaysAndWhenInUseUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSLocationAlwaysUsageDescription

string

(set by the build)

@IosPrivacy(locationAlwaysUsageDescription)

The text iOS shows when the app first asks for the location always. It becomes the NSLocationAlwaysUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSLocationWhenInUseUsageDescription

string

(set by the build)

@IosPrivacy(locationWhenInUseUsageDescription)

The text iOS shows when the app first asks for the location when in use. It becomes the NSLocationWhenInUseUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSMicrophoneUsageDescription

string

(set by the build)

@IosPrivacy(microphoneUsageDescription)

The text iOS shows when the app first asks for the microphone. It becomes the NSMicrophoneUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSNearbyInteractionAllowOnceUsageDescription

string

(set by the build)

@IosPrivacy(nearbyInteractionAllowOnceUsageDescription)

The pre-iOS 16 spelling of the nearby-interaction usage description, supplied automatically when the app references the nearby APIs.

ios.NSNearbyInteractionUsageDescription

string

(set by the build)

@IosPrivacy(nearbyInteractionUsageDescription)

Why the app measures distance and direction to nearby devices. Supplied automatically when the app references the nearby APIs; set it to say something more specific than the default.

ios.NSRemindersFullAccessUsageDescription

string

(set by the build)

@IosPrivacy(remindersFullAccessUsageDescription)

The text iOS shows when the app first asks for the reminders full access. It becomes the NSRemindersFullAccessUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSRemindersUsageDescription

string

(set by the build)

@IosPrivacy(remindersUsageDescription)

The text iOS shows when the app first asks for the reminders. It becomes the NSRemindersUsageDescription key in Info.plist. The App Store rejects an app that touches this resource without one.

ios.NSSpeechRecognitionUsageDescription

string

(set by the build)

@IosPrivacy(speechRecognitionUsageDescription)

Why the app sends speech for recognition. Supplied automatically when the app references the speech APIs; set it to say something more specific.

ios.NSXXXUsageDescription

string

(none)

(none)

iOS privacy flags for using certain APIs. Starting with Xcode 8, you’re required to add usage description strings for certain APIs. Find a full list of the available keys in Apple’s docs. Some relevant ones include ios.NSCameraUsageDescription, ios.NSContactsUsageDescription, ios.NSLocationAlwaysUsageDescription, NSLocationUsageDescription, ios.NSMicrophoneUsageDescription, ios.NSPhotoLibraryAddUsageDescription, ios.NSSpeechRecognitionUsageDescription, ios.NSSiriUsageDescription

ios.UIRequiredDeviceCapabilities

string

(none)

(none)

ios.actionSheetStyle

string

(none)

(none)

ios.add_libs

list (; delimited)

(set by the build)

@Ios(addLibs)

A semicolon separated list of libraries that should be linked to the app to build it

ios.afterFinishLaunching

string

(none)

(none)

Objective-C code that can be injected into the iOS app delegate at the bottom of the body of the didFinishLaunchingWithOptions callback method

ios.appAttest

boolean

false

(none)

ios.appAttest.environment

string

(none)

(none)

ios.appUsesNonExemptEncryption

string

(none)

(none)

ios.app_groups

string

(none)

(none)

Space-delimited list of app groups that this app belongs to as described in Apple’s documentation. These are added to the entitlements file with key com.apple.security.application-groups.

ios.appext.NAME.provisioningURL

string

(none)

(none)

Cloud device builds only. URL of the provisioning profile for a generic app extension dropped into ios/app_extensions/NAME/ (or a generated extension such as CN1Widgets), used when the extension folder doesn’t bundle a .mobileprovision itself. The profile is installed on the build machine and added to the export options per bundle id. Used for both debug and release builds unless a qualified variant (below) is set. An extension is signed against its own App ID, so a device build with no profile for it — by any of the three carriers — is refused unless the app’s own profile is a wildcard that covers the extension’s bundle id.

ios.applicationDidEnterBackground

string

(none)

(none)

Objective-C code that can be injected into the iOS callback method (message) applicationDidEnterBackground.

ios.applicationQueriesSchemes

list (, delimited)

(set by the build)

@Ios(applicationQueriesSchemes)

Comma separated list of url schemes that canExecute will respect on iOS. If the url scheme isn’t mentioned here canExecute will return false starting with iOS 9. Notice that this collides with ios.plistInject when used with the <key>LSApplicationQueriesSchemes</key>…​ value so you should use one or the other. For example, to enable canExecute for a url like myurl://xys you can use: myurl,myotherurl

ios.application_exits

boolean

(none)

(none)

true/false (defaults to false). Indicates whether the application should exit on home button press. The default is to exit, leaving the application running is only tested at the moment.

ios.associatedDomains

string

(none)

(none)

Comma-delimited list of domains associated with this app. Each domain should be prefixed by a supported prefix. For example, "applinks:" or "webcredentials:." See Apple’s documentation on Associated domains for more information.

ios.backgroundProcessingIds

string

(none)

(none)

ios.background_modes

string

(none)

(none)

ios.beforeFinishLaunching

text_block

(set by the build)

@Ios(beforeFinishLaunching)

Objective-C code that can be injected into the iOS app delegate at the top of the body of the didFinishLaunchingWithOptions callback method

ios.bitcode

boolean

false

(none)

true/false defaults to false. Enables bitcode support for the build.

ios.blockScreenshotsOnEnterBackground

boolean

false

(none)

true/false (defaults to false). Indicates that app should prevent iOS from taking screenshots when app enters background. Described here.

ios.bluetooth.background

string

(none)

(none)

ios.buildType

string

debug

(none)

ios.bundleVersion

version

(set by the build)

@Ios(bundleVersion)

Indicates the version number of the bundle, this is useful if you want to create a minor version number change for the beta testing support

ios.call.appGroup

string

(none)

(none)

The App Group the call directory extension shares with the app. The extension runs in its own process, so the blocked-number list is handed over through the group rather than passed in. Defaults to group.<packageName>.

ios.call.directory.buildSettings.*

string

(none)

(properties file only)

Xcode build settings for the generated call directory extension target. PRODUCT_BUNDLE_IDENTIFIER is read in two places — the target’s own settings and the CN1CallDirectoryExtensionIdentifier the host plist carries — so an override has to reach both or the app asks the system to reload an identifier nothing installed.

ios.call.icon

string

(none)

(none)

A template image in the bundle shown beside the call in the system UI.

ios.call.providerName

string

(none)

(none)

The name shown above a call in the system call UI. Defaults to the app’s display name. Baked into Info.plist because CallKit needs it during launch, before any of your code has run.

ios.call.pushTTL

int

30

(none)

Seconds a pushed call waits for your code before the system ends it as unanswered.

ios.call.recents

boolean

true

(none)

Whether calls appear in the system call log.

ios.call.ringtone

string

(none)

(none)

A sound file in the bundle to ring incoming calls with. Defaults to the system ringtone.

ios.call.unknownCaller

string

(none)

(none)

The name shown when a VoIP push carries no displayName.

ios.call.video

boolean

false

(none)

Whether the provider offers video calls. Overrides call.video on iOS.

ios.carplay.audio

boolean

false

(none)

ios.carplay.messaging

boolean

false

(none)

ios.carplay.navigation

boolean

false

(none)

ios.carplay.poi

boolean

false

(none)

ios.continuity.sync

boolean

(none)

(none)

Whether this project wants the iCloud key-value store behind com.codename1.continuity.sync. Left unset the build decides from the bytecode, which is usually what you want. Set false when the App ID has no iCloud capability and the app can live without a synced store: the entitlement is dropped and SyncedStore reports itself unsupported at runtime rather than the build failing to sign. Set true to say so explicitly, which is what lets the signing preflight check the profile before the build is sent. Handing work to a nearby device is unaffected either way — that half needs no entitlement.

ios.convertSignalsToExceptions

boolean

true

(none)

ios.criticalAlerts

boolean

false

(none)

ios.crypto.gcm

boolean

false

(none)

ios.debug.archs

string

(none)

(none)

Can be set to "armv7" to force iOS debug builds to be 32 bit. By default, debug builds are 64 bit only.

ios.debug.distributionMethod

string

(none)

(none)

Specifies distribution type for debug iOS builds only. This is used for enterprise or ad-hoc builds (using values "enterprise" and "ad-hoc" respectively).

ios.debug.teamId

string

(none)

(none)

Specifies the team ID associated with the iOS debug provisioning profile and certificate.

ios.delayPushCompletion

boolean

false

(none)

ios.dependencyManager

auto, cocoapods, spm, both, none

(set by the build)

@Ios(dependencyManager)

Which native dependency manager to use: auto picks one from whichever of ios.pods and ios.spm.packages is set, and cocoapods, spm or both require the matching hint to be set. An unrecognized value fails the build.

ios.deployment_target

version

(set by the build)

@Ios(deploymentTarget)

Minimum iOS version the build targets. Set it to the lowest iOS you actually support; a higher value excludes older devices from the App Store listing.

ios.detectJailbreak

boolean

false

(none)

true/false (defaults to false). When true, the iOS app will exit on launch if it detects that it’s running on a jailbroken device.

ios.devLocale

string

(none)

(none)

ios.disableScreenshots

boolean

false

(none)

ios.distributionMethod

string

(none)

(none)

Specifies distribution type for debug iOS builds. This is used for enterprise or ad-hoc builds (using values "enterprise" and "ad-hoc" respectively).

ios.documentProvider.appGroup

string

(none)

(none)

App Group id starting with 'group.' shared by the app and the generated CN1Documents extension, defaulting to 'group.' followed by the app’s package name. This id carries everything the two processes share: the app publishes its index into the group and the extension reads it from there, so without it the published location appears empty.

ios.documentProvider.buildSettings.*

string

(none)

(properties file only)

Overrides an Xcode build setting for the generated document provider extension. Applied last, so it wins over the generated defaults.

ios.documentProvider.deploymentTarget

version

16.0

(none)

Minimum OS version the generated extension runs on. At or above 16.0 the extension is an NSFileProviderReplicatedExtension; below it Codename One generates the deprecated NSFileProviderExtension instead. The host app’s own deployment target is unaffected.

ios.documentProvider.displayName

string

(none)

(none)

Name shown for this location in the file browser. Defaults to the app’s display name.

ios.documentProvider.enabled

boolean

false

(none)

Declares that this project publishes documents to the system file browser. The build detects a reference to com.codename1.documents on its own, so this is redundant for the build itself; it exists because the Certificate Wizard and the signing preflight work without reading bytecode and need to know that the CN1Documents extension will be generated.

ios.documentProvider.extension

boolean

true

(none)

Set false to skip the iOS lowering entirely — no extension target, no app group, no plist keys — leaving com.codename1.documents an inert no-op at runtime.

ios.enableAutoplayVideo

boolean

false

(none)

Boolean true/false defaults to false. Makes videos "autoplay" when loaded on iOS

ios.enableBadgeClear

boolean

true

(none)

Boolean true/false defaults to true. Clears the badge value with every load of the app, this is useful if the app doesn’t manually keep track of number values for the badge

ios.enableGalleryMultiselect

boolean

false

(none)

ios.enableStatusBar7

boolean

true

(none)

ios.entitlements.*

string

(none)

(properties file only)

Adds an arbitrary entitlement key to the generated entitlements file.

ios.entitlements.com.apple.developer

string

(none)

(none)

ios.entitlements.com.apple.developer.applesignin

string

(none)

(none)

ios.entitlements.com.apple.developer.healthkit

boolean

false

(none)

ios.entitlements.com.apple.developer.homekit

string

(none)

(none)

ios.entitlements.com.apple.developer.networking.HotspotConfiguration

string

(none)

(none)

ios.entitlements.com.apple.developer.nfc.hce

string

(none)

(none)

ios.entitlements.com.apple.developer.nfc.readersession.formats

string

(none)

(none)

ios.entitlementsInject

xml

(none)

(none)

Content to inject into the iOS entitlements file. This should be in the Plist XML format. See Apple Entitlements Documentation.

ios.facebook.usePods

boolean

true

(none)

ios.facebook.version

string

~>5.6.0

(none)

ios.facebook_permissions

string

(none)

(none)

Permissions for Facebook used in the Android build target, applicable only if Facebook native integration is used.

ios.failOnWarning

boolean

false

(none)

ios.fieldNullChecks

boolean

false

(none)

ios.fileSharingEnabled

boolean

false

(none)

ios.firebaseAnalytics

boolean

false

(none)

ios.firebaseAnalyticsVersion

string

(none)

(none)

ios.force64

boolean

false

(none)

ios.glAppDelegateBody

string

(none)

(none)

Objective-C code that can be injected into the iOS app delegate within the body of the file before the end. This only makes sence for methods that aren’t already declared in the class

ios.glAppDelegateHeader

text_block

(set by the build)

@Ios(glAppDelegateHeader)

Objective-C code that can be injected into the iOS app delegate at the top of the file. For example, if you need to include headers or make special imports for other injected code

ios.googleAdUnitId

string

(none)

(none)

Allows integrating admob/google play ads, this is effectively identical to google.adUnitId but only applies to iOS

ios.googleAdUnitIdPadding

string

(none)

(none)

Indicates the amount of padding to pass to the Google Ads placed at the bottom of the screen with google.adUnitId

ios.googleAdUnitTestDevice

string

97cfc76e5efbc6dfa7eb2e6857b613a0

(none)

ios.gplus.clientId

string

(none)

(none)

ios.hceAids

string

(none)

(none)

ios.headphoneCallback

boolean

false

(none)

Boolean true/false defaults to false. When set to true it assumes the main class has two methods: headphonesConnected & headphonesDisconnected which it invokes appropriately as needed

ios.health.backgroundDelivery

boolean

false

(none)

ios.health.recalibrateEstimates

boolean

false

(none)

ios.health.required

boolean

false

(none)

ios.home.appGroup

string

(none)

(none)

ios.home.commissioning

boolean

true

(none)

ios.home.commissioning.buildSettings.*

string

(none)

(properties file only)

Overrides an Xcode build setting for the Matter commissioning extension.

ios.home.commissioning.displayName

string

(none)

(none)

ios.home.commissioning.fabric

string

(none)

(none)

ios.home.commissioning.vendorId

string

0xFFF1

(none)

ios.home.required

boolean

false

(none)

ios.includeNullChecks

boolean

true

(none)

ios.includePush

boolean

(set by the build)

@Ios(includePush)

true/false (defaults to false). Whether to include the push capabilities in the iOS build. Notice that the IDE plugin has an "Include Push" check box you should use under the iOS section.

ios.intents.appIntents

boolean

true

(none)

ios.intents.minDeploymentTarget

string

(none)

(none)

ios.interface_orientation

string

(set by the build)

@Ios(interfaceOrientation)

UIInterfaceOrientationPortrait by default. Indicates the orientation, one or more of (separated by colon :): UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight. Notice that the IDE plugin has an "Interface Orientation" combo box you should use under the iOS section.

ios.invite.appClip

boolean

true

(none)

Whether the build generates and embeds the App Clip that makes invite attribution exact on iOS. The App Store carries no referrer of its own, so without the clip an iOS install can’t be attributed at all. Set it to false only if you ship an App Clip of your own: the build then generates no clip, but the app still carries the shared app group and the reader, so a clip of yours that writes the handoff below is still picked up. This is the ONLY hint that suppresses the clip: ios.invite.universalLinks=false means you manage the associated domains yourself and leaves the clip, the shared app group and the reader in place, because an app that configured its own domains correctly still needs them.

THE HANDOFF, for a clip of your own. Write one entry into the NSUserDefaults suite named by ios.invite.appGroup, under the key cn1-invite-app-clip-handoff. The value is a dictionary with code, a string holding the invite code taken from the last path segment of the invite url, and clicked, a number holding the tap time as whole seconds since the epoch. clicked may be omitted, and the App Clip is the only thing that ever observes that time — the invocation never reaches the redirect — so a clip that drops it leaves every attribution dated zero. A code containing a newline is rejected on the way in.

Then call synchronize on the suite before returning. A clip is killed without notice the moment the App Store sheet takes over, and the write IS the attribution — there is no second chance to make it.

Don’t clear the entry after writing it. The installed app reads it, keeps it until its own record is durable, and clears it then; a clip that clears its own copy destroys the code whenever that write fails or the process exits first, and the install is then reported as organic permanently.

ios.invite.appGroup

string

(none)

(none)

The app group the invite App Clip hands the invite code to the installed app through. Defaults to group.<package name>.cn1invite, and is added to ios.app_groups automatically. It must start with group. and must be registered on your developer account, or the clip and the app both sign and neither can read what the other wrote.

ios.invite.appStoreId

string

(none)

(none)

The numeric App Store identifier of this app, which the invite App Clip uses to offer the full app through SKOverlay. Leave it unset before your first release: the clip still records the invite code, it simply shows no install sheet until the app exists in the store.

ios.invite.buildSettings.*

string

(none)

(properties file only)

Xcode build settings for the generated invite App Clip target. The clip is a separate application bundle embedded in the app, so its deployment target and device family are its own and an override reaches only it.

ios.invite.universalLinks

boolean

true

(none)

Whether the build appends applinks: for the invite link domain to ios.associatedDomains and requests the matching com.apple.developer.associated-domains entitlement. Set it to false to manage both yourself. The provisioning profile must grant the Associated Domains capability either way, or invite links open Safari instead of the app with no error reported.

ios.keyboardOpen

boolean

true

(none)

Flips between iOS keyboard open mode and autofold keyboard mode. Defaults to true which means the keyboard will remain open and not fold automatically when editing moves to another field.

ios.keychainAccessGroup

string

(none)

(none)

Space-delimited list of keychain access groups that this app has access to as described in Apple’s documentation. These are added to the entitlements file with the key keychain-access-groups.

ios.launchPlaceholder

boolean

true

(none)

ios.locationUsageDescription

string

(none)

(none)

This flag is required for iOS 8 and newer if you’re using the location API. It needs to include a description of the reason for which you need access to the users location

ios.lowMemCamera

boolean

false

(none)

ios.maps.provider

string

(none)

(none)

iOS’s own native map provider, overriding maps.provider.

ios.metal

boolean

true

(none)

Boolean true/false defaults to true. Selects the Metal rendering backend (CAMetalLayer) over the legacy OpenGL ES 2 path (CAEAGLLayer). Metal is the supported iOS graphics API; OpenGL ES is deprecated. Set to false to opt out if you hit a Metal-only rendering regression. See Working with iOS / Metal renderer for details.

ios.metal.colorSpace

string

sRGB

(none)

Selects the CAMetalLayer.colorspace for the Metal renderer. Accepts sRGB (default), displayP3, deviceRGB, linearSRGB, extendedSRGB, extendedLinearSRGB, or none. Has no effect when ios.metal=false. See Working with iOS / Choosing a color space for the full table.

ios.minDeploymentTarget

version

(set by the build)

@Ios(minDeploymentTarget)

The null and empty-string reads of this hint are presence checks; 6.0 is the substantive one.

ios.mopubAdSize

string

MOPUB_BANNER_SIZE

(none)

ios.mopubId

string

(none)

(none)

ios.mopubTabletAdSize

string

MOPUB_LEADERBOARD_SIZE

(none)

ios.mopubTabletId

string

(none)

(none)

ios.multitasking

boolean

true

(none)

Set to true to enable iOS multitasking and split-screen support. This only works if ios.xcode_verson=9.2.

ios.nativeVerify

string

(none)

(none)

nativeVerify for the iOS translation alone.

ios.nearby.accessoryServices

list (, delimited)

(none)

(none)

Bluetooth service UUIDs published as NSAccessorySetupBluetoothServices, so AccessorySetupKit can show a picker for them. Unset publishes none.

ios.nearby.background

boolean

false

(none)

Requests the nearby-interaction entitlement and the background mode that go with ranging while backgrounded. Off by default because the entitlement has to be on the provisioning profile, and requesting it without one fails signing for every ranging app.

ios.nearby.serviceType

string

(none)

(none)

Bonjour service type the nearby transport advertises. Derived from the package name when unset.

ios.newPipeline

boolean

(none)

(none)

Boolean true/false defaults to true. Allows toggling the OpenGL ES 2.0 drawing pipeline off to the older OGL ES 1.0 pipeline.

ios.newStorageLocation

boolean

(set by the build)

@Ios(newStorageLocation)

Stores app files under the documents directory rather than caches, which is the location Apple recommends but which may break compatibility with an app that already shipped. Described in this issue

ios.noUIWebView

boolean

true

(none)

ios.no_strip

boolean

false

(none)

ios.notificationPermissionAtLaunch

boolean

false

(none)

true/false (defaults to false). Backward-compatibility flag for the pre-issue-#4876 behavior. By default, the iOS notification permission prompt is deferred until the app calls Push.register() or schedules a LocalNotification, matching the Android flow and giving the developer a chance to display a rationale screen first. Set this hint to true to restore the legacy behavior in which the prompt fires automatically inside application:didFinishLaunchingWithOptions: as soon as the app launches. Existing apps relying on the prompt being shown at launch should set this to true; new apps should leave it disabled and trigger the prompt explicitly when they’re ready to ask for permission.

ios.objC

boolean

(set by the build)

@Ios(objC)

Added the -ObjC compile flag to the project files which some native libraries require

ios.onDeviceDebug

boolean

(set by the build)

@OnDeviceDebug(ios)

Boolean true/false defaults to false. When true, the iOS build links a small JDWP listener thread (cn1_debugger) into the binary and the ParparVM translator emits source-line and locals metadata so a desktop proxy can serve the running app to any JDWP-speaking debugger. Has no effect on release builds. See the On-Device Debugging (iOS) chapter for the full flow.

ios.onDeviceDebug.proxyHost

string

(set by the build)

@OnDeviceDebug(iosProxyHost)

Hostname or IP address the device-side listener dials to reach the desktop proxy. Default 127.0.0.1 (correct for the native iOS simulator). For a physical device, set this to the developer laptop’s LAN IP. Has no effect unless ios.onDeviceDebug=true.

ios.onDeviceDebug.proxyPort

int

(set by the build)

@OnDeviceDebug(iosProxyPort)

TCP port on ios.onDeviceDebug.proxyHost where the proxy is listening for the device. Default 55333. Has no effect unless ios.onDeviceDebug=true.

ios.onDeviceDebug.waitForAttach

boolean

(set by the build)

@OnDeviceDebug(iosWaitForAttach)

Boolean true/false defaults to false. When true, the app blocks at startup until the proxy connects and the IDE tells the VM to continue. Useful when the breakpoint to investigate fires during app boot. Has no effect unless ios.onDeviceDebug=true.

ios.openURLInject

xml

(none)

(none)

ios.optimizer

string

on

(none)

ios.plistInject

xml

(set by the build)

@Ios(plistInject)

entries to inject into the iOS plist file during build.

ios.pods

list (, delimited)

(set by the build)

@Ios(pods)

A comma separated list of Cocoa Pods that should be linked to the app to build it. For example, AFNetworking ~> 2.6, ORStackView ~> 3.0, SwiftyJSON ~> 2.3

ios.pods.build.*

string

(none)

(properties file only)

Overrides an Xcode build setting for the generated CocoaPods project.

ios.pods.build.CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES

string

(none)

(none)

ios.pods.build.CLANG_ENABLE_MODULES

string

(none)

(none)

ios.pods.platform

version

(set by the build)

@Ios(podsPlatform)

Sets the Cocoapods 'platform' for the Cocoapods. Some Cocoapods require a minimum platform level. For example, ios.pods.platform=7.0.

ios.pods.sources

list (, delimited)

(set by the build)

@Ios(podsSources)

Extra CocoaPods spec repositories to search, in addition to the default trunk.

ios.pods.use_frameworks!

boolean

false

(none)

ios.prerendered_icon

boolean

(set by the build)

@Ios(prerenderedIcon)

true/false defaults to false. The iOS build process adapts the submitted icon for iOS conventions (adding an overlay) that might not be appropriate on some icons. Setting this to true leaves the icon unchanged (only scaled).

ios.project_type

ios, ipad, iphone

(set by the build)

@Ios(projectType)

one of ios, ipad, iphone (defaults to ios). Indicates whether the resulting binary is targeted to the iphone only or ipad only. Notice that the IDE plugin has a "Project Type" combo box you should use under the iOS section.

ios.release.archs

string

(none)

(none)

Can be set to "arm64" to only build iOS release builds for 64 bit. By default, release builds are both 32 and 64 bit.

ios.release.distributionMethod

string

(none)

(none)

Specifies distribution type for release iOS builds only. This is used for enterprise or ad-hoc builds (using values "enterprise" and "ad-hoc" respectively).

ios.release.teamId

string

(none)

(none)

Specifies the team ID associated with the iOS release provisioning profile and certificate.

ios.rpmalloc

string

(none)

(none)

true/false Use rpmalloc instead of malloc/free for memory allocation in ParparVM. This will cause the deployment target to be changed to a minimum of iOS 8.0.

ios.shareAppGroup

string

(none)

(none)

ios.spm.packages

list (; delimited)

(set by the build)

@Ios(spmPackages)

Swift Package Manager packages to link, one per entry, each written as identity|url|requirement.

ios.spm.products.*

string

(none)

(properties file only)

Selects which products of a Swift Package Manager package to link, keyed by package identity.

ios.statusBarFG

string

(none)

(none)

ios.statusbar_hidden

boolean

(none)

(none)

true/false defaults to false. Hides the iOS status bar if set to true.

ios.superfastBuild

boolean

false

(none)

ios.surfaces.appGroup

string

(none)

(none)

ios.surfaces.buildSettings.*

string

(none)

(properties file only)

Overrides an Xcode build setting for the external-surfaces extension.

ios.surfaces.deploymentTarget

version

16.1

(none)

ios.surfaces.extension

boolean

true

(none)

ios.surfaces.frequentUpdates

boolean

false

(none)

ios.swiftVersion

version

5.0

(none)

ios.teamId

string

(set by the build)

@Ios(teamId)

Specifies the team ID associated with the iOS provisioning profile and certificate. Use ios.debug.teamId and ios.release.teamId to specify different team IDs for debug and release builds respectively.

ios.testFlight

boolean

(none)

(none)

Boolean true/false defaults to false and works only for pro accounts. Enables the testflight support in the release binaries for easy beta testing. Notice that the IDE plugin has a "Test Flight" check box you should use under the iOS section.

ios.themeMode

auto, modern, ios7, legacy

(set by the build)

@Ios(themeMode)

auto (default), modern, ios7, legacy. auto (unset) keeps the existing iOS 7 flat theme so pre-refactor screenshot goldens and apps see no behavior change. modern / liquid opts in to the CSS-generated iOS Modern (liquid-glass) theme shipped from native-themes/ios-modern/theme.css. ios7 / flat is the same as auto - pre-liquid iOS 7 flat theme; legacy / iphone loads the pre-iOS 7 iPhone theme. The auto → modern flip is planned for a future release.

ios.timeSensitiveNotifications

boolean

false

(none)

ios.twoDigitVersion

boolean

false

(none)

ios.urlScheme

string

(set by the build)

@Ios(urlScheme)

Allows intercepting a URL call using the syntax <string>urlPrefix<string>

ios.urlSchemes

string

(none)

(none)

ios.useAVKit

boolean

true

(none)

Use AVKit for video components on iOS rather than MPMoviePlayerController on iOS versions 8 through 12. iOS 13 will always use AVKit, and iOS 7 and lower will always use MPMoviePlayerController. Default value false

ios.useJavascriptCore

boolean

false

(none)

ios.usePhotoKitForMultigallery

boolean

false

(none)

ios.usePrintf

boolean

false

(none)

ios.useWKWebView

boolean

true

(none)

ios.usesBackgroundProcessing

boolean

false

(none)

ios.viewDidLoad

string

(none)

(none)

Objective-C code that can be injected into the iOS callback method (message) viewDidLoad

ios.viewDidLoadInclude

string

(none)

(none)

ios.vpn.tunnel

boolean

false

(none)

Generates the iOS packet tunnel: a Network Extension carrying a virtual machine, running the VpnTunnel subclass named by ios.vpn.tunnel.class. Two things have to be true, and this hint is the second: the app references com.codename1.vpn.tunnel, and the App ID holds com.apple.developer.networking.networkextension, which Apple grants case by case rather than self-serve. Setting this hint is the project asserting the grant — generating the target without it fails codesigning with an error naming an entitlement nobody asked for, which is why referencing the package alone never produces one. BOTH App IDs need the grant: the extension is the provider, and the app drives it through NETunnelProviderManager, which is Network Extension API as well — so the build writes the entitlement into the app and into the extension, and refuses before the archive if either provisioning profile doesn’t grant it. A device archive also needs a provisioning profile for the extension’s own App ID, <packageName>.vpntunnel, passed as ios.appext.CN1VpnTunnel.provisioningData or .provisioningURL. Left false, the build produces no extension and Tunnels.isSupported() answers false. The extension carries the translated program and the virtual machine and NO networking stack: com.codename1.io.Socket and everything else that reaches Util.getImplementation() finds nothing there, and ParparVM’s java.net is URI and URL. An iOS tunnel can therefore inspect, rewrite, drop and forward packets, but it can’t relay them to a remote server — on Android it can, because the tunnel runs in the app’s own process. The extension is a translation of its own, rooted at the tunnel: it carries what the tunnel reaches and nothing of the application, so a tunnel that reaches for the app’s own classes fails the extension’s link rather than misbehaving at runtime.

ios.vpn.tunnel.buildSettings.*

string

(none)

(properties file only)

Xcode build settings for the generated packet tunnel extension target. PRODUCT_BUNDLE_IDENTIFIER is read in two places — the target’s own settings and the CN1VpnTunnelExtensionIdentifier the host plist carries, which is how the app names the provider it starts — so an override has to reach both or the app asks the system to start an extension installed under some other name.

ios.vpn.tunnel.class

string

(none)

(none)

Which VpnTunnel subclass the generated packet tunnel extension runs, fully qualified — for example com.example.MyTunnel. Required when ios.vpn.tunnel is true. It has to be named rather than discovered: VpnTunnel is a class rather than an interface, so the shared class scanner skips it, and an app may have several subclasses while an extension runs exactly one. A wrong guess would build the wrong tunnel into the extension and fail at link on a symbol nobody wrote. The build checks the name against the compiled classes and refuses one it can’t find, rather than letting the generated entry point fail javac on a source file the developer never wrote. It has to be a class this project compiles: the translator reads loose class files, so a tunnel that lives only inside a submitted library jar is never translated and can’t be the extension’s entry point.

ios.wallet.appGroup

string

(none)

(none)

App Group id starting with group. shared by the app and the generated Wallet extensions. The app publishes pass entries into this group through com.codename1.payment.WalletExtension and the group is added to the app and extension entitlements automatically. Required when ios.wallet.extension=true.

ios.wallet.authEndpoint

string

(none)

(none)

HTTPS URL the generated login UI extension POSTs {"username","password"} to; the JSON response’s token is stored in the App Group for the provisioning request. Required when ios.wallet.includeUI=true.

ios.wallet.extension

boolean

false

(none)

Boolean true/false defaults to false. Generates an Apple Wallet issuer provisioning extension (the "From apps on your iPhone" flow in the Wallet app) and embeds it in the build. Requires ios.wallet.appGroup and ios.wallet.issuerEndpoint. See the Apple Wallet Extension chapter.

ios.wallet.generateRequestInject

string

(none)

(none)

Swift injected at the generate-request marker of the non-UI Wallet extension.

ios.wallet.generateResponseInject

string

(none)

(none)

Swift injected at the generate-response marker of the non-UI Wallet extension.

ios.wallet.includeUI

boolean

false

(none)

Boolean true/false defaults to false. Also generates the Wallet authorization UI extension - a login form shown inside the Wallet app when the app reports that authentication is required. Requires ios.wallet.authEndpoint.

ios.wallet.issuerEndpoint

string

(none)

(none)

HTTPS URL of the issuer backend endpoint that produces the encrypted provisioning payload. The generated extension POSTs Apple’s certificates/nonce plus the card identifier and auth token there as JSON. Required when ios.wallet.extension=true.

ios.wallet.nonuiExtensionName

string

WalletNonUIExtension

(none)

ios.wallet.nonuiImportsInject

string

(none)

(none)

Extra import lines for the non-UI Wallet extension.

ios.wallet.passEntriesInject

string

(none)

(none)

Swift injected where the non-UI Wallet extension lists its pass entries.

ios.wallet.remotePassEntriesInject

string

(none)

(none)

Swift injected where the non-UI Wallet extension lists its remote pass entries.

ios.wallet.statusInject

string

(none)

(none)

Swift injected at the status marker of the non-UI Wallet extension.

ios.wallet.uiAuthRequestInject

string

(none)

(none)

Swift injected at the auth-request marker of the UI Wallet extension.

ios.wallet.uiAuthResponseInject

string

(none)

(none)

Swift injected at the auth-response marker of the UI Wallet extension.

ios.wallet.uiExtensionName

string

WalletUIExtension

(none)

ios.wallet.uiImportsInject

string

(none)

(none)

Extra import lines for the UI Wallet extension.

ios.wallet.uiViewDidLoadInject

string

(none)

(none)

Swift injected into viewDidLoad of the UI Wallet extension.

ios.xcode_version

string

(none)

(none)

The version of Xcode used on the server. Defaults to 4.5; accepts 5.0 as an option and nothing else.

ios.zbar_flash

boolean

true

(none)

javascript.includeVideoJS

boolean

false

(none)

javascript.inject.afterHead

string

(none)

(none)

Content to be injected into the index.html file at the end of the <head> tag.

javascript.inject.beforeHead

string

(none)

(none)

Content to be injected into the index.html file at the beginning of the <head> tag.

javascript.inject_proxy

boolean

true

(none)

true/false (defaults to true). The ParparVM builder generates a same-origin proxy bundle and configures the app to use it. Setting this to false disables both proxy generation and proxy URL injection.

javascript.minifying

boolean

(none)

(none)

true/false (defaults to true). By default the JavaScript code is minified to reduce file size. You may optionally disable minification by setting javascript.minifying to false.

javascript.port

string

(none)

(none)

parparvm (default) or teavm. Selects the public JavaScript compiler for cloud builds. teavm retains the original builder as a compatibility fallback.

javascript.portSources

string

(none)

(none)

javascript.proxy.allowedTargets

string

(none)

(none)

Comma-separated target origins, host names, or wildcard subdomains that a generated proxy may access, for example https://api.example.com,*.services.example.org. If omitted, the proxy accepts any HTTP or HTTPS target and the build emits a warning.

javascript.proxy.target

string

jakarta-servlet

(none)

The generated ParparVM proxy deployment platform. Supported values are jakarta-servlet (default), javax-servlet, node, php, aws-lambda, google-cloud-functions, cloudflare-workers, and none.

javascript.proxy.url

string

(none)

(none)

The URL of an existing proxy to use for network requests. Setting it suppresses generated proxy packaging unless javascript.proxy.target is also set. If javascript.inject_proxy is false, this build hint is ignored.

javascript.sourceFilesCopied

boolean

(none)

(none)

true/false (defaults to false). Setting this flag to true will cause available java source files to be included in the resulting .zip and .war files. These may be used by Chrome during debugging.

javascript.stopOnErrors

boolean

(none)

(none)

true/false (defaults to true). Causes a TeaVM JavaScript build to fail when the compiler reports warnings. Setting this to false may allow the fallback builder to complete, but can turn compiler diagnostics into runtime failures that are more difficult to debug.

javascript.teavm.version

string

(none)

(none)

(Optional) The version of TeaVM to use for the build. Use caution, only use this property if you know what you’re doing!

linux.arch

string

(none)

(none)

linux.cc

string

(none)

(none)

linux.debug

boolean

false

(none)

linux.libc

string

glibc

(none)

linux.musl

boolean

false

(none)

linux.muslNativeCc

boolean

false

(none)

linux.nativeVerify

string

(none)

(none)

nativeVerify for the native Linux translation alone.

linux.toolchain

string

(none)

(none)

desktop.mac.cef

boolean

(none)

(none)

Whetherto use CEF for media or BrowserComponent instead of JavaFX in Mac desktop builds. true/false. Default value is false (Jan 2021), but this will be changed to true in a future version.

macNative.appCategory

string

(none)

(none)

Mac Native builds only. LSApplicationCategoryType in the generated Info.plist. Default public.app-category.utilities.

macNative.bundleId

string

(none)

(none)

Mac Native builds only. Used only when macNative.deriveBundleId=false. Default: <packageName>.mac.

macNative.copyright

string

(none)

(none)

Mac Native builds only. NSHumanReadableCopyright in the Info.plist. Defaults to Copyright (c) <year> <vendor>.

macNative.deriveBundleId

boolean

true

(none)

Mac Native builds only. true (default) maps to Xcode’s DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER=YES (Xcode appends .maccatalyst to the iOS bundle ID). Set to false to take the bundle ID verbatim from macNative.bundleId.

macNative.distribution

string

appStore

(none)

Mac Native builds only. appStore (default), developerID, or both. Selects which entitlements + ExportOptions plist + signing certificate to emit. both emits parallel -AppStore.entitlements / -DeveloperID.entitlements and matching ExportOptions-*-Mac.plist files so a single project can be archived to either channel.

macNative.enabled

boolean

false

(none)

macNative.entitlements.allowJit

string

(none)

(none)

Mac Native builds only. true enables com.apple.security.cs.allow-jit for hardened runtime. ParparVM is AOT-compiled so this is false by default; flip when bundling a JIT-using cn1lib.

macNative.entitlements.appSandbox

string

(none)

(none)

Mac Native builds only. true enables com.apple.security.app-sandbox. Default is true for the appStore channel (Mac App Store requires the sandbox), false for developerID.

macNative.entitlements.device.camera

boolean

(none)

(none)

Sandboxed Mac Native builds only. Toggles com.apple.security.device.camera. Defaults to whether the app sets ios.NSCameraUsageDescription, so an app that asks for the camera gets the entitlement without naming it twice.

macNative.entitlements.device.microphone

boolean

(none)

(none)

Sandboxed Mac Native builds only. Toggles com.apple.security.device.microphone. Defaults to whether the app sets ios.NSMicrophoneUsageDescription.

macNative.entitlements.extra

string

(none)

(none)

Mac Native builds only. Free-form XML inserted verbatim inside the <dict>…​</dict> of the generated entitlements plist. Use for entitlements Codename One doesn’t expose individually.

macNative.entitlements.files.userSelected

string

readwrite

(none)

Mac Native builds only. readwrite (default), readonly, or none. Sets the matching com.apple.security.files.user-selected.* entitlement.

macNative.entitlements.hardenedRuntime

string

(none)

(none)

Mac Native builds only. true enables hardened runtime restrictions. Default is true for developerID (notarization requires it), false for appStore.

macNative.entitlements.network.client

string

(none)

(none)

Mac Native builds only. Toggles com.apple.security.network.client. Default true.

macNative.entitlements.network.server

string

(none)

(none)

Mac Native builds only. Toggles com.apple.security.network.server. Default false.

macNative.entitlements.personalInformation.calendars

boolean

(none)

(none)

Sandboxed Mac Native builds only. Toggles com.apple.security.personal-information.calendars, which gates all EventKit access. Defaults to whether the app sets any calendar or reminder usage description, including the write-only and reminders-only ones.

macNative.fixedWindowSize

string

(none)

(none)

Mac Native builds only. Opt-in. Format <width>x<height> — for example 1024x685. When set, the Catalyst window’s UISceneSession.sizeRestrictions minimum and maximum are pinned to the requested size so every launch produces a byte-identical window. Default unset, in which case the window is resizable. The CI screenshot pipeline turns this on to keep the strict-pixel golden comparison stable; production apps should leave it off.

macNative.iosMinDeploymentTarget

version

13.1

(none)

Mac Native builds only. iOS deployment-target floor for the Catalyst slice (IPHONEOS_DEPLOYMENT_TARGET). Default 13.1. The plugin coerces the iOS slice’s minimum upward when set.

macNative.minDeploymentTarget

version

10.15

(none)

Mac Native builds only. Minimum macOS version (MACOSX_DEPLOYMENT_TARGET). Default 10.15 — earlier versions don’t support Mac Catalyst.

macNative.multiWindow

boolean

false

(none)

Mac Native builds only. Declares that the app uses com.codename1.ui.Window, which needs multiple UIScenes and exists only on the Mac Catalyst slice. Writes UIApplicationSupportsMultipleScenes and a scene configuration into the Mac slice’s own Info.plist; getWindowManager() reads that key back out of the bundle, so without it windows are reported unsupported and constructing one throws. Off by default: multi-window support relayouts the app into a resizable window, a change an app that never asked for windows has no reason to take on.

macNative.notarize

boolean

false

(none)

macNative.notarize.appleId

string

(none)

(none)

macNative.notarize.keychainProfile

string

(none)

(none)

macNative.notarize.password

secret

(none)

(none)

macNative.notarize.teamId

string

(none)

(none)

macNative.provisioningProfile.*

string

(none)

(properties file only)

Per-profile provisioning data for a native macOS build, keyed by profile name.

macNative.provisioningProfile.appStore

string

(none)

(none)

Mac Native builds only. Provisioning profile name for App Store distribution — used only when macNative.signing.style=manual.

macNative.provisioningProfile.developerID

string

(none)

(none)

Mac Native builds only. Provisioning profile name for Developer ID distribution — used only when macNative.signing.style=manual.

macNative.signing.style

string

automatic

(none)

Mac Native builds only. automatic (default) lets Xcode pick the signing certificate; manual forces the certificate identity hints below to be respected verbatim.

macNative.signingIdentity.appStore

string

Apple Distribution

(none)

Mac Native builds only. Signing certificate identity for the App Store channel. Default Apple Distribution.

macNative.signingIdentity.developerID

string

Developer ID Application

(none)

Mac Native builds only. Signing certificate identity for the Developer ID channel. Default Developer ID Application.

macNative.teamId

string

(none)

(none)

Mac Native builds only. Apple Developer Team ID (alphanumeric). Falls back to ios.release.teamIdios.teamIdios.debug.teamId since most apps share a single Apple Developer Team for iOS and Mac.

macos.add_libs

list (; delimited)

(set by the build)

@Mac(addLibs)

macOS builds. Frameworks to link in addition to the ones the build detects for itself, separated by a semicolon, a comma or a colon — for example Speech.framework;CoreMIDI.framework. ios.add_libs is read when this is unset, so a project migrated from the Mac Catalyst build keeps linking what its native sources need.

macos.appCategory

string

(set by the build)

@Mac(appCategory)

macOS builds. LSApplicationCategoryType in the generated Info.plist. Default public.app-category.utilities. See Apple’s category list.

macos.arch

string

(set by the build)

@Mac(arch)

macOS builds. The architectures to compile, as an ARCHS value. Default arm64 x86_64, which is what a Mac application is expected to be: a single-architecture build is the kind of thing nobody notices until an Intel user reports it.

macos.bundleId

string

(set by the build)

@Mac(bundleId)

macOS builds. Used only when macos.deriveBundleId=false. Default: <packageName>.mac.

macos.bundleVersion

string

(set by the build)

@Mac(bundleVersion)

macOS builds. CFBundleVersion in the Info.plist. ios.bundleVersion is read when this is unset, and the project’s version when neither is set.

macos.configuration

string

(set by the build)

@Mac(configuration)

macOS builds. The Xcode configuration to archive, as passed to xcodebuild -configuration. Default Release.

macos.copyright

string

(set by the build)

@Mac(copyright)

macOS builds. NSHumanReadableCopyright in the Info.plist. Defaults to Copyright (c) <year> <vendor>.

macos.crypto.gcm

boolean

(set by the build)

@Mac(cryptoGcm)

macOS builds. Whether AES-GCM is compiled into the bundled crypto library. On by default wherever the crypto API is on, matching what an iOS build of the same application gets; set false to leave it out and keep the symbol set smaller. ios.crypto.gcm is read when this is unset.

macos.deriveBundleId

boolean

(set by the build)

@Mac(deriveBundleId)

macOS builds. false (default) gives the app its own bundle identifier, <packageName>.mac, because a macOS app and an iOS app are separate products in App Store Connect. true reuses the iOS identifier. On the legacy Mac Catalyst target this maps instead to Xcode’s DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER, which appends .maccatalyst.

macos.distribution

string

(set by the build)

@Mac(distribution)

macOS builds. Every macos. hint below is also accepted spelled macNative., which is what the legacy Mac Catalyst target reads, so an existing Catalyst project keeps building unchanged. developerID (default), appStore, or both. Selects the signing certificate, the entitlements and the default packaging. both is genuinely two builds: the channels differ in the certificate and in the entitlements the signature carries — the App Store one has to be sandboxed — so one binary can’t be relabelled into the other channel afterwards. It produces <App>-appstore.app and <App>-developerid.app, each with its own container.

macos.entitlements.allowJit

boolean

(set by the build)

@Mac(entitlementsAllowJit)

macOS builds. true enables com.apple.security.cs.allow-jit for hardened runtime. ParparVM is AOT-compiled so this is false by default; flip when bundling a JIT-using cn1lib.

macos.entitlements.appSandbox

boolean

(set by the build)

@Mac(entitlementsAppSandbox)

macOS builds. true enables com.apple.security.app-sandbox. Default is true for the appStore channel, false for developerID. The App Store channel is always sandboxed whatever this says — the Mac App Store requires it, and a package built without the sandbox gets rejected at submission rather than at build time. The refusal is reported in the build log.

macos.entitlements.extra

string

(set by the build)

@Mac(entitlementsExtra)

macOS builds. Free-form XML inserted verbatim inside the <dict>…​</dict> of the generated entitlements plist. Use for entitlements Codename One doesn’t expose individually.

macos.entitlements.files.downloads

boolean

(set by the build)

@Mac(entitlementsFilesDownloads)

macOS builds. true adds com.apple.security.files.downloads.read-write, which is access to the Downloads folder without a panel. Default false, and separate from macos.entitlements.files.userSelected above because it’s a wider grant than picking a file.

macos.entitlements.files.userSelected

readwrite, readonly, none

(set by the build)

@Mac(entitlementsFilesUserSelected)

macOS builds. readwrite (default), readonly, or none. Sets the matching com.apple.security.files.user-selected.* entitlement — the files the user picks in an open or save panel, and nothing else.

macos.entitlements.hardenedRuntime

boolean

(set by the build)

@Mac(entitlementsHardenedRuntime)

macOS builds. true writes com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory into the entitlements as explicit denials; false leaves them out. It doesn’t switch the hardened runtime on or off — that’s macos.hardenedRuntime above. Default is true for developerID, false for appStore.

macos.entitlements.network.client

boolean

(set by the build)

@Mac(entitlementsNetworkClient)

macOS builds. Toggles com.apple.security.network.client. Default true.

macos.entitlements.network.server

boolean

(set by the build)

@Mac(entitlementsNetworkServer)

macOS builds. Toggles com.apple.security.network.server. Default false.

macos.fixedWindowSize

string

(set by the build)

@Mac(fixedWindowSize)

macOS builds. Opt-in. Format <width>x<height> — for example 1024x685. When set, the window’s minimum and maximum size are pinned to the requested size so every launch produces a byte-identical window. Default unset, in which case the window is resizable. The CI screenshot pipeline turns this on to keep the strict-pixel golden comparison stable; production apps should leave it off.

macos.hardenedRuntime

boolean

(set by the build)

@Mac(hardenedRuntime)

macOS builds. Sets Xcode’s ENABLE_HARDENED_RUNTIME. Default true, because notarization requires it. This is the build setting; the entitlement hint below is a different thing despite the similar name.

macos.loadsExternalCode

boolean

(set by the build)

@Mac(loadsExternalCode)

macOS builds. true grants the hardened-runtime exception for loading unsigned libraries. A Codename One application doesn’t load code that way, but a cn1lib shipping a dylib needs this, or the load is refused at runtime with nothing in the application’s own logs.

macos.minDeploymentTarget

string

(set by the build)

@Mac(minDeploymentTarget)

macOS builds. Minimum macOS version (MACOSX_DEPLOYMENT_TARGET). Default 11.0 on the native macOS build, which is the floor for a universal Apple silicon binary. The legacy Mac Catalyst target defaults to 10.15.

macos.packaging

string

(set by the build)

@Mac(packaging)

macOS builds. app, dmg, pkg or both. Unset, each channel takes its own default — pkg for appStore, because productbuild’s output is what you upload, and dmg for developerID. Set explicitly, the value applies to every channel. A cloud build always ships a file, so app there means the bundle zipped with ditto rather than the raw .app directory.

macos.plistInject

string

(set by the build)

@Mac(plistInject)

macOS builds. Raw XML members added to the generated Info.plist, the same form ios.plistInject takes — for example <key>NSAppTransportSecurity</key><dict/>. A key that the build also generates is replaced by the injected one, and the build log names it. ios.plistInject is read when this is unset, so a project migrated from the Mac Catalyst build keeps its injections.

macos.provisioningProfile.appStore

string

(set by the build)

@Mac(provisioningProfileAppStore)

macOS builds. Provisioning profile name for App Store distribution — used only when macNative.signing.style=manual.

macos.provisioningProfile.developerID

string

(set by the build)

@Mac(provisioningProfileDeveloperID)

macOS builds. Provisioning profile name for Developer ID distribution — used only when macNative.signing.style=manual.

macos.signing.style

string

(set by the build)

@Mac(signingStyle)

macOS builds. manual (default) signs with the certificate identity hints below, verbatim. automatic lets Xcode resolve the certificate from the team and provisioning profile instead. Manual is the default because a build server has an installed certificate and no Xcode account session, and automatic signing there stops to ask you to sign in; use automatic when building on your own machine.

macos.signingIdentity.appStore

string

(set by the build)

@Mac(signingIdentityAppStore)

macOS builds. Signing certificate identity for the App Store channel. Default Apple Distribution. Unlike the Developer ID channel, this one rejects none. An unsigned application still gets packaged into a signed .pkg, so the build reports success and App Store Connect rejects the upload hours later for an application with no signature and none of the sandbox entitlements it has to carry. The build fails immediately instead, naming this hint. Build only the developerID channel to get an unsigned application. An empty value reads as unset and takes the default, which is why the Developer ID channel spells the escape hatch none rather than "".

macos.signingIdentity.developerID

string

(set by the build)

@Mac(signingIdentityDeveloperID)

macOS builds. Signing certificate identity for the Developer ID channel. Default Developer ID Application. Set it to none to build unsigned.

macos.signingIdentity.installer

string

(set by the build)

@Mac(signingIdentityInstaller)

macOS builds. The certificate productbuild signs a .pkg with — 3rd Party Mac Developer Installer for the App Store, Developer ID Installer for direct distribution. This is a different certificate from macos.signingIdentity.appStore, which signs the application, so it has a hint of its own rather than being derived from that one. Required whenever a package is produced, which includes the App Store default: the build fails with an explanatory error rather than writing an unsigned package, because App Store Connect refuses one and Gatekeeper won’t accept it as Developer ID distribution however well the enclosed application is signed.

macos.signingIdentity.installer.appStore

string

(set by the build)

@Mac(signingIdentityInstallerAppStore)

macOS builds. The installer certificate for the App Store channel specifically, when macos.distribution=both produces a package on each side. They’re different certificates, so one shared value signs both packages with the same one and leaves one of them unusable. Unset, the shared macos.signingIdentity.installer applies.

macos.signingIdentity.installer.developerID

string

(set by the build)

@Mac(signingIdentityInstallerDeveloperID)

macOS builds. The installer certificate for the Developer ID channel specifically. Unset, the shared macos.signingIdentity.installer applies.

macos.sourceOnly

boolean

(set by the build)

@Mac(sourceOnly)

macOS builds. true stops after generating the Xcode project, which is what the mac-source target delivers. Set by that target rather than by hand.

macos.teamId

string

(set by the build)

@Mac(teamId)

macOS builds. Apple Developer Team ID (alphanumeric). Falls back to ios.release.teamIdios.teamIdios.debug.teamId since most apps share a single Apple Developer Team for iOS and Mac.

macos.themeMode

string

(set by the build)

@Mac(themeMode)

macOS builds. Which native theme the application installs: modern (equivalently liquid or material) or ios7 (equivalently flat). Defaults to modern, which is where this target parts company with iOS — iOS keeps the legacy theme by default so that applications already shipped, and their screenshot baselines, keep rendering as before. There is no such history here, and the legacy theme defines no dark styles at all, so an application on it renders light however it asks for dark. The cross-platform nativeTheme hint is honoured when this is unset, with legacy mapping to ios7.

macos.urlSchemes

string

(set by the build)

@Mac(urlSchemes)

macOS builds. Custom URL schemes to register, comma separated. ios.urlSchemes and then ios.urlScheme are read when this is unset, so a project migrated from the Mac Catalyst build keeps its deep links.

tvNative.bundleId

string

(none)

(none)

Bundle identifier of the tvOS app. Defaults to <packageName>.tvos.

tvNative.displayName

string

(none)

(none)

The tvOS app name shown on Apple TV. Defaults to the app’s display name.

tvNative.enabled

boolean

false

(none)

true/false (defaults to false). Adds an Apple TV (tvOS) application target to the iOS build. The tvOS app is a separate appletvos target built from the same Java/Kotlin sources through ParparVM (UIKit + Metal; tvOS has no OpenGL ES). Enabling it doesn’t change the iOS app — in particular it doesn’t override the iOS app’s ios.metal setting. Also turned on implicitly by codename1.tvMain.

tvNative.mainClass

string

(none)

(none)

tvNative.minDeploymentTarget

version

13.0

(none)

TVOS_DEPLOYMENT_TARGET for the tvOS target. Defaults to 13.0.

tvNative.teamId

string

(none)

(none)

Apple Developer Team ID used to sign the tvOS target. Falls back to the iOS team id (ios.release.teamId / ios.teamId / ios.debug.teamId).

watchNative.enabled

boolean

false

(none)

watchNative.health

string

(none)

(none)

watchNative.health.workoutProcessing

boolean

false

(none)

watchNative.mainClass

string

(none)

(none)

watchNative.surfaces.deploymentTarget

string

10.0

(none)

Deployment target of the WidgetKit extension that carries the watch complication. This is the WATCH APP’s floor rather than the extension’s own: WidgetKit reaches back to watchOS 9, but the extension is embedded in the watch app, so advertising a version the app itself refuses to install on claims support the user never gets.

win.desktop-vm

string

(none)

(none)

The JVM that should be bundled in the Windows desktop build. Windows desktop builds only. Supported values: zulu8, zuluFx8, zulu8-32bit, zuluFx8-32bit, zulu11, zuluFx11, zulu11-32bit, zuluFx11-32bit

win.installDirName

string

(none)

(none)

Windows desktop builds only. Overrides the default installation folder name suggested by the installer (under Program Files). Defaults to the application’s main class name for backward compatibility. Use this build hint to set a user-friendly installation folder name (for example, win.installDirName=My Application). The application ID used by Windows for upgrade detection is unaffected, so existing installations continue to upgrade.

win.shortcutName

string

(none)

(none)

Windows desktop builds only. Overrides the name used for the Start Menu shortcut, the Desktop shortcut and (when win.launchOnStart=true) the autostart shortcut. Defaults to the application’s main class name for backward compatibility. Use this build hint to set a user-friendly shortcut label (for example, win.shortcutName=My Application).

win.vm32bit

boolean

(none)

(none)

true/false (defaults to false). Forces windows desktop builds to use the Win32 JVM instead of the 64 bit VM making them compatible with older Windows Machines. This is off by default at the moment because of a bug in JDK 8 update 112 that might cause this to fail for some cases

windows.arch

string

(none)

(none)

Native Windows port only (the windows-native build target — not the JVM win.* desktop hints above). Target CPU architecture for the standalone .exe: x64 (the default) or arm64. Accepts the usual synonyms (x86_64/amd64, aarch64). clang-cl cross-compiles to the chosen architecture from either host. See the Working with the native Windows port chapter.

windows.calendar.restrictedCapability

boolean

false

(none)

windows.debug

boolean

false

(none)

Native Windows port only. true/false (defaults to false). When false the .exe is built optimized and stripped — no PDB, dead-stripped unreferenced code (/OPT:REF) and folded identical functions (/OPT:ICF) — which is the shipping default. Set true to keep debug symbols (a .pdb next to the exe, via RelWithDebInfo / clang-cl /Zi + linker /DEBUG) so a native crash address can be symbolized during development. Optimizations stay on in both cases.

windows.extensions

string

(none)

(none)

Historical build hint for the discontinued UWP target. It’s retained here only for legacy reference and isn’t used by current supported build targets.

windows.msix

boolean

false

(none)

windows.msix.identityName

string

(none)

(none)

windows.msix.password

secret

(none)

(none)

windows.msix.pfx

string

(none)

(none)

windows.msix.publisher

string

(none)

(none)

windows.msix.version

string

(none)

(none)

windows.nativeVerify

string

(none)

(none)

nativeVerify for the native Windows translation alone.

windows.sdkRoot

string

(none)

(none)

Native Windows port only; used when building on a non-Windows host (for example a Linux build server). Path to a Windows SDK laid out by xwin splat (a directory containing crt/include and sdk/include/um), used to cross-compile the .exe with clang-cl + lld-link instead of a Visual Studio environment. If unset, the CN1_XWIN_SYSROOT environment variable is used. Ignored on Windows hosts, which build through Visual Studio. The same SDK serves both windows.arch targets (its x86_64 / aarch64 lib subdirs).

windows.signing

boolean

true

(none)

Native Windows port only. true/false (default true). Set false to force an unsigned build even when a certificate is available.

windows.signing.digest

string

sha256

(none)

Native Windows port only. Signature digest algorithm. Default sha256.

windows.signing.name

string

(none)

(none)

windows.signing.password

secret

(none)

(none)

windows.signing.pkcs12

string

(none)

(none)

windows.signing.timestampUrl

string

http://timestamp.digicert.com

(none)

Native Windows port only. RFC 3161 timestamp server used when signing, so the signature stays valid after the certificate expires. Default http://timestamp.digicert.com; set empty to disable timestamping.

windows.signing.url

string

(none)

(none)

Versioned builds

By default a cloud build always uses the current Codename One release. With a versioned build you can instead pin the build to a specific previously released framework version, so you get reproducible output over time. This helps when stabilizing a release, supporting an older app, or isolating a regression: if an app that built correctly in the past starts failing, rebuilding against the last known good version tells you whether the change came from your code or from the framework and build server.

Set the build.cn1Version build hint to the version you want to target. Versions use the standard Maven release scheme, for example 7.0.182:

codename1.arg.build.cn1Version=7.0.182

The build server fetches that version’s framework artifacts from the Codename One Maven repository and builds against them, falling back to Maven Central for releases published before the move. To keep your simulator and local compile classpath aligned with the same release, set the matching cn1.version in your project pom.xml.

Versioned builds are a Pro and Enterprise feature, and how far back you can target depends on the subscription tier:

TierVersions you can target

Pro

Any version released within the last two months

Enterprise

Any version released within the last six months

If you request a version older than your tier’s window, a version that was never published, or you aren’t on a Pro or Enterprise plan, the build fails with an explanatory message instead of falling back to the current release.

Building against master

Set build.cn1Version to master to build against the current development head (the latest master of the Codename One repository) instead of a released version:

codename1.arg.build.cn1Version=master

Every push to master publishes a fresh set of framework artifacts and the build server always grabs the latest, so this is the quickest way to verify an unreleased fix or feature end to end on a device. Because master tracks active development it can be less stable than a release, so use it for verification rather than for shipping production builds. Building against master is available to Pro and Enterprise subscribers.

Android permissions

One of the annoying tasks when programming native Android applications is tuning all the required permissions to match your codes requirements, Codename One aims to simplify this. The build server automatically introspects the classes sent to it as part of the build and injects the right set of permissions required by the app.

For example, 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:

android.permission.WRITE_EXTERNAL_STORAGE - included by default (up to Android 13) so file APIs continue to work unless you block it with android.blockExternalStoragePermission=true.

android.permission.INTERNET - always requested because the networking stack depends on it even for offline features like local web views.

android.permission.CAMERA & android.permission.RECORD_AUDIO - required when capturing photos, video, or audio. The builder injects them automatically and the Android port prompts the user the first time the API is used.

android.permission.READ_PHONE_STATE - used by telephony-aware APIs like Display.getMsisdn() and by media integration so audio pauses on calls.

android.permission.ACCESS_FINE_LOCATION & android.permission.ACCESS_COARSE_LOCATION - requested when using com.codename1.location or embedded maps so location fixes can be delivered.

android.permission.ACCESS_BACKGROUND_LOCATION - added when background geofencing or fetch tasks are enabled; the Android port verifies the permission on Android 10+.

android.permission.POST_NOTIFICATIONS - prompted on Android 13+ for both push registration and local notifications.

package.permission.C2D_MESSAGE, com.google.android.c2dm.permission.RECEIVE, and android.permission.RECEIVE_BOOT_COMPLETED - bundled with the push subsystem so Firebase Cloud Messaging can wake the app after device restarts.

android.permission.READ_CONTACTS - requested when accessing the device address book through Display.getAllContacts() and related APIs.

android.permission.READ_MEDIA_VIDEO & android.permission.READ_MEDIA_AUDIO - added on API 33 and above when the app plays a URI through MediaManager.createMedia(String, boolean) or createMediaAsync(String, boolean, Runnable), since that URI can point at the shared media store. The InputStream overloads don’t add them: the Android port copies the stream into app private storage and reads nothing shared. Block them with android.blockReadMediaPermissions=true.

android.permission.READ_MEDIA_IMAGES - never added by media playback, because no Codename One API requests it at runtime. Declaring it alongside READ_MEDIA_VIDEO places the app under Google Play’s Photo and Video Permissions policy, so ask for it only when you need it, with android.requestReadMediaPermissions=true.

Permissions under Marshmallow (Android 6+)

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 for example: 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 through external settings UI

This is 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

By default, Codename One’s Gradle 8 based Android builder uses the highest Android SDK you’ve installed, with a minimum of API 36, for both the compile and target SDK versions. If you override the target through the android.targetSDKVersion build hint, the builder honors it while keeping the compile SDK at least as high as the target SDK. Lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance.

Permission prompts

To test this API see the following simple contacts app:

Form f = new Form("Contacts", BoxLayout.y());
f.add(new InfiniteProgress());
Display.getInstance().invokeAndBlock(() -> {
    Contact[] ct = Display.getInstance().getAllContacts(true, true, false, true, true, false);
    Display.getInstance().callSerially(() -> {
        f.removeAll();
        for (Contact c : ct) {
            MultiButton mb = new MultiButton(c.getDisplayName());
            mb.setTextLine2(c.getPrimaryPhoneNumber());
            f.add(mb);
        }
        f.revalidate();
    });
});

f.show();

If you explicitly lower the target SDK (for example: android.targetSDKVersion=21) and install this app on an Android 6 device you will still see the legacy install prompt with all permissions listed up front:

Install UI when using the old permissions system
Figure 314. Install UI when using the old permissions system

When you keep the default target (API 36+) the installer defers to the runtime permission flow and the installation UI looks like this instead:

Install UI when using the new permissions system
Figure 315. Install UI when using the new permissions system

When you launch the UI under the old permissions system you see the contacts instantly. In the new system you’re presented with this UI:

Native permission prompt first time
Figure 316. Native permission prompt first time

If you accept and allow all is good and the app loads as usual but if you deny then Codename One provides the user another chance to request the permission. Notice that in this case you can customize the prompt string as explained below.

Codename One permission prompt
Figure 317. Codename One permission prompt

If you select don’t ask then you will get a blank screen since the contacts will return as a 0 length 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. For example, 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:

Native permission prompt second time
Figure 318. Native permission prompt second time

Notice that denying this second request won’t trigger another Codename One prompt.

Code changes

No explicit code changes needed for this functionality to "work." The respective APIs will work like they always worked and will prompt the user seamlessly for permissions.

Some behaviors that never occurred on Android but were legal in the past might start occurring with the switch to the new API. For example: 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 through Display properties. For example: to customize the rationale text of the contacts permission:

Display.getInstance().setProperty(
        "android.permission.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 UIManager.localize() 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:

com.codename1.ui.plaf.UIManager.getInstance().setBundle(new java.util.Hashtable<String, String>() {{
    put("android.permission.READ_CONTACTS", "Localized rationale for contacts");
    put("android.permission.READ_CONTACTS.title", "Localized permission title");
    put("android.permission.READ_CONTACTS.askAgain", "Localized ask again");
    put("android.permission.READ_CONTACTS.dontAsk", "Localized don't ask");
}});

For example, if the permission key is android.permission.READ_CONTACTS, you can localize these keys:

  • android.permission.READ_CONTACTS (dialog body)

  • android.permission.READ_CONTACTS.title

  • android.permission.READ_CONTACTS.askAgain

  • android.permission.READ_CONTACTS.dontAsk

The same pattern applies to other permissions. android.permission.ACCESS_BACKGROUND_LOCATION also supports: android.permission.ACCESS_BACKGROUND_LOCATION.settings, android.permission.ACCESS_BACKGROUND_LOCATION.cancel, android.permission.ACCESS_BACKGROUND_LOCATION.explanation_title, android.permission.ACCESS_BACKGROUND_LOCATION.explanation_body, and android.permission.ACCESS_BACKGROUND_LOCATION.ok.

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 for example: 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.

Simulate permission prompts menu item in the simulator
Figure 319. Simulate permission prompts menu item in the simulator

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’re probably familiar with the AndroidNativeUtil class from the com.codename1.impl.android 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 Codename One uses the same underlying logic, namely: checkForPermission.

To get a permission you can use this code as such:

if (!AndroidNativeUtil.checkForPermission(
        android.Manifest.permission.READ_PHONE_STATE,
        "This should be the description shown to the user...")) {
    // 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 invoked on the event dispatch thread without concern.

By default, fallback prompts are displayed using Codename One’s Dialog.show(…​). If you’re writing native Android code and need to use your own prompt implementation, you can install a custom callback:

AndroidNativeUtil.setPermissionPromptCallback(new PermissionPromptCallback() {
    @Override
    public boolean showPermissionPrompt(String permission, String title, String body, String positiveButtonText, String negativeButtonText) {
        return com.codename1.ui.Dialog.show(title, body, positiveButtonText, negativeButtonText);
    }

    @Override
    public void showPermissionMessage(String permission, String title, String body, String okButtonText) {
        com.codename1.ui.Dialog.show(title, body, okButtonText, null);
    }
});

Pass null to AndroidNativeUtil.setPermissionPromptCallback() to restore the default behavior.

On device debugging

On-device debugging now has two dedicated chapters, one per platform:

  • On-Device Debugging (iOS) covers attaching a standard Java debugger (IntelliJ, jdb, VS Code) to an iOS app running on a device or in the native iOS simulator, as well as debugging the generated Xcode project natively.

  • On-Device Debugging (Android) covers the equivalent adb/JDWP-based flow for Android devices and emulators, including wireless debugging and debugging the generated Gradle project from Android Studio.

Native interfaces

Sometimes you may wish to use an API that’s unsupported by Codename One or integrate with a 3rd party library/framework that isn’t supported. These are achievable tasks when writing native code and Codename One lets you encapsulate such native code using native interfaces.

Introduction

Notice that when you say "native" you don’t mean C/C++ always but rather the platforms "native" environment. For Android, Java or Kotlin code can be invoked with full access to the Android API. In case of iOS, Objective-C or Swift code can be invoked 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 allow primitive types, Strings, arrays of primitive types (single dimension ) & PeerComponent values. Any other kind of parameter/return type is prohibited. For example, once in the native layer the native code can act and query the Java layer for more 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 for example: 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 don’t support such features.

Don’t rely on pass by reference/value behavior since they vary between platforms

The shape is always the same, whether you are reaching one platform API or wrapping a whole third party SDK. Portable Java sits on one side, your per-platform implementation class on the other, and between them a narrow interface carrying only the types listed above: primitives, strings, single dimension primitive arrays and PeerComponent values. Nothing calls the vendor’s SDK except that implementation class.

How a call crosses into native code and a result comes back
Figure 320. The native interface round trip

A method that returns one of those types returns it directly — the value is marshalled back the same way the arguments went out. The return path only gets interesting when the work finishes later: a native SDK that answers through its own callback has nothing to return by then, so it calls a static method on the Java class instead, and that method resolves whatever the caller was waiting on. Integrating Android 3rd party libraries & JNI covers bundling the vendor binary on Android, ios.pods does the same for iOS, and Libraries - cn1lib covers shipping the result as a library.

Implementing a native layer effectively means:

  1. Creating an interface that extends NativeInterface and defines methods with the arguments/return values declared in the previous paragraph.

  2. Creating the proper native implementation hierarchy based on the call conventions for every platform, in each platform module’s own source directory

For example: to create a simple hello world interface do something like:

public interface MyNative extends NativeInterface {
    String helloWorld(String hi);
}

You now need to right-click the class in the IDE and select the Generate Native Access menu item:

Generating the native code
Figure 321. Generating the native code
Once generated you’re prompted that the native code is in the "native" directory
Figure 322. Once generated you’re prompted that the native code is in the "native" directory

You 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:

Native directory structure containing stubs for the various platforms
Figure 323. 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 files you deleted/renamed will be recreated
Running "Generate Native Access" when some/all the native files exist already
Figure 324. Running "Generate Native Access" when some/all the native files exist already

For now lets leave the stubs and come back to them soon. From the Codename One Java code you can call the implementation of this native interface using:

MyNative my = NativeLookup.create(MyNative.class);
if (my != null && my.isSupported()) {
    Log.p(my.helloWorld("Hi"));
}

Notice that for this to work you must implement the native code on all supported platforms.

You will 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 android/src/main/java:

class MyNativeImplStub {
    public String helloWorld(String param) {
        return null;
    }

    public boolean isSupported() {
        return false;
    }
}

The stub implementation always returns false, null or 0 by default. The isSupported also defaults to false thus allowing you to implement a NativeInterface on some platforms and leave the rest out without knowing anything about these platforms.

You can implement the Android version in Java or Kotlin. Here is the Java version:

public class MyNativeImpl { // (2) (3)
    public String helloWorld(String param) {
        Log.d("MyApp", param); // (1)
        return "Tada";
    }

    public boolean isSupported() {
        return true; // (4)
    }
}
  1. Notice that you’re using the Android native android.util.Log class which isn’t accessible from standard Codename One code

  2. The impl class doesn’t physically implement the MyNative interface!
    This is intentional and due to the PeerComponent functionality mentioned below. You don’t need to add an implements clause.

  3. Notice that there is no constructor and the class is public. It’s crucial that the system will be able to assign 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.

  4. The native method is implemented, and isSupported answers true.

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 for example: the full Android SDK or the full Xcode Objective-C runtime. For example, since the native code is compiled on the servers (where these runtimes 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 identical for Android (Java/Kotlin) & Java SE.

Swift (iOS) and Kotlin (Android) options

For iOS native interfaces you can implement the generated …​Impl class in Objective-C or Swift.
For Android native interfaces you can implement the generated …​Impl class in Java or Kotlin.

PlatformTypical file locations for native interface implementations

Android (Java/Kotlin)

android/src/main/java/com/mycompany/myapp/MyNativeImpl.java
android/src/main/java/com/mycompany/myapp/MyNativeImpl.kt

iOS (Objective-C/Swift)

ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.h
ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.m
ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.swift

Native Windows (win32, C)

win/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c

Native Linux ©

linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c

JavaScript

javascript/src/main/javascript/com_mycompany_myapp_MyNative.js

The native Windows and native Linux ports compile your app to C (no JVM), so their native interface implementations are plain C, generated alongside the others by the "Generate Native Sources" tool. The native Mac target reuses the iOS Objective-C/Swift implementation, since it rides the same build.

Use the Android main thread (native EDT)

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. For example: Android will respect calls off of the EDT and iOS will often crash. Some OSes enforce EDT access and will throw an exception when you violate that…​

You don’t need to know about these things, hidden functionality within your implementation bridges between your EDT and the native EDT to provide consistent cross-platform behavior. However, when you write native code you need awareness.

Why not Implicitly call Native Interfaces on the Native EDT?

Calling into the native EDT includes overhead and it might not be necessary for some features (for example, IO, polling etc.). Furthermore, some calls might work well with asynchronous calls while others might need synchronous results and you can’t know in advance which ones you would need.

How do you access the native EDT

Within your native code in Android do something like:

com.codename1.impl.android.AndroidNativeUtil.getActivity().runOnUiThread(new Runnable() {
    public void run() {
       // your native code here...
    }
});

This will execute the block within run() asynchronously on the native Android UI thread. If you need synchronous execution you have a special method for Codename One:

com.codename1.impl.android.AndroidImplementation.runOnUiThreadAndBlock(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 your 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.

For example: you published a library that added support for Intercom. The native Android integration instructions for the library looked like this:

Add the following dependency to your app’s build.Gradle file:

dependencies {
    compile 'io.intercom.android:intercom-sdk:3.+'
}

Which instantly raises the question: "How do you do that in Codename One"?

Well, it’s actually pretty simple. You can add the build hint:

android.gradleDep=compile 'io.intercom.android:intercom-sdk:3.+'

This would "work" but there is a catch…​

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 you also add:

android.playServices=9.8.0
android.sdkVersion=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.

Objective-C and Swift (iOS)

When generating the Objective-C code the "Generate Native Sources" tool produces two files by default: com_mycompany_myapp_MyNativeImpl.h & com_mycompany_myapp_MyNativeImpl.m. If you enable Swift stub generation in the Maven goal, it can also produce com_mycompany_myapp_MyNativeImpl.swift.

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>

@interface com_mycompany_myapp_MyNativeImpl : NSObject {
}

-(NSString*)helloWorld:(NSString*)param;
-(BOOL)isSupported;
@end

And com_mycompany_myapp_MyNativeImpl.m contains:

#import "com_mycompany_myapp_MyNativeImpl.h"

@implementation com_mycompany_myapp_MyNativeImpl

-(NSString*)helloWorld:(NSString*)param{
    return nil;
}

-(BOOL)isSupported{
    return NO;
}

@end
Objective-C relies on argument names as part of the message (method) signature. -(NSString*)helloWorld:(NSString*)param isn’t the same as -(NSString*)helloWorld:(NSString*)iChangedThisName!
don’t change argument names in the Objective-C native interface!

Here is a simple implementation like above:

#import "com_mycompany_myapp_MyNativeImpl.h"

@implementation com_mycompany_myapp_MyNativeImpl

-(NSString*)helloWorld:(NSString*)param{
    NSLog(@"MyApp: %@", param);
    return @"Tada";
}

-(BOOL)isSupported{
    return YES;
}

@end

If you prefer Swift for iOS native interfaces, keep the same class naming convention (com_mycompany_myapp_MyNativeImpl) and annotate the class with @objc(…​) so the runtime can discover it.

Using the iOS main thread (native EDT)

iOS has a native thread you should use for all calls like Android. Check out the Native EDT on Android section above for reference.

On iOS this is pretty like 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 cautious with this call!

Use Cocoapods for dependencies

Cocoapods are the iOS equivalent of Gradle dependencies.

CocoaPods allow you to add a native library dependency to iOS far more than Gradle. A pod that needs a newer iOS than the rest of your app says so through the ios.pods.platform build hint. The deployment target the build uses is the highest of everything that asks for one — the builder’s own floor, ios.deployment_target, and ios.pods.platform — so that hint can raise the target and never lower it.

Including intercom itself required a single build hint: ios.pods=Intercom which you can obviously extend by using commas to include many libraries. You can search the CocoaPods website 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 you’ve to CocoaPods is faster. Another advantage is the ability to keep up with the latest developments from the library providers.

C (native Windows and native Linux)

The native Windows (win32) and native Linux ports have no JVM: the "clean" ParparVM target compiles your whole app to C and then to a native binary. A native interface on these ports is therefore implemented in plain C rather than C#/Objective-C/Java. The "Generate Native Sources" tool emits one C file per interface:

  • win/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c (native Windows)

  • linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c (native Linux)

The two files are identical, so a native interface written once works on both ports; only the directory differs. (The native Mac target reuses the iOS Objective-C/Swift implementation, because it rides the iOS build.)

The translator emits one C function per interface method. Unlike the Objective-C stub, the function name is the fully qualified, mangled C symbol the translated app links against, so don’t rename the functions or change their signatures; just fill in the bodies. Each function takes the thread state as its first argument and JAVA_* typed parameters. For example, the interface:

public interface MyNative extends NativeInterface {
    String helloWorld(String hi);
}

This generates the C stub below (returning JAVA_NULL until you implement it):

#include "cn1_globals.h"

JAVA_OBJECT com_mycompany_myapp_MyNativeImplCodenameOne_helloWorld___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT param1) {
    return JAVA_NULL;
}

Type mapping: the primitives (int, long, boolean, byte, short, char, float, double) become their JAVA_INT/JAVA_LONG/JAVA_BOOLEAN equivalents; String, arrays and other objects become JAVA_OBJECT; a void method returns JAVA_VOID and omits the R suffix in its name; and a com.codename1.ui.PeerComponent is passed and returned as a JAVA_LONG native peer handle. Use the core helpers declared in cn1_globals.h (for example newStringFromCString / stringToUTF8) to convert between C and Java values. As with the other ports, you can drop additional .c/.h sources and prebuilt libraries into the same src/main/c directory and they’re compiled into the binary.

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.helloWorld__java_lang_String = function(param1, callback) {
        callback.error(new Error("Not implemented yet"));
    };

    o.isSupported_ = function(callback) {
        callback.complete(false);
    };

exports.com_mycompany_myapp_MyNative= o;

})(cn1_get_native_interfaces());

A simple implementation looks like this:

(function(exports){

var o = {};

    o.helloWorld__java_lang_String = function(param1, callback) {
        callback.complete("Hello World!!!");
    }

    o.isSupported_ = function(callback) {
        callback.complete(true);
    };

exports.com_my_code_MyNative = o;

})(cn1_get_native_interfaces());

Notice that you 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’s calling your native interface will block until your method calls callback.complete(). This allows you to use asynchronous APIs inside your native method while still allowing Codename One to work use your native interface through a synchronous API.

Make sure you call either callback.complete() or callback.error() in your method at some point, or you will cause a deadlock in your app (code calling your native method will 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. (For example: int to "int," double to "double," etc.).

  2. Reference types are mapped to their fully qualified class name with '.' replaced with underscores. For example: java.lang.String would be java_lang_String.

  3. Array parameters are marked by their scalar type name followed by an underscore and 1ARRAY. For example: int[] would be "int_1ARRAY" and String[] would be java_lang_String_1ARRAY.

JavaScript examples

Java API:

public interface JavaScriptNativeApi {
    void print(String str);
}

becomes

o.print__java_lang_String = function(param1, callback) {
    console.log(param1);
    callback.complete();
}

Java API:

interface JavaScriptNativeApiAddTwoArgs {
    int add(int a, int b);
}

becomes

o.add__int_int = function(param1, param2, callback) {
    callback.complete(param1 + param2);
}
interface JavaScriptNativeApiAddArray {
    int add(int[] a);
}

becomes

o.add__int_1ARRAY = function(param1, callback) {
    var c = 0, len = param1.length;
    for (var i =0; i<len; i++) {
        c += param1[i];
    }
    callback.complete(c);
}

Native GUI components

PeerComponent return values are automatically translated to the platform native peer as an expected return value. For example: for a NativeInterface method such as this:

interface NativePeerProvider {
    PeerComponent createPeer();
}

Android native implementation would use:

class AndroidPeerImplementation {
    public View createPeer() {
        return null;
    }
}

The iphone would need to return a pointer to a view for example:

- (UIView*)createPeer;
Not all platforms support native peers. Specifically Java SE doesn’t support them due to the way the Java SE 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 (for example: a <div> tag to be returned.). For example:

o.createHelloComponent_ = function(callback) {
    var c = jQuery('<div>Hello World</div>')
            .css({'background-color' : 'yellow', 'border' : '1px solid blue'});
    callback.complete(c.get(0));
};

Notice that if you want to use a native library (jar,.a file etc.) place it in the resources of the matching platform module — android/src/main/resources, ios/src/main/resources — and it will be packaged into the final executable. You would 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.

This is discussed further below.

Type mapping & rules

Several rules govern the creation of NativeInterfaces and you covered some of them.

  • The implementation class must have a default public constructor or no constructor at all

  • Native methods can’t throw exceptions, checked or otherwise

  • A native method can’t have the name init as this is a reserved method in Objective-C

  • Only the supported types listed below can be used

  • 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

Table 19. NativeInterface Supported Types
JavaAndroidJava SEObj-CC#

byte

byte

byte

char

sbyte

boolean

boolean

boolean

BOOL

bool

char

char

char

int

char

short

short

short

short

short

int

int

int

int

int

long

long

long

long

long

float

float

float

float

float

double

double

double

double

double

String

String

String

NSString*

String

byte[]

byte[]

byte[]

NSSData*

sbyte[]

boolean[]

boolean[]

boolean[]

NSData*

bool[]

char[]

char[]

char[]

NSData

char[]

short[]

short[]

short[]

NSData*

short[]

int[]

int[]

int[]

NSData*

int[]

long[]

long[]

long[]

NSData*

long[]

float[]

float[]

float[]

NSData*

float[]

double[]

double[]

double[]

NSData*

double[]

PeerComponent

android.view.View

PeerComponent

void*

FrameworkElement

JavaScript is excluded from the table above as it isn’t a type safe language and thus has no such type mapping
PeerComponent on iOS is void* but UIView is expected as a result

The examples below show the signatures for this method on all platforms:

Listing 27. NativeInterface definition
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);
}
Listing 28. Android Version
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, android.view.View param17) {
    }
}
Listing 29. iOS Version
-(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
    param15:(NSData*)param15 param16:(NSData*)param16
    param17:(void*)param17;
The lines are broken for the print version — the JavaScript name is one long method name that ran off the page.
Listing 30. JavaScript Version
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) {
    callback.error(new Error("Not implemented yet"));
};
Listing 31. Java SE Version
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) {
    }
}

Android native permissions

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.

For example, when accessing native functionality this won’t work since native code might require specialized permissions and you don’t/can’t run any serious analysis on it (it can be about anything).

If you require more permissions in your Android native code you need to define them in the build arguments using android.permission.<PERMISSION_NAME>=true for each permission you want to include. A full list of permissions are listed in Android’s Manifest.permission documentation.

For example:

android.permission.ADD_VOICEMAIL=true
android.permission.BATTERY_STATS=true
...

You can specify the maximum SDK version in which the permission is needed using the android.permission.<PERMISSION_NAME>.maxSdkVersion build hint. You can also specify whether the permission is required for the app to run using the android.permission.<PERMISSION_NAME>.required build hint.

For example:

android.permission.ADD_VOICEMAIL=true
android.permission.BATTERY_STATS=true
android.permission.ADD_VOICEMAIL.required=false
android.permission.ADD_VOICEMAIL.maxSdkVersion=18
...

You can or use the android.xpermissions build hint to inject <uses-permission> tags into the manifest file. For example:

android.xpermissions=<uses-permission android:name="android.permission.READ_CALENDAR" />
You need to include the full XML snippet. You can unify many lines into a single line in the GUI as XML allows that.

Native AndroidNativeUtil

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 from the native code. For example: many Android APIs need access to the Activity which you can get by calling AndroidNativeUtil.getActivity().

The native util class includes a few other features such as:

  • runOnUiThreadAndBlock(Runnable) - this is such a common pattern that it was generalized into a public static method. Its identical to Activity.runOnUiThread but blocks until the runnable finishes execution.

  • addLifecycleListener/removeLifecycleListener - These essentially provide you with a callback to lifecycle events: onCreate etc. which can be pretty useful for some cases.

  • registerViewRenderer - PeerComponent's are shown on top of the UI since they’re rendered within their own thread outside of the EDT cycle. When you need to show a Dialog on top of the peer you grab a screenshot of the peer, hide it and then show the dialog with the image as the background (the same applies for transitions). Some components (specifically the MapView) might not render and require custom code to implement the transferal to a native Bitmap, this API allows you to do that.

You can work with AndroidNativeUtil using native code such as this:

class NativeCallsImpl {
     public void nativeMethod() {
        AndroidNativeUtil.getActivity().runOnUiThread(new Runnable() {
            public void run() {
               // ...
            }
        });
     }
    // ...
}

Broadcast receiver

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 you’d need a broadcast 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 android/src/main/java. It will get compiled with the rest of the native code and "works." You can therefore place this class under android/src/main/java/com/codename1/sms/intercept:

public class SMSListener extends BroadcastReceiver {

    @Override
    public void onReceive(Context cntxt, Intent intent) {
        if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
            Bundle bundle = intent.getExtras();
            SmsMessage[] msgs = null;
            if (bundle != null) {
                try {
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];
                    for (int i = 0; i < msgs.length; i++) {
                        msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                        String msgBody = msgs[i].getMessageBody();
                        SMSCallback.smsReceived(msgBody);
                    }
                } catch (Exception e) {
                    Log.e(e);
                    SMSCallback.smsReceiveError(e);
                }
            }
        }
    }
}

The code above is pretty standard native Android code, it’s a callback in which most of the logic is like the native Android code mentioned in this stackoverflow question.

However, there is still more you need to do. To implement this natively you need to register the permission and the receiver in the manifest.xml file as explained in that question. This is how their native manifest looked:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bulsy.smstalk1">
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS" />

    <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="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.bulsy.smstalk1.SmsListener"
               android:enabled="true"
               android:permission="android.permission.BROADCAST_SMS"
               android:exported="true">
            <intent-filter android:priority="2147483647">//this doesnt work
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

You need the broadcast permission XML and the permission XML. Both are doable through the build hints. The former is pretty easy:

android.xpermissions=<uses-permission android:name="android.permission.RECEIVE_SMS" />

The latter isn’t much harder; notice that many lines have been merged into a single line for convenience:

android.xapplication=<receiver android:name="com.codename1.sms.intercept.SMSListener"  android:enabled="true" android:permission="android.permission.BROADCAST_SMS"  android:exported="true">                    <intent-filter android:priority="2147483647"><category android:name="android.intent.category.DEFAULT" />        <action android:name="android.provider.Telephony.SMS_RECEIVED" />                 </intent-filter>             </receiver>

Here it’s formatted for readability:

<receiver android:name="com.codename1.sms.intercept.SMSListener"
              android:enabled="true"
              android:permission="android.permission.BROADCAST_SMS"
              android:exported="true">
                   <intent-filter android:priority="2147483647">
                          <category android:name="android.intent.category.DEFAULT" />
                          <action android:name="android.provider.Telephony.SMS_RECEIVED" />
                   </intent-filter>
</receiver>

Listening & permissions

You will notice that these don’t include the actual binding or permission prompts you would expect for something like this. To do this you need a native interface.

The native sample in stack overflow bound the listener in the activity but here you want the app code to decide when you should bind the listening:

public interface NativeSMSInterceptor extends NativeInterface {
    void bindSMSListener();
    void unbindSMSListener();
}

That’s easy!

Notice that isSupported() returns false for all other OSes so you won’t need to ask whether this is "Android" you can use isSupported().

The implementation is pretty easy too:

public class NativeSMSInterceptorImpl {
    private SMSListener smsListener;
    public void bindSMSListener() {
        if (AndroidNativeUtil.checkForPermission(Manifest.permission.RECEIVE_SMS, "We can automatically enter the SMS code for you")) { // (1)
            smsListener = new SMSListener();
            IntentFilter filter = new IntentFilter();
            filter.addAction("android.provider.Telephony.SMS_RECEIVED");
            AndroidNativeUtil.getActivity().registerReceiver(smsListener, filter); // (2)
        }
    }

    public void unbindSMSListener() {
        AndroidNativeUtil.getActivity().unregisterReceiver(smsListener);
    }

    public boolean isSupported() {
        return true;
    }
}
  1. 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!

  2. Here you actually bind the listener, this allows you to grab one SMS and not listen in on every SMS coming through

Native code callbacks

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 more complicated since its platform specific and more error prone.

A common "trick" for calling back is to define a static method and then trigger it from native code. This works for Android & Java SE since those platforms use Java for their "native code." Mapping this to iOS requires some basic understanding of how the iOS VM works.

For this explanation lets pretend you have a class called NativeCallback in the src hierarchy under the package com.mycompany that has the method: public static void callback():

public class NativeCallback {
    public static void callback() {
        // do stuff
    }

    public static void callback(int arg) {
        // do stuff
    }

    public static void callback(String arg) {
        // do stuff
    }

    public static int callbackR(int arg) {
        // do stuff
        return arg;
    }
}

If you want to call it from Android or all the Java based platforms you can write this in the "native" code:

public class NativeCallbackUsage {
    public void invokeCallbacks() {
        NativeCallback.callback();
        NativeCallback.callback("My Arg");
    }
}

You can also pass an argument as you do later on:

public class NativeCallbackUsage {
    public void invokeCallbacks() {
        NativeCallback.callback();
        NativeCallback.callback("My Arg");
    }
}

Accessing callbacks from Objective-C

If you want to invoke that method from Objective-C you need to do the following.

Add an include statement as such:

#include "com_mycompany_NativeCallback.h"
#include "CodenameOne_GLViewController.h"

Notice that the CodenameOne_GLViewController.h include defines various macros such as CN1_THREAD_STATE_PASS_SINGLE_ARG.

Then when you want to trigger the method do:

com_mycompany_NativeCallback_callback__(CN1_THREAD_STATE_PASS_SINGLE_ARG);
For most callbacks you should use the macro CN1_THREAD_GET_STATE_PASS_SINGLE_ARG instead of CN1_THREAD_STATE_PASS_SINGLE_ARG also make sure to add `#include "cn1_globals.h" in the file

The VM passes the thread context along method calls to save on API calls (thread context is used in Java for synchronization, gc and more).

You can pass arguments like:

public static void callback(int arg) {
    // do stuff
}

Which maps to native as (notice the extra _ before the int):

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.

The reason for this dates to the migration from XMLVM [9] 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 NSString* which is iOS equivalent. Assuming a method like this:

public static void callback(String arg) {
    // do stuff
}

You would need to convert the NSString* value you already have to a java.lang.String which the callback expects.

The fromNSString function also needs this special argument so you will need to change 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 you might want to return a value from callback as such:

public static int callbackR(int arg) {
    // do stuff
    return arg;
}

This is tricky since the method name changes to support covariant return types and so the signature would be:

com_mycompany_NativeCallback_callback___int_R_int(intValue);

The upper case R allows you to differentiate between void callback(int,int) and int callback(int).

Covariant return types are a little known Java 5 feature. For example: the method Object getX() can be overridden by MyObject getX(). For example, in the VM level they can both exist side by side.

Accessing callbacks from JavaScript

The mechanism for invoking static callback methods from JavaScript (for the JavaScript port ) is like 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 like 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) {
        // ...
    }
}

It’s defined in the com.codename1.googlemaps.MapContainer class.

This method is called from JavaScript inside a native interface using the following code:

var fireMapChangeEvent = this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double;
google.maps.event.addListener(this.map, 'bounds_changed', function() {
    fireMapChangeEvent(self.mapId, self.map.getZoom(), self.map.getCenter().lat(), self.map.getCenter().lng());
});

In this example you first get a reference to the fireMapChangeEvent method, and then call it later. For example, you could have called it directly also.

Your code MUST contain the full string path this.$GLOBAL$.your_class_name.your_method_name or the build server won’t be able to recognize that your code requires this method. The $GLOBAL$ object is populated by the build server with those classes and methods that are used inside your native methods. If the build server doesn’t recognize that the methods are being used (through this pattern) it won’t generate the necessary wrappers for your JavaScript code to access the Java methods.

Callbacks of the SMS receiver

The SMS Broadcast Receiver code from before also used callbacks such as this:

package com.codename1.sms.intercept; // (1)

import com.codename1.util.FailureCallback;
import com.codename1.util.SuccessCallback;
import static com.codename1.ui.CN.*;

class SMSCallback {
    static SuccessCallback<String> onSuccess;
    static FailureCallback onFail;

    public static void smsReceived(String sms) {
        if (onSuccess != null) {
            SuccessCallback<String> s = onSuccess;
            onSuccess = null;
            onFail = null;
            SMSInterceptor.unbindListener();
            callSerially(() -> s.onSucess(sms)); // (2)
        }
    }

    public static void smsReceiveError(Exception err) {
        if (onFail != null) {
            FailureCallback f = onFail;
            onFail = null;
            SMSInterceptor.unbindListener();
            onSuccess = null;
            callSerially(() -> f.onError(null, err, 1, err.toString()));
        } else {
            if (onSuccess != null) {
                SMSInterceptor.unbindListener();
                onSuccess = null;
            }
        }
    }
}
  1. 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 through the API (the class doesn’t have the public modifier)

  2. You 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

Asynchronous callbacks & threading

One of the problematic aspects of calling back into Java from JavaScript is that JavaScript has no notion of multi-threading. If the method you’re calling uses Java’s threads at all (for example: 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. For example: With the Google Maps example above, you would change :

to

var asyncFireMapChangeEvent = 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 with threads - and it’s 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.

Usually you should use the async version of a method when calling it from your native method. Only use the synchronous (default) version if you’re sure that the method doesn’t use any threading primitives.

Libraries - cn1lib

Support for JAR files in Codename One has been a source of confusion so its probably a good idea to revisit this subject again and clarify all the details.

The first source of confusion is changing the classpath. You should NEVER change the classpath or add an external JAR through the IDE classpath UI. The reasoning here is simple, these IDEs don’t package the JARs into the final executable and even if they did these JARs would probably use features unavailable or inappropriate for the device (for example: java.io.File etc.).

Don’t change the classpath, this is how it should look for a typical Java 8 Codename One application
Figure 325. 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 physically a zip file containing other zip files and some meta-data.

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 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 JARs and they both have different solutions:

  1. Modularity

  2. Working with an existing JARs

Cn1lib’s address the modularity aspect allowing you to break that down. Existing jars can sometimes be used native code settings but you would want to adapt the code to abide by Codename One restrictions.

How to use cn1libs

Codename One has a large repository of 3rd party cn1libs. With the Maven build tool, you consume a cn1lib the same way you consume any other Maven dependency: by adding it to the <dependencies> section of your application’s common/pom.xml file. A typical entry looks like:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>mylib-lib</artifactId>
    <version>1.0.0</version>
    <type>pom</type>
</dependency>

Add the dependency before the <!-- INJECT DEPENDENCIES -→ comment in common/pom.xml. The -lib suffix on the artifactId and the <type>pom</type> declaration are both important - they tell Maven to pull in the library’s "lib" aggregator module rather than a plain jar. See Codename One libraries for the full library-consumption guide.

Some cn1libs require additional configuration such as build hints. Always read the library author’s instructions when integrating a 3rd party library.
Legacy Ant projects

If you’re still maintaining an Ant-based project, you can install a cn1lib by dropping its .cn1lib file into the project’s lib directory and then right-clicking the project and selecting Codename OneRefresh cn1lib files (which invokes the refresh-libs Ant task). This unpacks classes and stub sources into lib/impl/cls and lib/impl/stubs, and native sources into lib/impl/native. New projects should use Maven instead - see Maven Project Workflow.

Creating a simple cn1lib

Codename One libraries are generated from the cn1lib-archetype Maven archetype, which creates a multi-module project containing a common module for your cross-platform Java sources and one module per native platform (iOS, Android, Java SE, JavaScript) for any native interfaces. The detailed walkthrough - command-line, IntelliJ IDEA, NetBeans, and Eclipse - lives in Codename One libraries.

The quickest path from a shell:

mvn archetype:generate \
  -DarchetypeArtifactId=cn1lib-archetype \
  -DarchetypeGroupId=com.codenameone \
  -DarchetypeVersion=LATEST \
  -DgroupId=com.example.mylib \
  -DartifactId=mylib \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Once the project is generated, add your cross-platform Java sources under common/src/main/java, CSS under common/src/main/css, and run mvn install from the project root to build. The build produces both a Maven artifact for pom-style consumption and a legacy .cn1lib file in common/target for users still on Ant.

Build hints in cn1libs

Some cn1libs are pretty simple to install - just add them as a Maven dependency in common/pom.xml. For example, many of the more elaborate cn1libs need some pretty complex configurations. This is the case when native code is involved where you 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 you 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 write a build hint as codename1.arg.ios.plistInject=…​ for the various hints.

Notice the usage of the properties syntax for the build hint with the codename1.arg prefix you would also need to escape reserved characters for properties files.
The best way to discover the right syntax for such build hints is to set them through the build hints GUI in a regular project and copy/paste them from codenameone_settings.properties into the cn1lib file.

The obvious question is why do you need two files?

There are two types of build hints: required and appended.

Required build hints can be something like ios.objC=true which you want to always work. For example: if a cn1lib defines ios.objC=true and another cn1lib defines ios.objC=false things won’t work since one cn1lib won’t get what it needs…​
In this case you’d want the build to fail so you can remove the faulty cn1lib.

If two cn1libs define ios.objC=true there will be no collision as the value would be identical

An appended property would be something like ios.plistInject=<key>UIBackgroundModes</key><array><string>audio</string> </array>

Notice that this can still collide for example: if a different cn1lib defines its own background mode. For example, there are many valid cases where ios.plistInject can be used for other things. In this case you will append the content of the ios.plistInject into the build hint if it isn’t already there.

Keep these things in mind:

  • 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…​
    be 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.

These build hints are probably of the "required" type:

android.debug
android.release
android.installLocation
android.licenseKey
android.stack_size
android.statusbar_hidden
android.googleAdUnitId
android.includeGPlayServices
android.headphoneCallback
android.gpsPermission
android.asyncPaint
android.supportV4
android.theme
android.cusom_layout1
android.versionCode
android.captureRecord
android.removeBasePermissions
android.blockExternalStoragePermission
android.min_sdk_version
android.smallScreens
android.streamMode
android.enableProguard
android.targetSDKVersion
android.web_loading_hidden
facebook.appId
facebook.clientToken
ios.keyboardOpen
ios.project_type
ios.newStorageLocation
ios.prerendered_icon
ios.application_exits
ios.themeMode
ios.xcode_version
javascript.inject_proxy
javascript.minifying
javascript.proxy.url
javascript.sourceFilesCopied
javascript.teavm.version
google.adUnitId
ios.includePush
ios.headphoneCallback
ios.enableAutoplayVideo
ios.googleAdUnitId
ios.googleAdUnitIdPadding
ios.enableBadgeClear
ios.locationUsageDescription
ios.bundleVersion
ios.objC
ios.testFlight
ios.metal
desktop.width
desktop.height
desktop.adaptToRetina
desktop.resizable
desktop.fontSizes
desktop.theme
desktop.themeMac
desktop.themeWin
desktop.windowsOutput
noExtraResources
android.permission.<PERMISSION_NAME>

These build hints should probably be appended:

android.xapplication
android.xpermissions
android.xintent_filter
android.facebook_permissions
android.stringsXml
android.style
android.nonconsumable
android.xapplication_attr
android.xactivity
android.pushVibratePattern
android.proguardKeep
android.sharedUserId
android.sharedUserLabel
ios.urlScheme
ios.interface_orientation
ios.plistInject
ios.facebook_permissions
ios.applicationDidEnterBackground
ios.viewDidLoad
ios.glAppDelegateHeader
ios.glAppDelegateBody
ios.beforeFinishLaunching
ios.afterFinishLaunching
ios.add_libs
cn1lib Structure/File Format

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:

Table 20. cn1lib structure
File NameRequiredPurpose

main.zip

Contains the bytecode and the library binary data. This is effectively the portable portion of the jar

stubs.zip

Stub source files (autogenerated) containing javadocs to provide code completion

manifest.properties

×

General properties of the library, this isn’t used for much at the moment

codenameone_ library_ appended.properties

×

Discussed above

codenameone_ library_ required.properties

×

Discussed above

nativeios.zip

×

Native iOS sources if applicable

nativeand.zip

×

Native Android sources if applicable

nativejavascript.zip

×

Native JavaScript sources if applicable

nativese.zip

×

Native Java SE sources if applicable

nativewin.zip

×

Native Windows sources if applicable

nativeme.zip

×

Native Java ME sources if applicable

Integrating Android 3rd party libraries & JNI

While its pretty easy to use native interfaces to write Android native code some things aren’t necessarily as obvious. For example: if you want to integrate a 3rd party library, specifically one that includes native C JNI code this process isn’t as straightforward.

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 place a Jar file in android/src/main/resources. This will link your binary with the jar file, and the build server picks it up and adds it to the classpath.
    Notice that Android release apps are obfuscated by default which might cause issues with such libraries if they reference APIs that are unavailable on Android. You can work around this by adding a build hint to the proguard obfuscation code that blocs the obfuscation of the problematic classes using the build hint:
    android.proguardKeep=-keep class com.mypackage.ProblemClass { *; }`

  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.

    You can link an aar file by placing it in android/src/main/resources and the build server will link it to the project.

Older material describes a third option, a zipped Eclipse library project renamed to .andlib. That format is gone. The Android builder stops with andlib format is not supported anymore, use aar instead the moment it finds one, so a project carrying an .andlib doesn’t build at all. Convert it to an aar.

Integrating iOS 3rd party libraries

Reach for ios.pods first. CocoaPods resolves the dependency and its own dependencies for you, keeps the upload small, and is described in Using Cocoapods.

Not every SDK is published as a pod, though, and a vendor shipping a static library and headers can’t be installed that way. Those files go into the ios module, which packages two directories into the native archive: put anything you would compile or #import beside your own native sources in ios/src/main/objectivec, and the vendor’s binaries in ios/src/main/resources.

  • .h headers go with the sources, in ios/src/main/objectivec.

  • .a static libraries and .bundle resource directories go in ios/src/main/resources.

  • A .framework has to be zipped first, keeping its name, so MyFramework.framework is placed as ios/src/main/resources/MyFramework.framework.zip.

Those are the Maven paths. A legacy Ant project put every one of those in native/ios instead, which is the layout older material refers to.

An SDK usually also expects a handful of the platform’s own frameworks to be linked. Name those in the ios.add_libs build hint, separated by semicolons. A good many are linked by default already, so start with the vendor’s list, build, and add only what the linker actually asks for.

Drag & drop

Unlike other platforms that tried to create overly generic catch all APIs Codename One tried to make things as simple as possible.

In Codename One 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.

This lightweight dragging never leaves the application. To drag content into another application, onto the desktop or into a file manager — and to accept a drag coming the other way — see Native operating system drag & drop below.

The code below allows you to rearrange the items based on a sensible order. Notice it relies on the default Container drop behavior:

public class RearrangeableItemsDemo {
    public void show() {
        Form hi = new Form("Rearrangeable Items", new BorderLayout());
        String[] buttons = {"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" };

        Container box = new Container(BoxLayout.y());
        box.setScrollableY(true);
        box.setDropTarget(true);
        java.util.List<String> got = Arrays.asList(buttons);
        Collections.shuffle(got);
        for (String current : got) {
            MultiButton mb = new MultiButton(current);
            box.add(mb);
            mb.setDraggable(true);
        }

        hi.add(BorderLayout.NORTH, "Arrange The Titles").add(BorderLayout.CENTER, box);
        hi.show();
    }
}
Drag demo
Figure 326. Drag demo

To enable dragging a component it must be flagged as draggable using setDraggable(true), to allow dropping the component onto another component you must first enable the drop target with setDropTarget(true) and override some methods (more on that later).

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.

You can override these methods in the draggable components:

  • 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 as an icon based on location etc. (for example: a move/copy icon).

In the drop target you can override the following methods:

  • 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!

Native operating system drag & drop

The section above moves a component around inside one form. Native dragging hands the gesture to the operating system instead, so a drag can end in another application, on the desktop or in a file manager, and a drag started anywhere on the machine can end on one of your components.

The payload is a ClipboardContent, the same object a copy publishes. That’s the whole idea: a drag is a copy that the user aims with the pointer, so whatever the application can already put on the clipboard it can already drag out, and whatever it can paste it can already accept as a drop. Offering several representations lets one drag land correctly in unrelated applications — a text editor takes the text/html representation, a plain text field takes text/plain, and the desktop takes the file list.

To drag something out, give the component an operation:

public void showDragSource() {
    Form hi = new Form("Drag Out", BoxLayout.y());
    Label card = new Label("Drag me into another application");

    ClipboardContent content = new ClipboardContent()
            .setData(ClipboardContent.MIME_TEXT, "Dragged out of Codename One")
            .setData(ClipboardContent.MIME_HTML, "<b>Dragged</b> out of Codename One");

    card.setNativeDragOperation(new NativeDragOperation(content)
            .setAllowedActions(NativeDragOperation.ACTION_COPY));

    hi.add(card);
    hi.show();
}

Dragging a file works the same way, except that the file often doesn’t exist yet and the user may drop it nowhere at all. Register it as a provider and it’s written at the moment a receiver asks for it rather than when the drag starts:

public void showFileDragSource() {
    Form hi = new Form("Drag A File Out", BoxLayout.y());
    Label card = new Label("Drag me onto the desktop");

    // The file is promised rather than written. The provider runs when a receiver actually
    // asks for the file list, so a drag the user abandons costs nothing.
    ClipboardContent content = new ClipboardContent()
            .setData(ClipboardContent.MIME_TEXT, "note.txt")
            .setDataProvider(ClipboardContent.MIME_FILE, new ClipboardDataProvider() {
                @Override
                public Object getClipboardData(String mimeType) {
                    return writeNote();
                }
            });

    NativeDragOperation op = new NativeDragOperation(content);
    op.addCompletionListener(e ->
            System.out.println("performed action: "
                    + ((NativeDragOperation) e.getSource()).getPerformedAction()));
    card.setNativeDragOperation(op);

    hi.add(card);
    hi.show();
}

private String writeNote() {
    FileSystemStorage fs = FileSystemStorage.getInstance();
    String path = fs.getAppHomePath() + "note.txt";
    try {
        OutputStream out = fs.openOutputStream(path);
        try {
            out.write("Written on drop\n".getBytes("UTF-8"));
        } finally {
            out.close();
        }
        return path;
    } catch (Exception err) {
        return null;
    }
}

Two platforms can’t defer quite that far. An Android clip has to be complete before the drag begins, so a provider runs there as the drag starts; an iOS drag resolves its file list at the same moment, because the system needs to know how many items the drag carries. Keep a provider cheap enough to run once per drag.

NativeDragOperation.ACTION_MOVE means the receiver takes ownership and the source deletes its copy. The source only learns whether that happened once the operating system has finished, which is why the outcome arrives through the completion listener rather than from the call that started the drag.

Receiving a drop is the mirror image. The deepest component under the pointer that’s a native drop target and accepts the content wins, so a target nested inside another takes precedence and a target that refuses a particular payload lets an ancestor have it:

public void showDropTarget() {
    Form hi = new Form("Drop Here", new BorderLayout());
    Container zone = new Container(BoxLayout.y());
    zone.add(new Label("Drop files here"));

    zone.setNativeDropTarget(true);
    zone.setAcceptedDropMimeTypes(ClipboardContent.MIME_FILE);
    zone.setAcceptedDropActions(NativeDragOperation.ACTION_COPY);
    zone.addNativeDropListener(e -> {
        NativeDropEvent drop = (NativeDropEvent) e;
        for (String path : drop.getFiles()) {
            zone.add(new Label(path));
        }
        zone.getComponentForm().revalidateWithAnimationSafety();
    });

    hi.add(BorderLayout.CENTER, zone);
    hi.show();
}

NativeDropEvent also reports whether the drag started inside this application, which is how a container that reorders its own items tells that case from an import.

A complete runnable example lives in Samples/samples/NativeDragAndDropSample: one card drags plain text and HTML together so every receiver takes the best form it knows, another promises a file that’s written only when a receiver asks for it, and a drop zone describes whatever arrives.

Where it works

Native dragging needs the platform to have it, and platforms differ on whether a drag may leave the application at all — a desktop can drop onto any other window, a tablet can drop into an application beside it, and a phone in full screen has nowhere for a drag to go even though drags within the application still work. Check both before offering the affordance:

public boolean canDragOut() {
    return NativeDragAndDrop.isSupported()
            && NativeDragAndDrop.isDragOutsideApplicationSupported();
}

Where the platform has none of this, the calls are harmless no-ops: the component simply can’t be dragged through the operating system, and the lightweight dragging above is unaffected.

PlatformDragsLeaves the appNotes

Desktop (the simulator and "run as desktop app")

yes

yes

Drops onto other windows, the desktop and file managers. Text, HTML, images, arbitrary binary payloads and multiple files.

Android

yes

Nougat and later

A drag crosses applications through DRAG_FLAG_GLOBAL, which arrived in Nougat. Files travel as content URIs, the same way the clipboard carries them.

iPadOS and Mac Catalyst

yes

yes

Drops into another application beside this one, and into Files or the Finder.

iPhone

yes

iOS 15 and later

iOS 15 brought dragging between applications to the phone: hold the item with one finger, switch applications with another, drop. Before that the session worked but could only end on one of the application’s own components.

Everything else

no

no

isSupported() answers false and the lightweight dragging above is unaffected.

Threading

Drops arrive on the operating system’s own drag thread rather than the event dispatch thread. The framework resolves which component a drag is over on that thread, from the accepted MIME types and actions alone, and dispatches the callbacks — nativeDragEnter, nativeDragOver, nativeDragExit and nativeDrop — on the event dispatch thread. A filter set through setAcceptedDropMimeTypes therefore takes effect from the first drag event, while a decision made inside a callback reaches the cursor one event later.

The one method that runs off the event dispatch thread is canAcceptNativeDrop, which exists for a target whose answer depends on more than the MIME type. Read the content and the component’s own configuration there; leave the user interface alone.

Coloring the native Android chrome

Codename One switches the native action bar off — the generated theme sets android:windowActionBar to false — so the title you see belongs to the Toolbar or the SideMenuBar and Codename One draws it. What’s left for the platform to color is the chrome around your app: the status bar, and the card the task switcher shows for it.

Define a colors.xml file under android/src/main/resources for that. Every <color> whose name is an Android theme attribute becomes an android: item in the generated theme:

<resources>
   <color name="colorPrimary">#ff00ff00</color>
   <color name="colorPrimaryDark">#80ff0000</color>
   <color name="colorAccent">#800000ff</color>
</resources>
The colors reach the Material variant of the generated theme, so a device old enough to fall back to the Holo variant ignores them.
A color whose name isn’t an Android theme attribute stays an ordinary @color/ resource and never reaches the theme, and the build log names it. Android Studio puts ordinary app colors such as ic_launcher_background in this same file, so an imported colors.xml keeps working — but a misspelled attribute produces no theming, and that log line is where it says so. The names the build accepts are read from the installed platform, so any android: theme attribute works, not a fixed list.

Intercepting URLs on iOS & Android

A common trick in mobile application development, is communication between two unrelated applications.

In Android you can use intents which are pretty elaborate and can be used through Display.execute, but 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 built into Codename One, but it does expose enough of the platform capabilities to enable that functionality rather on Android.

On Android you need to define an intent filter which you can do using the android.xintent_filter build hint, this accepts the XML to filter whether a request is relevant to your application:

android.xintent_filter=<intent-filter>   <action android:name="android.intent.action.VIEW" />    <category android:name="android.intent.category.DEFAULT" />    <category android:name="android.intent.category.BROWSABLE" />    <data android:scheme="myapp" />  </intent-filter>

You can read more about it in this stack overflow question.

To bind the myapp:// URL to your application. As a result typing myapp://x into the Android browser will launch the application.

Passing launch arguments to the app

You can access the value of the URL that launched the app using:

public class RearrangeableItemsDemo {
    public void show() {
        Form hi = new Form("Rearrangeable Items", new BorderLayout());
        String[] buttons = {"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" };

        Container box = new Container(BoxLayout.y());
        box.setScrollableY(true);
        box.setDropTarget(true);
        java.util.List<String> got = Arrays.asList(buttons);
        Collections.shuffle(got);
        for (String current : got) {
            MultiButton mb = new MultiButton(current);
            box.add(mb);
            mb.setDraggable(true);
        }

        hi.add(BorderLayout.NORTH, "Arrange The Titles").add(BorderLayout.CENTER, box);
        hi.show();
    }
}

This value would be null if the app was launched through the icon.

Android App-to-App contracts (Wallet/Issuer style flows)

Some integrations (wallet provisioning, issuer verification, secure step-up) need more than a URL launch. They require:

  • a custom Android intent action,

  • caller validation,

  • asynchronous app logic (login/back end verification),

  • a native setResult(…​) response back to the caller.

In Codename One, keep this flow in two layers:

  1. Android bridge activity for contract lifecycle and setResult(…​).

  2. Codename One app code for business logic in start().

Why this isn’t implicitly generated by AndroidGradleBuilder

A generic bridge can’t infer:

  • expected caller package(s),

  • required result extra names,

  • approval/decline mapping rules,

  • timeout/failure policy.

Those are contract-specific, so the bridge activity is intentionally app-defined.

Registering the bridge activity

Use android.xapplication to inject a dedicated bridge activity and bind the action it should receive:

codename1.arg.android.xapplication=<activity android:name="com.example.wallet.WalletBridgeActivity" android:exported="true" android:launchMode="singleTop" android:noHistory="true" android:excludeFromRecents="true" android:theme="@android:style/Theme.Translucent.NoTitleBar"><intent-filter><action android:name="com.example.wallet.ACTION_VERIFY"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity>

Keep codename1.arg.android.activity.launchMode=singleTask on your main CN1 activity so app resume behavior is predictable.

Bridge activity responsibilities

A minimal bridge activity should:

  1. Receive and parse the incoming intent.

  2. Capture caller identity using getCallingPackage().

  3. Launch or resume the CN1 app activity.

  4. Hold contract state until CN1 completes verification.

  5. Return setResult(…​) and finish().

For payment/wallet trust decisions, treat only getCallingPackage() as verified identity. Don’t rely on referrer extras for caller authentication because callers can spoof those values. This implies the contract should be launched with startActivityForResult(…​) so caller identity is available.
Wallet Contract Security Guidelines

Use this checklist when the integration can activate payment credentials or accounts:

  1. Fail closed on missing verified caller. If android.intent.caller is empty (or android.intent.caller.verified isn’t true), reject the request and return a failure/cancel result.
    Where: CN1 start() guard before any business logic.

  2. Use an allow-list of trusted caller package names and reject everything else.
    Where: CN1 start() (or native bridge) before network calls.

  3. Verify signing certificate fingerprints natively for each allowed package. Package name checks alone aren’t enough in high-trust wallet scenarios.
    Where: native Android code (bridge activity/native interface), because this requires Android PackageManager APIs.

  4. Check payload freshness (nonce/timestamp/challenge) before approving.
    Where: back end/API service as the source of truth.

  5. Bind response to request (request ID / correlation ID) and reject mismatched or replayed responses.
    Where: back end first, then enforce in CN1/native completion flow.

  6. Time-box bridge activity state and fail closed on timeout, process death, or missing bridge instance.
    Where: native Android bridge + CN1 timeout handling.

  7. Audit log caller package, request ID, decision, and failure reason (without storing full PAN/token data).
    Where: back end and security telemetry pipeline.

Optional native Android example for certificate fingerprint verification (called from bridge/native layer before trusting caller):

public static boolean isTrustedCallerSignature(Context ctx, String packageName, Set<String> allowedSha256) {
    if (ctx == null || packageName == null || packageName.isEmpty() || allowedSha256 == null || allowedSha256.isEmpty()) {
        return false;
    }
    try {
        PackageManager pm = ctx.getPackageManager();
        PackageInfo info;
        if (Build.VERSION.SDK_INT >= 28) {
            info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES);
            Signature[] sigs = info.signingInfo != null
                    ? info.signingInfo.getApkContentsSigners()
                    : null;
            return hasAllowedFingerprint(sigs, allowedSha256);
        } else {
            info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
            return hasAllowedFingerprint(info.signatures, allowedSha256);
        }
    } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException ex) {
        return false;
    }
}

private static boolean hasAllowedFingerprint(Signature[] sigs, Set<String> allowedSha256)
        throws NoSuchAlgorithmException {
    if (sigs == null) {
        return false;
    }
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    for (Signature s : sigs) {
        String fp = toHex(md.digest(s.toByteArray()));
        if (allowedSha256.contains(fp)) {
            return true;
        }
    }
    return false;
}

private static String toHex(byte[] data) {
    char[] digits = "0123456789ABCDEF".toCharArray();
    StringBuilder out = new StringBuilder(data.length * 2);
    for (byte b : data) {
        int value = b & 0xff;
        out.append(digits[value >>> 4]);
        out.append(digits[value & 0x0f]);
    }
    return out.toString();
}
public class WalletBridgeActivity extends Activity {
    private static WeakReference<WalletBridgeActivity> active;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        active = new WeakReference<>(this);

        Intent in = getIntent();
        String caller = getCallingPackage();
        String payload = in == null ? null : in.getStringExtra("payload");

        Intent launch = getPackageManager().getLaunchIntentForPackage(getPackageName());
        if (launch == null) {
            finish();
            return;
        }
        launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        launch.putExtra("wallet_payload", payload);
        launch.putExtra("wallet_caller", caller == null ? "" : caller);
        startActivity(launch);
        // Do not finish yet. CN1 will finish this activity via native callback once verification completes.
    }

    @Override
    protected void onDestroy() {
        WalletBridgeActivity current = getActive();
        if (current == this) {
            active = null;
        }
        super.onDestroy();
    }

    public static WalletBridgeActivity getActive() {
        return active == null ? null : active.get();
    }
}

Reading contract data in start()

Codename One exposes launch metadata through AppArg and Display properties. Read these in start():

  • AppArg (primary launch payload)

  • android.intent.action

  • android.intent.data

  • android.intent.type

  • android.intent.caller

  • android.intent.caller.verified

  • android.intent.extra.<KEY>

public void start() {
    Display d = Display.getInstance();
    String action = d.getProperty("android.intent.action", null);
    if ("com.example.wallet.ACTION_VERIFY".equals(action)) {
        boolean callerVerified = "true".equals(d.getProperty("android.intent.caller.verified", "false"));
        String payload = d.getProperty("android.intent.extra.wallet_payload", d.getProperty("AppArg", null));
        String caller = d.getProperty("android.intent.caller", "");

        if (!callerVerified || !isAllowedCaller(caller)) {
            failClosed(); // return declined/canceled via bridge completion API
            return;
        }

        verifyWalletPayload(payload, caller);
        return;
    }
    showMainForm();
}

Returning results to the wallet API

Implement a small native interface method (Android implementation) that:

  1. gets WalletBridgeActivity.getActive(),

  2. verifies getActive() is non-null and fails closed if the bridge timed out/canceled,

  3. builds caller-specific result extras,

  4. calls setResult(Activity.RESULT_OK, resultIntent),

  5. calls finish() on the bridge activity.

This is where you bind to the wallet contract’s exact response schema. Keep these extra names in one place so upgrades are manageable.

Foreground behavior on Android 14/15

Foreground transitions are best-effort because Android may block background launches in some states. Build for:

  • immediate resume when allowed,

  • notification fallback when blocked.

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 ios.plistInject build hint.

The equivalent on the iOS side would be:

ios.plistInject=<key>CFBundleURLTypes</key>     <array>         <dict>             <key>CFBundleURLName</key>             <string>com.yourcompany.myapp</string>         </dict>         <dict>             <key>CFBundleURLSchemes</key>             <array>                 <string>myapp</string>             </array>         </dict>     </array>

For example, that can conflict with the Facebook integration if you use FacebookConnect which needs access to the schemes. To work around this you can use the build hint ios.urlScheme for example:

ios.urlScheme=<string>myapp</string>

Native peer components

Many Codename One developers don’t truly grasp the reason for the separation between peer (native) components and Codename One components. This is a crucial thing you need to understand if you plan on working with native widgets for example: Web Browser, native maps, text input, media and native interfaces (which can return a PeerComponent).

Codename One draws all 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 for example: glasspane and layered pane.

  3. Consistency - provides identical functionality on all platforms.

This all contributes to your ease of working with Codename One and maintaining Codename One. More than 95% of Codename One’s code is in Java hence its portable and pretty easy to maintain!

Why does Codename One need native widgets at all

You need the native device to do input, html rendering etc. These are too big and too complex tasks for Codename One to do from scratch.

They’re sometimes impossible to perform without the native platform. For example: 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 age without a JavaScript JIT and including a JIT within an iOS app is prohibited by Apple.

Problems with native widgets

Codename One does pretty much everything on the EDT (Event Dispatch Thread), this provides a lot of cool features for example: modal dialogs, invokeAndBlock etc.

For example native widgets have to be drawn on the devices native UI thread.
The oldest answer to that was to paint in two passes: loop over the Codename One components and paint them, then loop over the native peers and let the platform paint those over the result. That’s where the rule that a peer always covers whatever sits under it comes from, and it isn’t how the current ports work.

Display asks the implementation one question, paintNativePeersBehind(), and a false answer doesn’t mean the peer can’t be covered. It leaves the compositing to the port, and the ports differ:

  • iOS and the JavaScript port answer true. The native layer sits underneath and Codename One paints above it, punching a hole through its own surface with clearRect wherever a peer belongs.

  • Android hands the peer’s native View to the same ordered stream of drawing operations as every other component, so a component painted after the peer covers it. The simulator does the same for a peer that renders into a buffer.

  • Where the peer owns a surface of its own, the system composites that surface and paint order never reaches it. The screenshot technique below can’t reach it either, for the same reason.

Where a native peer is composited relative to the Codename One layer, and how it differs by port
Figure 327. Where a peer sits, and what follows from it
This was also the case in AWT/Swing to one degree or another…​

Showing dialogs on top of peer components

Under the first two models this needs nothing from you: a Dialog paints after the peer and covers it. What follows is for the third, where the system composites the peer’s own surface and no amount of painting reaches it.

A form transition tells the peers about itself without any help from you: initTransition puts both forms into lightweight mode, which is how the framework says that a peer isn’t truly visible for a while. How a peer responds is left to the peer, and it varies by port. Codename One grabs a screenshot of the peer, hide it and then you can show the screenshot. Since the screenshot is static it can be rendered through the standard UI. You 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.

PeerComponent declares that callback empty, so a port opts into the still by overriding it. The iOS and Android peers do, and on Android what the still contains varies again: the browser peer overrides the capture and draws its WebView into the bitmap, the default renders the View the ordinary way, and a peer that owns a surface has nothing there to render, so its still is blank. That doesn’t hide the peer, though. In super peer mode setLightweightMode only generates the image, it never changes the view’s visibility, and AndroidGLSurface asks for setZOrderMediaOverlay(true), so the live surface goes on drawing above the Codename One canvas while a blank still is painted into the canvas underneath it. The transition animates around a peer that never moved. The OpenGL peer keeps a copy of each frame read back with glReadPixels, but that copy feeds the screenshot path rather than the peer’s still image, so it doesn’t cover this. The JavaScript peer overrides nothing, so it stays live throughout.

Why can’t you combine peer component scrolling and Codename One scrolling

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 your scrolling?

Native components in the first Form

Another problem might be counterintuitive. 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 different from the web component running on the device.

The launch storyboard replaced the build server’s screenshot generator. See launch screen storyboard best practices.

Building your own Layout manager

A Layout contains all the logic for positioning Codename One components. It essentially traverses a Codename One Container and positions components based on internal logic.

When you build the layout you 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 invokes it at the last possible moment. Since this method is expensive (imagine the recursion with nested layouts). Codename One 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 impossible to implement efficiently for all the use cases of a 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, when scrolling and also when sizing parent containers.

This is a layout manager that arranges components in a center column aligned to the middle. You then show the proper usage of margin to create a stair like effect with this layout manager:

class CenterLayout extends Layout {
    public void layoutContainer(Container parent) {
        int components = parent.getComponentCount();
        Style parentStyle = parent.getStyle();
        int centerPos = parent.getLayoutWidth() / 2 + parentStyle.getMargin(Component.LEFT);
        int y = parentStyle.getMargin(Component.TOP);
        boolean rtl = parent.isRTL();
        for (int iter = 0; iter < components; iter++) {
            Component current = parent.getComponentAt(iter);
            Dimension d = current.getPreferredSize();
            Style currentStyle = current.getStyle();
            int marginRight = currentStyle.getMarginRight(rtl);
            int marginLeft = currentStyle.getMarginLeft(rtl);
            int marginTop = currentStyle.getMarginTop();
            int marginBottom = currentStyle.getMarginBottom();
            current.setSize(d);
            int actualWidth = d.getWidth() + marginLeft + marginRight;
            current.setX(centerPos - actualWidth / 2 + marginLeft);
            y += marginTop;
            current.setY(y);
            y += d.getHeight() + marginBottom;
        }
    }

    public Dimension getPreferredSize(Container parent) {
        int components = parent.getComponentCount();
        Style parentStyle = parent.getStyle();
        int height = parentStyle.getMargin(Component.TOP) + parentStyle.getMargin(Component.BOTTOM);
        int marginX = parentStyle.getMargin(Component.RIGHT) + parentStyle.getMargin(Component.LEFT);
        int width = marginX;
        for (int iter = 0; iter < components; iter++) {
            Component current = parent.getComponentAt(iter);
            Dimension d = current.getPreferredSize();
            Style currentStyle = current.getStyle();
            width = Math.max(d.getWidth() + marginX + currentStyle.getMargin(Component.RIGHT)
                    + currentStyle.getMargin(Component.LEFT), width);
            height += currentStyle.getMargin(Component.TOP) + d.getHeight()
                    + currentStyle.getMargin(Component.BOTTOM);
        }
        Dimension size = new Dimension(width, height);
        return size;
    }
}

public class CenterLayoutDemo {
    public static Form createForm() {
        Form hi = new Form("Center Layout", new CenterLayout());
        for (int iter = 1; iter < 10; iter++) {
            Label l = new Label("Label: " + iter);
            l.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_DIPS);
            l.getUnselectedStyle().setMarginLeft(iter * 4);
            l.getUnselectedStyle().setMarginRight(0);
            hi.add(l);
        }
        hi.add(new Label("Really Wide Label Text!!!"));
        return hi;
    }

    public void show() {
        createForm().show();
    }
}
Center layout staircase effect with margin
Figure 328. Center layout staircase effect with margin

Porting a Swing/AWT Layout manager

The GridBagLayout was ported to Codename One considering the complexity of that specific layout manager. Here are some tips you should consider when porting a Swing/AWT layout manager:

  1. Codename One doesn’t have Insets, but adds some support for them 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 fixing method and import statements, which are slightly different. Pretty trivial stuff.

Update framework

When Codename One launched in 2012 there was a need to ship updates and fixes faster than the plugin update system, leading to the client lib update system. There was also a need to update the designer tool (resource editor), the GUI builder, and the skins, as well as the built-in builder code (CodeNameOneBuildClient.jar).

The Update Framework solves many problems in the old systems:

  • Download once - if you have many projects the library will download once to the .codenameone directory. All the projects will update from local storage

  • Skins update automatically - this is hugely important. When you change a theme you 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 designer tools without an IDE plugin update - The IDE plugin update process is slow and tedious. This way a GUI builder fix can ship without a new IDE plugin release. The standalone Settings artifact is versioned with the Maven plugin instead.

This framework should be seamless. You should no longer see the "downloading" message whenever you push an update after your build client is updated. Your system would poll for a new version daily and update when new updates are available.

To force an update check, run mvn cn1:update from the project root.

How does it work

mvn cn1:update does two separate things.

First it makes sure ~/.codenameone/UpdateCodenameOne.jar is there — installed from a resource bundled in the plugin, or downloaded if that resource is missing — and runs it. That’s what keeps the shared tooling and the device skins under ~/.codenameone current, and it’s why the download happens once per machine rather than once per project. It no longer runs against your project directory, which is why a Maven project has no jars of its own to update: the plugin hands it a synthetic directory under common/target/codenameone/update-dummy, seeded with the project’s codenameone_settings.properties. It’s under common because that module holds codenameone_settings.properties and is therefore the one the plugin treats as the Codename One project; run from the root, the aggregator skips itself and common does the work.

Then it reads the plugin’s Maven metadata, resolves the newest published release and rewrites the cn1.version and cn1.plugin.version properties in the pom.

Which metadata it reads depends on the project. Releases are published to the Codename One repository, and the mojo uses it only when the project declares that host in BOTH <repositories> and <pluginRepositories>. A project that declares it in one section only, or in neither, is offered what Maven Central holds instead — and Central keeps every version published before the cutover but receives no new ones, so such a project is told it’s up-to-date at the last pre-cutover release however long it waits. The build log says so when it happens. Adding the repository to both sections is the fix. The framework and plugin artifacts for that version then resolve on the next build like any other dependency. The Resource Editor doesn’t: it’s a separate artifact of around 43MB that’s fetched when you run mvn cn1:designer.

A project pinned to a -SNAPSHOT keeps its version, because a snapshot already tracks the newest build. To move to a particular release rather than the newest one, name it: mvn cn1:update -DnewVersion=X.Y.Z.

What isn’t covered

You will notice 3 big things that aren’t covered in this unified framework:

  • You don’t update cn1libs - whether you would like to update them automatically is unclear

  • Versioned builds - a lot of complexity exists in the versioned build system. This might be something you address in the future but for now the framework stays simple.

9. The old Codename One VM