-
Notifications
You must be signed in to change notification settings - Fork 1
GCM and Server Utilities
##GCM Utility class GCM server needs registration ID to deliver message to a device. The registration ID identifies the device and application, as well as which servers are allowed to send messages. So to send or receive messages, you first need to get a registration ID. The official docs explains in detail how to use GoogleCloudMessaging API to register the application for GCM and obtain the registration ID. In short, we get an instance of GoogleCloudMessaging and invoke its register(senderID) method where senderID is the project number we obtained earlier. This should be done asynchronously (not on the UI thread).
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... params) {
long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
for (int i = 1; i <= MAX_ATTEMPTS; i++) {
Log.d(TAG, "Attempt #" + i + " to register");
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(ctx);
}
String regid = gcm.register(Common.getSenderId());
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
ServerUtilities.register(Common.getPreferredEmail(), regid);
// Save the regid - no need to register again.
setRegistrationId(regid);
return Boolean.TRUE;
} catch (IOException ex) {
Log.e(TAG, "Failed to register on attempt " + i + ":" + ex);
if (i == MAX_ATTEMPTS) {
break;
}
try {
Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
Thread.sleep(backoff);
} catch (InterruptedException e1) {
// Activity finished before we complete - exit.
Log.d(TAG, "Thread interrupted: abort remaining retries!");
Thread.currentThread().interrupt();
}
// increase backoff exponentially
backoff *= 2;
}
}
return Boolean.FALSE;
}
@Override
protected void onPostExecute(Boolean status) {
//broadcastStatus(status);
}
}.execute(null, null, null);
There is sample code available on Google Code for working with GCM. We reused some of the code to create an utility class that you can get from here.
Once we obtain the registration ID from GCM we send it to our server so that it can be used while sending messages. Our server will persist the registration ID along with the chat email ID. The server is responsible for sending the message to GCM server. More about this later when we implement the server code. We reuse the ServerUtilities class from Google Code with a little modification which you can get from here. It basically contains utility methods to send HTTP POST request to a server.
/**
* Issue a POST request to the server.
*
* @param endpoint POST address.
* @param params request parameters.
*
* @throws IOException propagated from POST.
*/
private static void post(String endpoint, Map<String, String> params) throws IOException {
URL url;
try {
url = new URL(endpoint);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("invalid url: " + endpoint);
}
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
// constructs the POST body using the parameters
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString();
//Log.v(TAG, "Posting '" + body + "' to " + url);
byte[] bytes = body.getBytes();
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
// post the request
OutputStream out = conn.getOutputStream();
out.write(bytes);
out.close();
// handle the response
int status = conn.getResponseCode();
if (status != 200) {
throw new IOException("Post failed with error code " + status);
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
##The GCM BroadcastReceiver GCM delivers messages as a broadcast. Recall that we already registered the reciever in the manifest with appropriate permission and intent filter. Let's implement the class now.
public class GcmBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "GcmBroadcastReceiver";
private Context ctx;
@Override
public void onReceive(Context context, Intent intent) {
ctx = context;
PowerManager mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
WakeLock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.acquire();
try {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
String messageType = gcm.getMessageType(intent);
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error", false);
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server", false);
} else {
String msg = intent.getStringExtra(DataProvider.COL_MSG);
String email = intent.getStringExtra(DataProvider.COL_FROM);
ContentValues values = new ContentValues(2);
values.put(DataProvider.COL_MSG, msg);
values.put(DataProvider.COL_FROM, email);
context.getContentResolver().insert(DataProvider.CONTENT_URI_MESSAGES, values);
if (Common.isNotify()) {
sendNotification("New message", true);
}
}
setResultCode(Activity.RESULT_OK);
} finally {
mWakeLock.release();
}
}
Since a broadcast might wake up the device so first get hold of the wake lock. Then insert the message into the database and create a notification to alert the user. Finally, release the wake lock. You can optionally implement an IntentService to handle the intent and perform background tasks.
private void sendNotification(String text, boolean launchApp) {
NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder mBuilder = new Notification.Builder(ctx)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(ctx.getString(R.string.app_name))
.setContentText(text);
if (!TextUtils.isEmpty(Common.getRingtone())) {
mBuilder.setSound(Uri.parse(Common.getRingtone()));
}
if (launchApp) {
Intent intent = new Intent(ctx, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pi);
}
mNotificationManager.notify(1, mBuilder.getNotification());
}
We set a intent to the notification so that user can launch the app directly by clicking the notification. Next, we'll develop the user interface of the app.