Welcome to the Android Push Notification using GCM tutorial.

Introduction: Many app developers still struggle to implement Android Push Notification in their apps/games.So what is actually a push notification.Push Notification are used by developers to inform user about new messages,events or any other thing which they can see without opening their app.

In this tutorial i am going to tell you how to use GCM (Google Cloud Messaging) to send push notification on Android device.You can use this code in your android app,games made with LibGDX,Cocos2d-x with little modification.This tutorial contains PHP,MySQL & JAVA code.

Step by Step sheet

  • Getting project id & project number from Google developers site.
  • Application Code
  • Server side code (PHP,MySQL)
  • Sending push notifications

Let’s get started!

Getting project id & project number from Google developers site.

Go to this site https://developers.google.com/mobile/add?platform=android (Make sure you are logged in your google account)

  • Fill app name – Push Testing
  • Fill package name – com.developerhouse.push
  • Click on “continue to” button
Google Project Console

Google Project Console

 

wait for few seconds it will take some time.
click on enable Google cloud messaging

Enable Cloud Messaging

Enable Cloud Messaging

Now the project has been created we have to go on this link https://console.developers.google.com/ to edit IP address from which we can make request to send push messages & to get the API Key & Project ID

Select project from drop down on top ,note down the Project ID & Project Number

Select Project From Console

Select Project From Console

Now Click on Credentials sub menu button under API & auth

you will see something like this, note down the API Key and click on it

Select Credentials in Project Console

Select Credentials in Project Console

 

This is a optional step to make your push notification secure by adding the ip address of your server

Add IP Address

Add IP Address

add ip address & click on save button

Application Code

We have to write the code for android application which will send the device id first time so that it can be save on our server
Create a application with the same package name which we have used while setting up GCM. Add google play services library in your project

AndroidManifest.xml

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

 <uses-sdk
 android:minSdkVersion="10"
 android:targetSdkVersion="21" />
 <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
 <uses-permission android:name="android.permission.WAKE_LOCK"/>
 <uses-permission android:name="android.permission.VIBRATE"/>
 <uses-permission android:name="android.permission.INTERNET"/>
 <application
 android:allowBackup="true"
 android:icon="@drawable/ic_launcher"
 android:label="@string/app_name" >
 <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
 <activity
 android:name=".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>
 <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.developerhouse.push"/>
 </intent-filter>
 </receiver>

<service
 android:name="com.developerhouse.push.MyGcmListenerService"
 android:exported="false" >
 <intent-filter>
 <action android:name="com.google.android.c2dm.intent.RECEIVE" />
 </intent-filter>
 </service>
 <service
 android:name="com.developerhouse.push.MyInstanceIDListenerService"
 android:exported="false">
 <intent-filter>
 <action android:name="com.google.android.gms.iid.InstanceID"/>
 </intent-filter>
 </service>
 <service
 android:name="com.developerhouse.push.RegistrationIntentService"
 android:exported="false">
 </service>
 </application>

</manifest>

This class is used for registering the Token in our web server .First it will get the token then it will save on our web server.

RegistrationIntentService.java

 

package com.developerhouse.push;

import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class RegistrationIntentService extends IntentService {

 private static final String TAG = "RegServicePush";

 public RegistrationIntentService() {
 super(TAG);
 }

 @Override
 protected void onHandleIntent(Intent intent) {

 try {
 InstanceID instanceID = InstanceID.getInstance(this);
 String token = instanceID.getToken(Constants.GCM_SENDER_ID,
 GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

 // TODO: Implement this method to send any registration to your app's servers.
 sendRegistrationToServer(token);

 } catch (Exception e) {
 Log.d(TAG, "Failed to complete token refresh", e);
 }
 // Notify UI that registration has completed, so the progress indicator can be hidden.

 }

 private void sendRegistrationToServer(String token) {MainActivity.newRegID=token;
 WebServerRegistrationTask webServer=new WebServerRegistrationTask();
 webServer.execute();
 }

public class WebServerRegistrationTask extends AsyncTask<Void, Void, Void> {

 @Override
 protected Void doInBackground(Void... params) {
 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RegistrationIntentService.this);

 URL url = null;
 try {
 url = new URL(Constants.WEB_SERVER_URL);
 } catch (MalformedURLException e) {
 e.printStackTrace();

 sharedPreferences.edit().putString(Constants.PREF_GCM_REG_ID, "").apply();
 }
 Map<String, String> dataMap = new HashMap<String, String>();
 dataMap.put("regID", MainActivity.newRegID);

 StringBuilder postBody = new StringBuilder();
 Iterator<Map.Entry<String, String>> iterator = dataMap.entrySet().iterator();

 while (iterator.hasNext()) {
 Entry<String,String> param = (Entry<String,String>) iterator.next();
 postBody.append(param.getKey()).append('=')
 .append(param.getValue());
 if (iterator.hasNext()) {
 postBody.append('&');
 }
 }
 String body = postBody.toString();
 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");

 OutputStream out = conn.getOutputStream();
 out.write(bytes);
 out.close();
 String response="";
 InputStream is = null;
 try {
 is = conn.getInputStream();
 int ch;
 StringBuffer sb = new StringBuffer();
 while ((ch = is.read()) != -1) {
 sb.append((char) ch);
 }
 response=sb.toString();

 } catch (IOException e) {
 throw e;
 } finally {
 if (is != null) {
 is.close();
 }
 }
 int status = conn.getResponseCode();
 if (status == 200) {
 if(response.equals("1")){
 sharedPreferences.edit().putString(Constants.PREF_GCM_REG_ID, MainActivity.newRegID).apply();
 Intent registrationComplete = new Intent(Constants.SERVER_SUCCESS);
 LocalBroadcastManager.getInstance(RegistrationIntentService.this).sendBroadcast(registrationComplete);
 }
 } else {
 throw new IOException("Request failed with error code "
 + status);
 }
 } catch (ProtocolException pe) {
 pe.printStackTrace();
 sharedPreferences.edit().putString(Constants.PREF_GCM_REG_ID, "").apply();

 } catch (IOException io) {
 io.printStackTrace();
 sharedPreferences.edit().putString(Constants.PREF_GCM_REG_ID, "").apply();

 } finally {
 if (conn != null) {
 conn.disconnect();
 }
 }

 return null;
 }
}
}

MyInstanceIDListenerService.java

package com.developerhouse.push;

import android.content.Intent;
import com.google.android.gms.iid.InstanceIDListenerService;

public class MyInstanceIDListenerService extends InstanceIDListenerService {

 @Override
 public void onTokenRefresh() {

 Intent intent = new Intent(this, RegistrationIntentService.class);
 startService(intent);
 }

}

MyGcmListenerService.java

package com.developerhouse.push;

import com.google.android.gms.gcm.GcmListenerService;

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;

public class MyGcmListenerService extends GcmListenerService {

 public static final int MESSAGE_NOTIFICATION_ID = 435345;
 private NotificationManager mNotificationManager;

 @Override
 public void onMessageReceived(String from, Bundle data) {
 String message = data.getString("message");

 createNotification( message);
 }

 // Creates notification based on title and body received
 private void createNotification( String body) {
 mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

 Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
 long[] pattern = {500};

 PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
 new Intent(this, MainActivity.class), 0);
 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
 this).setSmallIcon(R.drawable.ic_launcher)
 .setContentTitle("Push Notification Test").setVibrate(pattern)
 .setStyle(new NotificationCompat.BigTextStyle().bigText(body))
 .setContentText(body)
 .setAutoCancel(true).setSound(sound);
 mBuilder.setContentIntent(contentIntent);
 mNotificationManager.notify(MESSAGE_NOTIFICATION_ID, mBuilder.build());

 }
}

Line 20 : MESSAGE_NOTIFICATION_ID is used to make notification same for example if you send another notification and user have not read the old one it will replace the old one
Line 24 : Method which will receive the content from GCM
Line 34 : Sound for notification
Line 35 : Vibration Pattern
Line 37 : Intent which will run on click of notification you can open link to browser or you can open the activity

Constants.java

package com.developerhouse.push;
public class Constants {
 public static String PREF_GCM_REG_ID = "PREF_GCM_REG_ID";
 public static String GCM_SENDER_ID = "339415449819";
 public static String WEB_SERVER_URL = "http://192.168.1.103/push/reg.php";
 public static final String SERVER_SUCCESS="server_success";
}

GCM_SENDER_ID = Project ID
WEB_SERVER_URL = URL to register user unique id

MainActivity.java

package com.developerhouse.push;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends Activity {
 private static final String TAG = "MainActivity";

 public static String newRegID="";

 private BroadcastReceiver mRegistrationBroadcastReceiver;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);

 mRegistrationBroadcastReceiver = new BroadcastReceiver() {
 @Override
 public void onReceive(Context context, Intent intent) {

 SharedPreferences sharedPreferences =
 PreferenceManager.getDefaultSharedPreferences(context);
 String sentToken = sharedPreferences
 .getString(Constants.PREF_GCM_REG_ID, "");
 System.out.println("SERVER_SUCCESS")
; if (sentToken.equals("")) {
 Toast.makeText(MainActivity.this, "Failed to save on server", Toast.LENGTH_SHORT).show();
 } else {
 Toast.makeText(MainActivity.this, "Succesfully saved on server", Toast.LENGTH_SHORT).show();
 }
 }
 };
 if (checkPlayServices()) {

 Intent intent = new Intent(this, RegistrationIntentService.class);
 startService(intent);
 }

 }
 @Override
 protected void onResume() {
 super.onResume();
 LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
 new IntentFilter(Constants.SERVER_SUCCESS));
 }

 @Override
 protected void onPause() {
 LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
 super.onPause();
 }
 private boolean checkPlayServices() {
 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
 if (resultCode != ConnectionResult.SUCCESS) {
 if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
 GooglePlayServicesUtil.getErrorDialog(resultCode, this,
 9000).show();
 } else {
 Log.i(TAG, "This device is not supported.");
 finish();
 }
 return false;
 }
 return true;
 }
}</pre>
<pre>

Server side code (PHP,MySQL)
In Server side we have to create a 4 PHP scripts for inserting the registration id & sending push notification.I am not going to explain basic steps to create PHP Script and MySQL DB for that you can check this link

http://developerhouse.com/blog/2015/08/tutorial-1-connect-android-with-php-mysql/
I am not focusing on design of the admin panel or anything related to it because our main focus will be on coding and getting your work done by adding Login Page(some of readers will be using it in their apps so it can be misuse)
Also i am not saving the username and pass in MySQL DB there is no need for it at this moment it’s just for learning purpose so that android developer can easily get push notification implemented without needing any PHP developer for php code.

Create Database named push
Table named registration

PHPMyAdmin Registration Table

PHPMyAdmin Registration Table

These are the structure of these tables

Name Type Length/Values A I (Auto Increment)
id INT Yes
gcm_regid VARCHAR 300 No (By Default)
created_at timestamp No (By Default)

 

PHP Files
connect.php // Change this for settings of MySQL DB
login.php // To login into admin panel
reg.php // For saving registration ID’s
send.php // For sending the push notification

connect.php
It’s used for changing login page username,password & also for changing mysql details.

<?php
define("apiKey","AIzaXXXXXXXXXXXXXXXXXXXXXXXX");//Fill the key which we got from Developer Console
$user="admin";
$pass="password";
$host="localhost";
$db_user_name="root";
$db_password="";
$db_name="push";
$con = new PDO('mysql:host='.$host.';dbname='.$db_name.';charset=utf8', $db_user_name, $db_password);
$con->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
?>

login.php

It’s used for login into Android Push Notification panel so that no one can access it without username and password .You can change the username & password in connect.php file


<?php
session_start();
if (isset($_POST['submit'])) {
if (empty($_POST['username']) || empty($_POST['password'])) {
echo "Login is invalid";
}
else
{
$username=$_POST['username'];
$password=$_POST['password'];
include_once "connect.php";
if ($username==$user & $password==$pass) {
$_SESSION['login_user']=$user;
header("location: send.php");
exit();
} else {
echo "Login is invalid";
}

}
}
if(isset($_SESSION['login_user'])){
header("location: send.php");
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Admin Panel Login: Developer House</title>
</head>
<body>
<div id="main">
<h1>Admin Panel Login Push Notification</h1>
<div id="login">
<h2>Login Form</h2>
<form action="" method="post">
<label>Username :</label>
<input id="name" name="username" placeholder="username" type="text">
<label>Password :</label>
<input id="password" name="password" placeholder="**********" type="password">
<input name="submit" type="submit" value=" Login ">
</form>
</div>
</div>
</body>
</html>

reg.php
This is the file which will be requested through Android platform via HTTP Request.

<?php

include_once "connect.php";
 function storeUser($con,$gcm_regid) {

 $stmt = $con->prepare("INSERT INTO registration(gcm_regid, created_at) VALUES(:field, NOW())");
$stmt->execute(array(':field' => $gcm_regid));

 $id = $con->lastInsertId();

 $result = $con->query("SELECT * FROM registration WHERE id = $id");
 if ($result->rowCount() > 0) {
 return true;
 } else {
 return false;
 }
 }

 function getSame($con,$gcm_regid) {

 $result =$con->query("SELECT * FROM registration WHERE gcm_regid=\"".$gcm_regid."\"");

 if ($result->rowCount()>0) {
 return true;
 } else {
 return false;
 }
 }
if (isset($_POST["regID"])) { 

 $gcm_regid = $_POST["regID"];

if(!getSame($con,$gcm_regid)){
storeUser($con,$gcm_regid);
 echo "1";

 }else{echo "0";}
} else {
echo "0";
}?>

send.php
This file is used for sending the push notification of our choice to the all users in group.

<?php
session_start();
include_once 'connect.php';

function send_notification($con,$registatoin_ids, $message) {
 $url = 'https://android.googleapis.com/gcm/send';

 $fields = array(
 'data' =>array("message" => $message),
 'registration_ids' => $registatoin_ids
 );
 $headers = array(
 'Authorization: key=' . apiKey,
 'Content-Type: application/json'
 );
 $ch = curl_init();
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_POST, true);
 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
 $result = curl_exec($ch);
 if ($result === FALSE) {
 die('Curl failed: ' . curl_error($ch));
 }
 curl_close($ch);
 echo $result;

 }

if(isset($_SESSION['login_user'])){
 if(isset($_POST["submit"]) && isset($_POST["message"])) {
$num=$con->query("SELECT gcm_regid from registration")->rowCount();
$current_num=0;
$message=$_POST["message"];
for($i=0;$i<$num/1000;$i++){

$query=$con->query("SELECT gcm_regid from registration LIMIT $current_num,1000");

foreach($query as $data) {
$row[]=$data["gcm_regid"];
}

$pushStatus = send_notification($con,$row, $message);
$current_num+=1000;
}
}else if(isset($_POST["logout"])){

if(session_destroy()) // Destroying All Sessions
{
header("Location: login.php"); // Redirecting To Home Page
}
}

?>

<?php
?><!DOCTYPE html>
<html>
 <head>
 <title>Send Push Notification : Developer House</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 </head>
 <body>
 <?php

 $result = $con->query("SELECT * FROM registration");

 if ($result != false)
 $no_of_users =$result->rowCount();
 else
 $no_of_users = 0;
 ?>
 <div >
 <h1>No of Devices Registered: <?php echo $no_of_users; ?></h1>
 <hr/>
 <?php
 if ($no_of_users > 0) {
 ?>

 <form method="POST" action="">
 <textarea rows="3" name="message" cols="25" placeholder="Type message here"></textarea>
 <button type="submit" name="submit" value="Submit">Submit</button>
 <button type="submit" name="logout" value="Submit">Logout</button>
 </div>
 </form>
 <?php
 } else { ?>
 <li>
 No Users Registered!
 </li>
 <?php }
 }?>
 </div>
 </body>
</html>

 

Sending push notifications

Now if you have followed the tutorial this step will work for you.

So steps are

  1. Open Android Costants.java file & replace GCM_SENDER_ID variable value to the project number from project console ,WEB_SERVER_URL value to the one with your server url and file path for example if you have uploaded in wamp www then check IP of your computer it should be like this http://192.168.1.103/reg.php or if you have used a Linux Hosting and uploaded in public_html your main domain is developerhouse.com then it will be http://developerhouse.com/reg.php
  2. Open connect.php change apiKey value to the one we got from project console Api & auth -> Credentials
  3. Compile & Run the android application . Don’t worry if it does not show anything because we have not made it for this purpose .
  4. It will show Toast Registered with web server if everything works well
  5. Now check PHPMyAdmin it will have a new value in registration table
  6. Now to send the Push Notification go to this url http://192.168.1.103/login.php .Fill username & password.You will be redirected to the http://192.168.1.103/send.php file it will show no. of registered user & a textarea with two buttons
  7. Press submit & check notification.
  8. Finish

Code Download Click here (Dropbox)

Few Notes

  • In send.php i have used 1000 limit code because i had read this on few sites that in 1 request you cannot send push notification to more then 1000 device id so i have created a loop for this (Of-course you can try other approach)
  • I have not gave my focus to the design of Login page or Send page because you are admin you don’t have to show this page to the world But a nice design is always best so feel free to change it .
  • I have edited the code from first version because it was not using MyInstanceIDListenerService class it’s useful if the token got expired or compromised it will refresh the token in onTokenRefresh which will call RegistrationIntentService .Another reason to edit the post was using PDO in PHP Files which can helps us to prevent SQL Injection.