Skip to content

GCM and Server Utilities

sojan v jose edited this page Jun 20, 2014 · 1 revision

##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.

Server Utility class

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.

Clone this wiki locally