Thursday, July 11, 2013

Push Notification Services with Android, GCM and Google App Engine (Part 3)

 

 

Introduction

As the last piece of the 3-piece architecture is implementation of the Android application which will be able to register and receive notification messages from GCM.

The complete Android application can be downloaded from here.

The sending of the notification is done in following order:

1. Google App engine – submit a message

2. Android application receives a notification 3. Android notification tray preview
Send 01 Screenshot_2013-07-11-18-46-07 Screenshot_2013-07-11-18-46-28

 

Implementation

The first part of Android application would be the communication to backend server that we developed before by using Google App Engine. This communication is done in asynchronous way and for this reason  AsyncTask class has been extended. GAEClient is in charge of handling HTTP requests. There is only single method implementation (storeRegistrationId) that is called once the device token is received and needs to be stored on the server.

public class GAEClient extends AsyncTask<Object, Void, String> {
// TODO: Change URL to Google App Engine application URL
private static final String URL = "http://<APP_NAME>.appspot.com/";
private static final String POST_ID_PARAM = "txtRegId";
private static final String POST_MSG_PARAM = "txtInput";
private String res;

@Override
// Checks HTTP page requests
protected String doInBackground(Object... objects) {
String action = (String)objects[0];
if(action.contains("storeid"))
{
String param = (String) objects[1];
return storeRegistrationId(param);
}else{
return null;
}
}

// Stores device token on the backend server
public String storeRegistrationId(String id){
String result = null;
try{
DefaultHttpClient client = new DefaultHttpClient();
HttpPost request = new HttpPost(URL+"storeid");
request.setHeader("Content-type", "application/x-www-form-urlencoded");
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair(POST_ID_PARAM, id));
request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(request);
result = String.valueOf(response.getStatusLine().getStatusCode());
return result;
}catch(Exception e){
e.printStackTrace();
return "400";
}
}

@Override
protected void onPostExecute(String result) {
res = result;
}
}

 


The second part is GCMIntentService. This class is in charge of receiving a notification from the GCM and dispatching its message via broadcast to all Activities that will receive this message if they have broadcast listener implemented. In this simple demo, with single activity, only MainActivity will receive a message.


public class GCMIntentService extends GCMBaseIntentService {

@Override
protected void onMessage(Context context, Intent intent) {
String message = intent.getStringExtra("message");
if(message!=null){
Intent i = new Intent();
i.setAction("GCM_RECEIVED_ACTION");
i.putExtra("gcm", message);
context.sendBroadcast(i);
}
}

@Override
protected boolean onRecoverableError(Context context, String errorId) {
return super.onRecoverableError(context, errorId); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
protected void onError(Context context, String s) {
//To change body of implemented methods use File | Settings | File Templates.
}

@Override
protected void onRegistered(Context context, String s) {
//To change body of implemented methods use File | Settings | File Templates.
}

@Override
protected void onUnregistered(Context context, String s) {
//To change body of implemented methods use File | Settings | File Templates.
}
}

 


Once the Activity receives a message, the following step is to preview a message. They are few ways to display a notification, but the most common way is to use notification drawer (i.e. notification tray). NotificationDrawerMng builds notification for notification drawer preview.


public class NotificationDrawerMng{

public static Notification createNotification(Context context, String title, String message) {
// Prepare intent for notification display in
// the notification tray
Intent intent = new Intent();
intent.putExtra("message",message);
intent.putExtra("title",title);
int requestID = (int) System.currentTimeMillis();
PendingIntent pIntent = PendingIntent.getActivity(context, requestID, intent, 0);

// Build notification for tray display
Notification notification = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
return notification;
}
}

 


In the broadcast listener, in the MainActivity, notification will be actually set inside the notification tray.


BroadcastReceiver gcmReceiver = new BroadcastReceiver() {
// Broadcast listener that receives a dispatched notification previously received
// from GCMIntentService
@Override
public void onReceive(Context context, Intent intent) {
broadcastMessage = intent.getExtras().getString("gcm");

if (broadcastMessage != null) {
Toast.makeText(getApplicationContext(), broadcastMessage, Toast.LENGTH_LONG).show();
Notification notification = NotificationDrawerMng.createNotification(getApplicationContext(), "GCMAndroidDemo", broadcastMessage);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

notification.flags = notification.flags | Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
}
};

The last step is to add additional permissions in AndroidManifest for using GCM. Although we are using GCM don’t get confused since C2DM remained a configuration tag.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gcmdemo.GCMAndroidDemo"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="14"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<permission android:name="com.gcmdemo.GCMAndroidDemo.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.gcmdemo.GCMAndroidDemo.permission.C2D_MESSAGE" />
<!-- App receives GCM messages. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- GCM connects to Google Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<activity android:name="MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<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.gcmdemo.GCMAndroidDemo" />
</intent-filter>
</receiver>
<service android:name="com.gcmdemo.GCMIntentService" />
</application>
</manifest>

3 comments:

  1. A great tutorial, thanks!
    I have a question, what i need to do to display incoming notification message in the "Hello world, MainActivity" ?

    ReplyDelete
    Replies
    1. Hi DM,

      Notification message display is defined in onReceive() method of BroadcastReceiver.
      For example you can use a simple Toast message preview by using:

      Toast.makeText(getApplicationContext(), your_notification_msg_text, Toast.LENGTH_LONG).show();

      The rest of the code in onReceive method is regarding NotificationManager which will display your notification in notification tray.

      If you want to use any other display technique, just replace the code under
      if (broadcastMessage != null) {
      ...
      }
      section of onReceive method.

      Hope this helps.

      Delete
  2. Vladimir,

    I am looking for someone to build a simple push-notification app using GCM etc. Perhaps you are interested, or you know someone who is?

    I own a small software development company in Melbourne, Australia. My main product is a relatively large ERP/Call Centre system designed for mobile service workers. I now wish to integrate the system with mobile devices. The push notification is necessary to alert workers to new job bookings, or changed requirements etc.
    However I wish to start with an extremely simple app, to just test the architecture.

    Please respond to me here robert-dot-blair-at-ozemail-dot-com-dot-au

    Robert Blair :)

    ReplyDelete