
In this second part we will integrate GCM push notification in our Android app.
For the first part of the tutorial to create GCM Server using MEAN (Mongo, Express, Angular, Node) Stack.
In this second part we will integrate GCM push notification in our Android app.
Complete Project Files
You can download the complete project as zip or fork from our Github repository.
Creating Project
Create a new Android Studio project with the name and package you entered while generating API key. Here I have created a new project with GCMDemo as project name and com.learn2crack.gcmdemo as package.
Adding config file
Add the google-services.json config file which you downloaded after registering your app with Google, to our project’s app directory.
Adding Dependencies
We need to modify both the build.gradle files. Open the Project’s build.gradle file (outer build.gradle). Add the following dependency for the Google Services plugin.
com.google.gms:google-services:2.0.0-alpha6
Then open the app’s build.gradle file and apply the google services plugin,
apply plugin: 'com.google.gms.google-services'
Then add the dependency for Google Play Services,
compile "com.google.android.gms:play-services-gcm:8.3.0"
The other dependencies we use are,
compile 'com.android.support:design:23.3.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.learn2crack.gcmdemo"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile "com.google.android.gms:play-services-gcm:8.3.0"
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
}
Creating Layout
Our layout just has single button to register with GCM Server.
The content_main.xml and activity_main.xml is given as,
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.learn2crack.gcmdemo.MainActivity"
tools:showIn="@layout/activity_main">
<android.support.v7.widget.AppCompatButton
android:id="@+id/btn_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"
android:text="Register Device" />
</RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:id="@+id/coordinatorLayout"
tools:context="com.learn2crack.gcmdemo.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>
Creating Model class
We need to create two model classes to hold Request and Response data. Then these data in these classes will be serialized by Gson Converter which is send as POST request to server and vice versa. The RequestBody class is used to send request.
The RequestBody class has three fields deviceName, deviceId,registrationId. Generate setters for the fields.
RequestBody.java
package com.learn2crack.gcmdemo.models;
public class RequestBody {
private String deviceName;
private String deviceId;
private String registrationId;
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setRegistrationId(String registrationId) {
this.registrationId = registrationId;
}
}
The ResponseBody class is used for receiving response. The ResponseBody class has two fields result and message. Generate getters for the fields.
ResponseBody.java
package com.learn2crack.gcmdemo.models;
public class ResponseBody {
private String result;
private String message;
public String getResult() {
return result;
}
public String getMessage() {
return message;
}
}
Creating Retrofit Interface
We need to create a Interface and define the Request endpoints. Here we use POST request to send JSON data. The endpoint is defined using @POST annotation. Our request URL is http://10.0.2.2:8080/devices, where http://10.0.2.2:8080/ is base url and devices is endpoint.
For our request method registerDevice() the ResponseBody object is wrapped in Call object. By using Call the request is made Asynchronous so you need not worry about UI blocking or AsyncTask.
We have set the RequestBody object as body for the Request. The data stored in RequestBody object is serialized by Gson to JSON and send as request. After the JSON response received it is stored in ResponseBody object. It is defined as,
RequestInterface.java
package com.learn2crack.gcmdemo;
import com.learn2crack.gcmdemo.models.RequestBody;
import com.learn2crack.gcmdemo.models.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface RequestInterface {
@POST("devices")
Call<ResponseBody> registerDevice(@Body RequestBody body);
}
Creating Services
We need to create three services. Lets discuss each. The first one is RegistrationIntentService which extends IntentService. It is launched when Register Button is pressed. Here we obtain the GCM registration Id using getToken() method on InstanceId object.
String registrationId = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
After that registerDeviceProcess() method is called. In that we use Retrofit to make a POST request to our server with GCM registration Id, IMEI and Device name. On getting response the message is broadcasted using LocalBroadcastManager.
RegistrationIntentService.java
package com.learn2crack.gcmdemo;
import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import com.learn2crack.gcmdemo.models.RequestBody;
import com.learn2crack.gcmdemo.models.ResponseBody;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RegistrationIntentService extends IntentService{
public RegistrationIntentService() {
super("RegistrationIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
String deviceId = intent.getStringExtra("DEVICE_ID");
String deviceName = intent.getStringExtra("DEVICE_NAME");
try {
InstanceID instanceID = InstanceID.getInstance(this);
String registrationId = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
registerDeviceProcess(deviceName,deviceId,registrationId);
} catch (IOException e) {
e.printStackTrace();
}
}
private void registerDeviceProcess(String deviceName, String deviceId, String registrationId){
RequestBody requestBody = new RequestBody();
requestBody.setDeviceId(deviceId);
requestBody.setDeviceName(deviceName);
requestBody.setRegistrationId(registrationId);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(http://10.0.2.2:8080/)
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface request = retrofit.create(RequestInterface.class);
Call<ResponseBody> call = request.registerDevice(requestBody);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
ResponseBody responseBody = response.body();
Intent intent = new Intent(MainActivity.REGISTRATION_PROCESS);
intent.putExtra("result", responseBody.getResult());
intent.putExtra("message",responseBody.getMessage());
LocalBroadcastManager.getInstance(RegistrationIntentService.this).sendBroadcast(intent);
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(getApplicationContext(),t.getMessage(),Toast.LENGTH_SHORT).show();
}
});
}
}
The next service is GCMInstanceIDListenerService which extends InstanceIDListenerService. It overrides onTokenRefresh() method, and it is called when the GCM token need to be refreshed.
GCMInstanceIDListenerService.java
package com.learn2crack.gcmdemo;
import android.content.Intent;
import com.google.android.gms.iid.InstanceIDListenerService;
public class GCMInstanceIDListenerService extends InstanceIDListenerService {
@Override
public void onTokenRefresh() {
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
}
The final service is GCMService which extends GcmListenerService. It overrides onMessageReceived() which is called when a new GCM message is received. We broadcast this message using LocalBroadcastManager which we will handle in our Activity using a BroadcastReceiver. We also send a notification to notify that a new message is received. The notification is defined in sendNotification() method.
GCMService.java
package com.learn2crack.gcmdemo;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import com.google.android.gms.gcm.GcmListenerService;
public class GCMService extends GcmListenerService {
@Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("message");
Intent intent = new Intent(MainActivity.MESSAGE_RECEIVED);
intent.putExtra("message",message);
LocalBroadcastManager.getInstance(GCMService.this).sendBroadcast(intent);
sendNotification(message);
}
private void sendNotification(String message) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setAction(MainActivity.MESSAGE_RECEIVED);
intent.putExtra("message",message);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("GCM Message Received")
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
Creating Activity
In our MainActivity we get IMEI from TelephonyManager, for that READ_PHONE_STATE permission is required. The device name is obtained using the Build class.
Initially we create a BroadcastReceiver and register it using LocalBroadcastManager. The BroadcastReceiver handles the message we broadcasted from RegistrationIntentService and GCMService. A dialog is displayed when a new message is received.
The checkPlayServices() method is used to check whether Google Play services is present or not. GCM works only if Google play services is present. The checkPermission() method is used to check whether READ_PHONE_STATE permission is granted or not. If not we request the permission for API 23.
MainActivity.java
package com.learn2crack.gcmdemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.Manifest;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
public class MainActivity extends AppCompatActivity {
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private static final int PERMISSION_REQUEST_CODE = 1;
private static final String TAG = MainActivity.class.getSimpleName();
private Button btn_register;
public static final String REGISTRATION_PROCESS = "registration";
public static final String MESSAGE_RECEIVED = "message_received";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initViews();
registerReceiver();
Intent intent = getIntent();
if(intent != null){
if(intent.getAction().equals(MESSAGE_RECEIVED)){
String message = intent.getStringExtra("message");
showAlertDialog(message);
}
}
}
private void initViews(){
btn_register = (Button) findViewById(R.id.btn_register);
btn_register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (checkPlayServices()) {
startRegisterProcess();
}
}
});
}
private void startRegisterProcess(){
if(checkPermission()){
startRegisterService();
} else {
requestPermission();
}
}
private void startRegisterService(){
Intent intent = new Intent(MainActivity.this, RegistrationIntentService.class);
intent.putExtra("DEVICE_ID", getDeviceId());
intent.putExtra("DEVICE_NAME",getDeviceName());
startService(intent);
}
private void registerReceiver(){
LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(REGISTRATION_PROCESS);
intentFilter.addAction(MESSAGE_RECEIVED);
bManager.registerReceiver(broadcastReceiver, intentFilter);
}
private void showAlertDialog(String message){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("GCM Message Received !");
builder.setMessage(message);
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.show();
}
private String getDeviceId(){
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
return telephonyManager.getDeviceId();
}
private String getDeviceName(){
String deviceName = Build.MODEL;
String deviceMan = Build.MANUFACTURER;
return deviceMan + " " +deviceName;
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(REGISTRATION_PROCESS)){
String result = intent.getStringExtra("result");
String message = intent.getStringExtra("message");
Log.d(TAG, "onReceive: "+result+message);
Snackbar.make(findViewById(R.id.coordinatorLayout),result + " : " + message,Snackbar.LENGTH_SHORT).show();
} else if (intent.getAction().equals(MESSAGE_RECEIVED)){
String message = intent.getStringExtra("message");
showAlertDialog(message);
}
}
};
private boolean checkPermission(){
int result = ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE);
if (result == PackageManager.PERMISSION_GRANTED){
return true;
} else {
return false;
}
}
private void requestPermission(){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_PHONE_STATE},PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startRegisterService();
} else {
Snackbar.make(findViewById(R.id.coordinatorLayout),"Permission Denied, Please allow to proceed !.",Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPlayServices() {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
.show();
} else {
Log.i(TAG, "This device is not supported.");
finish();
}
return false;
}
return true;
}
}
Adding Permissions, GCMService, Receiver in Manifest
The permissions we require are,
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
android:name="com.learn2crack.gcmdemo.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.learn2crack.gcmdemo.permission.C2D_MESSAGE" />
The READ_PHONE_STATE permission is used to read the device’s IMEI number. In the C2D_MESSAGE permission replace com.learn2crack.gcmdemo with your app’s package.
The created services and the GCM broadcast receiver should also be added to manifest.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.learn2crack.gcmdemo">
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
android:name="com.learn2crack.gcmdemo.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.learn2crack.gcmdemo.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<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.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.example.gcm" />
</intent-filter>
</receiver>
<service
android:name="com.learn2crack.gcmdemo.GCMService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service
android:name="com.learn2crack.gcmdemo.GCMInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
<service
android:name="com.learn2crack.gcmdemo.RegistrationIntentService"
android:exported="false">
</service>
</application>
</manifest>
Finally test this in a Emulator or a Device. If you are testing in a emulator use Google API system image since it has Google Play services.
Screenshots
Enjoy Coding 🙂