How To Setup Native Android Push Notifications

Following Steps will help you connect your native Android Application with PushAssist to send notifications the way you want. Please read and implement the steps given below carefully and replace your GCM Keys, PushAssist Auth keys where applicable

GCM Architecture

  1. GCM connection server : It receives the messages from application server and send these messages to the GCM enabled android devices.
  2. Application server : It sends the message to the GCM connection server. I will use PHP to build the application server in this tutorial.
  3. Android Application : It receives the messages from GCM connection server after application server sends message to the GCM connection server.

Life Cycle Flow

You should understand the life cycle flow of an Android Notification system

 

  1. Android application enables the GCM by registering to the GCM. The application needs Sender ID to get the registration ID.
  2. GCM connection server receives the sender ID from application and returns the unique registration id.
  3. The application send the registration ID to the back end application server for the storage.
  4. The application server, stores the registration Id in the database.
  5. When a new message need to send, the application server fetches the registration ids from database and send to the GCM connection serer along with the message.
  6. The GCM server sends the message to the application.

Libraries & Tools

Step I : Install Google Play Services SDK
In order to use Google Services like GCM, you need to have Google Play Services SDK. Look at this official documentation to setup the SDK. One important thing you should take care is in referencing the library. You should not reference the library directly from the Android SDK. Instead first copy the library (i.e. google-play-services_lib) into your current workspace and then reference. In Eclipse, you can do this by checking the "Copy projects into workspace" checkbox while importing the project.

Step II: Install Google APIs
For testing the project in emulator, you need Google APIs. Install the Google APIs and create a new AVD with Google APIs as the platform target.

Step III: Install GCM for Android library
In SDK Manager.exe, expand the extras, select and install the Google Cloud Messaging for Android. Now you have setup all the library needed to create a GCM application.

Register with Google Cloud Messaging (GCM)

  1. Open the Google Cloud Console.
  2. If you haven't created the API project yet, click CREATE PROJECT. Give the name of the project and click Create.



  3. Note down the project number. You will use the project number as sender ID in the registration process.
  4. In the sidebar on the left, click APIs and auth.
  5. In the displayed list of APIs, turn the Google Cloud Messaging for Android toggle to ON.
  6. In the sidebar on the left, click APIs & auth >> Credentials.
  7. Click CREATE NEW KEY and select Server Key.



  8. Provide the list of IP address from which the GCM server accepts the request. Left blank if you want to allow any IP.
  9. Copy down the Server Key, you will need this later.

Create Your Android Application

The final steps is to create a android application named GCMDemo. Ensure the following project structure

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.programmingtechniques.gcmdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="19" />

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission android:name="com.programmingtechniques.gcmdemo.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
 <uses-permission android:name="com.programmingtechniques.gcmdemo.permission.C2D_MESSAGE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.programmingtechniques.gcmdemo.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>
        <meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

        <receiver
            android:name=".GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.programmingtechniques.gcmdemo" />
            </intent-filter>
        </receiver>
        <service android:name=".GcmIntentService" />
    </application>

</manifest>

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@ id/register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Register Device">
    </Button>"

</RelativeLayout>

GcmBoradcaseReceiver

package com.programmingtechniques.gcmdemo;
 
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
 
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
  
    @Override
    public void onReceive(Context context, Intent intent) {
        // Explicitly specify that GcmIntentService will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}

GcmIntentService.java

package com.programmingtechniques.gcmdemo;
 
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
 
public class GcmIntentService extends IntentService {
    public static final int NOTIFICATION_ID = 1;
 private static final String TAG = "GcmIntentService";
    private NotificationManager mNotificationManager;
    NotificationCompat.Builder builder;
 
    public GcmIntentService() {
        super("GcmIntentService");
    }
 
    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        // The getMessageType() intent parameter must be the intent you received
        // in your BroadcastReceiver.
        String messageType = gcm.getMessageType(intent);
 
        if (!extras.isEmpty()) {  // has effect of unparcelling Bundle
            /*
             * Filter messages based on message type. Since it is likely that GCM
             * will be extended in the future with new message types, just ignore
             * any message types you're not interested in, or that you don't
             * recognize.
             */
            if (GoogleCloudMessaging.
                    MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
                sendNotification("Send error: "   extras.toString());
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_DELETED.equals(messageType)) {
                sendNotification("Deleted messages on server: "  
                        extras.toString());
            // If it's a regular GCM message, do some work.
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_MESSAGE.equals(messageType)) {
                // This loop represents the service doing some work.
                for (int i=0; i<5; i  ) {
                    Log.i(TAG, "Working... "   (i 1)
                              "/5 @ "   SystemClock.elapsedRealtime());
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                    }
                }
                Log.i(TAG, "Completed work @ "   SystemClock.elapsedRealtime());
                // Post notification of received message.
                sendNotification(extras.getString("message"));
                Log.i(TAG, "Received: "   extras.toString());
            }
        }
        // Release the wake lock provided by the WakefulBroadcastReceiver.
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }
 
    // Put the message into a notification and post it.
    // This is just one simple example of what you might choose to do with
    // a GCM message.
    private void sendNotification(String msg) {
        mNotificationManager = (NotificationManager)
                this.getSystemService(Context.NOTIFICATION_SERVICE);
 
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, MainActivity.class), 0);
 
        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(this)
       // .setSmallIcon(R.drawable.ic_stat_gcm)
        .setContentTitle("GCMDemo")
        .setSmallIcon(R.drawable.ic_launcher)
        .setStyle(new NotificationCompat.BigTextStyle()
        .bigText(msg))
        .setContentText(msg);
 
        mBuilder.setContentIntent(contentIntent);
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
    }
}

RegisterApp.java

package com.programmingtechniques.gcmdemo;
 
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
 
 
public class RegisterApp extends AsyncTask<Void, Void, String> {
 
 private static final String TAG = "GCMRelated";
 Context ctx;
 GoogleCloudMessaging gcm;
 String SENDER_ID = "343594554298";
 String regid = null;
 private int appVersion;
 public RegisterApp(Context ctx, GoogleCloudMessaging gcm, int appVersion){
  this.ctx = ctx;
  this.gcm = gcm;
  this.appVersion = appVersion;
 }
  
  
 @Override
 protected void onPreExecute() {
  super.onPreExecute();
 }
 
 
 @Override
 protected String doInBackground(Void... arg0) {
  String msg = "";
        try {
            if (gcm == null) {
                gcm = GoogleCloudMessaging.getInstance(ctx);
            }
            regid = gcm.register(SENDER_ID);
            msg = "Device registered, registration ID="   regid;
 
            // 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.
            // The request to your server should be authenticated if your app
            // is using accounts.
            sendRegistrationIdToBackend();
 
            // For this demo: we don't need to send it because the device
            // will send upstream messages to a server that echo back the
            // message using the 'from' address in the message.
 
            // Persist the regID - no need to register again.
            storeRegistrationId(ctx, regid);
        } catch (IOException ex) {
            msg = "Error :"   ex.getMessage();
            // If there is an error, don't just keep trying to register.
            // Require the user to click a button again, or perform
            // exponential back-off.
        }
        return msg;
 }
 
 private void storeRegistrationId(Context ctx, String regid) {
  final SharedPreferences prefs = ctx.getSharedPreferences(MainActivity.class.getSimpleName(),
             Context.MODE_PRIVATE);
     Log.i(TAG, "Saving regId on app version "   appVersion);
     SharedPreferences.Editor editor = prefs.edit();
     editor.putString("registration_id", regid);
     editor.putInt("appVersion", appVersion);
     editor.commit();
   
 }
 
 
 private void sendRegistrationIdToBackend() {
  URI url = null;
  try {
   url = new URI("https://api2.pushassist.com/device/subscribe/");
	//IMPORTANT POST JSON HERE LIKE
	//AUTH KEYS in Header Like
	//X-Auth-Token : {{YOUR AUTH KEY}}
	//X-Auth-Secret: {{YOUR SECRET KEY}}
	/*{
		 "device_token" : "jaskldfhjaksdfk789asdfsdff",
		 "device_type": "Android",
		 "ip_address" : "192.168.1.1",
		 "channel_ids" : "1,3"
	  }
	 */
  } catch (URISyntaxException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  HttpClient httpclient = new DefaultHttpClient();
  HttpGet request = new HttpGet();
  request.setURI(url);
  try {
   httpclient.execute(request);
  } catch (ClientProtocolException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 
 @Override
 protected void onPostExecute(String result) {
  super.onPostExecute(result);
  Toast.makeText(ctx, "Registration Completed.", Toast.LENGTH_SHORT).show();
  Log.v(TAG, result);
 }
}

MainActivity.java

package com.programmingtechniques.gcmdemo;
 
import java.util.concurrent.atomic.AtomicInteger;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
public class MainActivity extends Activity {
  
 private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
 public static final String EXTRA_MESSAGE = "message";
 public static final String PROPERTY_REG_ID = "registration_id";
 private static final String PROPERTY_APP_VERSION = "appVersion";
 private static final String TAG = "GCMRelated";
 GoogleCloudMessaging gcm;
 AtomicInteger msgId = new AtomicInteger();
 String regid;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  final Button button = (Button) findViewById(R.id.register);
   
  if (checkPlayServices()) {
      gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
            regid = getRegistrationId(getApplicationContext());
            if(!regid.isEmpty()){
             button.setEnabled(false);
            }else{
             button.setEnabled(true);
            }
  }
   
  button.setOnClickListener(new View.OnClickListener() {
    
   @Override
   public void onClick(View view) {
    // Check device for Play Services APK.
       if (checkPlayServices()) {
        gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
              regid = getRegistrationId(getApplicationContext());
               
              if (regid.isEmpty()) {
               button.setEnabled(false);
                  new RegisterApp(getApplicationContext(), gcm, getAppVersion(getApplicationContext())).execute();
              }else{
               Toast.makeText(getApplicationContext(), "Device already Registered", Toast.LENGTH_SHORT).show();
              }
       } else {
              Log.i(TAG, "No valid Google Play Services APK found.");
       }
   }
  });
   
   
 }
 
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }
  
 /**
  * Check the device to make sure it has the Google Play Services APK. If
  * it doesn't, display a dialog that allows users to download the APK from
  * the Google Play Store or enable it in the device's system settings.
  */
  
 private boolean checkPlayServices() {
     int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
     if (resultCode != ConnectionResult.SUCCESS) {
         if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
             GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                     PLAY_SERVICES_RESOLUTION_REQUEST).show();
         } else {
             Log.i(TAG, "This device is not supported.");
             finish();
         }
         return false;
     }
     return true;
 }
  
 /**
  * Gets the current registration ID for application on GCM service.
  *
  * If result is empty, the app needs to register.
  *
  * @return registration ID, or empty string if there is no existing
  *         registration ID.
  */
 private String getRegistrationId(Context context) {
     final SharedPreferences prefs = getGCMPreferences(context);
     String registrationId = prefs.getString(PROPERTY_REG_ID, "");
     if (registrationId.isEmpty()) {
         Log.i(TAG, "Registration not found.");
         return "";
     }
     // Check if app was updated; if so, it must clear the registration ID
     // since the existing regID is not guaranteed to work with the new
     // app version.
     int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
     int currentVersion = getAppVersion(getApplicationContext());
     if (registeredVersion != currentVersion) {
         Log.i(TAG, "App version changed.");
         return "";
     }
     return registrationId;
 }
  
 /**
  * @return Application's {@code SharedPreferences}.
  */
 private SharedPreferences getGCMPreferences(Context context) {
  // This sample app persists the registration ID in shared preferences, but
     // how you store the regID in your app is up to you.
     return getSharedPreferences(MainActivity.class.getSimpleName(),
             Context.MODE_PRIVATE);
 }
  
 /**
  * @return Application's version code from the {@code PackageManager}.
  */
 private static int getAppVersion(Context context) {
     try {
         PackageInfo packageInfo = context.getPackageManager()
                 .getPackageInfo(context.getPackageName(), 0);
         return packageInfo.versionCode;
     } catch (NameNotFoundException e) {
         // should never happen
         throw new RuntimeException("Could not get package name: "   e);
     }
 }
}

When you run the above android application, it displays a layout with a simple button. After clicking the button, the application checks whether it has registration id or not. If not then the application registers the device in background. Once it gets the registration id, it sends that Id to the PushAssist server where it's stored in our cloud. The application now can receive the messages sent by the application server. Whenever it sense the new message, then it pushes the message as a notification using notification manager.

Don't forget to add your Keys whereever applicable. Also, we are here to help, should you need assistance or have any question please let me know.