Sending Arguments To The Build Server

When sending a build to the server we can provide additional parameters to the build, which are incorporated into the build process on the server to "hint" on multiple different build time options.

These hints are often referred to as "build hints" or "build arguments", they are effectively very much like souped up compiler flags that you can use to tune the build server’s behavior. This is useful for fast iteration on new functionality without building plugin UI for every change. This is also useful for exposing very low level behavior such as customizing the Android manifest XML or the iOS plist.

You can set these hints by right clicking the project in the IDE and selecting Preferences then within the Codename One preferences area selecting the Build Hints tab. The hints use the key=value style of data.

The build hints UI in NetBeans
Figure 1. The build hints UI in NetBeans
The build hints UI in Eclipse
Figure 2. The build hints UI in the Eclipse IDE
In IntelliJ/IDEA this is reachable via the global preferences menu, we have plans to completely redo the IntelliJ UI
The build hints UI in IntelliJ/IDEA
Figure 3. The build hints UI in IntelliJ/IDEA

You can set the build hints in the codenameone_settings.properties file directly notice that when you do that all settings need to start with the codename1.arg. prefix. When editing the properties file directly we would need to define something like android.debug=true as codename1.arg.android.debug=true.

Some build hints conflict with GUI settings that are available via custom UI. E.g. the iOS section in the preferences has an Include Push checkbox that conflicts with ios.includePush this can result in build hints that are ignored!
Make sure you use the UI elements when they are available!
The GUI based settings might conflict with some of the build hints
Figure 4. The GUI based settings might conflict with some of the build hints

Here is the current list of supported arguments, notice that build hints are added all the time so consult the discussion forum if you don’t find what you need here:

Table 1. Build hints
Name Description

android.debug

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

android.release

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

android.installLocation

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

android.gradle

true/false defaults to false prior to 3.3 and true after. Uses Gradle instead of Ant to build the Android app

android.xapplication

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

android.xpermissions

additional permissions for the Android manifest

android.xintent_filter

Allows adding an intent filter to the main android activity

android.licenseKey

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

android.stack_size

Size in bytes for the Android stack thread

android.statusbar_hidden

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

android.facebook_permissions

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

android.googleAdUnitId

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

android.googleAdUnitTestDevice

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

android.includeGPlayServices

Deprecated, please android.playService.*! Indicates whether Goolge 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 us to use a more refined location implementation and invoke some Google specific functionality from native code.

android.playService.plus, android.playService.auth, android.playService.base, android.playService.identity, android.playService.indexing, android.playService.appInvite, android.playService.analytics, android.playService.cast, android.playService.gcm, android.playService.drive, android.playService.fitness, android.playService.location, android.playService.maps, android.playService.ads, android.playService.vision, android.playService.nearby, android.playService.panorama, android.playService.games, android.playService.safetynet, android.playService.wallet, android.playService.wearable

Allows including only a specific play services library portion. Notice that this setting conflicts with the deprecated android.includeGPlayServices and only works with the gradle build (which is on by default but can be toggled using android.gradle).

If none of the services are defined to true then plus, auth, base, analytics, gcm, location, maps & ads will be set to true. If one or more of the android.playService entries are defined to something then all entries will default to false.

android.headphoneCallback

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

android.gpsPermission

Indicates whether the GPS permission should be requested, it is auto-detected by default if you use the location API. However, some code might want to explicitly define it

android.asyncPaint

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

android.stringsXml

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

android.supportV4

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

android.style

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

android.cusom_layout1

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

android.keyboardOpen

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

android.versionCode

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

android.captureRecord

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

android.nonconsumable

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

android.removeBasePermissions

Boolean true/false defaults to false. Disables the builtin permissions specifically INTERNET permission (i.e. no networking…​)

android.blockExternalStoragePermission

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

android.min_sdk_version

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

android.smallScreens

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

android.xapplication_attr

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

android.xactivity

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

android.streamMode

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

android.pushVibratePattern

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

android.enableProguard

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

android.proguardKeep

Arguments for the keep option in proguard allowing us to keep a pattern of files e.g. -keep class com.mypackage.ProblemClass { *; }

android.sharedUserId

Allows adding a manifest attribute for the sharedUserId option

android.sharedUserLabel

Allows adding a manifest attribute for the sharedUserLabel option

android.targetSDKVersion

Indicates the Android SDK used to compile the Android build currently defaults to 21. Notice that not all targets will work since the source might have some limitations and not all SDK targets are installed on the build servers.

android.theme

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

android.web_loading_hidden

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

block_server_registration

true/false flag defaults to false. By default Codename One applications register with our server, setting this to true blocks them from sending information to our cloud. We keep this data for statistical purposes and intend to provide additional installation stats in the future.

facebook.appId

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

ios.bitcode

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

ios.distributionMethod

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

ios.debug.distributionMethod

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

ios.release.distributionMethod

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

ios.keyboardOpen

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

ios.urlScheme

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

ios.teamId

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

ios.debug.teamId

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

ios.release.teamId

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

ios.project_type

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

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

ios.newStorageLocation

true/false defaults to false but defined on new projects as true by default. This changes the storage directory on iOS from using caches to using the documents directory which is more correct but might break compatibility. This is described in this issue

ios.prerendered_icon

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

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

ios.themeMode

default/legacy/modern/auto (defaults to default). Default means you don’t define a theme mode. Currently this is equivalent to legacy. In the future we will switch this to be equivalent to auto. legacy - this will behave like iOS 6 regardless of the device you are running on. modern - this will behave like iOS 7 regardless of the device you are running on. auto - this will behave like iOS 6 on older devices and iOS 7 on newer devices.

ios.interface_orientation

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

ios.xcode_version

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

java.version

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

javascript.inject_proxy

true/false (defaults to true) By default, the build server will configure the .war version of your app to use the bundled proxy servlet for HTTP requests (to get around same-origin restrictions on network requests). Setting this to false prevents this, causing the application to make network requests without a proxy.

javascript.minifying

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

javascript.proxy.url

The URL to the proxy servlet that should be used for making network requests. If this is omitted, the .war version of the app will be set to use the bundled proxy servlet, and the .zip version of the app will be set to use no proxy. If javascript.inject_proxy is false, this build-hint will be ignored.

javascript.sourceFilesCopied

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

javascript.stopOnErrors

true/false (defaults to true). Cause javascript build to fail if there are warnings during the build. In some cases build warnings won’t affect the running of the app. E.g. if the Javascript port is missing a method that the app depends on, but it isn’t used in most of the app. Or if there is multithreaded code detected in static initializers, but that code-path isn’t used by the app. Setting this to false may allow you to get past some build errors, but it might just result in runtime errors later on, which are much more difficult to debug. *This build hint is only available in Codename One 3.4 and later.

javascript.teavm.version

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

rim.askPermissions

true/false defaults to true. Indicates whether the user is prompted for permissions on Blackberry devices.

google.adUnitId

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

rim.ignor_legacy

true/false defaults to false. When set to true the Blackberry build targets only 5.0 devices and newer and doesn’t build the 4.x version. rim.nativeBrowser true/false defaults to false. Enables the native blackberry browser on OS 5 or higher. It is disabled by default since it might casue crashes on some cases.

rim.obfuscation

true/false defaults to false. Obfuscate the JAR before invoking the rimc compiler.

ios.plistInject

entries to inject into the iOS plist file during build.

ios.includePush

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

ios.newPipeline

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

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

ios.facebook_permissions

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

ios.applicationDidEnterBackground

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

ios.enableAutoplayVideo

Boolean true/false defaults to false. Makes videos "auto-play" when loaded on iOS

ios.googleAdUnitId

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

ios.viewDidLoad

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

ios.googleAdUnitIdPadding

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

ios.enableBadgeClear

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

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

ios.glAppDelegateBody

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

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

ios.afterFinishLaunching

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

ios.locationUsageDescription

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

ios.add_libs

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

ios.pods

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

ios.bundleVersion

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

ios.objC

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

ios.testFlight

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.

desktop.width

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

desktop.height

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

desktop.adaptToRetina

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

desktop.resizable

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

desktop.fontSizes

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

desktop.theme

Name of the theme res file 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.

desktop.themeMac

Same as desktop.theme but specific to Mac OS

desktop.themeWin

Same as desktop.theme but specific to Windows

desktop.windowsOutput

Can be exe or msi depending on desired results

noExtraResources

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

j2me.iconSize

Defaults to 48x48. The size of the icon in the format of width x height (without the spacing).

The Architecture Of The Old GUI Builder

We are in the process of replacing the GUI builder with a new GUI builder that has a more conventional architecture. However, the old GUI builder is still supported and this section is left "as is" for reference.

The Codename One GUI builder has several unique underlying concepts that aren’t as common among such tools, in this article I will try to clarify some of these basic ideas.

Basic Concepts

The Codename One Designer isn’t a standard code generator; the UI is saved within the resource file and can be designed without the source files available. This has several advantages:

  1. No fragile generated code to break.

  2. Designers who don’t know Java can use the tool.

  3. The "Codename One LIVE!" application can show a live preview of your design as you build it.

  4. Images and theme settings can be integrated directly with the GUI without concern.

  5. The tool is consistent since the file you save is the file you run.

  6. GUI’s/themes can be downloaded dynamically without replacing the application (this can reduce download size).

  7. It allows for control over application flow. It allows preview within the tool without compilation.

This does present some disadvantages and oddities:

  1. It’s harder to integrate custom code into the GUI builder/designer tool.

  2. The tool is somewhat opaque; there is no "code" you can inspect to see what was accomplished by the tool.

  3. If the resource file grows too large it can significantly impact memory/performance of a running application.

  4. Binding between code and GUI isn’t as intuitive and is mostly centralized in a single class.

In theory you don’t need to generate any code, you can load any resource file that contains a UI element as you would normally load a Resource file:

Resources r = Resources.open("/myFile.res");

Then you can just create a UI using the UIBuilder API:

UIBuilder u = new UIBuilder();
Container c = u.createContainer(r, "uiNameInResource");

(Notice that since Form & Dialog both derive from Container you can just downcast to the appropriate type).

This would work for any resource file and can work completely dynamically! E.g. you can download a resource file on the fly and just show the UI that is within the resource file…​ That is what Codename One LIVE! is doing internally.

IDE Bindings

While the option of creating a Resource file manually is powerful, its not nearly as convenient as modern GUI builders allow. Developers expect the ability to override events and basic behavior directly from the GUI builder and in mobile applications even the flow for some cases.

To facilitate IDE integration we decided on using a single Statemachine class, similar to the common controller pattern. We considered multiple classes for every form/dialog/container and eventually decided this would make code generation more cumbersome.

The designer effectively generates one class StatemachineBase which is a subclass of UIBuilder (you can change the name/package of the class in the Codename One properties file at the root of the project). StatemachineBase is generated every time the resource file is saved assuming that the resource file is within the src directory of a Codename One project. Since the state machine base class is always generated, all changes made into it will be overwritten without prompting the user.

User code is placed within the Statemachine class, which is a subclass of the Statemachine Base class. Hence it is a subclass of UIBuilder!

When the resource file is saved the designer generates 2 major types of methods into StatemachineBase:

  1. Finders - findX(Container c). Finders are shortcut methods that allow us to find a component instance within the container hierarch. Effectively this is a shortcut syntax for UIBuilder.findByName(), its still useful since the method is type safe. Hence if a resource component name is changed the find() method will fail in subsequent compilations.

  2. Callback events - these are various callback methods with common names e.g.: onCreateFormX(), beforeFormX() etc. These will be invoked when a particular event/behavior occurs.

Within the GUI builder, the event buttons would be enabled and the GUI builder provides a quick and dirty way to just override these methods. To prevent a future case in which the underlying resource file will be changed (e.g formX could be renamed to formY) a super method is invoked e.g. super.onCreateFormX();

This will probably be replaced with the @Override annotation when Java 5 features are integrated into Codename One.

Working With The Generated Code

The generated code is rather simplistic, e.g. the following code from the tzone demo adds a for the remove button toggle:

protected void onMainUI_RemoveModeButtonAction(Component c, ActionEvent event) {
   // If the resource file changes the names of components this call will break notifying you that you should fix the code
   super.onMainUI_RemoveModeButtonAction(c, event);
   removeMode = !removeMode;
   Container friendRoot = findFriendsRoot(c.getParent());
   Dimension size = null;
   if(removeMode) {
       if(Display.getInstance().getDeviceDensity() > Display.DENSITY_LOW) {
           findRemoveModeButton(c.getParent()).setText("Finish");
       }
   } else {
       size = new Dimension(0, 0);
       if(Display.getInstance().getDeviceDensity() > Display.DENSITY_LOW) {
           findRemoveModeButton(c.getParent()).setText("Remove");
       }
   }
   for(int iter = 0 ; iter < friendRoot.getComponentCount() ; iter++) {
       Container currentFriend = (Container)friendRoot.getComponentAt(iter);
       currentFriend.setShouldCalcPreferredSize(true);
       currentFriend.setFocusable(!removeMode);
       findRemoveFriend(currentFriend).setPreferredSize(size);
       currentFriend.animateLayout(800);
   }
}

As you can see from the code above implementing some basic callbacks within the state machine is rather simple. The method findFriendsRoot(c.getParent()); is used to find the "FriendsRoot" component within the hierarchy, notice that we just pass the parent container to the finder method. If the finder method doesn’t find the friend root under the parent it will find the "true" root component and search there.

The friends root is a container that contains the full list of our "friends" and within it we can just work with the components that were instantiated by the GUI builder. Implementing Custom Components There are two basic approaches for custom components:

  1. Override a specific type - e.g. make all Form’s derive a common base class.

  2. Replace a deployed instance.

The first uses a feature of UIBuilder which allows overriding component types, specifically override createComponentInstance to return an instance of your desired component e.g.:

protected Component createComponentInstance(String componentType, Class cls) {
   if(cls == Form.class) {
       return new MyForm();
   }
   return super.createComponentInstance(componentType, cls);
}

This code allows me to create a unified global form subclass. That’s very useful when I want so global system level functionality that isn’t supported by the designer normally.

The second approach allows me to replace an existing component:

protected void beforeSplash(Form f) {
   super.beforeSplash(f);

   splashTitle = findTitleArea(f);

   // create a "slide in" effect for the title
   dummyTitle = new Label();
   dummyTitle.setPreferredSize(splashTitle.getPreferredSize());
   f.replace(splashTitle, dummyTitle, null);
}

protected void postSplash(Form f) {
   super.postSplash(f);

   f.replace(dummyTitle, splashTitle, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 1000));
   splashTitle = null;
   dummyTitle = null;
}

Notice that we replace the title with an empty label; in this case we do this so we can later replace it while animating the replace behavior thus creating a slide-in effect within the title. It can be replaced though, for every purpose including the purpose of a completely different custom made component. By using the replace method the existing layout constraints are automatically maintained.

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.

However, sometimes developers might find the permissions that come up a bit confusing and might not understand why a specific permission came up. This maps Android permissions to the methods/classes in Codename One that would trigger them. Notice that this list isn’t exhaustive as the API is rather large:

android.permission.WRITE_EXTERNAL_STORAGE - this permission appears by default for Codename One applications, since the FileSystemStorage API (which is used extensively) might have some dependencies on it. You can explicitly disable it using the build hint android.blockExternalStoragePermission=true, notice that this is something we don’t test and it might fail on devices.

android.permission.INTERNET - this is a hardcoded permission in Codename One, the ability to connect to the network is coded into all Codename One applications.

android.hardware.camera & android.permission.RECORD_AUDIO - are triggered by com.codename1.Capture

android.permission.RECORD_AUDIO - is triggered by usage of MediaManager.createMediaRecorder() & Display.createMediaRecorder()

android.permission.READ_PHONE_STATE - is triggered by com.codename1.ads package, com.codename1.components.Ads, com.codename1.components.ShareButton, com.codename1.media, com.codename1.push, Display.getUdid() & Display.getMsisdn(). This permission is required for media in order to suspend audio playback when you get a phone call.

android.hardware.location, android.hardware.location.gps, android.permission.ACCESS_FINE_LOCATION, android.permission.ACCESS_MOCK_LOCATION & android.permission.ACCESS_COARSE_LOCATION - map to com.codename1.maps & com.codename1.location.

package.permission.C2D_MESSAGE, com.google.android.c2dm.permission.RECEIVE, android.permission.RECEIVE_BOOT_COMPLETED - are requested by the com.codename1.push package

android.permission.READ_CONTACTS - triggers by the package com.codename1.contacts & Display.getAllContacts().

android.permission.VIBRATE - is triggered by Display.vibrate() and Display.notifyStatusBar()

android.permission.SEND_SMS - is triggered by Display.sendSMS()

android.permission.WAKE_LOCK - is triggered by Display.lockScreen() & Display.setScreenSaverEnabled()

android.permission.WRITE_CONTACTS - is triggered by Display.createContact(), Display.deleteContact(), ContactsManager.createContact() & ContactsManager.deleteContact()

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 e.g. when accessing contacts the user will receive a prompt whether to allow contacts access.

Permission can be denied and a user can later on revoke/grant a permission via external settings UI

This is really great as it allows apps to be installed with a single click and no permission prompt during install which can increase conversion rates!

Enabling Permissions

Codenmae One compiles Android targets with SDK level 23 but not with target level 23!

This means that by default the new permission mode is still off and you won’t see any of the effects mentioned below.

This will probably change to the default in the future but at the moment the target SDK defaults to 21

To activate this functionality you will need to set the target SDK to level 23 by using the android.targetSDKVersion=23 build hint.

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();

When we try to install this app without changing anything on an Android 6 device we see this UI:

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

When we set android.targetSDKVersion=23 in the build hints and try to install again the UI looks like this:

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

When we launch the UI under the old permissions system we see the contacts instantly. In the new system we are presented with this UI:

Native permission prompt first time
Figure 7. Native permission prompt first time

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

Codename One permission prompt
Figure 8. Codename One permission prompt

If we 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. However, if the user realizes his mistake he can double back and ask to re-prompt for permission in which case he will see this native prompt:

Native permission prompt second time
Figure 9. Native permission prompt second time

Notice that denying this second request will not trigger another Codename One prompt.

Code Changes

There are no explicit code changes needed for this functionality to "just work". The respective API’s will work just like they always worked and will prompt the user seamlessly for permissions.

Some behaviors that never occurred on Android but were perfectly legal in the past might start occurring with the switch to the new API. E.g. the location manager might be null and your app must always be ready to deal with such a situation

When permission is requested a user will be seamlessly prompted/warned, Codename One has builtin text to control such prompts but you might want to customize the text. You can customize permission text via the Display properties e.g. to customize the text of the contacts permission we can do something such as:

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");

This is optional as there is a default value defined. You can define this once in the init(Object) method but for some extreme cases permission might be needed for different things e.g. you might ask for this permission with one reason at one point in the app and with a different reason at another point in the app.

The following permission keys are supported: "android.permission.READ_PHONE_STATE" android.permission.WRITE_EXTERNAL_STORAGE, android.permission.ACCESS_FINE_LOCATION, android.permission.SEND_SMS, android.permission.READ_CONTACTS, android.permission.WRITE_CONTACTS, android.permission.RECORD_AUDIO.

Simulating Prompts

You can simulate permission prompts by checking that option in the simulator menu.

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

This will produce a dialog to the user whenever this happens in Android and will try to act in a similar way to the device. Notice that you can test it in the iOS simulator too.

AndroidNativeUtil’s checkForPermission

If you write Android native code using native interfaces you are 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 we introduced the same underlying logic we used namely: checkForPermission.

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

if(!com.codename1.impl.android.AndroidNativeUtil.checkForPermission(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 safely invoked on the event dispatch thread without concern.

On Device Debugging

Codename One supports debugging applications on devices by using the natively generated project. All paid subscription levels include the ability to check an Include Source flag in the settings that returns a native OS project. You can debug that project in the respective native IDE.

In iOS this is usually strait forward, just open the project with xcode and run it optionally disabling bitcode.

With Android Studio this is sometimes as very easy task as it is possible to actually open the gradle project in Android Studio and just run it. However, due to the fragile nature of the gradle project this stopped working for some builds and has been "flaky".

Android Studio Debugging (Easy Way)

By default you should be able to open the gradle project in Android Studio and just run it. To get this to work open the Android Studio Setting and select gradle 2.11.

Gradle settings UI in Android Studio
Figure 11. Gradle settings UI in Android Studio (notice you need gradle 2.11 and not 2.8 as pictured here)

If this works for you then you can ignore the section below.

Android Studio Debugging the Hard Way

In some cases the gradle project might not work or this might fail with a change from Google.

Here are steps that should work for everyone:

  1. Check the include source flag in the IDE and send a build

  2. Download the sources.zip result from the build server

  3. Launch Android Studio and create a new project

  4. Make sure to use the same package and app name as you did in the Codename One project, select to not create an activity

  5. Unzip the sources.zip file and copy the main directory from its src directory to the Android Studio projects src directory make sure to overwrite files/directories.

  6. Copy its libs directory on top of the existing libs

  7. Copy the source gradle dependencies content to the destination gradle file

  8. Connect your device and press the Debug button for the IDE

You might need to copy additional gradle file meta-data such as multi-dexing etc.

You might not need to repeat the whole thing with every build. E.g. it might be practical to only copy the userSources.jar from the libs directory to get the latest version of your code. You can copy the src/main directory to get the latest up to date Android port.

Native Interfaces

Sometimes you may wish to use an API that is 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.

Notice that when we say "native" we do not mean C/C++ always but rather the platforms "native" environment. So in the case of Android the Java code will be invoked with full access to the Android API, in case of iOS an Objective-C message would be sent and so forth.

You can still access C code under Android either by using JNI from the Android native code or by using a library

Native interfaces are designed to only allow primitive types, Strings, arrays of primitive types (single dimension only) & PeerComponent values. Any other type of parameter/return type is prohibited. However, once in the native layer the native code can act freely and query the Java layer for additional information.

The reason for the limits is the disparity between the platforms. Mapping a Java Object to an Objective-C NSObject is possible but leads to odd edge cases and complexity e.g. GC vs. ARC in a disparate object graph

Furthermore, native methods should avoid features such as overloading, varargs (or any Java 5+ feature for that matter) to allow portability for languages that do not support such features.

Do not rely on pass by reference/value behavior since they vary between platforms

Implementing a native layer effectively means:

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

  2. Creating the proper native implementation hierarchy based on the call conventions for every platform within the native directory

E.g. to create a simple hello world interface do something like:

package com.mycompany.myapp;
import com.codename1.system.NativeInterface;
public interface MyNative extends NativeInterface {
    String helloWorld(String hi);
}

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

Generating the native code
Figure 12. Generating the native code
Once generated we are prompted that the native code is in the
Figure 13. Once generated we are prompted that the native code is in the "native" directory

We can now look int the native directory in the project root (in NetBeans you can see that in the Files tab) and you can see something that looks like this:

Native directory structure containing stubs for the various platforms
Figure 14. Native directory structure containing stubs for the various platforms

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

If you re-run the Generate Native Access tool you will get this dialog, if you answer yes all the files will be overwritten, if you answer no only files you deleted/renamed will be recreated
Running
Figure 15. Running "Generate Native Access" when some/all of the native files exist already

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

MyNative my = 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.

We’ll start with Android which should be familiar and intuitive to many developers, this is how the generated file under the native/android directory looks:

package com.mycompany.myapp;

public class MyNativeImpl {
    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 us to implement a NativeInterface on some platforms and leave the rest out without really knowing anything about these platforms.

We can implement the Android version using code similar to this:

package com.mycompany.myapp;

import android.util.Log;(1)

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

    public boolean isSupported() { (3)
        return true;
    }
}
1 Notice that we are using the Android native android.util.Log class which isn’t accessible from standard Codename One code
2 The impl class doesn’t physically implement the MyNative interface!
This is intentional and due to the PeerComponent functionality mentioned below. You don’t need to add an implements clause.
3 Notice that there is no constructor and the class is public. It is crucial that the system will be able to allocate the class without obstruction. You can use a constructor but it can’t have any arguments and you shouldn’t rely on semantics of construction.
4 We implemented the native method and that we set isSupported to true.
The IDE won’t provide completion suggestions and will claim that there are errors in the code!
Codename One doesn’t include the native platforms in its bundle e.g. the full Android SDK or the full xcode Objective-C runtime. However, since the native code is compiled on the servers (where these runteims are present) this shouldn’t be a problem
When implementing a non-trivial native interface, send a server build with the "Include Source" option checked. Implement the native interface in the native IDE then copy and paste the native code back into Codename One

The implementation of this interface is nearly identical for Android, J2ME & Java SE.

Objective-C (iOS)

When generating the Objective-C code the "Generate Native Sources" tool produces two files: com_mycompany_myapp_MyNativeImpl.h & com_mycompany_myapp_MyNativeImpl.m.

The .m files are the Objective-C equivalent of .c files and .h files contain the header/include information. In this case the com_mycompany_myapp_MyNativeImpl.h contains:

#import <Foundation/Foundation.h>

@interface com_mycompany_myapp_MyNativeImpl : NSObject {
}

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

And com_mycompany_myapp_MyNativeImpl.m contains:

#import "com_mycompany_myapp_MyNativeImpl.h"

@implementation com_mycompany_myapp_MyNativeImpl

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

-(BOOL)isSupported{
    return NO;
}

@end
Objective-C relies on argument names as part of the message (method) signature. So -(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 similar to 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

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 we use the complete() method of the provided callback to pass the return value rather than using the return statement. This is to work around the fact that Javascript doesn’t natively support threads. The Java thread that is calling your native interface will block until your method calls callback.complete(). This allows you to use asynchronous APIs inside your native method while still allowing Codename One to work use your native interface via a synchronous API.

Make sure you call either callback.complete() or callback.error() in your method at some point, or you will cause a deadlock in your app (code calling your native method will just sit and "wait" forever for your method to return a value).

The naming conventions for the methods themselves are modeled after the naming conventions shown in the previous examples:

<method-name>__<param-1-type>_<param-2-type>_…​<param-n-type>

Where <method-name> is the name of the method in Java, and the `<param-X-type>`s are a string representing the parameter type. The general rule for these strings are:

  1. Primitive types are mapped to their type name. (E.g. int to "int", double to "double", etc…​).

  2. Reference types are mapped to their fully-qualified class name with '.' replaced with underscores. E.g. java.lang.String would be "java_lang_String".

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

JavaScript Examples

Java API:

public void print(String str);

becomes

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

Java API:

public int add(int a, int b);

becomes

o.add__int_int = function(param1, param2, callback) {
    callback.complete(param1 + param2);
}
public 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. E.g. for a NativeInterface method such as this:

public PeerComponent` createPeer();

Android native implementation would use:

public View createPeer() {
    return null;
}

The iphone would need to return a pointer to a view e.g.:

- (UIView*)createPeer;
Not all platforms support native peers. Specifically JavaSE doesn’t support them due to the way the JavaSE native interfaces are mapped to their implementation.
Note that this won’t limit the code from running on an unsupported platform. Only that specific method won’t work.

Javascript would expect a DOM Element (e.g. a <div> tag to be returned.). E.g.

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.) just places it within the appropriate native directory and it will be packaged into the final executable. You would only be able to reference it from the native code and not from the Codename One code, which means you will need to build native interfaces to access it.

This is discussed further below.

Type Mapping & Rules

Several rules govern the creation of NativeInterfaces and we only briefly covered some of them.

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

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

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

  • Only the supported types listed below can be used

  • Native implementations can’t rely on pass by reference/value semantics as those might change between platforms

  • hashCode, equals & toString are reserved and won’t be mapped to native code

Table 2. NativeInterface Supported Types
Java Android RIM JavaSE Obj-C C#

byte

byte

byte

byte

char

sbyte

boolean

boolean

boolean

boolean

BOOL

bool

char

char

char

char

int

char

short

short

short

short

short

short

int

int

int

int

int

int

long

long

long

long

long long

long

float

float

float

float

float

float

double

double

double

double

double

double

String

String

String

String

NSString*

String

byte[]

byte[]

byte[]

byte[]

NSSData*

sbyte[]

boolean[]

boolean[]

boolean[]

boolean[]

NSData*

bool[]

char[]

char[]

char[]

char[]

NSData

char[]

short[]

short[]

short[]

short[]

NSData*

short[]

int[]

int[]

int[]

int[]

NSData*

int[]

long[]

long[]

long[]

long[]

NSData*

long[]

float[]

float[]

float[]

float[]

NSData*

float[]

double[]

double[]

double[]

double[]

NSData*

double[]

PeerComponent

android.view.View

net.rim.device.api.ui.Field

PeerComponent

void*

FrameworkElement

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

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

NativeInterface definition
public 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);
Android Version
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) {
}
iOS Version
-(void)test:(char)param param1:(BOOL)param1 param2:(int)param2 param3:(short)param3 param4:(int)param4 param5:(long long)param5 param6:(float)param6 param7:(double)param7 param8:(NSString*)param8 param9:(NSData*)param9 param10:(NSData*)param10 param11:(NSData*)param11 param12:(NSData*)param12 param13:(NSData*)param13 param14:(NSData*)param14 param15:(NSData*)param15 param16:(NSData*)param16 param17:(void*)param17;
}
JavaScript Version
o.test__byte_boolean_char_short_int_long_float_double_java_lang_String_byte_1ARRAY_boolean_1ARRAY_char_1ARRAY_short_1ARRAY_int_1ARRAY_long_1ARRAY_float_1ARRAY_double_1ARRAY_com_codename1_ui_PeerComponent = function(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, callback) {
    callback.error(new Error("Not implemented yet"));
};
Java SE Version
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, com.codename1.ui.PeerComponent param17) {
}
RIM/Blackberry Version
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, net.rim.device.api.ui.Field param17) {
}
Java ME Version
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, Object param17) {
}
C# Version
public void test(byte param, bool param1, char param2, short param3, int param4, long param5, float param6, double param7, String param8, byte[] param9, boolean[] param10, char[] param11, short[] param12, int[] param13, long[] param14, float[] param15, double[] param16, FrameworkElement param17) {
}

Android Native Permissions

Normally permissions in Codename One are seamless. Codename One traverses the bytecode and automatically assigns permissions to Android applications based on the API’s used by the developer.

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

So if you require additional permissions in your Android native code you need to define them in the build arguments using the android.xpermissions build argument and setting it to your additional permissions e.g.:

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

Native AndroidNativeUtil

If you do any native interfaces programming in Android you should be familiar with the AndroidNativeUtil class which allows you to access native device functionality more easily from the native code. E.g. many Android API’s need access to the Activity which you can get by calling AndroidNativeUtil.getActivity().

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

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

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

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

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

import com.codename1.impl.android.AndroidNativeUtil;

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

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

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

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

package com.mycompany;
public class NativeCallback {
    public static void callback() {
        // do stuff
    }
}

So if I want to call it from Android or all of the Java based platforms I can just write this in the "native" code:

com.mycompany.NativeCallback.callback();

I can also pass a argument as we do later on:

com.mycompany.NativeCallback.callback("My Arg");
Accessing Callbacks from Objective-C

If we want to invoke that method from Objective-C we 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 we want to trigger the method just do:

com_mycompany_NativeCallback_callback__(CN1_THREAD_STATE_PASS_SINGLE_ARG);

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

We can easily pass arguments like:

public static void callback(int arg)

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

com_mycompany_NativeCallback_callback___int(CN1_THREAD_GET_STATE_PASS_ARG intValue);

Notice that there is no comma between the CN1_THREAD_GET_STATE_PASS_ARG and the value!

Why No Comma?

The comma is included as part of the macro which makes for code that isn’t as readable.

The reason for this dates to the migration from XMLVM [1] to the current ParparVM implementation. CN1_THREAD_GET_STATE_PASS_ARG is defined as nothing in XMLVM since it didn’t use that concept. Yet under ParparVM it will include the necessary comma.

A common use case is passing string values to the Java side, or really NSString* which is iOS equivalent. Assuming a method like this:

public static void callback(String arg)

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 modify the method as such:

com_mycompany_NativeCallback_callback___java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG nsStringValue));

And finally you might want to return a value from callback as such:

public static int callback(int 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 us to differentiate between void callback(int,int) and int callback(int).

Covariant return types are a little known Java 5 feature. E.g. the method Object getX() can be overriden by MyObject getX(). However, in the VM level they can both exist side by side.
Accessing Callbacks from Javascript

The mechanism for invoking static callback methods from Javascript (for the Javascript port only) is similar to Objective-C’s. The this object in your native interface method contains a property named $GLOBAL$ that provides access to static java methods. This object will contain Javascript mirror objects for each Java class (though the property name is mangled by replacing "." with underscores). Each mirror object contains a wrapper method for its underlying class’s static methods where the method name follows the same naming convention as is used for the Javascript native methods themselves (and very similar to the naming conventions used in Objective-C).

For example, the Google Maps project includes the static callback method:

static void fireMapChangeEvent(int mapId, final int zoom, final double lat, final double lon) { ... }

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 we first obtain a reference to the fireMapChangeEvent method, and then call it later. However, we could have called it directly also.

Your code MUST contain the full string path this.$GLOBAL$.your_class_name.your_method_name or the build server will not be able to recognize that your code requires this method. The $GLOBAL$ object is populated by the build server only 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 (via this pattern) it won’t generate the necessary wrappers for your Javascript code to access the Java methods.
Asynchronous Callbacks & Threading

One of the problematic aspects of calling back into Java from Javascript is that Javascript has no notion of multi-threading. Therefore, if the method you are calling uses Java’s threads at all (e.g. It includes a wait(), notify(), sleep(), callSerially(), etc…​) you need to call it asynchronously from Javascript. You can call a method asynchronously by appending $async to the method name. E.g. With the Google Maps example above, you would change :

this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double;

to

this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double$async;

This will cause the call to be wrapped in the appropriate bootstrap code to work properly with threads - and it is absolutely necessary in cases where the method may use threads of any kind. The side-effect of calling a method with the $async suffix is that you can’t use return values from the method.

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

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 via the IDE classpath UI. The reasoning here is very simple, these IDE’s don’t package the JAR’s into the final executable and even if they did these JAR’s would probably use features unavailable or inappropriate for the device (e.g. java.io.File etc.).

Don't change the classpath
Figure 16. Don’t change the classpath, this is how it should look for a typical Java 8 Codename One application

Cn1libs are Codename One’s file format for 3rd party extensions. It’s physicially a zip file containing other zip files and some meta-data.

Why Not Use JAR?

A jar can be compiled with usage of any Java API that might not be supported, it can be compiled with a Java target version that isn’t tested.

Jars don’t include support for writing native code, you could use JNI in jars (awkwardly) but that doesn’t match Codename One’s needs for native support (see section above).

Jars don’t support "proper" code completion, a common developer trick is to stick source code into the jar but that prevents usage with proprietary code. Cn1libs provide full IDE code completion (with JavaDoc hints) without exposing the sources.

There are two use cases for wanting JAR’s and they both have very different solutions:

  1. Modularity

  2. Working with an existing JARs

Cn1lib’s address the modularity aspect allowing you to break that down. Existing jars can sometimes be used native code settings but for the most part 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, you can install a cn1lib by placing it in the lib directory of your project then right clicking the project and selecting Codename OneRefresh cn1lib files.

Refresh cn1lib files menu option
Figure 17. Refresh cn1lib files menu option

Once refreshed the content of the cn1lib will be available to code completion and you could just use it.

Notice that some cn1libs require additional configurations such as build hints etc. so make sure to read the developers instructions when integrating a 3rd party library.
Under the Hood of cn1lib Install

Refresh cn1lib files invokes the ant task refresh-libs. You could automatically trigger a refresh as part of your build process by invoking that ant task manually.

Technically that task invokes a custom task that unzips the content of the cn1lib into a set of directories accessible to the build process. Classes and stub sources are installed in lib/impl/cls & lib/impl/stubs respectively.

The native files are extracted to lib/impl/native. The classpath for the main project and the ant build process know about these directories and include them within their path.

Creating a Simple cn1lib

Creating a cn1lib is trivial, we will get into more elaborate uses soon enough but for a hello world cn1lib we can just use this 2 step process:

Select the CodenameOne Library Option
Figure 18. Select the CodenameOne Library Option
Select the file name/destination. Notice that a Java 8 cn1lib requires Java 8 support in the parent project!
Figure 19. Select the file name/destination. Notice that a Java 8 cn1lib requires Java 8 support in the parent project!

Once we go thru these steps we can define any source file within the library and it will be accessible to the users of the library.

Build Hints in cn1libs

Some cn1libs are pretty simple to install, just place them under the lib directory and refresh. However, many of the more elaborate cn1libs need some pretty complex configurations. This is the case when native code is involved where we need to add permissions or plist entries for the various native platforms to get everything to work. This makes the cn1lib’s helpful but less than seamless which is where we want to go.

Codename One cn1libs include two files that can be placed into the root: codenameone_library_required.properties & codenameone_library_appended.properties.

In these files you can just write a build hint as codename1.arg.ios.plistInject=…​ for the various hints.

Notice the usage of the properties syntax for the build hint with the codename1.arg prefix you would also need to escape reserved characters for properties files.
The best way to discover the right syntax for such build hints is to set them via 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 we need two files?

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

Required build hints can be something like ios.objC=true which we want to always work. E.g. 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 we’d want the build to fail so we can remove the faulty cn1lib.

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

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

Notice that this can still collide e.g. if a different cn1lib defines its own background mode. However, there are many valid cases where ios.plistInject can be used for other things. In this case we’ll append the content of the ios.plistInject into the build hint if it’s not already there.

There are a couple of things you need to keep in mind:

  • Properties are merged with every "refresh libs" call not dynamically on the server. This means it should be pretty simple for the developer to investigate issues in this process.

  • Changing flags is problematic - there is no "uninstall" process. Since the data is copied into the codenameone_settings.properties file. If you need to change a flag later on you might need to alert users to make changes to their properties essentially negating the value of this feature…​
    So be very careful when adding properties here.

It’s your responsibility as a library developer to decide which build hint goes into which file!
Codename One can’t automate this process as the whole process of build hints is by definition an ad hoc process.

The rule of thumb is that a build hint with a numeric or boolean value is always a required property. If an entry has a string that you can append with another string then its probably an appended entry.

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
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
rim.askPermissions
google.adUnitId
ios.includePush
ios.headphoneCallback
ios.enableAutoplayVideo
ios.googleAdUnitId
ios.googleAdUnitIdPadding
ios.enableBadgeClear
ios.locationUsageDescription
ios.bundleVersion
ios.objC
ios.testFlight
desktop.width
desktop.height
desktop.adaptToRetina
desktop.resizable
desktop.fontSizes
desktop.theme
desktop.themeMac
desktop.themeWin
desktop.windowsOutput
noExtraResources
j2me.iconSize

These build hints should probably be appended:

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

The cb1lib file format is quite simple, it’s a zip file containing zip files within it with fixed names to support the various features.

The table below covers the files that can/should be a part of a cn1lib file:

Table 3. cn1lib structure
File Name Required Purpose

main.zip

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

stubs.zip

Stub source files (auto-generated) containing javadocs to provide code completion

manifest.properties

×

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

codenameone_ library_ appended.properties

×

Discussed above

codenameone_ library_ required.properties

×

Discussed above

nativeios.zip

×

Native iOS sources if applicable

nativeand.zip

×

Native Android sources if applicable

nativejavascript.zip

×

Native JavaScript sources if applicable

nativerim.zip

×

Native RIM sources if applicable

nativese.zip

×

Native JavaSE sources if applicable

nativewin.zip

×

Native Windows sources if applicable

nativeme.zip

×

Native Java ME sources if applicable

Integrating Android 3rd Party Libraries & JNI

While its pretty easy to use native interfaces to write Android native code some things aren’t necessarily as obvious. E.g. 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 3 options:

  1. The first option (and the easiest one) is to just place a Jar file in the native/android directory. This will link your binary with the jar file. Just place the jar under the native/android and the build server will pick it up and will add it to the classpath.
    Notice that Android release apps are obfuscated by default which might cause issues with such libraries if they reference API’s that are unavailable on Android. You can workaround this by adding a build hint to the proguard obfuscation code that blocs the obfuscation of the problematic classes using the build hint:
    android.proguardKeep=-keep class com.mypackage.ProblemClass { *; }`

  2. The second option is to add an Android Library Project. Not all 3rd party tools can be packaged as a simple jar, some 3rd party tools need to declare activities add permissions, resources, assets, and/or even add native code (.so files).
    To link a Library project to your Codename One project open the Library project in Eclipse or Android Studio and make sure the project builds, after the project was built successfully remove the bin directory from the project and zip the whole project.

    Rename the extension from .zip to .andlib and place the andlib file under the native/android directory. The build server will pick it up and will link it to the project.

  3. The 3rd option is an aar files. The aar file is a binary format Google introduced to represent an Android Library project (similarly to the cn1lib format). One of the problem with the Android Library projects was the fact that it required the project sources which made it difficult for 3rd party vendors to publish libraries.
    As a result so android introduced the aar file which is a binary format that represents a Library project. To learn more about arr you can read this.

    You can link an aar file by placing it under the native/android and the build server will link it to the project.

Drag & Drop

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

In Codename One only components can be dragged and drop targets are always components. The logic of actually performing the operation indicated by the drop is the responsibility of the person implementing the drop.

Some platforms e.g. AWT allow dragging abstract concepts such as mime type elements. This allows dragging things like a text file into the app, but that use case isn’t realistic in mobile

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

Form hi = new Form("Rearrangeable Items", new BorderLayout());
String[] buttons = {"A Game of Thrones", "A Clash Of Kings",  "A Storm Of Swords",
    "A Feast For Crows", "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring" };

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

hi.add(BorderLayout.NORTH, "Arrange The Titles").add(BorderLayout.CENTER, box);
hi.show();
Drag and drop demo
Figure 20. Drag and drop demo

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

When dragging on top of a child component of a drop target the code recursively searches for a drop target parent. Dropping a component on the child will automatically find the right drop target, hence there is no need to make "everything" into a drop target.

You can override these methods in the draggable components:

  • getDragImage - this generates an image preview of the component that will be dragged. This automatically generates a sensible default so you don’t need to override it.

  • drawDraggedImage - this method will be invoked to draw the dragged image at a given location, it might be useful to override it if you want to display some drag related information such an additional icon based on location etc. (e.g. 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!

Continuous Integration & Release Engineering

Codename One was essentially built for continuous integration since the build servers are effectively a building block for such an architecture. However, there are several problems with that: the first of which is limited server capacity.

If all users would start sending builds with every commit the servers would instantly become unusable due to the heavy load. To circumvent this CI support is limited only on the Enterprise level which allows Codename One to stock more servers and cope with the rise in demand related to the feature.

To integrate with any CI solution just use the standard Ant targets such as build-for-android-device, build-for-iphone-device etc.

Normally, this would be a problem since the build is sent but since it isn’t blocking you wouldn’t get the build result and wouldn’t be able to determine if the build passed or failed. To enable this just edit the build XML and add the attribute automated="true" to the codeNameOne tag in the appropriate targets.

This will deliver a result.zip file under the dist folder containing the binaries of a successful build. It will also block until the build is completed. This should be pretty easy to integrate with any CI system together with our automated testing solutions .

E.g. we can do a synchronous build like this:

<target name="build-for-javascript-sync" depends="clean,copy-javascript-override,copy-libs,jar,clean-override">
    <codeNameOne
        jarFile="${dist.jar}"
        displayName="${codename1.displayName}"
        packageName = "${codename1.packageName}"
        mainClassName = "${codename1.mainName}"
        version="${codename1.version}"
        icon="${codename1.icon}"
        vendor="${codename1.vendor}"
        subtitle="${codename1.secondaryTitle}"
        automated="true"
        targetType="javascript"
        />
</target>

This allows us to build a JavaScript version of the app automatically as part of a release build script.

Android Lollipop ActionBar Customization

When running on Android Lollipop (5.0 or newer) the native action bar will use the Lollipop design. This isn’t applicable if you use the Toolbar or SideMenuBar this won’t be used.

To customize the colors of the native ActionBar on Lollipop define a colors.xml file in the native/android directory of your project. It should look like this:

<resources>
   <color name="colorPrimary">#ff00ff00</color>
   <color name="colorPrimaryDark">#80ff0000</color>
   <color name="colorAccent">#800000ff</color>
</resources>

Intercepting URL’s On iOS & Android

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

In Android we can use intents which are pretty elaborate and can be used via Display.execute, however what if you would like to expose the functionality of your application to a different application running on the device. This would allow that application to launch your application.

This isn’t something we builtin to Codename One, however it does expose enough of the platform capabilities to enable that functionality rather easily on Android.

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

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:

String arg = Display.getInstance().getProperty("AppArg");

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

iOS is practically identical to Android with some small caveats, iOS’s equivalent of the manifest is the plist.

You can inject more data into the plist by using the ios.plistInject build hint.

So the equivalent in 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>

However, that can conflict with the Facebook integration if you use FacebookConnect which needs access to the schemes. To workaround it you can use the build hint ios.urlScheme e.g.:

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 especially if you plan on working with native widgets e.g. Web Browser, native maps, text input, media and native interfaces (which can return a PeerComponent).

Codename One draws all of its widgets on its own, this is a concept which was modeled in part after Swing. This allows functionality that can’t be achieved in native widget platforms:

  1. The Codename One GUI builder & simulator are almost identical to the device - notice that this also enables the build cloud, otherwise device specific bugs would overwhelm development and make the build cloud redundant.

  2. Ability to override everything - paint, pointer, key events are all overridable and replaceable. Developers can also paint over everything e.g. glasspane and layered pane.

  3. Consistency - provides identical functionality on all platforms for the most part.

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

Why does Codename One Need Native Widgets at all?

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

They are sometimes impossible to perform without the native platform. E.g. the virtual keyboard input on the devices is tied directly to the native text input. It’s impractical to implement everything from scratch for all languages, dictionaries etc. The result would be sub-par.

A web browser can’t be implemented in this day and age without a JavaScript JIT and including a JIT within an iOS app is prohibited by Apple.

So what’s the problems with native widgets?

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

However native widgets have to be drawn on the devices native UI thread.
This means that drawing looks something like this:

  1. Loop over all Codename One components and paint them.

  2. Loop over all native peer components and paint them.

This effectively means that all peer components are drawn on top of the Codename One components.

This was also the case in AWT/Swing to one degree or another…​

So how do we show dialogs on top of Peer Components?

Codename One grabs a screenshot of the peer, hide it and then we can just show the screenshot. Since the screenshot is static it can be rendered via the standard UI. Naturally we can’t do that always since grabbing a screenshot is an expensive process on all platforms and must be performed on the native device thread.

Why can’t we 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 our scrolling?

Native Components In The First Form

There is also another problem that might be counter intuitive. iOS has screenshot images representing the first form. If your first page is an HTML or a native map (or other peer widget) the screenshot process on the build server will show fallback code instead of the real thing thus providing sub-par behavior.

Its impractical to support something like HTML for the screenshot process since it would also look completely different from the web component running on the device.

You can read more about the screenshot process here.

Integrating 3rd Party Native SDKs

The following is a description of the procedure that was used to create the Codename One FreshDesk library. This process can be easily adapted to wrap any native SDK on Android and iOS.

Step 1 : Review the FreshDesk SDKs

Before we begin, we’ll need to review the Android and iOS SDKs.

  1. FreshDesk Android SDK: Integration Guide | API Docs

  2. FreshDesk iOS SDK: Integration Guide | API Docs

In reviewing the SDKs, we are looking for answers to two questions:

  1. What should my Codename One FreshDesk API look like?

  2. What will be involved in integrating the native SDK in my app or lib?

Step 2: Designing the Codename One Public API

When designing the Codename One API, we should begin by looking at the Javadocs for the native Android SDK. If the class hierarchy doesn’t look too elaborate, we may decide to model our Codename One public API fairly closely on the Android API. On the other hand, if we only need a small part of the SDK’s functionality, we may choose to create my abstractions around just the functionality that we need.

In the case of the FreshDesk SDK, it looks like most of the functionality is handled by one central class Mobihelp, with a few other POJO classes for passing data to and from the service. This is a good candidate for a comprehensive Codename One API.

Before proceeding, we also need to look at the iOS API to see if there are any features that aren’t included. While naming conventions in the iOS API are a little different than those in the Android API, it looks like they are functionally the same.

Therefore, I choose to create a class hierarchy and API that closely mirrors the Android SDK.

Step 3: The Architecture and Internal APIs

A Codename One library that wraps a native SDK, will generally consist of the following:

  1. Public Java API, consisting of pure Java classes that are intended to be used by the outside world.

  2. Native Interface(s). The Native Interface(s) act as a conduit for the public Java API to communicate to the native SDK. Parameters in native interface methods are limited to primitive types, arrays of primitive types, and Strings, as are return values.

  3. Native code. Each platform must include an implementation of the Native Interface(s). These implementations are written in the native language of the platform (e.g. Java for Android, and Objective-C for iOS).

  4. Native dependencies. Any 3rd party libraries required for the native code to work, need to be included for each platform. On android, this may mean bundling .jar files, .aar files, or .andlib files. On iOS, this may mean bundling .h files, .a files, and .bundle files.

  5. Build hints. Some libraries will require you to add some extra build hints to your project. E.g. On Android you may need to add permissions to the manifest, or define services in the <Application> section of the manifest. On iOS, this may mean specifying additional core frameworks for inclusion, or adding build flags for compilation.

The following diagram shows the dependencies in a native library:

fc8a77d2 61e0 11e5 9ecf bf381d4ac966
Figure 21. Relationship between native & Codename One API UML Diagram

In the specific case of our FreshDesk API, the public API and classes will look like:

5fe88406 61e4 11e5 951e e09bd28a93c9
Figure 22. Freshdesk API Integration
Things to Notice
  1. The public API consists of the main class (Mobihelp), and a few supporting classes (FeedbackRequest, FeedbackType, MobihelpConfig, MobihelpCallbackStatus), which were copied almost directly from the Android SDK.

  2. The only way for the public API to communicate with the native SDK is via the MobihelpNative interface.

  3. We introduced the MobihelpNativeCallback class to facilitate native code calling back into the public API. This was necessary for a few methods that used asynchronous callbacks.

Step 4: Implement the Public API and Native Interface

We have already looked at the final product of the public API in the previous step, but let’s back up and walk through the process step-by-step.

I wanted to model my API closely around the Android API, and the central class that includes all of the functionality of the SDK is the com.freshdesk.mobihelp.Mobihelp class, so we begin there.

We’ll start by creating our own package (com.codename1.freshdesk) and our own Mobihelp class inside it.

Adapting Method Signatures
The Context parameter

In a first glance at the com.freshdesk.mobihelp.Mobihelp API we see that many of the methods take a parameter of type android.content.Context. This class is part of the core Android SDK, and will not be accessible to any pure Codename One APIs. Therefore, our public API cannot include any such references. Luckily, we’ll be able to access a suitable context in the native layer, so we’ll just omit this parameter from our public API, and inject them in our native implementation.

Hence, the method signature public static final void setUserFullName (Context context, String name) will simply become public static final void setUserFullName (String name) in our public API.

Non-Primitive Parameters

Although our public API isn’t constrained by the same rules as our Native Interfaces with respect to parameter and return types, we need to be cognizant of the fact that parameters we pass to our public API will ultimately be funnelled through our native interface. Therefore, we should pay attention to any parameters or return types that can’t be passed directly to a native interface, and start forming a strategy for them. E.g. consider the following method signature from the Android Mobihelp class:

public static final void showSolutions (Context activityContext, ArrayList<String> tags)

We’ve already decided to just omit the Context parameter in our API, so that’s a non-issue. But what about the ArrayList<String> tags parameter? Passing this to our public API is no problem, but when we implement the public API, how will we pass this ArrayList to our native interface, since native interfaces don’t allow us to arrays of strings as parameters?

I generally use one of three strategies in such cases:

  1. Encode the parameter as either a single String (e.g. using JSON or some other easily parseable format) or a byte[] array (in some known format that can easily be parsed in native code).

  2. Store the parameter on the Codename One side and pass some ID or token that can be used on the native side to retrieve the value.

  3. If the data structure can be expressed as a finite number of primitive values, then simply design the native interface method to take the individual values as parameters instead of a single object. E.g. If there is a User class with properties name and phoneNumber, the native interface can just have name and phoneNumber parameters rather than a single `user parameter.

In this case, because an array of strings is such a simple data structure, I decided to use a variation on strategy number 1: Merge the array into a single string with a delimiter.

In any case, we don’t have to come up with the specifics right now, as we are still on the public API, but it will pay dividends later if we think this through ahead of time.

Callbacks

It is quite often the case that native code needs to call back into Codename One code when an event occurs. This may be connected directly to an API method call (e.g. as the result of an asynchronous method invocation), or due to something initiated by the operating system or the native SDK on its own (e.g. a push notification, a location event, etc..).

Native code will have access to both the Codename One API and any native APIs in your app, but on some platforms, accessing the Codename One API may be a little tricky. E.g. on iOS you’ll be calling from Objective-C back into Java which requires knowledge of Codename One’s java-to-objective C conversion process. In general, I have found that the easiest way to facilitate callbacks is to provide abstractions that involve static java methods (in Codename One space) that accept and return primitive types.

In the case of our Mobihelp class, the following method hints at the need to have a "callback plan":

public static final void getUnreadCountAsync (Context context, UnreadUpdatesCallback callback)

The interface definition for UnreadUpdatesCallback is:

public interface UnreadUpdatesCallback {
    //This method is called once the unread updates count is available.
    void onResult(MobihelpCallbackStatus status, Integer count);

}

I.e. If we were to implement this method (which I plan to do), we need to have a way for the native code to call the callback.onResult() method of the passed parameter.

So we have two issues that will need to be solved here:

  1. How to pass the callback object through the native interface.

  2. How to call the callback.onResult() method from native code at the right time.

For the first issue, we’ll use strategy #2 that we mentioned previously: (Store the parameter on the Codename One side and pass some ID or token that can be used on the native side to retrieve the value).

For the second issue, we’ll create a static method that can take the token generated to solve the first issue, and call the stored callback object’s onResult() method. We abstract both sides of this process using the MobihelpNativeCallback class.

public class MobihelpNativeCallback {
    private static int nextId = 0;
    private static Map<Integer,UnreadUpdatesCallback> callbacks = new HashMap<Integer,UnreadUpdatesCallback>();

    static int registerUnreadUpdatesCallback(UnreadUpdatesCallback callback) {
        callbacks.put(nextId, callback);
        return nextId++;
    }

    public static void fireUnreadUpdatesCallback(int callbackId, final int status, final int count) {
        final UnreadUpdatesCallback cb = callbacks.get(callbackId);
        if (cb != null) {
            callbacks.remove(callbackId);
            Display.getInstance().callSerially(new Runnable() {

                public void run() {
                    MobihelpCallbackStatus status2 = MobihelpCallbackStatus.values()[status];
                    cb.onResult(status2, count);
                }

            });
        }
    }

}

Things to notice here:

  1. This class uses a static Map<Integer,UnreadUpdatesCallback> member to keep track of all callbacks, mapping a unique integer ID to each callback.

  2. The registerUnreadUpdatesCallback() method takes an UnreadUpdatesCallback object, places it in the callbacks map, and returns the integer token that can be used to fire the callback later. This method would be called by the public API inside the getUnreadCountAsync() method implementation to convert the callback into an integer, which can then be passed to the native API.

  3. The fireUnreadUpdatesCallback() method would be called later from native code. Its first parameter is the token for the callback to call.

  4. We wrap the onResult() call inside a Display.callSerially() invocation to ensure that the callback is called on the EDT. This is a general convention that is used throughout Codename One, and you’d be well-advised to follow it. Event handlers should be run on the EDT unless there is a good reason not to - and in that case your documentation and naming conventions should make this clear to avoid accidentally stepping into multithreading hell!

Initialization

Most Native SDKs include some sort of initialization method where you pass your developer and application credentials to the API. When I filled in FreshDesk’s web-based form to create a new application, it generated an application ID, an app "secret", and a "domain". The SDK requires me to pass all three of these values to its init() method via the MobihelpConfig class.

Note, however, that FreshDesk (and most other service provides that have native SDKs) requires me to create different Apps for each platform. This means that my App ID and App secret will be different on iOS than they will be on Android.

Therefore our public API needs to enable us to provide multiple credentials in the same app, and our API needs to know to use the correct credentials depending on the device that the app is running on.

There are many solutions to this problem, but the one I chose was to provide two different init() methods:

public final static void initIOS(MobihelpConfig config)

and

public final static void initAndroid(MobihelpConfig config)

Then I can set up the API with code like:

MobihelpConfig config = new MobihelpConfig();
config.setAppSecret("xxxxxxx");
config.setAppId("freshdeskdemo-2-xxxxxx");
config.setDomain("codenameonetest1.freshdesk.com");
Mobihelp.initIOS(config);

config = new MobihelpConfig();
config.setAppSecret("yyyyyyyy");
config.setAppId("freshdeskdemo-1-yyyyyyyy");
config.setDomain("https://codenameonetest1.freshdesk.com");
Mobihelp.initAndroid(config);
The Resulting Public API
public class Mobihelp {

    private static char[] separators = new char[]{',','|','/','@','#','%','!','^','&','*','=','+','*','<'};
    private static MobihelpNative peer;

    public static boolean isSupported() {
        ....
    }

    public static void setPeer(MobihelpNative peer) {
        ....
    }

    //Attach the given custom data (key-value pair) to the conversations/tickets.
    public final static void	addCustomData(String key, String value) {
        ...
    }
    //Attach the given custom data (key-value pair) to the conversations/tickets with the ability to flag sensitive data.
    public final static void	addCustomData(String key, String value, boolean isSensitive) {
        ...
    }
    //Clear all breadcrumb data.
    public final static void	clearBreadCrumbs() {
        ...
    }
    //Clear all custom data.
    public final static void	clearCustomData() {
        ...
    }
    //Clears User information.
    public final static void	clearUserData() {
        ...
    }
    //Retrieve the number of unread items across all the conversations for the user synchronously i.e.
    public final static int	getUnreadCount() {
        ...
    }

    //Retrieve the number of unread items across all the conversations for the user asynchronously, count is delivered to the supplied UnreadUpdatesCallback instance Note : This may return 0 or stale value when there is no network connectivity etc
    public final static void	getUnreadCountAsync(UnreadUpdatesCallback callback) {
        ...
    }
    //Initialize the Mobihelp support section with necessary app configuration.
    public final static void	initAndroid(MobihelpConfig config) {
        ...
    }

    public final static void initIOS(MobihelpConfig config) {
        ...
    }


    //Attaches the given text as a breadcrumb to the conversations/tickets.
    public final static void	leaveBreadCrumb(String crumbText) {
        ...
    }
    //Set the email of the user to be reported on the Freshdesk Portal
    public final static void	setUserEmail(String email) {
        ...
    }

    //Set the name of the user to be reported on the Freshdesk Portal.
    public final static void	setUserFullName(String name) {
        ...
    }

    //Display the App Rating dialog with option to Rate, Leave feedback etc
    public static void	showAppRateDialog() {
        ...
    }
    //Directly launch Conversation list screen from anywhere within the application
    public final static void	showConversations() {
        ...
    }
    //Directly launch Feedback Screen from anywhere within the application.
    public final static void	showFeedback(FeedbackRequest feedbackRequest) {
        ...
    }
    //Directly launch Feedback Screen from anywhere within the application.
    public final static void	showFeedback() {
        ...
    }
    //Displays the Support landing page (Solution Article List Activity) where only solutions tagged with the given tags are displayed.
    public final static void	showSolutions(ArrayList<String> tags) {
        ...
    }

    private static String findUnusedSeparator(ArrayList<String> tags) {
        ...

    }

    //Displays the Support landing page (Solution Article List Activity) from where users can do the following
    //View solutions,
    //Search solutions,
    public final static void	showSolutions() {
        ...
    }
    //Displays the Integrated Support landing page where only solutions tagged with the given tags are displayed.
    public final static void	showSupport(ArrayList<String> tags) {
        ...
    }

    //Displays the Integrated Support landing page (Solution Article List Activity) from where users can do the following
    //View solutions,
    //Search solutions,
    //  Start a new conversation,
    //View existing conversations update/ unread count etc
    public final static void	showSupport() {
        ...
    }

}
The Native Interface

The final native interface is nearly identical to our public API, except in cases where the public API included non-primitive parameters.

public interface MobihelpNative extends NativeInterface {

    /**
     * @return the appId
     */
    public String config_getAppId();

    /**
     * @param appId the appId to set
     */
    public void config_setAppId(String appId);

    /**
     * @return the appSecret
     */
    public String config_getAppSecret();

    /**
     * @param appSecret the appSecret to set
     */
    public void config_setAppSecret(String appSecret);
    /**
     * @return the domain
     */
    public String config_getDomain();
    /**
     * @param domain the domain to set
     */
    public void config_setDomain(String domain) ;

    /**
     * @return the feedbackType
     */
    public int config_getFeedbackType() ;

    /**
     * @param feedbackType the feedbackType to set
     */
    public void config_setFeedbackType(int feedbackType);

    /**
     * @return the launchCountForReviewPrompt
     */
    public int config_getLaunchCountForReviewPrompt() ;
    /**
     * @param launchCountForReviewPrompt the launchCountForReviewPrompt to set
     */
    public void config_setLaunchCountForReviewPrompt(int launchCountForReviewPrompt);
    /**
     * @return the prefetchSolutions
     */
    public boolean config_isPrefetchSolutions();
    /**
     * @param prefetchSolutions the prefetchOptions to set
     */
    public void config_setPrefetchSolutions(boolean prefetchSolutions);
    /**
     * @return the autoReplyEnabled
     */
    public boolean config_isAutoReplyEnabled();

    /**
     * @param autoReplyEnabled the autoReplyEnabled to set
     */
    public void config_setAutoReplyEnabled(boolean autoReplyEnabled) ;

    /**
     * @return the enhancedPrivacyModeEnabled
     */
    public boolean config_isEnhancedPrivacyModeEnabled() ;

    /**
     * @param enhancedPrivacyModeEnabled the enhancedPrivacyModeEnabled to set
     */
    public void config_setEnhancedPrivacyModeEnabled(boolean enhancedPrivacyModeEnabled) ;



    //Attach the given custom data (key-value pair) to the conversations/tickets.
    public void	addCustomData(String key, String value);
    //Attach the given custom data (key-value pair) to the conversations/tickets with the ability to flag sensitive data.
    public void addCustomDataWithSensitivity(String key, String value, boolean isSensitive);
    //Clear all breadcrumb data.
    public void	clearBreadCrumbs() ;
    //Clear all custom data.
    public void	clearCustomData();
    //Clears User information.
    public void	clearUserData();
    //Retrieve the number of unread items across all the conversations for the user synchronously i.e.
    public int getUnreadCount();

    //Retrieve the number of unread items across all the conversations for the user asynchronously, count is delivered to the supplied UnreadUpdatesCallback instance Note : This may return 0 or stale value when there is no network connectivity etc
    public void	getUnreadCountAsync(int callbackId);

    public void initNative();

    //Attaches the given text as a breadcrumb to the conversations/tickets.
    public  void leaveBreadCrumb(String crumbText);
    //Set the email of the user to be reported on the Freshdesk Portal

    public void setUserEmail(String email);

    //Set the name of the user to be reported on the Freshdesk Portal.
    public void setUserFullName(String name);

    //Display the App Rating dialog with option to Rate, Leave feedback etc
    public void	showAppRateDialog();
    //Directly launch Conversation list screen from anywhere within the application
    public void showConversations();

    //Directly launch Feedback Screen from anywhere within the application.
    public void	showFeedbackWithArgs(String subject, String description);
    //Directly launch Feedback Screen from anywhere within the application.
    public void	showFeedback();

    //Displays the Support landing page (Solution Article List Activity) where only solutions tagged with the given tags are displayed.
    public void	showSolutionsWithTags(String tags, String separator);

    //Displays the Support landing page (Solution Article List Activity) from where users can do the following
    //View solutions,
    //Search solutions,
    public void	showSolutions();
    //Displays the Integrated Support landing page where only solutions tagged with the given tags are displayed.
    public void	showSupportWithTags(String tags, String separator);

    //Displays the Integrated Support landing page (Solution Article List Activity) from where users can do the following
    //View solutions,
    //Search solutions,
    //  Start a new conversation,
    //View existing conversations update/ unread count etc
    public void	showSupport();
}

Notice also, that the native interface includes a set of methods with names prefixed with config__. This is just a naming conventions I used to identify methods that map to the MobihelpConfig class. I could have used a separate native interface for these, but decided to keep all the native stuff in one class for simplicity and maintainability.

Connecting the Public API to the Native Interface

So we have a public API, and we have a native interface. The idea is that the public API should be a thin wrapper around the native interface to smooth out rough edges that are likely to exist due to the strict set of rules involved in native interfaces. We’ll, therefore, use delegation inside the Mobihelp class to provide it a reference to an instance of MobihelpNative:

public class Mobihelp {
    private static MobihelpNative peer;
    //...
}

We’ll initialize this peer inside the init() method of the Mobihelp class. Notice, though that init() is private since we have provided abstractions for the Android and iOS apps separately:

    //Initialize the Mobihelp support section with necessary app configuration.
    public final static void initAndroid(MobihelpConfig config) {
        if ("and".equals(Display.getInstance().getPlatformName())) {
            init(config);
        }
    }

    public final static void initIOS(MobihelpConfig config) {
        if ("ios".equals(Display.getInstance().getPlatformName())) {
            init(config);
        }
    }

    private static void init(MobihelpConfig config) {
        peer = (MobihelpNative)NativeLookup.create(MobihelpNative.class);
        peer.config_setAppId(config.getAppId());
        peer.config_setAppSecret(config.getAppSecret());
        peer.config_setAutoReplyEnabled(config.isAutoReplyEnabled());
        peer.config_setDomain(config.getDomain());
        peer.config_setEnhancedPrivacyModeEnabled(config.isEnhancedPrivacyModeEnabled());
        if (config.getFeedbackType() != null) {
            peer.config_setFeedbackType(config.getFeedbackType().ordinal());
        }
        peer.config_setLaunchCountForReviewPrompt(config.getLaunchCountForReviewPrompt());
        peer.config_setPrefetchSolutions(config.isPrefetchSolutions());
        peer.initNative();

    }

Things to Notice:

  1. The initAndroid() and initIOS() methods include a check to see if they are running on the correct platform. Ultimately they both call init().

  2. The init() method, uses the NativeLookup class to instantiate our native interface.

Implementing the Glue Between Public API and Native Interface

For most of the methods in the Mobihelp class, we can see that the public API will just be a thin wrapper around the native interface. E.g. the public API implementation of setUserFullName(String) is:

public final static void setUserFullName(String name) {
    peer.setUserFullName(name);
}

For some other methods, the public API needs to break apart the parameters into a form that the native interface can accept. E.g. the init() method, shown above, takes a MobihelpConfig object as a parameter, but it passed the properties of the config object individually into the native interface.

Another example, is the showSupport(ArrayList<String> tags) method. The corresponding native interface method that is wraps is showSupport(String tags, `String separator)` - i.e it needs to merge all tags into a single delimited string, and pass then to the native interface along with the delimiter used. The implementation is:

public final static void showSupport(ArrayList<String> tags) {
    String separator = findUnusedSeparator(tags);
    StringBuilder sb = new StringBuilder();
    for (String tag : tags) {
        sb.append(tag).append(separator);
    }
    peer.showSupportWithTags(sb.toString().substring(0, sb.length()-separator.length()), separator);
}

The only other non-trivial wrapper is the getUnreadCountAsync() method that we discussed before:

   public final static void getUnreadCountAsync(UnreadUpdatesCallback callback) {
        int callbackId = MobihelpNativeCallback.registerUnreadUpdatesCallback(callback);
        peer.getUnreadCountAsync(callbackId);
    }

Step 5: Implementing the Native Interface in Android

Now that we have set up our public API and our native interface, it is time to work on the native side of things. You can generate stubs for all platforms in your IDE (Netbeans in my case), by right clicking on the MobihelpNative class in the project explorer and selecting "Generate Native Access".

Generate Native Access Menu Item
Figure 23. Generate Native Access Menu Item

This will generate a separate directory for each platform inside your project’s native directory:

Native generated sources directory view
Figure 24. Native generated sources directory view

Inside the android directory, this generates a com/codename1/freshdesk/MobihelpNativeImpl class with stubs for each method.

Our implementation will be a thin wrapper around the native Android SDK. See the source here.

Some highlights:

  1. Context : The native API requires us to pass a context object as a parameter on many methods. This should be the context for the current activity. It will allow the FreshDesk API to know where to return to after it has done its thing. Codename One provides a class called AndroidNativeUtil that allows us to retrieve the app’s Activity (which includes the Context). We’ll wrap this with a convenience method in our class as follows:

    private static Context context() {
        return com.codename1.impl.android.AndroidNativeUtil.getActivity().getApplicationContext();
    }

    This will enable us to easily wrap the freshdesk native API. E.g.:

    public void clearUserData() {
        com.freshdesk.mobihelp.Mobihelp.clearUserData(context());
    }
  2. runOnUiThread() - Many of the calls to the FreshDesk API may have been made from the Codename One EDT. However, Android has its own event dispatch thread that should be used for interacting with native Android UI. Therefore, any API calls that look like they initiate some sort of native Android UI process should be wrapped inside Android’s runOnUiThread() method which is similar to Codename One’s Display.callSerially() method. E.g. see the showSolutions() method:

        public void showSolutions() {
            activity().runOnUiThread(new Runnable() {
                public void run() {
                    com.freshdesk.mobihelp.Mobihelp.showSolutions(context());
                }
            });
    
        }

    (Note here that the activity() method is another convenience method to retrieve the app’s current Activity from the AndroidNativeUtil class).

  3. Callbacks. We discussed, in detail, the mechanisms we put in place to enable our native code to perform callbacks into Codename One. You can see the native side of this by viewing the getUnreadCountAsync() method implementation:

        public void getUnreadCountAsync(final int callbackId) {
            activity().runOnUiThread(new Runnable() {
                public void run() {
                    com.freshdesk.mobihelp.Mobihelp.getUnreadCountAsync(context(), new com.freshdesk.mobihelp.UnreadUpdatesCallback() {
                        public void onResult(com.freshdesk.mobihelp.MobihelpCallbackStatus status, Integer count) {
                            MobihelpNativeCallback.fireUnreadUpdatesCallback(callbackId, status.ordinal(), count);
                        }
                    });
                }
            });
    
        }

Step 6: Bundling the Native SDKs

The last step (at least on the Android side) is to bundle the FreshDesk SDK. For Android, there are a few different scenarios you’ll run into for embedding SDKs:

  1. The SDK includes only Java classes - NO XML UI files, assets, or resources that aren’t included inside a simple .jar file. In this case, you can just place the .jar file inside your project’s native/android directory.

  2. The SDK includes some XML UI files, resources, and assets. In this case, the SDK is generally distributed as an Android project folder that can be imported into an Eclipse or Android studio workspace. In general, in this case, you would need to zip the entire directory and change the extension of the resulting .zip file to ".andlib", and place this in your project’s native/android directory.

  3. The SDK is distributed as an .aar file - In this case you can just copy the .aar file into your native/android directory.

The FreshDesk SDK

The FreshDesk (aka Mobihelp) SDK is distributed as a project folder (i.e. scenario 2 from the above list). Therefore, our procedure is to download the SDK (download link), and rename it from mobihelp_sdk_android.zip to mobihelp_sdk_android.andlib, and copy it into our native/android directory.

Dependencies

Unfortunately, in this case there’s a catch. The Mobihelp SDK includes a dependency:

Mobihelp SDK depends on AppCompat-v7 (Revision 19.0+) Library. You will need to update project.properties to point to the Appcompat library.

If we look inside the project.properties file (inside the Mobihelp SDK directory--- i.e. you’d need to extract it from the zip to view its contents), you’ll see the dependency listed:

android.library.reference.1=../appcompat_v7

I.e. it is expecting to find the appcompat_v7 library located in the same parent directory as the Mobihelp SDK project. After a little bit of research (if you’re not yet familiar with the Android AppCompat support library), we find that the AppCompat_v7 library is part of the Android Support library, which can can installed into your local Android SDK using Android SDK Manager. Installation processed specified here.

After installing the support library, you need to retrieve it from your Android SDK. You can find that .aar file inside the ANDROID_HOME/sdk/extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/ directory (for version 19.1.0). The contents of that directory on my system are:

appcompat-v7-19.1.0.aar		appcompat-v7-19.1.0.pom
appcompat-v7-19.1.0.aar.md5	appcompat-v7-19.1.0.pom.md5
appcompat-v7-19.1.0.aar.sha1	appcompat-v7-19.1.0.pom.sha1

There are two files of interest here:

  1. appcompat-v7-19.1.0.aar - This is the actual library that we need to include in our project to satisfy the Mobisdk dependency.

  2. appcompat-v7-19.1.0.pom - This is the Maven XML file for the library. It will show us any dependencies that the appcompat library has. We will also need to include these dependencies:

      <dependencies>
        <dependency>
          <groupId>com.android.support</groupId>
          <artifactId>support-v4</artifactId>
          <version>19.1.0</version>
          <scope>compile</scope>
        </dependency>
      </dependencies>

    i.e. We need to include the support-v4 library version 19.1.0 in our project. This is also part of the Android Support library. If we back up a couple of directories to: ANDROID_HOME/sdk/extras/android/m2repository/com/android/support, we’ll see it listed there:

    appcompat-v7			palette-v7
    cardview-v7			recyclerview-v7
    gridlayout-v7			support-annotations
    leanback-v17			support-v13
    mediarouter-v7			support-v4
    multidex			test
    multidex-instrumentation

    + And if we look inside the appropriate version directory of support-v4 (in ANDROID_HOME/sdk/extras/android/m2repository/com/android/support/support-v4/19.1.0), we see:

    support-v4-19.1.0-javadoc.jar		support-v4-19.1.0.jar
    support-v4-19.1.0-javadoc.jar.md5	support-v4-19.1.0.jar.md5
    support-v4-19.1.0-javadoc.jar.sha1	support-v4-19.1.0.jar.sha1
    support-v4-19.1.0-sources.jar		support-v4-19.1.0.pom
    support-v4-19.1.0-sources.jar.md5	support-v4-19.1.0.pom.md5
    support-v4-19.1.0-sources.jar.sha1	support-v4-19.1.0.pom.sha1

    Looks like this library is pure Java classes, so we only need to include the support-v4-19.1.0.jar file into our project. Checking the .pom file we see that there are no additional dependencies we need to add.

So, to summarize our findings, we need to include the following files in our native/android directory:

  1. appcompat-v7-19.1.0.aar

  2. support-v4-19.1.0.jar

And since our Mobihelp SDK lists the appcompat_v7 dependency path as "../appcompat_v7" in its project.properties file, we are going to rename appcompat-v7-19.1.0.aar to appcompat_v7.aar.

When all is said and done, our native/android directory should contain the following:

appcompat_v7.aar	mobihelp.andlib
com			support-v4-19.1.0.jar

Step 7 : Injecting Android Manifest and Proguard Config

The final step on the Android side is to inject necessary permissions and services into the project’s AndroidManifest.xml file.

We can find the manifest file injections required by opening the AndroidManifest.xml file from the MobiHelp SDK project. Its contents are as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.freshdesk.mobihelp"
    android:versionCode="1"
    android:versionName="1.0" >

     <uses-sdk
        android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application>
        <activity
            android:name="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
            android:configChanges="orientation|screenSize"
            android:theme="@style/Theme.Mobihelp"
            android:windowSoftInputMode="adjustPan" >
        </activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.FeedbackActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:theme="@style/Theme.Mobihelp"
            android:windowSoftInputMode="adjustResize|stateVisible" >
        </activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.InterstitialActivity"
            android:configChanges="orientation|screenSize"
            android:theme="@style/Theme.AppCompat">
		</activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.TicketListActivity"
            android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
            android:theme="@style/Theme.Mobihelp" >

            <!-- Parent activity meta-data to support 4.0 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity" />
        </activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.SolutionArticleActivity"
            android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
            android:theme="@style/Theme.Mobihelp"
            android:configChanges="orientation|screenSize|keyboard|keyboardHidden" >

            <!-- Parent activity meta-data to support 4.0 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity" />
        </activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.ConversationActivity"
            android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
            android:theme="@style/Theme.Mobihelp"
            android:windowSoftInputMode="adjustResize|stateHidden" >

            <!-- Parent activity meta-data to support 4.0 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity" />
        </activity>
        <activity
            android:name="com.freshdesk.mobihelp.activity.AttachmentHandlerActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
            android:theme="@style/Theme.Mobihelp" >

            <!-- Parent activity meta-data to support 4.0 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity" />
        </activity>

        <service android:name="com.freshdesk.mobihelp.service.MobihelpService" />

        <receiver android:name="com.freshdesk.mobihelp.receiver.ConnectivityReceiver" >
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

We’ll need to add the <uses-permission> tags and all of the contents of the <application> tag to our manifest file. Codename One provides the following build hints for these:

  1. android.xpermissions - For your <uses-permission> directives. Add a build hint with name android.xpermissions, and for the value, paste the actual <uses-permission> XML tag.

  2. android.xapplication - For the contents of your <application> tag.

Proguard Config

For the release build, we’ll also need to inject some proguard configuration so that important classes don’t get stripped out at build time. The FreshDesk SDK instructions state:

If you use Proguard, please make sure you have the following included in your project’s proguard-project.txt

-keep class android.support.v4.** { *; }
-keep class android.support.v7.** { *; }

In addition, if you look at the proguard-project.txt file inside the Mobihelp SDK, you’ll see the rules:

-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.app.Activity
-keep public class * extends android.preference.Preference
-keep public class com.freshdesk.mobihelp.exception.MobihelpComponentNotFoundException

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

We’ll want to merge this and then paste them into the build hint android.proguardKeep of our project.

Troubleshooting Android Stuff

If, after doing all this, your project fails to build, you can enable the "Include Source" option of the build server, then download the source project, open it in Eclipse or Android Studio, and debug from there.

Part 2: Implementing the iOS Native Code

Part 1 of this tutorial focused on the Android native integration. Now we’ll shift our focus to the iOS implementation.

After selecting "Generate Native Interfaces" for our "MobihelpNative" class, you’ll find a native/ios directory in your project with the following files:

These files contain stub implementations corresponding to our MobihelpNative class.

We make use of the API docs to see how the native SDK needs to be wrapped. The method names aren’t the same. E.g. instead of a method showFeedback(), it has a message -presentFeedback:

We more-or-less just follow the iOS integration guide for wrapping the API. Some key points include:

  1. Remember to import the Mobihelp.h file in your header file:

    #import "Mobihelp.h"
  2. Similar to our use of runOnUiThread() in Android, we will wrap all of our API calls in either dispatch_async() or dispatch_sync() calls to ensure that we are interacting with the Mobihelp API on the app’s main thread rather than the Codename One EDT.

  3. Some methods/messages in the Mobihelp SDK require us to pass a UIViewController as a parameter. In Codename One, the entire application uses a single UIViewController: CodenameOne_GLViewController. You can obtain a reference to this using the [CodenameOne_GLViewController instance] message. We need to import its header file:

    #import "CodenameOne_GLViewController.h"

    As an example, let’s look at the showFeedback() method:

    -(void)showFeedback{
    
        dispatch_async(dispatch_get_main_queue(), ^{
            [[Mobihelp sharedInstance] presentFeedback:[CodenameOne_GLViewController instance]];
        });
    }

Using the MobihelpNativeCallback

We described earlier how we created a static method on the MobihelpNativeCallback class so that native code could easily fire a callback method. Now let’s take a look at how this looks from the iOS side of the fence. Here is the implementation of getUnreadCountAsync():

-(void)getUnreadCountAsync:(int)param{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[Mobihelp sharedInstance] unreadCountWithCompletion:^(NSInteger count){
            com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(CN1_THREAD_GET_STATE_PASS_ARG param, 3 /*SUCCESS*/, count);
        }];
    });
}

In our case the iOS SDK version of this method is +unreadCountWithCompletion: which takes a block (which is like an anonymous function) as a parameter.

The callback to our Codename One function occurs on this line:

com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(CN1_THREAD_GET_STATE_PASS_ARG param, 3 /*SUCCESS*/, count);

Some things worth mentioning here:

  1. The method name is the result of taking the FQN (com.codename1.freshdesk.MobihelpNativeCallback.fireUpdateUnreadUpdatesCallback(int, int, int)) and replacing all . characters with underscores, suffixing two underscores after the end of the method name, then appending _int once for each of the int arguments.

  2. We also need to import the header file for this class:

    #import "com_codename1_freshdesk_MobihelpNativeCallback.h"

Bundling Native iOS SDK

Now that we have implemented our iOS native interface, we need to bundle the Mobihelp iOS SDK into our project. There are a few different scenarios you may face when looking to include a native SDK:

  1. The SDK includes .bundle resource files. In this case, just copy the .bundle file(s) into your native/ios directory.

  2. The SDK includes .h header files. In this case, just copy the .h file(s) into your native/ios directory.

  3. The SDK includes .a files. In this case, just copy the .a file(s) into your native/ios directory.

  4. The SDK includes .framework files. This is a bit tricker as Codename One doesn’t support simply copying the .framework files inside your project. In this case you need to perform the following:

  5. Right click on the .framework file (if you are using OS X) and select "Show Package Contents".

  6. Find the "binary" file within the framework, and copy it into your native/ios directory - but rename it libXXX.a (where XXX is the name of the binary).

  7. Copy all .h files from the framework into your native/ios directory.

  8. Update all #import statements in the headers from #import <FrameworkName/FileName.h> format to simply #import "FileName.h"

The FreshDesk SDK doesn’t include any .framework files, so we don’t need to worry about that last scenario. We simply download the iOS SDK, copy the libFDMobihelpSDK.a, Mobihelp.h. MHModel.bundle, MHResources.bundle, and MHLocalization/en.proj/MHLocalizable.strings into native/ios.

Troubleshooting iOS

If you run into problems with the build, you can select "Include Sources" in the build server to download the resulting Xcode Project. You can then debug the Xcode project locally, make changes to your iOS native implementation files, and copy them back into your project once it is building properly.

Adding Required Core Libraries and Frameworks

The iOS integration guide for the FreshDesk SDK lists the following core frameworks as dependencies:

iOS link options
Figure 25. IOS link options

We can add these dependencies to our project using the ios.add_libs build hint. E.g.

iOS's
Figure 26. iOS’s "add libs" build hint

I.e. we just list the framework names separated by semicolons. Notice that my list in the above image doesn’t include all of the frameworks that they list because many of the frameworks are already included by default (I obtained the default list by simply building the project with "include sources" checked, then looked at the frameworks that were included).

Part 3 : Packaging as a .cn1lib

During the initial development, I generally find it easier to use a regular Codename One project so that I can run and test as I go. But once it is stabilized, and I want to distribute the library to other developers, I will transfer it over to a Codename One library project. This general process involves:

  1. Create a Codename One Library project.

  2. Copy the .java files from my original project into the library project.

  3. Copy the native directory from the original project into the library project.

  4. Copy the relevant build hints from the original project’s codenameone_settings.properties file into the library project’s codenameone_library_appended.properties file.

In the case of the FreshDesk .cn1lib, I modified the original project’s build script to generate and build a library project automatically. But that is beyond the scope of this tutorial.

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 absolutely based on internal logic.

When we build the layout we need to take margin into consideration and make sure to add it into the position/size calculations. Building a layout manager involves two simple methods: layoutContainer & getPreferredSize.

layoutContainer is invoked whenever Codename One decides the container needs rearranging, Codename One tries to avoid calling this method and only invokes it at the last possible moment. Since this method is generally very expensive (imagine the recursion with nested layouts). Codename One just marks a flag indicating layout is "dirty" when something important changes and tries to avoid "reflows".

getPreferredSize allows the layout to determine the size desired for the container. This might be a difficult call to make for some layout managers that try to provide both flexibility and simplicity.

Most of FlowLayout bugs stem from the fact that this method is just impossible to implement correctly & efficiently for all the use cases of a deeply nested FlowLayout. The size of the final layout won’t necessarily match the requested size (it probably won’t) but the requested size is taken into consideration, especially when scrolling and also when sizing parent containers.

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

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;
    }
}

Form hi = new Form("Center Layout", new CenterLayout());
for(int iter = 1 ; iter < 10 ; iter++) {
    Label l = new Label("Label: " + iter);
    l.getUnselectedStyle().setMarginLeft(iter * 3);
    l.getUnselectedStyle().setMarginRight(0);
    hi.add(l);
}
hi.add(new Label("Really Wide Label Text!!!"));
hi.show();
Center layout staircase effect with margin
Figure 27. Center layout staircase effect with margin

Porting a Swing/AWT Layout Manager

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

  1. Codename One doesn’t have Insets, we added some support for them in order to port GridBag but components in Codename One have a margin they need to consider instead of the Insets (the padding is in the preferred size and is thus hidden from the layout manager).

  2. AWT layout managers also synchronize a lot on the AWT thread. This is no longer necessary since Codename One is single threaded, like Swing.

  3. AWT considers the top left position of the Container to be 0,0 whereas Codename One considers the position based on its parent Container. The top left position in Codename One is getX(), getY().

Other than those things it’s mostly just fixing method and import statements, which are slightly different. Pretty trivial stuff.


1 The old Codename One VM