The 1st Part covers the server side implementation of the app i.e building the REST API and the server app. In this part we’ll integrate the GCM in your new / existing android app. In the 3rd part the final chat app modules will be integrated.
This article also provides you a test interface where you can test any of your gcm apps by sending a sample push notification message.
I am also posting the project structure of the Android Project to get a clear picture file structure needed to build the final app.
Integrating GCM In Your Project
Once you got the idea of the project structure, let’s start add the gcm into your app.
1. Create a new project in Android Studio and fill all the details required. When it prompts to select a default activity, select Blank Activity and proceed.
2. The next step is downloading the google-services.json file. This configuration file contains google services information specific to your app. You can configure google services like cloud messaging, google analytics, admob, sign in with google in the same configuration file. But in this article, we’ll choose only the gcm.
Goto this doc and click on Get a configuration file which prompts you to fill quick app related information.
3. On the developer console, choose the project that you previously created in Part 1, give the current project’s package name. Enable Google Cloud Messaging and click on Download google-services.json.
4. Add the downloaded google-services.json to your project’s app folder. Make sure that you are placing this json file correctly under app folder. Otherwise you won’t be successful in implementing the gcm.
5. Open build.gradle located under app level and do the below changes.
add apply plugin: ‘com.google.gms.google-services’ at the top.
add com.google.android.gms:play-services:8.3.0 dependency
apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' android { compileSdkVersion 'Google Inc.:Google APIs:23' buildToolsVersion "23.0.2" defaultConfig { applicationId "info.androidhive.gcm" minSdkVersion 15 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.+' compile 'com.android.support:design:23.+' compile "com.google.android.gms:play-services:8.3.0" }
6. One more important change is, open build.gradle located in root directory of the project and add
classpath ‘com.google.gms:google-services:1.5.0-beta2’
classpath ‘com.android.tools.build:gradle:2.0.0-alpha6’ in dependency and clean the project from Build => Clean Project.
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.0.0-alpha6' classpath 'com.google.gms:google-services:1.5.0-beta2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
7. Now under your project’s main package, create four packages named activity, app, gcm and helper. Move your main activity class into activity package.
8. Under app package, create a class named Config.java. This class contains app configuration information related to gcm.
package info.androidhive.gcm.app; /** * Created by Lincoln on 05/01/16. */ public class Config { // flag to identify whether to show single line // or multi line text in push notification tray public static boolean appendNotificationMessages = true; // global topic to receive app wide push notifications public static final String TOPIC_GLOBAL = "global"; // broadcast receiver intent filters public static final String SENT_TOKEN_TO_SERVER = "sentTokenToServer"; public static final String REGISTRATION_COMPLETE = "registrationComplete"; public static final String PUSH_NOTIFICATION = "pushNotification"; // type of push messages public static final int PUSH_TYPE_CHATROOM = 1; public static final int PUSH_TYPE_USER = 2; // id to handle the notification in the notification try public static final int NOTIFICATION_ID = 100; public static final int NOTIFICATION_ID_BIG_IMAGE = 101; }
9. Under helper package, create a class named MyPreferenceManager.java This class stores data in SharedPreferences. Here we temporarily stores the unread push notifications in order to append them to new messages.
package info.androidhive.gcm.helper; import android.content.Context; import android.content.SharedPreferences; public class MyPreferenceManager { private String TAG = MyPreferenceManager.class.getSimpleName(); // Shared Preferences SharedPreferences pref; // Editor for Shared preferences SharedPreferences.Editor editor; // Context Context _context; // Shared pref mode int PRIVATE_MODE = 0; // Sharedpref file name private static final String PREF_NAME = "androidhive_gcm"; // All Shared Preferences Keys private static final String KEY_USER_ID = "user_id"; private static final String KEY_USER_NAME = "user_name"; private static final String KEY_USER_EMAIL = "user_email"; private static final String KEY_NOTIFICATIONS = "notifications"; // Constructor public MyPreferenceManager(Context context) { this._context = context; pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE); editor = pref.edit(); } public void addNotification(String notification) { // get old notifications String oldNotifications = getNotifications(); if (oldNotifications != null) { oldNotifications += "|" + notification; } else { oldNotifications = notification; } editor.putString(KEY_NOTIFICATIONS, oldNotifications); editor.commit(); } public String getNotifications() { return pref.getString(KEY_NOTIFICATIONS, null); } public void clear() { editor.clear(); editor.commit(); } }
10. Create a class named MyApplication.java under app package. This is a singleton class needs to be added to AndroidManifest.xml file.
package info.androidhive.gcm.app; import android.app.Application; import info.androidhive.gcm.helper.MyPreferenceManager; public class MyApplication extends Application { public static final String TAG = MyApplication.class .getSimpleName(); private static MyApplication mInstance; private MyPreferenceManager pref; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized MyApplication getInstance() { return mInstance; } public MyPreferenceManager getPrefManager() { if (pref == null) { pref = new MyPreferenceManager(this); } return pref; } }
11. Create a folder named raw under res. Download notification.mp3 and paste it in res => raw folder. This is a custom notification sound which will be played whenever a new push notification is received.
12. Create a class named NotificationUtils.java under gcm package. This class is used to show the notification in notification tray. This also contains major functions like checking app’s running state (background / foreground), downloading notification image attachment from url, playing notification sound and clearing notification messages.
package info.androidhive.gcm.gcm; import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.support.v4.app.NotificationCompat; import android.text.Html; import android.text.TextUtils; import android.util.Patterns; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import info.androidhive.gcm.R; import info.androidhive.gcm.app.Config; import info.androidhive.gcm.app.MyApplication; /** * Created by Ravi on 01/06/15. */ public class NotificationUtils { private static String TAG = NotificationUtils.class.getSimpleName(); private Context mContext; public NotificationUtils() { } public NotificationUtils(Context mContext) { this.mContext = mContext; } public void showNotificationMessage(String title, String message, String timeStamp, Intent intent) { showNotificationMessage(title, message, timeStamp, intent, null); } public void showNotificationMessage(final String title, final String message, final String timeStamp, Intent intent, String imageUrl) { // Check for empty push message if (TextUtils.isEmpty(message)) return; // notification icon final int icon = R.mipmap.ic_launcher; intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); final PendingIntent resultPendingIntent = PendingIntent.getActivity( mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT ); final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( mContext); final Uri alarmSound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + mContext.getPackageName() + "/raw/notification"); if (!TextUtils.isEmpty(imageUrl)) { if (imageUrl != null && imageUrl.length() > 4 && Patterns.WEB_URL.matcher(imageUrl).matches()) { Bitmap bitmap = getBitmapFromURL(imageUrl); if (bitmap != null) { showBigNotification(bitmap, mBuilder, icon, title, message, timeStamp, resultPendingIntent, alarmSound); } else { showSmallNotification(mBuilder, icon, title, message, timeStamp, resultPendingIntent, alarmSound); } } } else { showSmallNotification(mBuilder, icon, title, message, timeStamp, resultPendingIntent, alarmSound); playNotificationSound(); } } private void showSmallNotification(NotificationCompat.Builder mBuilder, int icon, String title, String message, String timeStamp, PendingIntent resultPendingIntent, Uri alarmSound) { NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); if (Config.appendNotificationMessages) { // store the notification in shared pref first MyApplication.getInstance().getPrefManager().addNotification(message); // get the notifications from shared preferences String oldNotification = MyApplication.getInstance().getPrefManager().getNotifications(); List<String> messages = Arrays.asList(oldNotification.split("\\|")); for (int i = messages.size() - 1; i >= 0; i--) { inboxStyle.addLine(messages.get(i)); } } else { inboxStyle.addLine(message); } Notification notification; notification = mBuilder.setSmallIcon(icon).setTicker(title).setWhen(0) .setAutoCancel(true) .setContentTitle(title) .setContentIntent(resultPendingIntent) .setSound(alarmSound) .setStyle(inboxStyle) .setWhen(getTimeMilliSec(timeStamp)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), icon)) .setContentText(message) .build(); NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(Config.NOTIFICATION_ID, notification); } private void showBigNotification(Bitmap bitmap, NotificationCompat.Builder mBuilder, int icon, String title, String message, String timeStamp, PendingIntent resultPendingIntent, Uri alarmSound) { NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); bigPictureStyle.setBigContentTitle(title); bigPictureStyle.setSummaryText(Html.fromHtml(message).toString()); bigPictureStyle.bigPicture(bitmap); Notification notification; notification = mBuilder.setSmallIcon(icon).setTicker(title).setWhen(0) .setAutoCancel(true) .setContentTitle(title) .setContentIntent(resultPendingIntent) .setSound(alarmSound) .setStyle(bigPictureStyle) .setWhen(getTimeMilliSec(timeStamp)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), icon)) .setContentText(message) .build(); NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(Config.NOTIFICATION_ID_BIG_IMAGE, notification); } /** * Downloading push notification image before displaying it in * the notification tray */ public Bitmap getBitmapFromURL(String strURL) { try { URL url = new URL(strURL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); InputStream input = connection.getInputStream(); Bitmap myBitmap = BitmapFactory.decodeStream(input); return myBitmap; } catch (IOException e) { e.printStackTrace(); return null; } } // Playing notification sound public void playNotificationSound() { try { Uri alarmSound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + MyApplication.getInstance().getApplicationContext().getPackageName() + "/raw/notification"); Ringtone r = RingtoneManager.getRingtone(MyApplication.getInstance().getApplicationContext(), alarmSound); r.play(); } catch (Exception e) { e.printStackTrace(); } } /** * Method checks if the app is in background or not */ public static boolean isAppIsInBackground(Context context) { boolean isInBackground = true; ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) { List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) { if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { for (String activeProcess : processInfo.pkgList) { if (activeProcess.equals(context.getPackageName())) { isInBackground = false; } } } } } else { List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1); ComponentName componentInfo = taskInfo.get(0).topActivity; if (componentInfo.getPackageName().equals(context.getPackageName())) { isInBackground = false; } } return isInBackground; } // Clears notification tray messages public static void clearNotifications() { NotificationManager notificationManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancelAll(); } public static long getTimeMilliSec(String timeStamp) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { Date date = format.parse(timeStamp); return date.getTime(); } catch (ParseException e) { e.printStackTrace(); } return 0; } }
13. Create a class named GcmIntentService.java under gcm package. This service extends IntentService which acts as a background service. This service basically used for three purposes.
> To connect with gcm server and fetch the registration token. Uses registerGCM() method.
> Subscribe to a topic. Uses subscribeToTopic(“topic”) method.
> Unsubscribe from a topic. Uses unsubscribeFromTopic(“topic”) method.
package info.androidhive.gcm.gcm; import android.app.IntentService; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.widget.Toast; import com.google.android.gms.gcm.GcmPubSub; import com.google.android.gms.gcm.GoogleCloudMessaging; import com.google.android.gms.iid.InstanceID; import java.io.IOException; import info.androidhive.gcm.R; import info.androidhive.gcm.app.Config; public class GcmIntentService extends IntentService { private static final String TAG = GcmIntentService.class.getSimpleName(); public GcmIntentService() { super(TAG); } public static final String KEY = "key"; public static final String TOPIC = "topic"; public static final String SUBSCRIBE = "subscribe"; public static final String UNSUBSCRIBE = "unsubscribe"; @Override protected void onHandleIntent(Intent intent) { String key = intent.getStringExtra(KEY); switch (key) { case SUBSCRIBE: // subscribe to a topic String topic = intent.getStringExtra(TOPIC); subscribeToTopic(topic); break; case UNSUBSCRIBE: String topic1 = intent.getStringExtra(TOPIC); unsubscribeFromTopic(topic1); break; default: // if key is not specified, register with GCM registerGCM(); } } /** * Registering with GCM and obtaining the gcm registration id */ private void registerGCM() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); String token = null; try { InstanceID instanceID = InstanceID.getInstance(this); token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); Log.e(TAG, "GCM Registration Token: " + token); // sending the registration id to our server sendRegistrationToServer(token); sharedPreferences.edit().putBoolean(Config.SENT_TOKEN_TO_SERVER, true).apply(); } catch (Exception e) { Log.e(TAG, "Failed to complete token refresh", e); sharedPreferences.edit().putBoolean(Config.SENT_TOKEN_TO_SERVER, false).apply(); } // Notify UI that registration has completed, so the progress indicator can be hidden. Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE); registrationComplete.putExtra("token", token); LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete); } private void sendRegistrationToServer(final String token) { // Send the registration token to our server // to keep it in MySQL } /** * Subscribe to a topic */ public void subscribeToTopic(String topic) { GcmPubSub pubSub = GcmPubSub.getInstance(getApplicationContext()); InstanceID instanceID = InstanceID.getInstance(getApplicationContext()); String token = null; try { token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); if (token != null) { pubSub.subscribe(token, "/topics/" + topic, null); Log.e(TAG, "Subscribed to topic: " + topic); } else { Log.e(TAG, "error: gcm registration id is null"); } } catch (IOException e) { Log.e(TAG, "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage()); Toast.makeText(getApplicationContext(), "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } public void unsubscribeFromTopic(String topic) { GcmPubSub pubSub = GcmPubSub.getInstance(getApplicationContext()); InstanceID instanceID = InstanceID.getInstance(getApplicationContext()); String token = null; try { token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); if (token != null) { pubSub.unsubscribe(token, ""); Log.e(TAG, "Unsubscribed from topic: " + topic); } else { Log.e(TAG, "error: gcm registration id is null"); } } catch (IOException e) { Log.e(TAG, "Topic unsubscribe error. Topic: " + topic + ", error: " + e.getMessage()); Toast.makeText(getApplicationContext(), "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } }
14. Create a class named MyInstanceIDListenerService.java under gcm package. This service invokes onTokenRefresh() method whenever there is a change in gcm registration token.
package info.androidhive.gcm.gcm; import android.content.Intent; import android.util.Log; import com.google.android.gms.iid.InstanceIDListenerService; public class MyInstanceIDListenerService extends InstanceIDListenerService { private static final String TAG = MyInstanceIDListenerService.class.getSimpleName(); /** * Called if InstanceID token is updated. This may occur if the security of * the previous token had been compromised. This call is initiated by the * InstanceID provider. */ @Override public void onTokenRefresh() { Log.e(TAG, "onTokenRefresh"); // Fetch updated Instance ID token and notify our app's server of any changes (if applicable). Intent intent = new Intent(this, GcmIntentService.class); startService(intent); } }
15. Create a class MyGcmPushReceiver.java under gcm package. This is a receiver class in which onMessageReceived() method will be triggered whenever device receives new push notification.
package info.androidhive.gcmtest.gcm; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import com.google.android.gms.gcm.GcmListenerService; import info.androidhive.gcmtest.activity.MainActivity; import info.androidhive.gcmtest.app.Config; public class MyGcmPushReceiver extends GcmListenerService { private static final String TAG = MyGcmPushReceiver.class.getSimpleName(); private NotificationUtils notificationUtils; /** * Called when message is received. * * @param from SenderID of the sender. * @param bundle Data bundle containing message data as key/value pairs. * For Set of keys use data.keySet(). */ @Override public void onMessageReceived(String from, Bundle bundle) { String title = bundle.getString("title"); String message = bundle.getString("message"); String image = bundle.getString("image"); String timestamp = bundle.getString("created_at"); Log.e(TAG, "From: " + from); Log.e(TAG, "Title: " + title); Log.e(TAG, "message: " + message); Log.e(TAG, "image: " + image); Log.e(TAG, "timestamp: " + timestamp); if (!NotificationUtils.isAppIsInBackground(getApplicationContext())) { // app is in foreground, broadcast the push message Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION); pushNotification.putExtra("message", message); LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification); // play notification sound NotificationUtils notificationUtils = new NotificationUtils(); notificationUtils.playNotificationSound(); } else { Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class); resultIntent.putExtra("message", message); if (TextUtils.isEmpty(image)) { showNotificationMessage(getApplicationContext(), title, message, timestamp, resultIntent); } else { showNotificationMessageWithBigImage(getApplicationContext(), title, message, timestamp, resultIntent, image); } } } /** * Showing notification with text only */ private void showNotificationMessage(Context context, String title, String message, String timeStamp, Intent intent) { notificationUtils = new NotificationUtils(context); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); notificationUtils.showNotificationMessage(title, message, timeStamp, intent); } /** * Showing notification with text and image */ private void showNotificationMessageWithBigImage(Context context, String title, String message, String timeStamp, Intent intent, String imageUrl) { notificationUtils = new NotificationUtils(context); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); notificationUtils.showNotificationMessage(title, message, timeStamp, intent, imageUrl); } }
16. Open AndroidManifest.xml and do the below necessary changes.
> Add MyApplication to <application> tag using name attribute
> Add INTERNET, WAKE_LOCK, GET_TASKS and C2D_MESSAGE permissions.
> Add MyGcmPushReceiver & GcmIntentService as services and GcmReceiver as receiver.
> Replace info.androidhive.gcm with your current project’s package name.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.androidhive.gcm"> <uses-permission android:name="android.permission.INTERNET" /> <!-- needed for older devices - used to check app background / foreground status --> <uses-permission android:name="android.permission.GET_TASKS" /> <!-- START Added for GCM --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <permission android:name="info.androidhive.gcm.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="info.androidhive.gcm.permission.C2D_MESSAGE" /> <!-- END Added for GCM --> <application android:allowBackup="true" android:name=".app.MyApplication" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activity.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> <!-- START Added for GCM --> <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="info.androidhive.gcm" /> </intent-filter> </receiver> <service android:name=".gcm.MyGcmPushReceiver" android:exported="false"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> </intent-filter> </service> <service android:name=".gcm.GcmIntentService" android:exported="false"> <intent-filter> <action android:name="com.google.android.gms.iid.InstanceID" /> </intent-filter> </service> <!-- END Added for GCM --> </application> </manifest>
17. Finally open MainActivity.java and make the following changes.
> In order to receive the push notifications, device has to support google play services. So checkPlayServices() method is used to check the availability of google play services. If the play services are not available, we’ll simply close the app.
> Register a broadcast receiver in onResume() method for both REGISTRATION_COMPLETE and PUSH_NOTIFICATION intent filters.
> Unregister the broadcast receiver in onPause() method.
> Create an instance of broadcast receiver in onCreate() method in which onReceive() method will be triggered whenever gcm registration process is completed and a new push message is received.
package info.androidhive.gcm.activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import info.androidhive.gcm.R; import info.androidhive.gcm.app.Config; import info.androidhive.gcm.gcm.GcmIntentService; public class MainActivity extends AppCompatActivity { private String TAG = MainActivity.class.getSimpleName(); private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; private BroadcastReceiver mRegistrationBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); mRegistrationBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // checking for type intent filter if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) { // gcm successfully registered // now subscribe to `global` topic to receive app wide notifications String token = intent.getStringExtra("token"); Toast.makeText(getApplicationContext(), "GCM registration token: " + token, Toast.LENGTH_LONG).show(); } else if (intent.getAction().equals(Config.SENT_TOKEN_TO_SERVER)) { // gcm registration id is stored in our server's MySQL Toast.makeText(getApplicationContext(), "GCM registration token is stored in server!", Toast.LENGTH_LONG).show(); } else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) { // new push notification is received Toast.makeText(getApplicationContext(), "Push notification is received!", Toast.LENGTH_LONG).show(); } } }; if (checkPlayServices()) { registerGCM(); } } // starting the service to register with GCM private void registerGCM() { Intent intent = new Intent(this, GcmIntentService.class); intent.putExtra("key", "register"); startService(intent); } 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. Google Play Services not installed!"); Toast.makeText(getApplicationContext(), "This device is not supported. Google Play Services not installed!", Toast.LENGTH_LONG).show(); finish(); } return false; } return true; } @Override protected void onResume() { super.onResume(); // register GCM registration complete receiver LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, new IntentFilter(Config.REGISTRATION_COMPLETE)); // register new push message receiver // by doing this, the activity will be notified each time a new message arrives LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, new IntentFilter(Config.PUSH_NOTIFICATION)); } @Override protected void onPause() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver); super.onPause(); } }
Now run & deploy the app on a device. Make sure that your device is having internet connection and you followed the above steps correctly. After the app is launched, you should able see the gcm registration token as a Toast message. You can also find the registration token in LogCat.
Testing Push Notifications
In order to test the push notifications, I have created a simple interface where you can type a sample message and send it to devices. Note that the sensitive information (API key & registration token) you are providing is not stored on the server in any manner. Also the demo test panel works when you are following the steps explained in this article only.
1. Goto to the push notifications test panel.
2. Enter your GCM API Key that you have obtained from google developer console.
3. Enter the gcm registration token which is logged in your Android Studio LogCat.
4. Type a message and click on Send Push Notification button. There is also an option provided to test push notification with image attachment.
If you are able to get the push notifications using the test interface, congratulations! You are only a few steps away from building a fully working realtime chat time. So let’s start building the remaining modules in 3rd Part.
Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: ravi@androidhive.info
thanks Ravi
great tutorial
You are welcome 🙂
Hi Ravi,
I can access the portal fine via http://192.168.11.1/gcm_chat/
but when the app call the API http://192.168.11.1/gcm_chat/v1/user/login
i get a 404 Page Not Found ?
{
“error”: true,
“message”: “Oops! An error occurred while registereing”
}
There could be 2 reasons….
* File doesnt exist really
* Or Try http://192.168.11.1/gcm_chat/v1/user/login.php if it works then the problem with htaccess and is not working. You need to restart apache
Is mod rewrite is enabled in your apache?
I am also getting the same error
http://192.168.11.1/gcm_chat/v1/user/login.php shows the FILE NOT FOUND
Postman also giving the same error
The url shoudn’t end with .php
It should be
http://192.168.11.1/gcm_chat/v1/user/login
hi Ravi
i need to use web server insted of local server!
i try to add the files but still not working
>is there any solution for this
i need this too, on my go daddy server i get the volley error : null
hey Ravi i follow this tutorial http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/ what part of this im going to change . .Like what you said all google Api’s you use from there are deprecated.
Just start everything from scratch.
Please I need help I want to put push notifications to my app as one of my features
hi Ravi, first of all thank you for the detailed tutorial. however i would like to use Retrofit instead of volley.. so is there any drawbacks of doing that? or is it fine to go with that + to use retrofit with your demo project approximately how much of the code should i change?
While logging getting error json parsing error: Value true at error of type java.lang.Boolean cannot be converted to JSONObject
Can you post about retrofit
m using latest stable AS 1.5…and i have below line in project level build.gradle:
classpath ‘com.android.tools.build:gradle:1.5.0’
when i try to change it as you meintioned which is:
classpath ‘com.android.tools.build:gradle:2.0.0-alpha6’
it gives me this warning:
Warning:Gradle version 2.10 is required. Current version is 2.8. If using the gradle wrapper, try editing the distributionUrl in C:UsersBhushanAndroidStudioProjectsGCM3.0gradlewrappergradle-wrapper.properties to gradle-2.10-all.zip
so i dug further n changed that gradle-wrapper.properties.. tried to download latest gradle-2.11.zip n gave related path in settings>gradle as well as made 2.11 in project structure>project>gradle version, but still gave me errors.. so does AS1.5 stable not support alpha/beta gradle?? or i need to update AS to latest canary/dev/beta? or is it ok if i stick with gradle:1.5.0 in classpath with that warning?? thanks
Edit: problem solved.
how did you solve your problem??
does this app support one to one chat also instead of just group chap…thanks
someone please reply…thanks
Hello there, I am trying to develop a trivia game app and I want to have the push notification to set up a request to connect. From there both people will be on a screen where they will be answering questions. Do you have any ideas on how I go about this? I am a beginner and I am trying to understand all this material.
You can’t use GCM for this. Check these links
http://gamedev.stackexchange.com/questions/43881/google-cloud-messaging-gcm-for-turn-based-mobile-multiplayer-server
https://developers.google.com/games/services/common/concepts/realtimeMultiplayer
your demo app is not working.. it shows volley timeout error.
So I want to use Skiller SDK platform but their website does not work. But I also want push notification as well to send invites to connect to someone. Will Skiller provide this or GCM?
Sorry, I have no idea about Skiller 🙁
i am trying to upload all file in my webhost server but no thing work at all !!!
any help ???
@Ravi Tamada:disqus Waiting for the update of article http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/
I’ve used the current chat article to send notifications to device, but it is not like previous article, I’m not able to print the received notification on screen, the string extra which we sending through intent to main activity is always null
Can you give me the code of both. The code sending data to main activity and the code receiving the intent in main activity.
@Ravi Tamada:disqus http://paste.ofcode.org/MaQLSVXGNVCcDYafg4rYn2 Here I’ve used Shared preference and Intent to save the message. But, both of them return null. When the Notification is received I need to display it on screen when notification is opened. Similar to GCMMessageView.class of old article
Is the message is printing in Log using Log.e(“msg”,message) ?
No. It’s not. http://paste.ofcode.org/3iHzbuiZ4TcBH9ADksqNzd These are the 2 classes which is handling notification
You need to verify few things.
1. Make sure that you are using correct google API key in php project.
2. Make sure that you have downloaded correct google-services.json and added it to android project.
3. Verify the logs in MyGcmPushReceiver class first as push messages will be received by this class first.
4. In main activity you are reading the push notification wrongly. The push message will be received in the below method, not in onCreate method.
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// checking for type intent filter
if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) {
// gcm successfully registered
// now subscribe to `global` topic to receive app wide notifications
String token = intent.getStringExtra(“token”);
Toast.makeText(getApplicationContext(), “GCM registration token: ” , Toast.LENGTH_LONG).show();
} else if (intent.getAction().equals(Config.SENT_TOKEN_TO_SERVER)) {
// gcm registration id is stored in our server’s MySQL
Toast.makeText(getApplicationContext(), “GCM registration token is stored in server!”, Toast.LENGTH_LONG).show();
} else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
// new push notification is received
Toast.makeText(getApplicationContext(), “Push notification is received! n message is – ” , Toast.LENGTH_LONG).show();
}
}
};
Thumbs up 🙂 I love this tutorial 😀
Thanks
brother Ravi Tamada
thanks aloot
plz want your help to upload php files to 00webhost….
waiting you brother
i have a problem. AndroidRuntime: FATAL EXCEPTION: main Process: PID: 10695
java.lang.NullPointerException
at android.support.v7.widget.RecyclerView.computeVerticalScrollRange(RecyclerView.java:1654)
can you help me?
That is such a wonderful tutorial will you please tell us how to implement a “Poll System (just like twitter have)” in android application?
You can write a service which executes for an interval?
Hi Ravi. I need some help in my project. Do you mind if i contact you?
Yeah tell me.
Hi Ravi, excellent tutorial, I have an error when I test the application on a physical cell , with the simulator I have no problems , the error is in this line
StringRequest strReq = new StringRequest(Request.Method.POST, EndPoints.LOGIN, new Response.Listener() {, thank you
What is the error?
My bad, the error is, Volley error: null, but with genymotion i have no problems with the login, however with a real cellphone the login fail.
What is your url address you are accessing from the device?
Hi Ravi, Thanks for the tutorial. its excellent.. I am facing a problem. During login i am getting Volley error: null, code: null. The username and email id is not added in database. When I am using Postman it works well. Please help me.
Are you accessing the project by ip address?
yes.. I think its volley timeout error.. i tried using this too but still not working
request.setRetryPolicy(new RetryPolicy() {
@Override
public int getCurrentTimeout() {
return 50000;
}
@Override
public int getCurrentRetryCount() {
return 50000;
}
@Override
public void retry(VolleyError error) throws VolleyError {
}
});
same prob i am facing
yes i am using ip
@ravi sir please solve this issue many people are facing the same issue
How to create chat app without using php coding
why we are storing all deta in sql.insted of google storage
Done with 2nd Part of the tutorial without any error. Brilliant tutorial @@Ravi Tamada:disqus Thank you
Keep going 🙂
Hello Ravi, Thank you for this very nice tutorial. But I have got some error while updating build.gradle (both) is as below:
Error:Execution failed for task ‘:app:processDebugGoogleServices’.
> Please fix the version conflict either by updating the version of the google-services plugin (information about the latest version is available at https://bintray.com/android/android-tools/com.google.gms.google-services/) or updating the version of com.google.android.gms to 8.3.0.
Can you please help me to solve this error? I have searched in google but didn’t get proper solution.
Looking forward to your reply.
post your build.gradle
Show Error:InvalidRegistration during place notification. And also when launch app on device it crash first time after toast message of registration token.Second time it run fine.please solve my problem Thanks in advance 🙂
Once again brillient tutorial from Ravi…Thanks
Hi Ravi, First of all thanks a lot for great tutorial. I have implemented this successfully, although I am facing small issue. In main activity, recyclerview notification count n message will not be cleared even if I check the message from chat page.
Code download isn’t working.
Error:Execution failed for task ‘:app:processDebugGoogleServices’.
> File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it.
This is d bug which i have got…and i have google-services.json nut don’t knw wer to paste it…i hav tried al d combination of roots as well…plzz fix dis ASAP ji
it works great when i send a push notification with an image, if i exclude an image it breaks,what could be the problem ? @Ravi Tamada:disqus
Are you seeing any error in logcat?
.NullPointerException
at com.apps.mudau.pfunzo.karabossolution.gcm.NotificationUtils.showSmallNotification(NotificationUtils.java:105)
at com.apps.mudau.pfunzo.karabossolution.gcm.NotificationUtils.showNotificationMessage(NotificationUtils.java:93)
at com.apps.mudau.pfunzo.karabossolution.gcm.NotificationUtils.showNotificationMessage(NotificationUtils.java:53)
at com.apps.mudau.pfunzo.karabossolution.gcm.MyGcmPushReceiver.showNotificationMessage(MyGcmPushReceiver.java:71)
at com.apps.mudau.pfunzo.karabossolution.gcm.MyGcmPushReceiver.onMessageReceived(MyGcmPushReceiver.java:58)
at com.google.android.gms.gcm.GcmListenerService.zzq(Unknown Source)
at com.google.android.gms.gcm.GcmListenerService.zzp(Unknown Source)
at com.google.android.gms.gcm.GcmListenerService.zzo(Unknown Source)
at com.google.android.gms.gcm.GcmListenerService.zza(Unknown Source)
at com.google.android.gms.gcm.GcmListenerService$1.run(Unknown Source)
if the app is opened it works without an image, but if it’s running on the background and i send a push notification without an image it breaks
brother Ravi Tamada
thanks aloot
plz want your help to upload php files to 00webhost….
waiting you brother
Can you explain about batch push notification or any other istead of parse
Batch push notification can be done by sending the push to a channel. All the users who subscribed to that channel gets the notification. By the way this article doesn’t uses parse. It uses direct GCM.
I’m using parse push notification for my app and parse already stopped.so which is good alternative for parse and give me an explanation
Is there any other push notification instead of parse
thanx for this awesome tutorial ! everything’s good, but NotificationCompat.BigPictureStyle is not working on my case, my device always get small notifications. i do some logging and the method “showBigNotification()” on NotificationUtils.class is called, and image URL and bitmap is not null TT (i expect panda bitmap hehe) , anyway thx a lot Ravi 😀
If you are sure that your BigPictureStyle function is called and image is downloaded, there may be space issue to display then notification. Normally image will be hidden if there is not enough space in notification tray. Clear all the notification except your notification also remove the USB as it takes two notification spaces. Also swipe down the notification to see the image.
Let me know if none of them are not fixing the issue.
aaaahh yeeahh you’re right ! i just swipe down the notification and the image is there ! i don’t know it’s have to be like that because other app usually show full notification with image, now it’s complete thanx 😀
Yeah, android hides the image if there is no enough space. Anyway congrats.
Happy Coding 🙂
These 3 articles have cleared all my doubts about GCM and Notifications.And you took about a week to get them ready.Your something else.Please keep up the good work.
Thanks A Million.
You are welcome Trey.
Error:Execution failed for task ‘:app:processDebugGoogleServices’.
> File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it.
This
is d bug which i have got…and i have google-services.json nut don’t
knw wer to paste it…i hav tried al d combination of roots as
well…plzz fix dis ASAP ji
Awesome tutorial …Ravi Sir..
Thank you.
A group can contain up to 20 devices only?
Using this logic how can i implement single user chating?
Error:Execution failed for task ‘:app:processDebugGoogleServices’.
> Please fix the version conflict either by updating the version of the google-services plugin (information about the latest version is available at https://bintray.com/android/android-tools/com.google.gms.google-services/) or updating the version of com.google.android.gms to 8.3.0.
Hey, I’m having this problem and Stack Overflow solutions are not working.
I tried adding plugin line at the bottom of app level build.gradle, but it’s not working either
Can you please look into it?
Error:Execution failed for task ‘:app:processDebugGoogleServices’.
> File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it.
This
is d bug which i have got…and i have google-services.json nut don’t
knw wer to paste it…i hav tried al d combination of roots as
well…plzz fix dis ASAP ji
Remove This from gradle:
apply plugin: ‘com.google.gms.google-services’
and add this in string.xml:
Your Project ID
what is “your project id” means and where do I get this….
Add this google-service.json in app folder or module folder and then after apply plugin your bugs is solve
k i ll try out…tankz fr ur concern
hav tried bt not able to fix it…same popin up
Plugin with id ‘com.google.gms.google-services’ not found in Android Studio 🙁
this possible after putting .json file in app folder without adding this file in app folder you get error
I thought so too, but I have placed the google-services.json in the app folder of the Android project.My project structure shows the google-services.json file exactly as in the given screenshot.
you have also change Project build.gradle by adding this
classpath ‘com.google.gms:google-services:1.5.0-beta2’
Hello Ravi sir,
I first time implement the Push notification concept in android apps.
I have implemented this push notification code in my project and i try for test its give just toast message ” Push notification is received! ” but no notification i shows till now in my device and i test this by your testing tools and one another testing tools both gives just Toast no notification are give.
So what is the problem and why it not shows any notifications.
Thanks & Regards
Is your device getting the gcm token which we printed in LogCat?
Yes
Token Printed in toast and Logcat also This Token and My Apps API Key use for sendinf Push then its just display Push notification Received
If you are getting a Toast saying “Push notification received” then you successfully integrated the gcm. Handling the push notification is covered in the 3rd part.
Thanks for your reply.
if i want notification when my apps is close then i need to refer 3 rd part. 3rd part is for Chat Apps but i don’t want chat apps so can i refer 3rd part
and its shows notification when apps is close because till now i try to get notification then toast is get when apps is open or else its shows apps unfortunately close.
If i need to show notification only from admin then which method i need to implement from 3rd part in client side.
thanks
3rd part is not about just chat app. It’s about handling the push notifications in various activities when the app is foreground / background state. Once you get the complete picture, you will be able integrate the push the way you wanted for your app.
ok sir,
I will try to implement
Thanks
Chavva Tutorial
Hello Sir,
I want to implement push notification in my app through eclipse .where i put google-services.json and how to implement please help as soon as possible
Don’t be afraid of Android Studio. Move to AS right away 🙂
But i want to completed my project in eclipse do help me how to implement push notification in eclipse and sir also provide some in app purchase subscription code through google wallet
Hello sir,
I implement deprecated push notification code all work fine but i didn’t get notification on my mobile.I used your push notifications test panel. to send message message it also show pop message send successful but i didn’t get message on my cell.
Api Key-AIzaSyBgvUlLc_PHr2kNALr80bh8uERGOaYcSXo
Registerred Id-APA91bGiXxrio1t6NXWFe5Z0CJS9nMNxZAB5tnuUqqzm-5TrmFt9jAH1OAV5z-X3uYBinwJdCXI2C1_z7rCMsFmnBfjsMtNredhhFcjuQyPbFxKdus__bqCjxxa-e2PcOtV15_3qvCIj
Please help to solve this. Thanks
Hello.
am getting an error with the R.string.gcm_defaultSenderId in the Gcm_Intent_service. please help me.
Make sure you applied this line in build.gradle
apply plugin: ‘com.google.gms.google-services’
I am struck with the login page. There is no response when i click the enter button
Hello Ravi, your work is awesome,
I have a question, why in my phone I still get notification, whereas I already change GOOGLE_API_KEY and google-services.json with my account. Something missing? thanks.
Normally we need to remove the user from GCM, which I haven’t explained in the article.
why you have to remove the user? this api key and google-services.json is not used your account anymore. Thank you
You have a typo in article 1. You wanted to write XMPP but wrote XAMP.
Thanks Trey. Updated now.