Thursday, July 11, 2013

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

 

Introduction

In this part of the tutorial will implement server side implementation of our 3 main components. For this purpose we’ll use Google App Engine (GAE) to implement simple JSP web application as well GAE Datastore that is delivered with application. The entire project can be downloaded from here.

The GAE web app will have two functionalities:

  • to send message to the GCM which will then dispatch notification to all targeted devices
  • to store device tokens, once they are received from GCM

For messaging sending will have very simple form where we can enter some message and submit for delivery:

GAE

Figure01: Web application in Google App Engine

Setup

Step01: Visit Google App Engine web site and create a new application.

GAECreateApp

Figure02: Create GAE application

Step02: Install GAE plugin within your Eclipse. Visit here for guidelines.

Step03: Once you installed the plugin, in Eclipse go to File > New > Other and choose under Google section Web application project.

GAEEclipse01

Figure03: Select GAE project in Eclipse

Step04: Name your project and namespace and uncheck the Generate project sample code option.

GAEEclipse

Figure04: Create GAE project in Eclipse

Implementation

Our web application consists of two servlets (MainActivityServlet.java, StoreIdServlet.java)  and two JSP pages (main.jsp, storeid.jsp) respectively.

GAEEclipse02

Figure05: GAE project structure in Eclipse

The MainActivityServlet.java servlet handles HTTP requests that are coming from the main.jsp form (see Figure01) and is in charge of sending notification messages to the GCM service. The second functionality for storing device tokens is implemented within StoreIdServlet.java servlet and it also handles requests from storeid.jsp to store device tokens manually if necessary.

Following code is the implementation of the MainActivityServlet.java servlet:

package com.guestapp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.MulticastResult;
import com.google.android.gcm.server.Sender;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

public class MainActivityServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(MainActivityServlet.class.getName());
// API_KEY is sender_auth_token (server key previously generated in GCM)
private static final String API_KEY = "";
// Datastore is database where all device tokens get stored
private static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();


// Handles HTTP GET request from the main.jsp
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/main.jsp");
}

// Handles HTTP POST request - submit message from the main.jsp
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String txtInput = req.getParameter("txtInput");

// Instantiating sender for dispatching message to GCM
Sender sender = new Sender(API_KEY);
// Creating a message for GCM
Message message = new Message
.Builder()
.addData("message", txtInput)
.build();

ArrayList<String> devices = getAllRegIds();
if(!devices.isEmpty()){
// Sending multicast message to GCM specifying all targeting devices
MulticastResult result = sender.send(message, devices, 5);
log.info("Message posted: " + txtInput);
resp.sendRedirect("/main.jsp?message="+txtInput);
}else{
log.info("No devices registered.");
resp.sendRedirect("/main.jsp?message=warning-no-devices");
}
}

// Reads all previously stored device tokens from the database
private ArrayList<String> getAllRegIds(){
ArrayList<String> regIds = new ArrayList<String>();
Query gaeQuery = new Query("GCMDeviceIds");
PreparedQuery pq = datastore.prepare(gaeQuery);
for (Entity result : pq.asIterable()){
String id = (String) result.getProperty("regid");
regIds.add(id);
}

return regIds;
}
}


The implementation of main.jsp page looks like:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List" %>
<%@ page import="com.google.appengine.api.users.User" %>
<%@ page import="com.google.appengine.api.users.UserService" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<html>
<body>
Message:
<% if (request.getParameter("message") != null) { %>
<%= request.getParameter("message")%>
<% } %>
<form action="/main" method="post">
<div><textarea name="txtInput" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Submit" /></div>
</form>
</body>
</html>


The implementation of second StoreIdServlet.java servlet is like following:


package com.guestapp;
import java.io.IOException;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public class StoreIdServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(MainActivityServlet.class.getName());
// Datastore is database where all device tokens get stored
private static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

// Handles HTTP GET request from the storeid.jsp
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/storeid.jsp");
}

// Handles HTTP POST request - submit message from the storeid.jsp
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String txtRegId = req.getParameter("txtRegId");

// Creates device token entity and saves it in the database
Entity regId = new Entity("GCMDeviceIds",txtRegId);
regId.setProperty("regid", txtRegId);
if(!isReqIdExist(txtRegId)){
saveToDB(regId);
log.info("RegId inserted into DB: " + txtRegId);
}
}

// Save device token in the database
private void saveToDB(Entity regId){
datastore.put(regId);
}

// Checks if the device token already exist in the database
private boolean isReqIdExist(String regId){
Key keyRegId = KeyFactory.createKey("GCMDeviceIds", regId);
Entity entity = null;
try {
entity = datastore.get(keyRegId);
} catch (EntityNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(entity!=null){
return true;
}
return false;
}
}


The corresponding JSP page, storeid.jsp looks like:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List" %>
<%@ page import="com.google.appengine.api.users.User" %>
<%@ page import="com.google.appengine.api.users.UserService" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<html>

<body>
Insert device Registration_id:
<form action="/storeid" method="post">
<div><textarea name="txtRegId" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Submit" /></div>
</form>
</body>
</html>


Deployment


Right click on the project and go to Google > App Engine Settings… Fill in the application id that goes before “.appspot.com” part.


GAEEclipse03


Figure06: GAE deployment configuration


Now go right click on the project Google > Deploy to App Engine and then click deploy.


Once your deployment is successfully finished you can go back to Google App Engine web site or check your application by visiting appid.appspot.com. Also, on Google App Engine web site you can check Datastore Viewer for preview your database records.


 


The remaining component of notification system is Android application. Check the implementation in the next blog.

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>

Wednesday, July 3, 2013

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

 

 

Introduction

In this blog I’ll demonstrate a solution for implementing push notification services for Android applications. It will leverage on the existing technologies that Google provides for the developers: Android platform, Google Cloud Messaging (GCM) and Google App Engine.

As you are probably already familiar, GCM is push notification service for Android platform that delivers notifications to your phone. Notifications are commonly used for notifying user about installed application updates or important status updates, for example, the Twitter application that notifies you about re-tweets. It is important to mention that push notification services (PNS) should be used with precaution since there are intended only to notify user about crucial things at the current moment. For example, if a user bids on an auction on the eBay, he/she should be notified if someone places bigger bid. Also, notifications should not be used if the messages will come too frequently. Too frequent notifications will annoy user and he/she will probably uninstall your app. PNSs are also not intended for delivering any confidential content since they don’t support encryption and they should not be used for these purposes. Instead, make sure you meet requirements as frequency, importance and sending it in a relevant time.

Architectural overview

First let’s get familiar with GCM, in order to better understand how our solutions will look like in the very end.
GCM system consists of three components:

  • Client (Android application) 
  • Backend server (Google App Engine backend)
  • GCM service

GCM provides us services for sending notifications to the Android smart phones. These notifications are messages that are constructed and sent to GCM by backend server who initiates notification sending (in Figure01 step a), i.e. backend server behaves as notification trigger. Backend server also stores all registration_ids of every subscriber (phone with installed application) and with these information server knows which group of subscribers it wants to target (in Figure01 step 3). Mobile app simply asks GCM for registration_id and connects to push service in order to receive notifications (in Figure01 step 1). It can also send registration_id delivered by GCM to the backend server (in Figure01 step 3).
For using GCM notification system several credentials need to be used:

  • sender_id - project number that uniquely identifies the project in Google Application Console,
  • application_id - identifies the application and it’s necessary to be used for receiving messages,
  • registration_id - identifies device with the installed app and it’s also necessary to be used for receiving messages. Device receives it from GCM service during connection process.
  • sender_auth_token - or API_KEY that is used on a server side in order to use GCM services
  • google_user_account - for Android versions lower than 4.0, Google account needs to be used.

GCM

Figure01 – GCM notification cycle

The complete process of notification sending can be explained with the following steps (see Figure01):

  • enabling GCM:
    1. (1) device is sending a request to the GCM. The request contains sender_id and app_id.
    2. (2) GCM sends response containing app_id and registration_id.
    3. (3) mobile app sends received registration_id to backend server, which stores the id
  • sending a message:
    1. (‘a’) backend server with defined API_KEY sends a message content with a set of registration_ids of device it wants to target
    2. (‘b’) GCM receives the message from server and broadcasts it to the devices
  • receiving a message (on a device)
    1. mobile app receives the notification, extracts it and processes data
    2. displays message content

Generating credentials

Now we’ll go through steps of how to generate mentioned credentials that we’ll later use in our implementation.

Step 01: Go to Google API Console web site and log in with your Gmail account.

Google Api Console

Figure02 – Google API Console start page

Step 02: Create project…

Step 03: Enter project name and continue

Step 04: In the left navigation menu go to Services and enable Google Cloud Messaging for Android

GCMEnable

Figure03 – Turn on GCM for Android

Step 05: In the same menu go to API Access. Now you need to create a server key. Click on Create New Server Key… and for this case leave IPs field empty and follow with create (no IP address needs to be specified if you want to be in public domain).

Step 06: In the same menu go to Overview.

So far we generated following credentials:

  • sender_id: in the Overview you can see Project Number and that is your sender_id (will be used in Android application)
  • application_id: as explained before will be received from the GCM when the mobile connects to the GCM
  • registration_id: as explained before will be received from the GCM when the mobile connects to the GCM
  • sender_auth_token: in the API Access you can see under Key for server apps (with IP locking) the API key which is your sender authentication token (will be used in Google App Engine web application)
  • google_user_account: is not necessary since in this tutorial will develop Android 4.0 application

Now we are ready to implement our notification system. Follow this next blog.