Push Notifications

This chapter discusses push support in Codename One applications. It covers how to set up push on the various platforms, how to respond to push notifications in your app, and how to send push notifications to your app.

Push notifications require a Codename One Pro account or higher. You must register your app to receive push notification using Display.getInstance().registerPush();

Understanding Push Notifications

Push notifications provide a way to inform users that something of interest has taken place. It is one of the few mechanisms available to interact with the user even when the app isn’t running. If your app is registered to receive push notifications, then you can send messages to the user’s device via REST API either from another device, or, as is usually the case, from a web server.

Messages may contain a short title and text body that will be displayed to the user in their device’s messages stream. They may also specify a badge to display on the app’s icon (e.g. that red circle on your mail app icon that indicates how many unread messages you have), a sound to play then the message arrives, an image attachment, and a set of "actions" that the user can perform directly in the push notification.

In addition to messages that the user sees, a push notification can contain non-visual information that is silently sent to your app.

Implementing Push Support

Enabling push support in your application is just a matter of implementing the PushCallback interface in your application’s main class (i.e. the class that includes your start(), init(), etc…​ methods.

public class MyApplication implements PushCallback {

    // ....

    /**
     * Invoked when the push notification occurs
     *
     * @param value the value of the push notification
     */
    public void push(String value) {
        System.out.println("Received push message: "+value);
    }

    /**
     * Invoked when push registration is complete to pass the device ID to the application.
     *
     * @param deviceId OS native push id you should not use this value and instead use <code>Push.getPushKey()</code>
     * @see Push#getPushKey()
     */
    public void registeredForPush(String deviceId) {
        System.out.println("The Push ID for this device is "+Push.getPushKey());
    }

    /**
     * Invoked to indicate an error occurred during registration for push notification
     * @param error descriptive error string
     * @param errorCode an error code
     */
    public void pushRegistrationError(String error, int errorCode) {
        System.out.println("An error occurred during push registration.");
    }
}

There will be additional steps required to deploy to each platform (e.g. iOS requires you to generate push certificates, Android needs you to register your app ID with their cloud messaging platform, etc…​), but, fundamentally, this is all that is required to enable push support in your app.

The Push Lifecycle

Let’s take minute to go over the three callbacks in our application.

Registration

When your application first opens, it needs to register with the platform’s central cloud messaging infrastructure. On Android this involves a call to their GCM/FCM server; on iOS, the call will be to the APNS server, on Windows (UWP) the call will be to the WNS server. And so on. That server will return a unique device ID that can be used to send push notifications to the device. This device ID will then be passed to your registeredForPush() method as the deviceId parameter so that you can save it somewhere. Typically you would send this value to your own web service so that you can use it to send notifications to the device later on. The device ID will generally not change unless you uninstall and reinstall the app, but you will receive the callback every time the app starts.

The deviceId parameter cannot be used directly when sending push messages via the Codename One push service. It needs to be prefixed with a platform identifier so the that push server knows which messaging service to route the message through. You can obtain the complete device ID, including the platform prefix, by calling Push.getPushKey()

If the registration failed for some reason, the the pushRegistrationError() callback will be fired instead.

Notice that all of this happens seamlessly behind the scenes when your app loads. You don’t need to initiate any of this workflow.

Sending a Push Notification

Codename One provides a secure REST API for sending push notifications. As an HTTP API, it is language agnostic. You can send push notifications to your app using Java, PHP, Python, Ruby, or even by hand using something like curl. Each HTTP request can contain a push message and a list of device IDs to which the message should be sent. You don’t need to worry about whether your app is running on Android, iOS, Windows, or the web. A single HTTP request can send a message to many devices at once.

Receiving a Push Notification

There are two different scenarios to be aware of when it comes to receiving push notifications. If your app is running in the foreground when the message arrives, then it will be passed directly to your push() callback. If your app is either in the background, or not running, then the notification will be displayed in your device’s notifications. If the user then taps the notification, it will open your app, and the push() callback will be run with the contents of the message.

Some push message types include hidden content that will not be displayed in your device’s notifications. These hidden messages (or portions of messages) are passed directly to the push() callback of your app for processing.

On iOS, hidden push messages (push type 2) will not be delivered when the app is in the background.

Testing Push Support

The easiest way to test push notifications in your app is to use the push simulation feature of the Codename One Simulator.

Push simulation menu item
Figure 1. The "Push Simulation" menu item in the simulator opens the push simulator tool.

The "Registered Successfully" button will trigger your app’s registeredForPush() method, and the "Registration Error" button will trigger your app’s pushRegistrationError() method.

The "Send" button will trigger the push() callback with the message body that you place in the "Send Message" field.

The "Push Type" drop-down allows you to select the "type" of the push message. This dictates how the message body (i.e. the contents of the "Send Message" field) is interpreted. Some push types simply pass the message to the device verbatim, while others assume that the message contains structure that is meant to be parsed by the client to extract such things as badges, sounds, images, and actions that are associated with the message. We’ll go over the available push types in a moment, but for now, we’ll keep it simple by just using a push type of "1" - which just sends the message verbatim.

Sending a basic Hello World push
Figure 2. Sending a basic hello world push from the push simulator

Let’s try a simple "hello world" push message. Select "1" from the "Push Type" drop-down menu, and enter "Hello World" into the "Send Message" field as shown above. Then press "send".

Assuming your push() method looks like:

public void push(String value) {
    System.out.println("Received push message: "+value);
}

You should see the following output in your console:

Received push message: Hello World

This experiment simulated a push notification while the app is running in the foreground. Now let’s simulate the case where the app is not running, or running in the background. We can simulate this by pausing the app. Return to the Codename One simulator window, and select "Pause App" from the "Simulate" menu as shown below.

Pausing app in the simulator
Figure 3. Pausing the app in the simulator so we can simulate push notifications while app is in the background.

When the app is paused it will simply display a white screen in the simulator with the text "Paused" in the middle.

Now return to the push simulator again, and press "Send" again with same values in the other fields (Push type 1, and Message "Hello World"). Rather than running the push() callback this time, it will display a popup dialog outside the app, as shown below.

Push popup when app paused
Figure 4. Push message causes a popup dialog in the simulator when the app is paused.

While this popup dialog doesn’t replicate what a push notification will look like in a device’s notifications stream when the app is closed, it does simulate the conceptual workflow. The process whereby the user is notified of the message outside of the app, and the app is not notified until/unless the user taps on the notification.

If you monitor the console for your app, you should notice that the push() callback hasn’t been called yet for this notification, but if you click "OK" in the dialog, your push() callback will be run. Clicking OK is analogous to the user tapping on the notification. If you simply close the dialog box (by clicking the "x" in the corner), this would be analogous to the user dismissing the notification. In this case the push() callback would not be called at all.

Push Types and Message Structure

The example above sends a simple message to be displayed to the user. Push notifications can include more data than just an alert message, though. When the selected "push type" is 0 or 1, the push server will interpret the message body a simple alert string. Selecting a different push type will affect how the message body is interpreted. The following push types are supported:

  • 0, 1 - The default push types, they work everywhere and present the string as the push alert to the user

  • 2 - hidden, non-visual push. This won’t show any visual indicator on any OS!
    In Android this will trigger the push(String) call with the message body. In iOS this will only happen if the application is in the foreground otherwise the push will be lost

  • 3 - 1 + 2 = 3 allows combining a visual push with a non-visual portion. Expects a message in the form: This is what the user won’t see;This is something he will see. E.g. you can bundle a special ID or even a JSON string in the hidden part while including a friendly message in the visual part.
    When active this will trigger the push(String) method twice, once with the visual and once with the hidden data.

  • 4 - Allows splitting a visual push request based on the format title;body to provide better visual representation in some OS’s.

  • 5 - Sends a regular push message but doesn’t play a sound when the push arrives

  • 99 - The message body is expected to be XML, where the root element contains at least type and body attributes which correspond to one of the other push push types and message body respectively. This push type supports additional information such as image attachments and push actions. E.g. <push type="1" body="Hello World"/>

  • 100 - Applicable only to iOS and Windows. Allows setting the numeric badge on the icon to the given number. The body of the message must be a number e.g. unread count.

  • 101 - identical to 100 with an added message payload separated with a space. E.g. 30 You have 30 unread messages will set the badge to "30" and present the push notification text of "You have 30 unread messages".

The following sections will show examples of the various kinds of pushes. You can try them out yourself by opening the push simulator.

Example Push Type 1

Push Type 1; Message Body: "Hello World"

Push popup when app paused
Figure 5. Push type 1 "Hello World" message in simulator.
Push type 1 on Android
Figure 6. Push type 1 "Hello World" message in Android when app is in background.
Push type 1 on iOS
Figure 7. Push type 1 "Hello World" message in iOS when app is in background.
Push type 1 on Chrome desktop
Figure 8. Push type 1 "Hello World" message in Chrome desktop.

In all cases, if the user taps/clicks the notification, it will bring the app to the foreground and call the push() callback with "Hello World" as the argument.

Example Push Type 2

Push Type 2; Message Body: "Hello World"

Push type 2 is a hidden push so it will behave differently on different platforms. On Android, the push() callback will be fired even if the app is in the background. On iOS, it will simply be ignored if the app is in the background.

If the app is in the foreground, this will trigger the push() callback with "Hello World" as the argument.

You can determine the the type of push that has been received in your push() callback by calling Display.getInstance().getProperty("pushType"). This will return a String of the push type. E.g. in this case Display.getInstance().getProperty("pushType") will return "2".

Example Push Type 3

Push Type 3; Message Body Hello World;{"from":"Jim", "content":"Hello World"}

Push type 3 combines an alert message with some hidden content that the user won’t see. In the example above, the alert message is "Hello World" and the hidden content is a JSON string that will be passed to our app to be parsed.

If the app is in the background, then the alert message will be posted to the user’s notifications. See "Example Push Type 1" above as this message will be identical.

Push type 3
Figure 9. Push type 3 shows only the alert message (the portion before the first ";").

This push will result in our push() callback being fired twice; once with the alert message, and once with the hidden content. When it is fired with the alert message, Display.getInstance().getProperty("pushType") will report a type of "1". When it is fired with the JSON hidden content, it will report a push type of "2".

Example Push Type 4

Push Type 4; Message Body "Hello World;I’m just saying hello"

Push type 4 specifies a title and a message body. In this example, alert title will be "Hello World", and the body will by "I’m just saying hello".

Push popup when app paused
Figure 10. Push type 4 "Hello World" message in simulator.
Push type 4 on Android
Figure 11. Push type 4 "Hello World" message in Android when app is in background.
Push type 4 on iOS
Figure 12. Push type 4 "Hello World" message in iOS when app is in background.
Push type 4 on Firefox desktop
Figure 13. Push type 4 "Hello World" message in Firefox desktop.

With this push type, the push() callback will be fired only if the user taps/opens the notification, and the argument will contain the entire message ("Title;Body").

On some platforms, the argument of the push() callback will only include the "body" portion of the payload, and in other platforms it will include the full "Title;Body" payload.

Example Push Type 5

Push Type 5; Message Body "Hello World"

Push type 5 will behave identically to push type 1, except that the notification won’t make any sound on the device. On some platforms, Display.getInstance().getProperty("pushType") will report a push type of "1", when it receives a push of type 5.

Example Push Type 100

Push Type 100; Message Body "5"

Push type 100 just expects an integer in the message body. This is interpreted as the badge that should be set on the app. This is currently only supported on Windows and iOS.

Push type 100 with badge set to 5 on iOS
Figure 14. Push type 100 on iOS, setting the badge to "5"

Push type 100 should not trigger the push() callback.

Example Push Type 101

Push Type 101; Message Body "5 Hello World"

Push type 101 combines a badge with an alert message. The badge number should be the first thing in the payload, followed by a space, and the remainder is the alert message.

On platforms that do not support badges (e.g. Android), Push type 101 will behave exactly as push type 1, except that the badge prefix will be stripped from the message.

The push() callback will be called only if the user taps the notification. Display.getInstance().getProperty("pushType") will return "1" for this type.

Badging on iOS

The badge number can be set thru code as well, this is useful if you want the badge to represent the unread count within your application.

To do this we have two methods in the Display class: isBadgingSupported() and setBadgeNumber(int). Notice that even if isBadgingSupported will return true, it will not work unless you activate push support!

To truly utilize this you might need to disable the clearing of the badges on startup which you can do with the build hint ios.enableBadgeClear=false.

Rich Push Notifications

Rich push notifications refer to push notifications that include functionality above and beyond simply displaying an alert message to the user. Codename One’s support for rich notifications includes image attachments and push actions.

Image Attachment Support

When you attach an image to a push notification, it will appear as a large image in the push notification on the user’s device if that device supports it. iOS supports image attachments in iOS 10, Android supports them in API 26. The Javascript port, and Windows (UWP) port do not currently support image attachments. If a platform that doesn’t support image attachments receives a push notification with an image attachment, it will just ignore it.

Push type "99" is used to send rich push notifications. It is sort of a "meta" push type, or a "container", as it can be used to send any of the other push types, but to attach additional content, such as image attachments.

The message body should be an XML string. A minimal example of a push type 99 that actually sends a push type 1, which message "Hello World", but with an attached image is:

<push type="1" body="Hello World"><img src="https://exmaple.com/myimage.jpg"/></push>
The image URL must be a secure URL (i.e. start with "https:" and not "http:", otherwise, iOS will simply ignore it.
Push popup when app paused
Figure 15. Push type 99 with attached image in simulator.
Push type 99 on Android with attached image
Figure 16. Push type 99 with attached image in Android when app is in background.
Push type 99 on iOS with attached image
Figure 17. Push type 99 with attached image in iOS when app is in background.
The image will only be shown if you press and pull down on the notification. When the notification initially appears in the user’s notifications it will appear like a normal alert - but possibly with the image shown as a small thumbnail.

The push() callback will receive "Hello World" as its argument and Display.getInstance().getProperty("pushType") will return "1" in this example.

You can access additional information about the push content using the com.codename1.push.PushContent class, as follows:

public void push(String message) {
    PushContent content = PushContent.get();
    if (content != null) {
        String imageUrl = content.getImageUrl();
            // The image attachment URL in the push notification
            // or `null` if there was no image attachment.
    }
}
Make sure to only call PushContent.get() once inside your push() callback, and store the return value. PushContent.get() works like a queue of size=1, and it pops off the item from the front of the queue when it is called. If you call it twice, the second time will return null.

Notification Actions

When you include actions in a push notification, the user will be presented with buttons as part of the notification on supported platforms. E.g. if the notification is intended to invite the user to an event, you might want to include buttons/actions like "Attending", "Not Attending", "Maybe", so that the user can respond quickly to the notification and not necessarily have to open your app.

You can determine whether the user has pressed a particular button on the notification using the `PushContent.getActionId()` method inside your `push()` callback.

How it works

Your app defines which action categories it supports, and associates a set of actions with each category. If a push notification includes a "category" attribute, then the notification will be presented with the associated actions manifested as buttons.

Defining the Categories and Actions

You can specify the available categories and actions for your app by implementing the com.codename1.push.PushActionsProvider interface in your app’s main class.

E.g.

import com.codename1.push.PushAction;
import com.codename1.push.PushActionCategory;
import com.codename1.push.PushActionsProvider;
...

public class MyApplication implements PushCallback, PushActionsProvider {

    ...

    @Override
    public PushActionCategory[] getPushActionCategories() {
        return new PushActionCategory[]{
            new PushActionCategory("invite", new PushAction[]{
                new PushAction("yes", "Yes"),
                new PushAction("no", "No"),
                new PushAction("maybe", "Maybe")
            })

        };
    }
}

In the above example, we create only a single category, "invite" that has actions "yes", "no", and "maybe".

Sending a Push Notification with "invite" Category

Now we can test our new category. In the push simulator, you can select Push Type "99", with the message body:

<push type="1" body="Would you like to come to a party?" category="invite"/>
Push with actions in simulator
Figure 18. Push notification with "invite" category on the simulator will show dialog with buttons to select between the actions defined in the "invite" category.
Push with actions on Android
Figure 19. Push notification with "invite" category on the android will show dialog with buttons to select between the actions defined in the "invite" category.
Push with actions on iOS
Figure 20. Push notification with "invite" category on the android will show dialog with buttons to select between the actions defined in the "invite" category.
Push with actions on Chrome Desktop
Figure 21. Push notification with "invite" category on the Chrome desktop includes a "More" dropdown where user can select the action.

The push() callback will be fired after the user taps on the notification, or one of its actions. If the user taps the notification itself, and not one of the actions, then your PushContent.getActionId() will return null. If they selected one of the actions, then the action ID of that action can be obtained from getActionId().

The category of the notification will be made available via the getCategory() method of PushContent.

E.g.

public void push(String message) {
    PushContent content = PushContent.get();
    if (content != null) {
        if ("invite".equals(content.getCategory())) {
            if (content.getActionId() != null) {
                System.out.println("The user selected the "+content.getActionid()+" action");
            } else {
                System.out.println("The user clicked on the invite notification, but didn't select an action.");
            }
        }
    }
}

Deploying Push-Enabled Apps to Device

So, you’ve implemented the Push callback in your app, and tested it in the push simulator and it works. If you try to deploy your app to a device, you may be disappointed to discover that your app doesn’t seem to be receiving push notifications when installed on the device. This is because each platform has its own bureaucracy and hoops that you have to jump through before they will deliver notifications to your app. Read on to find out how to satisfy their requirements.

The Push Bureaucracy - Android

To set the push icon place a 24x24 icon named ic_stat_notify.png under the native/android folder of the app. The icon can be white with transparency areas

Android Push goes thru Google servers and to do that we need to register with Google to get keys for server usage. Google uses Firebase for its cloud messaging, so we’ll begin by creating a Firebase project.

Go to https://console.firebase.google.com/ and click Add project:

Click
Figure 22. Click "Add project"

This will open a form as shown here:

Enter project name
Figure 23. Enter project name

Enter the project name, select your country, read/accept their terms, and press "Create Project".

Once the project has been created (should take only a few seconds), you’ll be sent to your new project’s dashboard.

Firebase project dashboard
Figure 24. Firebase Project Dashboard

Expand the "Grow" section of the left menu bar, then click on the "Cloud Messaging" link.

Expand Grow section and select Cloud Messaging
Figure 25. Expand "Grow" Section and select "Cloud Messaging"

On the next screen, click on the Android icon where is says "Add an app to get started".

Click on the Android icon
Figure 26. Click on the "Android" icon to add an Android App to the project

This will bring us to the "Add Application Form", which visually shows us the remainder of the steps.

Fill in the Android package name with the package name of your project, and the app nickname with your app’s name.

The Debug signing certificate SHA-1 is optional, but you can paste the SHA-1 from your app’s certificate here if you like.

Fill in the package name
Figure 27. Fill in the package name

Press "Register app" once you have filled in the required fields.

This will expand "Step 2" of this form: "Download config file".

Download google-services.json
Figure 28. Download the google-services.json file

All we need to do here is press the "Download google-services.json" file, then copy the file into your project’s native/android directory.

Firebase console directs you to copy the google-services.json file into the "app" directory of your project. Ignore this direction as it only applies for Android studio projects. For Codename One, this file goes into the native/android directory of your project.

There is one last piece of information that we need so that we can send push notifications to our app: The FCM_SERVER_API_KEY value.

Go to your project dashboard in Firebase console. Then click the "Settings" menu (the "Gear" icon next to "Project Overview" in the upper left):

Press Project Settings
Figure 29. Select "Project settings"

Then select the "Cloud Messaging" tab.

The "Server Key" displayed here is the FCM_SERVER_API_KEY that we refer to throughout this document. It will be used to send push notifications to your app from a server, or from another device. You can copy and paste this value now, or you can retrieve it later by logging into the Firebase console.

Save the Server Key for later use
Figure 30. Save the Server key for later use.
The Sender ID shown in the above is not required for our Android app, however it it is helpful/required to support Push notifications in Javascript builds (in Chrome). This value is referred to elsewhere in this document as GCM_SENDER_ID.

The Push Bureaucracy - iOS

Push on iOS is much harder to handle than the Android version, however we simplified this significantly with the certificate wizard.

iOS push needs two additional P12 certificates.

Please notice that these are NOT the signing certificates!
They are completely different certificates that have nothing to do with the build process!

The certificate wizard can generate these additional push certificates and do quite a few other things if you just check this flag in the end of the wizard:

Enable Push in the certificate wizard
Figure 31. Enable Push in the certificate wizard
If you already have signing certificated defined for your app just skip the certificate generation phase (answer no) the rest will work as usual.

You can then install the push certificates locally and use them later on but there is an easier way.

You need to host the push certificates in the cloud so they will be reachable by the push servers, Codename One that for you seamlessly.
Once you go thru the wizard you should get an automated email containing information about push and the location of the push certificates, it should start like this:

iOS Push certificates have been created for your app with bundle ID com.mycompany.myapp.mypushdemo.  Please file this email away for safe keeping as you will need the details about the certificate locations and passwords to use Push successfully in your apps.

Development Push Certificate URL:
https://codename-one-push-certificates.s3.amazonaws.com/com.mycompany.myapp.mypushdemo_DevelopmentPush_LONG_UNIQUE_KEY.p12

Development Push Certificate Password: ssDfdsfer324

Production Push Certificate URL:
https://codename-one-push-certificates.s3.amazonaws.com/com.mycompany.myapp.mypushdemo_ProductionPush_LONG_UNIQUE_KEY.p12

Production Push Certificate Password: ssDfdsfer324

The URL’s and passwords are everything that you will need later on to get push working on iOS. Notice that the wizard also performs a couple of other tasks specifically it sets the ios.includePush build hint to true & adds push to the provisioning profile etc.

You can read more about the certificate wizard in the signing section.

The Push Bureaucracy - UWP (Windows 10)

Push on UWP requires only that you register your app in the Windows Store Dashboard. You will then be provided with credentials (Package Security Identifier (SID) and a secret key) that you can use to send push notifications to your app. To begin this process, go to the Windows Dev Center and select "Dashboard".

You can read more about the registering your app in the Windows store here.

Once you have registered your app in the Windows Store, and completed the corresponding setup in Codename One settings (e.g. generated a certificate), you should proceed to configure your app for push notifications.

Navigate to the App overview page for your app inside the Windows store dashboard. Under the "Services" menu (left side), select "Push notifications".

Push notifications menu item in Windows Store dashboard
Figure 32. Push notifications menu item in Windows Store dashboard

Then, select the "WNS/MPNS" option that appears in the left menu under "Push notifications"

WNS menu item in Windows Store dashboard
Figure 33. WNS menu item in Windows Store dashboard

This will bring you to a page with information about WNS push notifications. You’ll be interested in the paragraph shown here:

Live services link
Figure 34. Live services link

Click on the "Live Services Site" link.

You’ll be prompted to log in using your Windows Store account. Then you’ll be taken to a page that contains the push credentials that you can use for sending push messages to your app. You’ll be interested in two values:

Package SID. (It will be of the form "ms-app://XXXXXXXXXXX…​")

WNS Package SID
Figure 35. WNS Package SID

Client Secret. This will be listed in a section called "Application Secrets"

WNS Client secret
Figure 36. WNS Client secret

You will use these two values for sending push notifications to your app.

Microsoft provides full instructions on setting up WNS notifications here but much of this is not relevant for Codename One apps. For Codename One apps, one need only obtain Package Security ID and client secret values for the app.

The Push Bureaucracy - Javascript

Codename One apps support push in browsers that implement the Web Push API. At time of writing, this list includes:

  • Firefox (Version 50)

  • Chrome (Version 49)

  • Opera (Version 42)

  • Chrome for Android (Version 56)

  • MS Edge

Firefox doesn’t require any special setup for Push. If your main class implements the PushCallback interface, it should just work.

Chrome uses FCM for its push notifications - the same system that Android uses. The directions for setting up a FCM account are the same as provided here, and you can reuse the same GCM_SENDER_ID and FCM_API_SERVER_KEY values. For Chrome push support you will need to add the GCM_SENDER_ID in the gcm.sender_id build hint so that the GCM_SENDER_ID will be added to the app’s manifest file:

gcm.sender_id=GCM_SENDER_ID

Where GCM_SENDER_ID is your GCM_SENDER_ID.

Push support requires that your app be served over https with a valid SSL certificate. It will not work with the "preview" version of your app. You’ll need to download the .zip or .war file and host the file on your own site - with a valid SSL certificate.

Sending Push Messages

You can send a push message in many ways e.g. from another device or any type of server but there are some values that you will need regardless of the way in which you send the push message.

private static final String PUSH_TOKEN = "********-****-****-****-*************";

The push token is a unique "key" that you can use to send push thru your Codename One account. It allows you to send push messages without placing your Codename One email or password into your source files.

You can get it by going to the Codename One build server dashboard at https://www.codenameone.com/build-server.html and selecting the Account tab.

The token should appear at the bottom as such:

Push Token from the build server
Figure 37. Push Token from the build server

The instructions for extracting the API key for Google are listed above.

private static final String FCM_SERVER_API_KEY = "******************-********************";

The instructions for extracting the SID and Client Secret for Windows are listed above.

private static final String WNS_SID = "ms-app://**************************************";
private static final String WNS_CLIENT_SECRET = "*************************";

When sending push to iOS devices we have two modes: - Production - Distribution

This allows you to debug the push related functionality without risking the possibility of sending a push into a production app. Its important to send the values to the right server during development/production.

private static final boolean ITUNES_PRODUCTION_PUSH = false;

iOS needs a certificate in order to send a push, this allows you to prove to Apples push servers that you are who you claim to be (the author of the app).

These are not the signing certificates and are completely separate from them!

You can obtain these two certificates (for development/appstore) via the certificate wizard as explained above.

private static final String ITUNES_PRODUCTION_PUSH_CERT = "https://domain.com/linkToP12Prod.p12";
private static final String ITUNES_PRODUCTION_PUSH_CERT_PASSWORD = "ProdPassword";
private static final String ITUNES_DEVELOPMENT_PUSH_CERT = "https://domain.com/linkToP12Dev.p12";
private static final String ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD = "DevPassword";

Sending a Push Message From Codename One

While normally sending a push message to a device should involve a server code there might be cases (e.g. instant messaging/social) where initiating a push from one client to another makes sense.

To simplify these use cases we added the Push API. To use the Push API you need the device key of the destination device to which you want to send the message. You can get that value from the Push.getPushKey() method. Notice that you need that value from the destination device and not the local device!

To send a message to another device just use:

String cert = ITUNES_DEVELOPMENT_PUSH_CERT;
String pass = ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD;
if(ITUNES_PRODUCTION_PUSH) {
    cert = ITUNES_PRODUCTION_PUSH_CERT;
    pass = ITUNES_PRODUCTION_PUSH_CERT_PASSWORD;
}
new Push(PUSH_TOKEN, "Hello World", deviceKey)
    .apnsAuth(cert, pass, ITUNES_PRODUCTION_PUSH)
    .gcmAuth(FCM_SERVER_API_KEY)
    .wnsAuth(WNS_SID, WNS_CLIENT_SECRET)
    .send();

The "builder" style API used in the above sample was added post Codename One 3.6 to facilitate the addition of new Push services. If you are building against Codename one 3.6 or earlier, you should use the static Push.sendPushMessage() instead as shown below:

Push.sendPushMessage(PUSH_TOKEN, "Hello World",
         ITUNES_PRODUCTION_PUSH, FCM_SERVER_API_KEY, cert, pass, 1, deviceKey));

This will send the push message "Hello World" to the device with the key deviceKey. The 1 argument represents the standard push message type, which we discussed previously.

Sending Push Message From A Java or Generic Server

Sending a push message from the server is a more elaborate affair and might require sending push messages to many devices in a single batch.

We can send a push message as an HTTP POST request to https://push.codenameone.com/push/push. That URL accepts the following arguments:

  • token - your developer token to identify the account sending the push - PUSH_TOKEN

  • device - one or more device keys to send the push to. You can send push to up to 500 devices with a single request -

  • type - the message type identical to the old set of supported types in the old push servers

  • body - the body of the message

  • auth - the Google push auth key - FCM_SERVER_API_KEY (also used for sending to Chrome Javascript Apps)

  • production - true/false whether to push to production or sandbox environment in iOS - ITUNES_PRODUCTION_PUSH

  • certPassword - password for the push certificate in iOS push - ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD or ITUNES_PRODUCTION_PUSH_CERT_PASSWORD

  • cert - http or https URL containing the push certificate for an iOS push - ITUNES_DEVELOPMENT_PUSH_CERT or ITUNES_PRODUCTION_PUSH_CERT

  • sid - The package security ID (SID) for UWP apps.

  • client_secret - The client secret for UWP apps.

We can thus send a push from Java EE using code like this:

HttpURLConnection connection = (HttpURLConnection)new URL("https://push.codenameone.com/push/push").openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
String cert = ITUNES_DEVELOPMENT_PUSH_CERT;
String pass = ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD;
if(ITUNES_PRODUCTION_PUSH) {
    cert = ITUNES_PRODUCTION_PUSH_CERT;
    pass = ITUNES_PRODUCTION_PUSH_CERT_PASSWORD;
}
String query = "token="  + PUSH_TOKEN +
    "&device=" + URLEncoder.encode(deviceId1, "UTF-8") +
    "&device=" + URLEncoder.encode(deviceId2, "UTF-8") +
    "&device=" + URLEncoder.encode(deviceId3, "UTF-8") +
    "&type=1" +
    "&auth=" + URLEncoder.encode(FCM_SERVER_API_KEY, "UTF-8") +
    "&certPassword=" + URLEncoder.encode(pass, "UTF-8") +
    "&cert=" + URLEncoder.encode(cert, "UTF-8") +
    "&body=" + URLEncoder.encode(MESSAGE_BODY, "UTF-8") +
    "&production=" + ITUNES_PRODUCTION_PUSH +
    "&sid=" + URLEncoder.encode(WNS_SID, "UTF-8") +
    "&client_secret=" + URLEncoder.encode(WNS_CLIENT_SECRET, "UTF-8");
try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes("UTF-8"));
}
int c = connection.getResponseCode();
// read response JSON

Notice that you can send a push to 500 devices. To send in larger batches you need to split the push requests into 500 device batches.

Server JSON Responses

The push servers send responses in JSON form. It’s crucial to parse and manage those as they might contain important information.

If there is an error that isn’t fatal such as quota exceeded etc. you will get an error message like this:

{"error":"Error message"}

A normal response, will be an array with results:

[
   {"id"="deviceId","status"="error","message"="Invalid Device ID"},
   {"id"="cn1-gcm-nativegcmkey","status"="updateId", "newId"="cn1-gcm-newgcmkey"},
   {"id"="cn1-gcm-okgcmkey","status"="OK"},
   {"id"="cn1-gcm-errorkey","status"="error","message"="Server error message"},
   {"id"="cn1-ios-iphonekey","status"="inactive"},
]

There are several things to notice in the responses above:

  • If the response contains status=updateId it means that the GCM server wants you to update the device id to a new device id. You should do that in the database and avoid sending pushes to the old key

  • iOS doesn’t acknowledge device receipt but it does send a status=inactive result which you should use to remove the device from the list of devices

APNS (Apple’s push service) returns uppercase key results. This means that code for managing the keys in your database must be case insensitive
Apple doesn’t always send back a result for a device being inactive and might fail silently