Android Tutorial Android Tutorial Part V

View the Project on GitHub FatFractal/hoodyoodoo

Push Notifications

Our last Tutorial will cover how to use push notifications to send messages to your users using the Google Cloud Messaging (GCM) service. It is important to note that despite the fact that this tutorial focuses on an Android app, the API you have created can send notifications to iPhone, Blackberry and WinPho devices, without any alteration.

If you wish, you can download the complete Part V project file from here and then unzip and then either run newapp again from this directory to use this application or copy the files that are enclosed to the application directory you created in a previous section Installing FatFractal allowing the copy to replace all the previous files.

If you have not scaffolded your app already, it is not a problem, just run ffef newapp, using your subdomain, as before from the directory with the Part V files you have just downloaded and it will set up all the things that you need.

> $HOME/ff/ffnsbin/ffef newapp hoodyoodoo <your subdomain>

Note: The subdomain refers to the organization name you signed up with, e.g. acme in acme.fatfractal.com

Note: When you download the project, the client application is configured to use a API that is already deployed and populated with data. This means you can run the application on your simulator immediately. To use your API, you should edit the Hoodyoodoo.java file as before to point to the API that you have set up.

Registering with Google

First, we will register our application with Google:

  1. Create a project for your application on the Google APIs Console.
  2. Click Overview on the left, and note the Project Number.
  3. Enable Google Cloud Messaging by clicking Services on the left, finding Google Cloud Messaging for Android in the list, then clicking the status button so that it reads On.
  4. Click API Access on the left, then click Create new Server key ..., then Create in the dialog box.
  5. Note the API key for the key you just created.

We will use the Project Number and API key below.

Adding push notifications to the API

We will now modify our event handler that we set up in Tutorial Part 3 to send out a push notification whenever a new top rated celebrity is created.

There are three ways that we can do this:

  1. Send the notification from the current handleWouldYaCreate event handler as it knows already when a new TopCelebrity is created.
  2. Add a new function to handleWouldYaCreate that is called when a new TopCelebrity is created.
  3. Create a new event handler that is triggered when the a new TopCelebrity is created and sends out a notification separately.

While #1 is simpler to implement and more efficient for the API to process than #2 or #3, in this tutorial we will implement #3 as it will better serve to illustrate the general case solution.

First, we will create a new javascript file called TopCelebrityEventHandlers.js and add it to the ff-scripts directory for my application. As before, the basic structure of the file is:

var ff = require('ffef/FatFractal');

function handleTopCelebrityUpdate(json) {
    … do some stuff
}

exports.handleTopCelebrityUpdate = handleTopCelebrityUpdate;

We want to create a function that will extract the first name and last name of the Celibrity from the event, create a message and then send to all users of the application.

We can do the first two in a single line:

var messageString = data.firstName + " " + data.lastName + " is our new Top Celebrity!";

To get your user information, you can use the very cool getAllGuids(url) method:

var userGuids = ff.getAllGuids("/FFUser");

Then, to send out the push notification, you can use the sendPushNotifications(guids, message) method:

var ff = require('ffef/FatFractal');

function handleTopCelebrityUpdate(json) {
    data = JSON.parse(json);
    var userGuids = ff.getAllGuids("/FFUser");
    var messageString = data.firstName + " " + data.lastName + " is our new Top Celebrity!"
    if (userGuids.length != 0) ff.sendPushNotifications (userGuids, messageString);
}

exports.handleTopCelebrityUpdate = handleTopCelebrityUpdate;

Update the Server Description

Then we modify the application.ffdl to add the new Event Handler to the configuration. we do this by adding the following line:

CREATE HANDLER TopCelebUpdate ON /TopCelebrity Update as javascript:var h = require ('scripts/TopCelebrityEventHandlers'); h.handleTopCelebrityUpdate (FF_EVENT_DATA_JSON);

We must also enable push notifications. Do so by adding the following lines:

SET PushIsInactive false
SET AndroidPushAuthToken your_API_key

where you should replace your_API_key with the API key you created for your Google API project above.

To make your API aware of these new items, you need to deploy again (have you noticed how little you have to fiddle around with your API so far??) by doing the following:

If you are running in the Cloud

From the hoodyoodoo application directory, deploy the modified hoodyoodoo API to the NoServer Public Cloud, you use the command line deployFFFabric:

> $HOME/ff/ffnsbin/ffef deployFFFabric

In a few seconds, your updated application will be available at http://<your subdomain>.fatfractal.com/hoodyoodoo

Running locally

Assuming that the FatFractal Engine is running, you can also deploy to your development machine using the command line deploylocal:

> $HOME/ff/ffnsbin/ffef deploylocal

In a few seconds, your modified application will be available at http://localhost:8080/hoodyoodoo

Modify the client and UI

You must also configure your application to handle push notifications as follows.

First, use the Android SDK Manager to download Extras > Google Cloud Messaging for Android Library. Then, add the file <sdk install dir>/extras/google/gcm/gcm-client/dist/gcm.jar to your project.

Next, add the following lines to the beginning of your app's AndroidManifest.xml file:

<permission android:name="com.hoodyoodoo.droidapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.hoodyoodoo.droidapp.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Also add the following lines at the end of your application element:

<service android:name=".GCMIntentService" />
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.hoodyoodoo.droidapp" />
</intent-filter>
</receiver>

Next, we'll edit the main application file, Hoodyoodoo.java. First, we need to add some more import statements:

import com.fatfractal.ffef.impl.FFPrefsAndroid;
import com.google.android.gcm.GCMRegistrar;

Add a constant to hold your C2DM account information:

public static String GCM_SENDER_ID = "your_google_project_id";

Here, replace your_google_project_id with the Project Number of your Google API project. Finally, add the following to the onCreate method:

FFPrefsAndroid.setContext(this);
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
String registrationId = ff.getNotificationID();
if (registrationId == null || registrationId.equals("")) {
    GCMRegistrar.register(this, GCM_SENDER_ID);
}

This code does a couple of things. First, it saves the application context to a place where the NoServer Framework can access it. Next, we call the getNotificationID() method. This first checks the application preferences for a saved GCM registration ID; if we don't find one, we use the GCMRegistrar.register method to register the device with the GCM service.

Next, we need to add a new class to the com.hoodyoodoo.droidapp package. The class will extend the GCMBaseIntentService class provided by the GCM library. It's purpose is to respond to messaging events, such as registering your device or receiving a push notification. Create the file GCMIntentService.java and enter the following:

package com.hoodyoodoo.droidapp;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.fatfractal.ffef.FFException;
import com.google.android.gcm.GCMBaseIntentService;
import com.hoodyoodoo.droidapp.activity.HoodyoodooActivity;

public class GCMIntentService extends GCMBaseIntentService {
    public GCMIntentService() {
        super(Hoodyoodoo.GCM_SENDER_ID);
    }

    @Override
    public void onRegistered(Context context, String regId) {
        try {
            Hoodyoodoo.getFF().registerNotificationID(regId);
        }
        catch (FFException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onUnregistered(Context context, String regId) {
        try {
            Hoodyoodoo.getFF().unregisterNotificationID();
        }
        catch (FFException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onError(Context context, String errorId) {
        Log.e(this.getClass().getName(), "GCM error : " + errorId);
    }
}

Note: The GCMIntentService class must be in the same package as your main application class.

Our app is almost ready to receive push notifications! The only thing left is to decide what to do when we actually receive a message. We want two things to happen: a) display a notification using the android.app.Notification class, and b) when the notification is clicked, launch Hoodyoodoo and show the TopCelebrity tab.

First, prepare HoodyoodooActivity by adding the following code to the class:

public static final String EXTRA_TAB_TAG       = "tab_tag";

@Override
protected void onStart() {
    Bundle extras = getIntent().getExtras();
    if (extras != null) {
        String tabtag = extras.getString(EXTRA_TAB_TAG);
        getTabHost().setCurrentTabByTag(tabtag);
    }
    super.onStart();
}

As we will see presently, we will direct the app to open the TopCelebrity tab by adding extras to the launch intent. In the code above, we examine the intent for desired tab, and select it.

Finally, add the following method to the GCMIntentService class you created above:

@Override
protected void onMessage(Context context, Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras != null) {
        String payloadMsg = extras.getString("payload");

        Notification.Builder notification = new Notification.Builder(context);
        notification.setContentTitle("Been outdone!");
        notification.setTicker("Been outdone!");
        notification.setContentText(payloadMsg);
        notification.setSmallIcon(R.drawable.ic_launcher);
        notification.setAutoCancel(true);

        Intent launchIntent = new Intent(this, HoodyoodooActivity.class);
        launchIntent.putExtra(HoodyoodooActivity.EXTRA_TAB_TAG, HoodyoodooActivity.TAB_TOP_CELEB_TAG);
        launchIntent.setAction(Intent.ACTION_VIEW); // necessary for extras to be passed on to Action
        notification.setContentIntent(PendingIntent.getActivity(context, 0, launchIntent, 0));

        NotificationManager nm = (NotificationManager)context.getSystemService(NOTIFICATION_SERVICE);
        nm.notify(0, notification.getNotification());
    }
}

That's it, the next time a TopCelebrity is created or updated, then a push notification will be sent to any application user that has authorized the application to receive them.

Important note for the Android emulator: The target of your AVD (Android Virtual Device) must be Google APIs. A plain Android target will not work.

Here is what a notification looks like:

Have fun!