Android + Google Cloud Messaging (GCM)
Let’s create a project in the console of the Google Cloud Platform and enable the Google Cloud Messaging API for this project.
Then, create a server API key for the Google Cloud Messaging.
In the Eclipse, let’s create a project of an Android application, in which add the library Google Play Services
https://developers.google.com/android/guides/setup#add_google_play_services_to_your_project.
Add permissions in the manifest file.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.tmsoftstudio.breakingnews.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.tmsoftstudio.breakingnews.C2D_MESSAGE" />
To register the Android application in the service GCM, add the following code in the class activity:
private String regid;
private String PROJECT_NUMBER = "1222. . .";
private SharedPreferences mSettings;
private Context context;
final int REQUEST_CODE = 0;
public final static int RESULT_CODE = 1;
public final static String PARAM_PINTENT = "PendingIntent";
public final static String PARAM_RESULT = "Result";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
context = this;
mSettings = getSharedPreferences("APP_PREFERENCES", Context.MODE_PRIVATE);
if (!mSettings.contains("SENDER_ID")) {
SharedPreferences.Editor editor = mSettings.edit();
editor.putString("SENDER_ID", PROJECT_NUMBER);
editor.commit();
}
if (!mSettings.contains("REG_ID")) {
PendingIntent pi;
pi = createPendingResult(REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(context, InstanceIDIntentService.class);
intent.putExtra(PARAM_PINTENT, pi);
startService(intent);
}
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (resultCode == RESULT_CODE) {
regid=data.getStringExtra(MainActivity.PARAM_RESULT);
View view = (View) findViewById(R.id.content);
Snackbar.make(view, regid, Snackbar.LENGTH_LONG).setAction("Action", null).show();
SharedPreferences.Editor editor = mSettings.edit();
editor.putString("REG_ID", regid);
editor.commit();
}
}
}
Here, the PROJECT_NUMBER is a number of the project created in the Google Cloud Platform.
The PendingIntent object is created by the method createPendingResult of an activity to obtain a unique registration token from the registration service to save it in the callback method onActivityResult of the activity.
Let’s create the registration service.
<service android:name=".InstanceIDIntentService" android:exported="false"/>
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class InstanceIDIntentService extends IntentService {
// abbreviated tag name
private static final String TAG = "InstanceIDIntentService";
private SharedPreferences mSettings;
public InstanceIDIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
InstanceID instanceID = InstanceID.getInstance(this);
mSettings = getSharedPreferences("APP_PREFERENCES", Context.MODE_PRIVATE);
String senderId = mSettings.getString("SENDER_ID", "");
try {
String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
int code=sendRegistrationToServer(token);
if(code==200){
PendingIntent pi = intent.getParcelableExtra(MainActivity.PARAM_PINTENT);
Intent intentResult = new Intent().putExtra(MainActivity.PARAM_RESULT, token);
pi.send(InstanceIDIntentService.this,MainActivity.RESULT_CODE, intentResult);
}else{
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() { Toast.makeText(InstanceIDIntentService.this.getApplicationContext(),"Registration failed!",Toast.LENGTH_SHORT).show();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
private int sendRegistrationToServer(String token) {
URL url;
HttpURLConnection conn=null;
int code=0;
try {
url = new URL("http://backend.appspot.com/backend");
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
Uri.Builder builder = new Uri.Builder()
.appendQueryParameter("token", token);
String query = builder.build().getEncodedQuery();
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(query);
writer.flush();
writer.close();
os.close();
code=conn.getResponseCode();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (conn != null) {
conn.disconnect();
}
}
return code;
}
}
Here, using a project number, a request is created to the GCM service and, in response, it receives a unique registration token that is then sent to the server and is passed via the PendingIntent object back to the activity.
To create a backend, in the Eclipse, let’s create a Web Application Project, which the file appengine-web.xml, let’s specify an identifier of the project of the Google Cloud Platform.
Let’s add the following code in the servlet of the GAE application to save the token.
import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
@SuppressWarnings("serial")
public class BackendServlet extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String token=req.getParameter("token");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity tokenEntity = new Entity("Token");
tokenEntity.setProperty("token", token);
datastore.put(tokenEntity);
}
}
Since the token is periodically updated by the GCM service, you need to create a listener that will be automatically started when updating the token by the GCM service, and it will update the token for the Android application and server part.
<service
android:name=".RefreshInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
import com.google.android.gms.iid.InstanceIDListenerService;
import android.content.Intent;
public class RefreshInstanceIDListenerService extends InstanceIDListenerService {
@Override
public void onTokenRefresh() {
// Fetch updated Instance ID token and notify of changes
Intent intent = new Intent(this, InstanceIDIntentService.class);
startService(intent);
}
}
To send a message from the server to the Android application in the lib folder and in the Build Path of the project of the GAE application, let’s add the libraries json-simple-1.1.jar (http://grepcode.com/snapshot/repo1.maven.org/maven2/com.googlecode.json-simple/json-simple/1.1) and gcm-server.jar (https://github.com/slorber/gcm-server-repository/tree/master/deployer).
Let’s add the following in the servlet code:
import java.io.IOException;
import javax.servlet.http.*;
import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.Sender;
import com.google.android.gcm.server.Result;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
@SuppressWarnings("serial")
public class BackendServlet extends HttpServlet {
private String SERVER_KEY="AIza. . .";
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String token=req.getParameter("token");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity tokenEntity = new Entity("Token");
tokenEntity.setProperty("token", token);
datastore.put(tokenEntity);
Sender sender = new Sender(SERVER_KEY);
Message message = new Message.Builder()
.addData("sender", "Project Name")
.addData("message", "this is the message")
.build();
Result result = sender.send(message, token, 1);
}
}
Here, the SERVER_KEY is the created server API key for the Google Cloud Messaging.
To get a message in an Android application, let’s add the declaration of the service in the manifest file that will handle a GCM message.
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.tmsoftstudio.breakingnews" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="com.tmsoftstudio.breakingnews"/>
</intent-filter>
</receiver>
As well as let’s create a service to handle incoming messages.
<service
android:name=".GcmMessageHandler"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
import com.google.android.gms.gcm.GcmListenerService;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
public class GcmMessageHandler extends GcmListenerService {
public static final int MESSAGE_NOTIFICATION_ID = 123123;
@Override
public void onMessageReceived(String from, Bundle data) {
String title = data.getString("sender");
String message = data.getString("message");
Context context = getBaseContext();
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_launcher).setContentTitle(title)
.setContentText(message);
NotificationManager mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(MESSAGE_NOTIFICATION_ID, mBuilder.build());
}
}
To publish topic messages, in the project of the GAE application, let’s replace the library gcm-server.jar (https://github.com/slorber/gcm-server-repository/tree/master/deployer) on the library https://github.com/google/gcm.
This will allow you to replace the command sender.send(message, token, 1); on the sender.send(message, "/topics/news", 1);
For the subscription of an Android application on topic messages, in the service InstanceIDIntentService, in the onHandleIntent method, let’s add the following code:
String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, "/topics/news", null);
Let’s change the service GcmMessageHandler.
import com.google.android.gms.gcm.GcmListenerService;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
public class GcmMessageHandler extends GcmListenerService {
public static final int MESSAGE_NOTIFICATION_ID = 123123;
@Override
public void onMessageReceived(String from, Bundle data) {
String title = data.getString("sender");
String message = data.getString("message");
if (from.startsWith("/topics/news")) {
Context context = getBaseContext();
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher).setContentTitle(title+"Topic:News")
.setContentText(message);
NotificationManager mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(MESSAGE_NOTIFICATION_ID, mBuilder.build());
}else{
Context context = getBaseContext();
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher).setContentTitle(title)
.setContentText(message);
NotificationManager mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(MESSAGE_NOTIFICATION_ID, mBuilder.build());
}
}
}
Cordova + Google Cloud Messaging (GCM)
For Cordova applications, the registration and receiving Push notifications are provided by the Cordova plugin phonegap-plugin-push (https://github.com/phonegap/phonegap-plugin-push).
In the Intel SDK, in the section Plugin Management of the Projects tab, let’s add the plugin phonegap-plugin-push. In section Bower Managed Libraries, let’s add the jquery library.
Let’s add the jquery library in the file index.html.
<script src="bower_components/jquery/dist/jquery.min.js"></script>
Let’s add the code of the registration and message processing in the file app.js.
function onAppReady() {
if( navigator.splashscreen && navigator.splashscreen.hide ) { navigator.splashscreen.hide() ;
}
var push = PushNotification.init({
android: {
senderID: "1222. . ."
},
ios: {
alert: "true",
badge: "true",
sound: "true"
},
windows: {}
});
push.on('registration', function(data) {
var token=data.registrationId;
jQuery.ajax({
method: "POST",
url: "http://backend.appspot.com/backend",
data: { token: token }
});
});
push.on('notification', function(data) {
alert(data.title+" Message: " +data.message);
});
push.on('error', function(e) {
alert(e.message);
});
}
document.addEventListener("app.Ready", onAppReady, false) ;
Here, the senderID is a number of the project of the Google Cloud Platform.
The servlet code that sends a message:
import java.io.IOException;
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.Sender;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
@SuppressWarnings("serial")
public class Backend_Breaking_NewsServlet extends HttpServlet {
private String SERVER_KEY="AIzaS. . .";
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String token=req.getParameter("token");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity tokenEntity = new Entity("Token");
tokenEntity.setProperty("token", token);
datastore.put(tokenEntity);
Sender sender = new Sender(SERVER_KEY);
Message message = new Message.Builder()
.addData("title", "Breaking News")
.addData("message", "this is the message")
.build();
sender.send(message, token, 1);
}
}
Here, the SERVER_KEY is the created server API key for the Google Cloud Messaging.
You can download the book Android Development in Details and Recipes here.