Friday 25 March 2016

Android/CloudBoost: Push Notifications With JavaSDK and GCM (Google play services)

NB: you can now read this post at my new site: http://www.egimaben.com/androidcloudboost-push-notifications-with-javasdk-and-gcm-google-play-services/
CloudBoost has made several new developments to its ecosystem in the past few weeks. Some of which include:

  1. Swagger API documentation: getting your way around the raw REST API for CloudBoost is no trivial task. This is no surprise considering the complexity that has been abstracted away for the developer by this BaaS. This is why there is need for the Software Development Kits in different languages to further hide this complexity. However, for the hackers that want to dig in and inspect Request Bodies and Responses, the Swagger-UI was launched on 15th March,2016.
  2. Swagger Codegen. Extending from the Swagger specification, CloudBoost is one of the companies that has quickly adopted the very powerful tool Swagger-codegen. There is a full list of companies using the tool here. Apart from the homegrown SDKs for C#, Java and Javascript which are already in use by date of posting, one can now obtain an autogenerated, lighter version of all SDKs in over 18 other languages available here.
  3. A newsletter was sent out on 21st March, 2016 announcing the release of the much anticipated C# SDK available on nuget.
In all these newsletters, an upcoming feature is the Push notifications that has been in so much demand especially for developers that are used to the feature in Parse. Probably in 2 weeks time or less, if you are subscribed to CloudBoost newsletter, you should be receiving one officially announcing the Push Notification feature. So this is not an official post, there could be some changes by the date of official release. Watch out of JavaSDK-1.0.5 official release.


Push Notification in CloudBoost

If you are new to the concept of Push notifications in Android using Google Cloud Messaging, please check out this tutorials to get upto speed. Though it uses deprecate libraries (Gcm.jar library), its basic concepts are still the same today as we use Google Play services library.

Usually if you are implementing Push notifications, all tutorials advise on having 3 main components:
  1. Device. your android or iOS phone the receives the notifications
  2. GCM Connection Servers. These servers receive and send notifications to devices that are already registered with them. They manage queueing of the same.
  3. Your Server. This is an implementation of your own backend server that is responsible for creating and pushing messages to GCM Connection Servers which in-turn pushes the messages to all devices registered to it's Sender-ID. 
CloudBoost Push notification API eliminates the third component for you the developer. In order for us to focus on the main aim of this tutorial, we shall use the official google quick start android project that demonstrates this feature. Please clone it from Google's github page and study how it works with its documentation. We are using Eclipse with ADT.

How it works

  1. You create an app from Google Developer console. The result of this is you obtaining a Sender ID/Project Number for your app and an API key.
  2. You store these credentials (SenderID and API key) on your server for later use.
  3. Your android app possesses your  SenderID and uses it to obtain a deviceToken/registrationId from the GCM Connection Servers using InstanceID API. This is most times a one time operation.
  4. Your android app registers itself, using deviceToken from step 3 above as a recipient of push notifications from your Server .
  5. You start publishing notifications.

Obtaining Credentials

For each device, first get a registration token for Google Cloud Messaging from the InstanceID API. This token is used to identify the instance of the quickstart running on your device. Retrieve the token using an IntentServiceas shown. Because this may result in a network connection, make sure it is executed off the UI thread.



public class RegistrationIntentService extends IntentService {
    // ...

    @Override
    public void onHandleIntent(Intent intent) {
        // ...
        InstanceID instanceID = InstanceID.getInstance(this);
        String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
                GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
        sendRegistrationToServer(token);


        // ...
    }

    // ...
}

Save Credentials to CloudBoost(replaces your server) 

The method call sendRegistrationToServer(token) above saves the credentials we fetched from GCM connection servers to CloudBoost.
With the Launch of Push Notifiations, an extra default table called Device is added onto the already existing Role and User tables. This table stores all devices that should subscribe to receive push notifications.
So starting with JavaSDK-1.0.5 we have the all new CloudPush API, with send and addDevice methods.
  1. Send: sends a message to you CloudBoost app to be broadcast to devices that have installed your android app.
  2. addDevice: subscribes every new instance of your android app to your server's push notifications
so the process of saving credentials to CloudBoost is simply a call to
addDevice(String token,String timezone,String[] channels, String appname,final CloudObjectCallback callback)
Here is my code inside the method sendRegistrationToServer which we already looked at

 private void sendRegistrationToServer(String token) {
  // Add custom implementation, as needed.
  // cloudboost code goes here for saving devicetoken
  final SharedPreferences sharedPreferences = PreferenceManager
    .getDefaultSharedPreferences(this);
  try {
   CloudPush push = new CloudPush();
   push.addDevice(token, "East Africa", new String[] { "shakers",
     "movers" }, "cloudboost-1258", new CloudObjectCallback() {
    @Override
    public void done(CloudObject arg0, CloudException arg1)
      throws CloudException {
     if (arg0 != null) {
      sharedPreferences
        .edit()
        .putBoolean(
          QuickstartPreferences.SENT_TOKEN_TO_SERVER,
          true).apply();
      sharedPreferences.edit().putString(
        QuickstartPreferences.ERROR, "SUCCESS");
     } else {
      throw arg1;

     }

    }
   });

  } catch (CloudException e) {
   sharedPreferences
     .edit()
     .putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER,
       false).apply();
   sharedPreferences
     .edit()
     .putString(
       QuickstartPreferences.ERROR,
       "failure:" + e.getMessage() == null ? "Request Timeout"
         : e.getMessage()).apply();
  }
 }
cloudboost-1258 is my app name on google console, East Africa is my timezone and the array parameter is a list of channels I want to subscribe to.

Publishing messages

We can now start publishing messages to be pushed to our devices:

CloudPush push=new CloudPush();
String[] channels={"movers","shakers"};
     push.send(new PushData("title", "my message"), channels, new CloudPushCallback() {
      
 @Override
 public void done(Object x, CloudException t) throws CloudException {
   //x is response, from server, could be a json or a string like "no devices found"   
         }
});

Receiving push messages

We use a class called MyGcmListenerService.java which extends GCM's GcmListenerService to handle messages captured by GcmReceiver.
GcmReceiver extends WakefulBroadcastReceiver, guaranteeing that the CPU is awake so that your listener service can complete its task.
By overriding the method GcmListenerService.onMessageReceived, you can perform actions based on the received message.sendNotification(message);

@Override
public void onMessageReceived(String from, Bundle data) {
    String message = data.getString("message");
    Log.d(TAG, "From: " + from);
    Log.d(TAG, "Message: " + message);

    if (from.startsWith("/topics/")) {
        // message received from some topic.
    } else {
        // normal downstream message.
    }
        /**
         * In some cases it may be useful to show a notification indicating to the user
         * that a message was received.
         */
        sendNotification(message);
    // ...
}
Notice that after your processing, you can still call sendNotification(message) to alert the user on the status bar about an incoming message.

FULL SOURCE CODE AVAILABLE

3 comments:

  1. Hello Bengi,

    Nice blog! I am editor at Java Code Geeks (www.javacodegeeks.com). We have the JCG program (see www.javacodegeeks.com/join-us/jcg/), that I think you’d be perfect for.

    If you’re interested, send me an email to eleftheria.drosopoulou.javacodegeeks.com and we can discuss further.

    Best regards,
    Eleftheria Drosopoulou

    ReplyDelete
  2. Hey Eleftheria Drosopoulou, thanks for appreciating my blog and the invitation to JCG program. I will be sure to look into this and get back to you in a few days.

    ReplyDelete