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

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.
| Name | Type | Default | Annotation | Description |
|---|---|---|---|---|
and.captureRecord | string | (none) | (none) | Override alias of |
and.facebook_permissions | string | (none) | (none) | Override alias of |
and.themeMode |
| (set by the build) |
|
|
android.NotificationChannel.description | string |
| (none) | |
android.NotificationChannel.enableLights | boolean |
| (none) | |
android.NotificationChannel.enableVibration | boolean |
| (none) | |
android.NotificationChannel.id | string |
| (none) | |
android.NotificationChannel.importance | int |
| (none) | |
android.NotificationChannel.lightColor | string | (none) | (none) | |
android.NotificationChannel.name | string |
| (none) | |
android.NotificationChannel.vibrationPattern | string | (none) | (none) | |
android.accessibilityGuard | boolean |
| (none) | |
android.accessibilityGuard.allow | string | (none) | (none) | |
android.accessibilityGuard.mode | string |
| (none) | |
android.activity.launchMode | string | (set by the build) |
| Allows explicitly setting the |
android.activityClassBody | string | (none) | (none) | |
android.activityClassImports | string | (none) | (none) | |
android.adaptiveIconBackground | string |
| (none) | Background color to use for adaptive icons when |
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.allowBackup | boolean |
| (none) | |
android.androidAuto.messaging | boolean |
| (none) | |
android.androidAuto.minCarApiLevel | int |
| (none) | |
android.androidAuto.navigation | boolean |
| (none) | |
android.androidAuto.poi | boolean |
| (none) | |
android.anyDensity | boolean |
| (none) | |
android.apacheLegacy | boolean |
| (none) | |
android.appBundle | boolean | (set by the build) |
| Produces an Android App Bundle (.aab) rather than an APK. Required for new Play Store submissions. |
android.appReview.version | version |
| (none) | |
android.ar.required | boolean |
| (none) | |
android.arrcompile | string | (none) | (none) | |
android.arrimplementation | string | (none) | (none) | |
android.asyncPaint | boolean |
| (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 |
| (none) | |
android.billingclient.version | version |
| (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 |
android.blockExternalStoragePermission | boolean |
| (none) | Boolean true/false defaults to false. Disables the external storage (SD card) permission |
android.blockLabel | boolean |
| (none) | Boolean true/false defaults to false. Leaves |
android.blockReadMediaPermissions | boolean | (none) | (none) | Boolean true/false, defaults to the value of |
android.bluetooth.neverForLocation | boolean |
| (none) | |
android.bluetooth.required | boolean |
| (none) | |
android.buildToolsVersion | version | (set by the build) |
| Android build-tools version. It also selects the compile SDK, so there is no separate compile-SDK hint. |
android.call.video | boolean |
| (none) | Whether the manifest declares |
android.captureRecord | string | (set by the build) |
| Indicates whether the |
android.carAppVersion | version |
| (none) | |
android.credentialsPlayServicesVersion | string | (none) | (none) | |
android.credentialsVersion | version |
| (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 |
android.customActivity | string |
| (none) | |
android.customTabsVersion | version |
| (none) | |
android.debug | boolean | (set by the build) |
| 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 |
| (none) | |
android.disableR8 | boolean | (set by the build) |
| Turns off R8, falling back to the older shrinker. Note that hardening requires R8, so this conflicts with harden.level. |
android.disableR8FullMode | boolean |
| (none) | |
android.disableScreenshots | boolean |
| (none) | |
android.documentProvider.enabled | boolean |
| (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 |
| (none) | Boolean true/false defaults to false. Enables Android adaptive icon generation in Android Gradle builds. When enabled, Codename One generates |
android.enableProguard | boolean | (set by the build) |
| Boolean true/false defaults to true. Allows disabling the proguard obfuscation even on release builds, notice that this isn’t recommended |
android.excludeBolts | boolean |
| (none) | |
android.extendAppCompatActivity | boolean |
| (none) | |
android.facebookSdkVersion | version |
| (none) | |
android.facebook_permissions | string |
| (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 |
| (none) | |
android.firebaseAnalyticsVersion | version |
| (none) | |
android.firebaseCoreVersion | string | (none) | (none) | |
android.firebaseMessagingVersion | string | (none) | (none) | |
android.foldableSupport | boolean |
| (none) | |
android.forceJava8Builder | boolean |
| (none) | |
android.foregroundServiceType | string |
| (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 |
| (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.fullScreenIntent | boolean |
| (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 |
| (none) | Device key used to mark a specific Android device as a test device for Google Play ads defaults to C6783E2486F0931D9D09FABC65094FDF |
android.gpsPermission | boolean |
| (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 ( | (set by the build) |
| 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 |
| (none) | |
android.hceAids | string |
| (none) | |
android.hceCategory | string |
| (none) | |
android.hceDescription | string | (none) | (none) | |
android.hceRequireUnlock | boolean |
| (none) | |
android.headphoneCallback | boolean |
| (none) | Boolean true/false defaults to false. When set to true it assumes the main class has two methods: |
android.health.background | boolean |
| (none) | |
android.health.connectVersion | string |
| (none) | |
android.health.history | boolean |
| (none) | |
android.health.privacyPolicyUrl | string | (none) | (none) | |
android.health.read | string | (none) | (none) | |
android.health.write | string | (none) | (none) | |
android.hideOverlayWindows | boolean |
| (none) | Boolean true/false defaults to false. Declares the |
android.hideStatusBar | boolean | (set by the build) |
| Hides the Android status bar. |
android.hms.pushVersion | string |
| (none) | |
android.home.playServicesVersion | string |
| (none) | |
android.includeGPlayServices | boolean |
| (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 |
| (none) | |
android.installLocation |
| (set by the build) |
| Maps to android:installLocation manifest entry defaults to auto. Can also be set to internalOnly or preferExternal. |
android.invite.appLinks | boolean |
| (none) | Whether the build injects the |
android.invite.signingFingerprint | list ( | (none) | (none) | Comma separated SHA-256 signing certificate fingerprints, in colon separated hex, enrolled in the shared |
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 |
| (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 |
| (none) | Boolean true/false defaults to true. Kotlin 1.8.0 moved the contents of |
android.largeScreens | boolean |
| (none) | |
android.licenseKey | string | (set by the build) |
| 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 |
| (none) | Whether |
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 |
android.messagingService | string | (none) | (none) | |
android.migrateToAndroidX | boolean |
| (none) | |
android.min_sdk_version |
| (set by the build) |
| 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.mockLocation | boolean |
| (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) |
| 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 |
| (none) | Offers the |
android.nearby.glassesProfile | boolean |
| (none) | Offers the |
android.nearby.watchProfile | boolean |
| (none) | Offers the |
android.newFirebaseMessaging | boolean | (set by the build) |
| 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 |
| (none) | |
android.onCreate | string | (none) | (none) | |
android.onDeviceDebug | boolean | (set by the build) |
| Boolean true/false defaults to false. When |
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 |
| (none) | |
android.playIntegrity.verifyUrl | string | (none) | (none) | |
android.playIntegrityVersion | version |
| (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 |
| (none) | |
android.playService.analytics | string | (none) | (none) | |
android.playService.appInvite | boolean |
| (none) | |
android.playService.auth | string | (none) | (none) | |
android.playService.base | string | (none) | (none) | |
android.playService.cast | boolean |
| (none) | |
android.playService.drive | boolean |
| (none) | |
android.playService.fitness | boolean |
| (none) | |
android.playService.games | boolean |
| (none) | |
android.playService.gcm | string | (none) | (none) | |
android.playService.identity | boolean |
| (none) | |
android.playService.indexing | boolean |
| (none) | |
android.playService.location | string | (none) | (none) | |
android.playService.maps | string | (none) | (none) | |
android.playService.nearby | boolean |
| (none) | |
android.playService.panorama | boolean |
| (none) | |
android.playService.plus | boolean |
| (none) | |
android.playService.safetynet | boolean |
| (none) | |
android.playService.vision | boolean |
| (none) | |
android.playService.wallet | boolean |
| (none) | |
android.playService.wearable | boolean |
| (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) |
| Arguments for the keep option in proguard allowing you to keep a pattern of files for example, |
android.proguardKeepOverride | string |
| (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 |
android.release | boolean | (set by the build) |
| true/false defaults to true - indicates whether to include the release version in the build |
android.removeBasePermissions | boolean |
| (none) | Boolean true/false defaults to false. Disables the built-in permissions specifically |
android.repositories | list (newline delimited) | (set by the build) |
| Extra Gradle repositories to resolve dependencies from. |
android.requestReadMediaPermissions | boolean |
| (none) | Boolean true/false defaults to false. Declares |
android.rootCheck | boolean |
| (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 |
| (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 |
| (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 |
| (none) | Boolean true/false defaults to true. Corresponds to the |
android.stack_size | string | (none) | (none) | Size in bytes for the Android stack thread |
android.statusbar_hidden | boolean |
| (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 |
android.stringsXml | string | (none) | (none) | Allows injecting more entries into the strings.xml file using a value that includes something like this |
android.style | string | (none) | (none) | Allows injecting more data into the |
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 |
| (none) |
|
android.surfaces.exactAlarms | boolean |
| (none) | |
android.tapjackingGuard | boolean |
| (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 |
| (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 |
android.tapjackingGuard.mode | string |
| (none) |
|
android.targetSDKVersion | int | (set by the build) |
| 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 |
| (none) | |
android.theme | string |
| (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) |
| Statements added to the top-level Gradle build file rather than the app module. |
android.tv | boolean |
| (none) | true/false (defaults to false). Marks the build as an Android TV / Google TV app. Adds the |
android.useAndroidX | boolean | (set by the build) |
| 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.watchModule | boolean |
| (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.watchVersionCodeOffset | int |
| (none) | How far above the phone’s version code the wear module’s sits when |
android.wear | boolean |
| (none) | |
android.wear.complicationsVersion | string |
| (none) | Version of |
android.wear.guavaVersion | string |
| (none) | Version of |
android.wear.protoLayoutVersion | string |
| (none) | Version of the |
android.wear.standalone | string | (none) | (none) | |
android.wear.tilesVersion | string |
| (none) | Version of |
android.web_loading_hidden | boolean |
| (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 |
| (none) | |
android.xactivity | xml | (none) | (none) | Allows injecting more attributes into the |
android.xapplication | xml | (set by the build) |
| 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 |
android.xgradle | list (newline delimited) | (set by the build) |
| 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 |
| (none) | |
android.xlayout_attr | string | (none) | (none) | |
android.xmanifest | xml | (none) | (none) | |
android.xpermissions | xml | (set by the build) |
| more permissions for the Android manifest |
desktop.adaptToRetina | boolean | (set by the build) |
| 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) |
| Starts the desktop build in full-screen mode. |
desktop.height | int | (set by the build) |
| 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) |
| Enables grab-able, click-to-page desktop scrollbars. |
desktop.resizable | boolean | (set by the build) |
| 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.themeMode | string | (set by the build) |
| 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. |
desktop.themeWin | string | (none) | (none) | Same as |
desktop.title | string | (none) | (none) | |
desktop.titleBar |
| (set by the build) |
| 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) |
| 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 |
desktop.windowsOutput | string | (none) | (none) | Can be exe or msi depending on desired results |
KeepScreenOn | boolean |
| (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 |
build.incSources | string | (none) | (none) | |
build.testReporter | string | (none) | (none) | |
build.unitTest | string | (none) | (none) | |
call.video | boolean |
| (none) | Whether video calls are offered, on both platforms. |
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 |
| (none) | |
cn1.harden.forceOff | string | (none) | (none) | |
cn1.hardenLevel | string |
| (none) | |
cn1.hardened | boolean |
| (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.mac.certificate | string | (none) | (none) | Mac Native cloud builds only. Path to the |
codename1.mac.certificatePassword | secret | (none) | (none) | Mac Native cloud builds only. Password to unlock the P12 referenced by |
codename1.mac.provision | string | (none) | (none) | Mac Native cloud builds only. Path to the Mac provisioning profile ( |
db.legacy | string | (none) | (none) | |
delayPushCompletion | boolean |
| (none) | |
facebook.appId | string | (set by the build) |
| 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) |
| 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) |
| 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 |
| (set by the build) |
| Overrides control-flow obfuscation independently of harden.level. |
harden.ios.enabled | boolean |
| (none) | |
harden.keep | text_block | (set by the build) |
| 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 |
| (set by the build) |
| 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 |
| (none) | |
harden.rename | boolean | (set by the build) |
| Overrides symbol renaming independently of harden.level. |
harden.strings |
| (set by the build) |
| Overrides string obfuscation independently of harden.level: off, constants or all. |
harden.tv.enabled | boolean |
| (none) | |
harden.watch.enabled | boolean |
| (none) | |
invite.domain | string |
| (none) | The host that serves Codename One invite links ( |
invite.slug | string | (none) | (none) | This app’s path segment in its invite links ( |
java.version | int |
| (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. |
nativeTheme |
| (set by the build) |
|
|
nativeVerify | string | (none) | (none) |
|
noExtraResources | boolean | (set by the build) |
| 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 |
| (none) | |
vserv.category | int |
| (none) | |
vserv.countryCode | string |
| (none) | |
vserv.locale | string |
| (none) | |
vserv.networkCode | string |
| (none) | |
vserv.scaleMode | boolean |
| (none) | |
vserv.transition | int |
| (none) | |
vserv.zone | string | (none) | (none) | |
watchMain | string | (none) | (none) | |
watchStandalone | boolean |
| (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 |
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) |
| Why the app uses Bluetooth. Supplied automatically when the app references |
ios.NSBluetoothPeripheralUsageDescription | string | (set by the build) |
| 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) |
| The text iOS shows when the app first asks for the calendars full access. It becomes the |
ios.NSCalendarsUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the calendars. It becomes the |
ios.NSCalendarsWriteOnlyAccessUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the calendars write only access. It becomes the |
ios.NSCameraUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the camera. It becomes the |
ios.NSHealthShareUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the health share. It becomes the |
ios.NSHealthUpdateUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the health update. It becomes the |
ios.NSLocalNetworkUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the local network. It becomes the |
ios.NSLocationAlwaysAndWhenInUseUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the location always and when in use. It becomes the |
ios.NSLocationAlwaysUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the location always. It becomes the |
ios.NSLocationWhenInUseUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the location when in use. It becomes the |
ios.NSMicrophoneUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the microphone. It becomes the |
ios.NSNearbyInteractionAllowOnceUsageDescription | string | (set by the build) |
| 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) |
| 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) |
| The text iOS shows when the app first asks for the reminders full access. It becomes the |
ios.NSRemindersUsageDescription | string | (set by the build) |
| The text iOS shows when the app first asks for the reminders. It becomes the |
ios.NSSpeechRecognitionUsageDescription | string | (set by the build) |
| 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.UIRequiredDeviceCapabilities | string | (none) | (none) | |
ios.actionSheetStyle | string | (none) | (none) | |
ios.add_libs | list ( | (set by the build) |
| 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 |
| (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 |
ios.appext.NAME.provisioningURL | string | (none) | (none) | Cloud device builds only. URL of the provisioning profile for a generic app extension dropped into |
ios.applicationDidEnterBackground | string | (none) | (none) | Objective-C code that can be injected into the iOS callback method (message) |
ios.applicationQueriesSchemes | list ( | (set by the build) |
| Comma separated list of url schemes that |
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) |
| 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 |
| (none) | true/false defaults to false. Enables bitcode support for the build. |
ios.blockScreenshotsOnEnterBackground | boolean |
| (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 |
| (none) | |
ios.bundleVersion | version | (set by the build) |
| 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 |
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 |
ios.call.pushTTL | int |
| (none) | Seconds a pushed call waits for your code before the system ends it as unanswered. |
ios.call.recents | boolean |
| (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 |
ios.call.video | boolean |
| (none) | Whether the provider offers video calls. Overrides |
ios.carplay.audio | boolean |
| (none) | |
ios.carplay.messaging | boolean |
| (none) | |
ios.carplay.navigation | boolean |
| (none) | |
ios.carplay.poi | boolean |
| (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 |
| (none) | |
ios.criticalAlerts | boolean |
| (none) | |
ios.crypto.gcm | boolean |
| (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 |
| (none) | |
ios.dependencyManager |
| (set by the build) |
| 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) |
| 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 |
| (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 |
| (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 |
| (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 |
| (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 |
| (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 |
| (none) | Boolean true/false defaults to false. Makes videos "autoplay" when loaded on iOS |
ios.enableBadgeClear | boolean |
| (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 |
| (none) | |
ios.enableStatusBar7 | boolean |
| (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 |
| (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 |
| (none) | |
ios.facebook.version | string |
| (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 |
| (none) | |
ios.fieldNullChecks | boolean |
| (none) | |
ios.fileSharingEnabled | boolean |
| (none) | |
ios.firebaseAnalytics | boolean |
| (none) | |
ios.firebaseAnalyticsVersion | string | (none) | (none) | |
ios.force64 | boolean |
| (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) |
| 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 |
ios.googleAdUnitTestDevice | string |
| (none) | |
ios.gplus.clientId | string | (none) | (none) | |
ios.hceAids | string | (none) | (none) | |
ios.headphoneCallback | boolean |
| (none) | Boolean true/false defaults to false. When set to true it assumes the main class has two methods: |
ios.health.backgroundDelivery | boolean |
| (none) | |
ios.health.recalibrateEstimates | boolean |
| (none) | |
ios.health.required | boolean |
| (none) | |
ios.home.appGroup | string | (none) | (none) | |
ios.home.commissioning | boolean |
| (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 |
| (none) | |
ios.home.required | boolean |
| (none) | |
ios.includeNullChecks | boolean |
| (none) | |
ios.includePush | boolean | (set by the build) |
| 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 |
| (none) | |
ios.intents.minDeploymentTarget | string | (none) | (none) | |
ios.interface_orientation | string | (set by the build) |
| UIInterfaceOrientationPortrait by default. Indicates the orientation, one or more of (separated by colon :): |
ios.invite.appClip | boolean |
| (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 THE HANDOFF, for a clip of your own. Write one entry into the Then call 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 |
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 |
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 |
| (none) | Whether the build appends |
ios.keyboardOpen | boolean |
| (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 |
ios.launchPlaceholder | boolean |
| (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 |
| (none) | |
ios.maps.provider | string | (none) | (none) | iOS’s own native map provider, overriding |
ios.metal | boolean |
| (none) | Boolean true/false defaults to true. Selects the Metal rendering backend ( |
ios.metal.colorSpace | string |
| (none) | Selects the |
ios.minDeploymentTarget | version | (set by the build) |
| The null and empty-string reads of this hint are presence checks; 6.0 is the substantive one. |
ios.mopubAdSize | string |
| (none) | |
ios.mopubId | string | (none) | (none) | |
ios.mopubTabletAdSize | string |
| (none) | |
ios.mopubTabletId | string | (none) | (none) | |
ios.multitasking | boolean |
| (none) | Set to true to enable iOS multitasking and split-screen support. This only works if |
ios.nativeVerify | string | (none) | (none) |
|
ios.nearby.accessoryServices | list ( | (none) | (none) | Bluetooth service UUIDs published as |
ios.nearby.background | boolean |
| (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) |
| 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 |
| (none) | |
ios.no_strip | boolean |
| (none) | |
ios.notificationPermissionAtLaunch | boolean |
| (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 |
ios.objC | boolean | (set by the build) |
| Added the |
ios.onDeviceDebug | boolean | (set by the build) |
| Boolean true/false defaults to false. When |
ios.onDeviceDebug.proxyHost | string | (set by the build) |
| Hostname or IP address the device-side listener dials to reach the desktop proxy. Default |
ios.onDeviceDebug.proxyPort | int | (set by the build) |
| TCP port on |
ios.onDeviceDebug.waitForAttach | boolean | (set by the build) |
| Boolean true/false defaults to false. When |
ios.openURLInject | xml | (none) | (none) | |
ios.optimizer | string |
| (none) | |
ios.plistInject | xml | (set by the build) |
| entries to inject into the iOS plist file during build. |
ios.pods | list ( | (set by the build) |
| A comma separated list of Cocoa Pods that should be linked to the app to build it. For example, |
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) |
| Sets the Cocoapods 'platform' for the Cocoapods. Some Cocoapods require a minimum platform level. For example, |
ios.pods.sources | list ( | (set by the build) |
| Extra CocoaPods spec repositories to search, in addition to the default trunk. |
ios.pods.use_frameworks! | boolean |
| (none) | |
ios.prerendered_icon | boolean | (set by the build) |
| 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 |
| (set by the build) |
| 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) |
|
ios.shareAppGroup | string | (none) | (none) | |
ios.spm.packages | list ( | (set by the build) |
| 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 |
| (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 |
| (none) | |
ios.surfaces.extension | boolean |
| (none) | |
ios.surfaces.frequentUpdates | boolean |
| (none) | |
ios.swiftVersion | version |
| (none) | |
ios.teamId | string | (set by the build) |
| Specifies the team ID associated with the iOS provisioning profile and certificate. Use |
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 |
| (set by the build) |
|
|
ios.timeSensitiveNotifications | boolean |
| (none) | |
ios.twoDigitVersion | boolean |
| (none) | |
ios.urlScheme | string | (set by the build) |
| Allows intercepting a URL call using the syntax |
ios.urlSchemes | string | (none) | (none) | |
ios.useAVKit | boolean |
| (none) | Use AVKit for video components on iOS rather than |
ios.useJavascriptCore | boolean |
| (none) | |
ios.usePhotoKitForMultigallery | boolean |
| (none) | |
ios.usePrintf | boolean |
| (none) | |
ios.useWKWebView | boolean |
| (none) | |
ios.usesBackgroundProcessing | boolean |
| (none) | |
ios.viewDidLoad | string | (none) | (none) | Objective-C code that can be injected into the iOS callback method (message) |
ios.viewDidLoadInclude | string | (none) | (none) | |
ios.vpn.tunnel | boolean |
| (none) | Generates the iOS packet tunnel: a Network Extension carrying a virtual machine, running the |
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 |
ios.wallet.appGroup | string | (none) | (none) | App Group id starting with |
ios.wallet.authEndpoint | string | (none) | (none) | HTTPS URL the generated login UI extension POSTs |
ios.wallet.extension | boolean |
| (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.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 |
| (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.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.nonuiExtensionName | string |
| (none) | |
ios.wallet.nonuiImportsInject | string | (none) | (none) | Extra |
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 |
| (none) | |
ios.wallet.uiImportsInject | string | (none) | (none) | Extra |
ios.wallet.uiViewDidLoadInject | string | (none) | (none) | Swift injected into |
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 |
| (none) | |
javascript.includeVideoJS | boolean |
| (none) | |
javascript.inject.afterHead | string | (none) | (none) | Content to be injected into the index.html file at the end of the |
javascript.inject.beforeHead | string | (none) | (none) | Content to be injected into the index.html file at the beginning of the |
javascript.inject_proxy | boolean |
| (none) | true/false (defaults to |
javascript.minifying | boolean | (none) | (none) | true/false (defaults to |
javascript.port | string | (none) | (none) |
|
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 |
javascript.proxy.target | string |
| (none) | The generated ParparVM proxy deployment platform. Supported values are |
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.sourceFilesCopied | boolean | (none) | (none) | true/false (defaults to |
javascript.stopOnErrors | boolean | (none) | (none) | true/false (defaults to |
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 |
| (none) | |
linux.libc | string |
| (none) | |
linux.musl | boolean |
| (none) | |
linux.muslNativeCc | boolean |
| (none) | |
linux.nativeVerify | string | (none) | (none) |
|
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 |
macNative.appCategory | string | (none) | (none) | Mac Native builds only. |
macNative.bundleId | string | (none) | (none) | Mac Native builds only. Used only when |
macNative.copyright | string | (none) | (none) | Mac Native builds only. |
macNative.deriveBundleId | boolean |
| (none) | Mac Native builds only. |
macNative.distribution | string |
| (none) | Mac Native builds only. |
macNative.enabled | boolean |
| (none) | |
macNative.entitlements.allowJit | string | (none) | (none) | Mac Native builds only. |
macNative.entitlements.appSandbox | string | (none) | (none) | Mac Native builds only. |
macNative.entitlements.device.camera | boolean | (none) | (none) | Sandboxed Mac Native builds only. Toggles |
macNative.entitlements.device.microphone | boolean | (none) | (none) | Sandboxed Mac Native builds only. Toggles |
macNative.entitlements.extra | string | (none) | (none) | Mac Native builds only. Free-form XML inserted verbatim inside the |
macNative.entitlements.files.userSelected | string |
| (none) | Mac Native builds only. |
macNative.entitlements.hardenedRuntime | string | (none) | (none) | Mac Native builds only. |
macNative.entitlements.network.client | string | (none) | (none) | Mac Native builds only. Toggles |
macNative.entitlements.network.server | string | (none) | (none) | Mac Native builds only. Toggles |
macNative.entitlements.personalInformation.calendars | boolean | (none) | (none) | Sandboxed Mac Native builds only. Toggles |
macNative.fixedWindowSize | string | (none) | (none) | Mac Native builds only. Opt-in. Format |
macNative.iosMinDeploymentTarget | version |
| (none) | Mac Native builds only. iOS deployment-target floor for the Catalyst slice ( |
macNative.minDeploymentTarget | version |
| (none) | Mac Native builds only. Minimum macOS version ( |
macNative.multiWindow | boolean |
| (none) | Mac Native builds only. Declares that the app uses |
macNative.notarize | boolean |
| (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.provisioningProfile.developerID | string | (none) | (none) | Mac Native builds only. Provisioning profile name for Developer ID distribution — used only when |
macNative.signing.style | string |
| (none) | Mac Native builds only. |
macNative.signingIdentity.appStore | string |
| (none) | Mac Native builds only. Signing certificate identity for the App Store channel. Default |
macNative.signingIdentity.developerID | string |
| (none) | Mac Native builds only. Signing certificate identity for the Developer ID channel. Default |
macNative.teamId | string | (none) | (none) | Mac Native builds only. Apple Developer Team ID (alphanumeric). Falls back to |
macos.add_libs | list ( | (set by the build) |
| 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 |
macos.appCategory | string | (set by the build) |
| macOS builds. |
macos.arch | string | (set by the build) |
| macOS builds. The architectures to compile, as an |
macos.bundleId | string | (set by the build) |
| macOS builds. Used only when |
macos.bundleVersion | string | (set by the build) |
| macOS builds. |
macos.configuration | string | (set by the build) |
| macOS builds. The Xcode configuration to archive, as passed to |
macos.copyright | string | (set by the build) |
| macOS builds. |
macos.crypto.gcm | boolean | (set by the build) |
| 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 |
macos.deriveBundleId | boolean | (set by the build) |
| macOS builds. |
macos.distribution | string | (set by the build) |
| macOS builds. Every |
macos.entitlements.allowJit | boolean | (set by the build) |
| macOS builds. |
macos.entitlements.appSandbox | boolean | (set by the build) |
| macOS builds. |
macos.entitlements.extra | string | (set by the build) |
| macOS builds. Free-form XML inserted verbatim inside the |
macos.entitlements.files.downloads | boolean | (set by the build) |
| macOS builds. |
macos.entitlements.files.userSelected |
| (set by the build) |
| macOS builds. |
macos.entitlements.hardenedRuntime | boolean | (set by the build) |
| macOS builds. |
macos.entitlements.network.client | boolean | (set by the build) |
| macOS builds. Toggles |
macos.entitlements.network.server | boolean | (set by the build) |
| macOS builds. Toggles |
macos.fixedWindowSize | string | (set by the build) |
| macOS builds. Opt-in. Format |
macos.hardenedRuntime | boolean | (set by the build) |
| macOS builds. Sets Xcode’s |
macos.loadsExternalCode | boolean | (set by the build) |
| macOS builds. |
macos.minDeploymentTarget | string | (set by the build) |
| macOS builds. Minimum macOS version ( |
macos.packaging | string | (set by the build) |
| macOS builds. |
macos.plistInject | string | (set by the build) |
| macOS builds. Raw XML members added to the generated |
macos.provisioningProfile.appStore | string | (set by the build) |
| macOS builds. Provisioning profile name for App Store distribution — used only when |
macos.provisioningProfile.developerID | string | (set by the build) |
| macOS builds. Provisioning profile name for Developer ID distribution — used only when |
macos.signing.style | string | (set by the build) |
| macOS builds. |
macos.signingIdentity.appStore | string | (set by the build) |
| macOS builds. Signing certificate identity for the App Store channel. Default |
macos.signingIdentity.developerID | string | (set by the build) |
| macOS builds. Signing certificate identity for the Developer ID channel. Default |
macos.signingIdentity.installer | string | (set by the build) |
| macOS builds. The certificate |
macos.signingIdentity.installer.appStore | string | (set by the build) |
| macOS builds. The installer certificate for the App Store channel specifically, when |
macos.signingIdentity.installer.developerID | string | (set by the build) |
| macOS builds. The installer certificate for the Developer ID channel specifically. Unset, the shared |
macos.sourceOnly | boolean | (set by the build) |
| macOS builds. |
macos.teamId | string | (set by the build) |
| macOS builds. Apple Developer Team ID (alphanumeric). Falls back to |
macos.themeMode | string | (set by the build) |
| macOS builds. Which native theme the application installs: |
macos.urlSchemes | string | (set by the build) |
| macOS builds. Custom URL schemes to register, comma separated. |
tvNative.bundleId | string | (none) | (none) | Bundle identifier of the tvOS app. Defaults to |
tvNative.displayName | string | (none) | (none) | The tvOS app name shown on Apple TV. Defaults to the app’s display name. |
tvNative.enabled | boolean |
| (none) | true/false (defaults to false). Adds an Apple TV (tvOS) application target to the iOS build. The tvOS app is a separate |
tvNative.mainClass | string | (none) | (none) | |
tvNative.minDeploymentTarget | version |
| (none) |
|
tvNative.teamId | string | (none) | (none) | Apple Developer Team ID used to sign the tvOS target. Falls back to the iOS team id ( |
watchNative.enabled | boolean |
| (none) | |
watchNative.health | string | (none) | (none) | |
watchNative.health.workoutProcessing | boolean |
| (none) | |
watchNative.mainClass | string | (none) | (none) | |
watchNative.surfaces.deploymentTarget | string |
| (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 |
win.shortcutName | string | (none) | (none) | Windows desktop builds only. Overrides the name used for the Start Menu shortcut, the Desktop shortcut and (when |
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.calendar.restrictedCapability | boolean |
| (none) | |
windows.debug | boolean |
| (none) | Native Windows port only. true/false (defaults to false). When |
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 |
| (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) |
|
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 |
windows.signing | boolean |
| (none) | Native Windows port only. |
windows.signing.digest | string |
| (none) | Native Windows port only. Signature digest algorithm. Default |
windows.signing.name | string | (none) | (none) | |
windows.signing.password | secret | (none) | (none) | |
windows.signing.pkcs12 | string | (none) | (none) | |
windows.signing.timestampUrl | string | (none) | Native Windows port only. RFC 3161 timestamp server used when signing, so the signature stays valid after the certificate expires. Default | |
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:
| Tier | Versions 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.
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:

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

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:

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.

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:

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.
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.titleandroid.permission.READ_CONTACTS.askAgainandroid.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.

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.
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.
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 graphFurthermore, 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.
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.
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:
Creating an interface that extends NativeInterface and defines methods with the arguments/return values declared in the previous paragraph.
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:


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:

These are effectively stubs you can edit to implement the methods in native code.

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)
}
}Notice that you’re using the Android native
android.util.Logclass which isn’t accessible from standard Codename One codeThe impl class doesn’t physically implement the
MyNativeinterface!
This is intentional and due to thePeerComponentfunctionality mentioned below. You don’t need to add an implements clause.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.
The native method is implemented, and
isSupportedanswers true.
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
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.
| Platform | Typical file locations for native interface implementations |
|---|---|
Android (Java/Kotlin) |
|
iOS (Objective-C/Swift) |
|
Native Windows (win32, C) |
|
Native Linux © |
|
JavaScript |
|
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.
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
-(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.
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:
Primitive types are mapped to their type name. (For example:
intto "int,"doubleto "double," etc.).Reference types are mapped to their fully qualified class name with '.' replaced with underscores. For example:
java.lang.Stringwould bejava_lang_String.Array parameters are marked by their scalar type name followed by an underscore and
1ARRAY. For example:int[]would be "int_1ARRAY" andString[]would bejava_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;
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
initas this is a reserved method in Objective-COnly 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&toStringare reserved and won’t be mapped to native code
| Java | Android | Java SE | Obj-C | C# |
|---|---|---|---|---|
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 |
PeerComponent on iOS is void* but UIView is expected as a resultThe examples below show the signatures for this method on all platforms:
interface AllTypesNativeApi {
void test(byte b, boolean boo, char c, short s,
int i, long l, float f, double d, String ss,
byte[] ba, boolean[] booa, char[] ca, short[] sa, int[] ia,
long[] la, float[] fa, double[] da,
PeerComponent cmp);
}class AndroidAllTypesImplementation {
public void test(byte param, boolean param1, char param2,
short param3, int param4, long param5, float param6,
double param7, String param8, byte[] param9,
boolean[] param10, char[] param11, short[] param12,
int[] param13, long[] param14, float[] param15,
double[] param16, android.view.View param17) {
}
}-(void)test:(char)param param1:(BOOL)param1
param2:(int)param2 param3:(short)param3 param4:(int)param4
param5:(long long)param5 param6:(float)param6
param7:(double)param7 param8:(NSString*)param8
param9:(NSData*)param9 param10:(NSData*)param10
param11:(NSData*)param11 param12:(NSData*)param12
param13:(NSData*)param13 param14:(NSData*)param14
param15:(NSData*)param15 param16:(NSData*)param16
param17:(void*)param17;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"));
};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" />
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 toActivity.runOnUiThreadbut blocks until the runnable finishes execution.addLifecycleListener/removeLifecycleListener- These essentially provide you with a callback to lifecycle events:onCreateetc. 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;
}
}This will trigger the permission prompt on Android 6 and newer. Even though the permission is declared in XML this isn’t enough for 6+. Notice that even when you run on Android 6 you still need to declare permissions in XML!
Here 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);
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 fileThe 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!
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).
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.
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;
}
}
}
}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)
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.
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.).

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:
Modularity
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.
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.
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.
ios.objC=true there will be no collision as the value would be identicalAn 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.propertiesfile. 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
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:
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 { *; }`Another option is the
aarfile 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 theaarfile which is a binary format that represents a Library project. To learn more about arr you can read this.You can link an
aarfile by placing it inandroid/src/main/resourcesand the build server will link it to the project.
.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.
.hheaders go with the sources, inios/src/main/objectivec..astatic libraries and.bundleresource directories go inios/src/main/resources.A
.frameworkhas to be zipped first, keeping its name, soMyFramework.frameworkis placed asios/src/main/resources/MyFramework.framework.zip.
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();
}
}

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.
| Platform | Drags | Leaves the app | Notes |
|---|---|---|---|
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 |
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 |
|
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>
@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:
Android bridge activity for contract lifecycle and
setResult(…).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:
Receive and parse the incoming intent.
Capture caller identity using
getCallingPackage().Launch or resume the CN1 app activity.
Hold contract state until CN1 completes verification.
Return
setResult(…)andfinish().
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.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.actionandroid.intent.dataandroid.intent.typeandroid.intent.callerandroid.intent.caller.verifiedandroid.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:
gets
WalletBridgeActivity.getActive(),verifies
getActive()is non-null and fails closed if the bridge timed out/canceled,builds caller-specific result extras,
calls
setResult(Activity.RESULT_OK, resultIntent),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:
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.
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.
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 withclearRectwherever a peer belongs.Android hands the peer’s native
Viewto 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.
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.
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();
}
}

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:
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 theInsets(the padding is in the preferred size and is thus hidden from the layout manager).AWT layout managers also synchronize a lot on the AWT thread. This is no longer necessary since Codename One is single threaded, like Swing.
AWT considers the top left position of the
Containerto be 0,0 whereas Codename One considers the position based on its parentContainer. The top left position in Codename One isgetX(),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
.codenameonedirectory. All the projects will update from local storageSkins 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.