Firebase Cloud Messaging with Android
Introduction
In this tutorial, I’m going to demonstrate how to send a push notification to your mobile using Firebase. To do that you should create a firebase project and configure your app with Firebase. Anyway, I’m going to do it from the beginning so anybody can understand each and every step.
Step 1
Go to Firebase web site. Create a project inside the Firebase. It is a simple task. Inside the dashboard, you can see an Add project button. When you pressed that it will pop up a dialog and ask the project name. After you entered the project name it will auto-generate a project ID. Then click on the Create button below. It will create a project on firebase and redirected you to the project overview. On the project overview it asks you to add Firebase to your app. select below android icon. there are 4 steps to follow on adding Firebase. As shown in the below
You have to add the Android Package name for this. You can easily find the package name from the project you created from the android studio. Open your created project. Then open Android Manifest file. Then you can find your package name. copy and past the name to the Firebase. There are other two entries which are optional to register the app in Firebase. If you wish you can add them.
Next step is downloading the configured JSON file.
After you downloaded this file from the Firebase you have to add it to the android project app folder as shown in this figure.
Finally, you have to add some code stuff for two files. That is adding dependencies to your Gradle file. Change your app level build.gradle file and project level build.gradle file. Do not confuse those two files.
After adding those two dependencies sync the project. Now you set up Firebase on your android project.
Then add the following dependency to the app level build gradle file.
dependencies {
...........
...........
implementation 'com.google.firebase:firebase-messaging:11.0.4'
}
Step 2
Now we have to create Java classes to handle the notification which is coming from the Firebase to the mobile. The first class we are going to create is a set of constant variables. These constants are going to use by the app in several other classes. So, create a class called Config.java and insert the following code.
package com.example.fcmnotification;
public class Config {
// global topic to receive app wide push notifications
public static final String TOPIC_GLOBAL = "global";
// broadcast receiver intent filters
public static final String REGISTRATION_COMPLETE = "registrationComplete";
public static final String PUSH_NOTIFICATION = "pushNotification";
// id to handle the notification in the notification tray
public static final int NOTIFICATION_ID = 100;
public static final int NOTIFICATION_ID_BIG_IMAGE = 101;
public static final String SHARED_PREF = "ah_firebase";
}
Step 3
Next, create a class called FirebaseInstanceIDService.java.
package com.example.fcmnotification;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class FirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = FirebaseInstanceIDService.class.getSimpleName();
@Override
public void onTokenRefresh() {
super.onTokenRefresh();
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
// Saving reg id to shared preferences
storeRegIdInPref(refreshedToken);
// sending reg id to your server
sendRegistrationToServer(refreshedToken);
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE);
registrationComplete.putExtra("token", refreshedToken);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
private void sendRegistrationToServer(final String token) {
// sending gcm token to server
Log.e(TAG, "sendRegistrationToServer: " + token);
}
private void storeRegIdInPref(String token) {
SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putString("regId", token);
editor.commit();
}
}
The purpose of this class is to handle the Firebase registration ID. Firebase registration ID is a unique ID for each app. This ID is needed to send messages for each device. In the above class, there are 2 main functions.
- onTokenRefresh() — This function will be called whenever the registration id/token is changed.
- storeRegIdInPref(token) — From this function, it will store the token in the shared preference.
Step 4
Create a class called FirebaseMessagingService.java and insert the following code.
package com.example.fcmnotification;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import com.google.firebase.messaging.RemoteMessage;
import org.json.JSONException;
import org.json.JSONObject;
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
private static final String TAG = FirebaseMessagingService.class.getSimpleName();
private NotificationUtils notificationUtils;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage == null)
return;
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.e(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
handleNotification(remoteMessage.getNotification().getBody());
}
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());
try {
JSONObject json = new JSONObject(remoteMessage.getData().toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
private void handleNotification(String message) {
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(getApplicationContext());
notificationUtils.playNotificationSound();
}else{
// If the app is in background, firebase itself handles the notification
}
}
private void handleDataMessage(JSONObject json) {
Log.e(TAG, "push json: " + json.toString());
try {
JSONObject data = json.getJSONObject("data");
String title = data.getString("title");
String message = data.getString("message");
boolean isBackground = data.getBoolean("is_background");
String imageUrl = data.getString("image");
String timestamp = data.getString("timestamp");
JSONObject payload = data.getJSONObject("payload");
Log.e(TAG, "title: " + title);
Log.e(TAG, "message: " + message);
Log.e(TAG, "isBackground: " + isBackground);
Log.e(TAG, "payload: " + payload.toString());
Log.e(TAG, "imageUrl: " + imageUrl);
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(getApplicationContext());
notificationUtils.playNotificationSound();
} else {
// app is in background, show the notification in notification tray
Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class);
resultIntent.putExtra("message", message);
// check for image attachment
if (TextUtils.isEmpty(imageUrl)) {
showNotificationMessage(getApplicationContext(), title, message, timestamp, resultIntent);
} else {
// image is present, show notification with image
showNotificationMessageWithBigImage(getApplicationContext(), title, message, timestamp, resultIntent, imageUrl);
}
}
} catch (JSONException e) {
Log.e(TAG, "Json Exception: " + e.getMessage());
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
/**
* 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);
}
}
In the above class, it manages the notification when the notification received to the device. onMessageReceived() function first it will check whether the incoming notification consists any payload. If so then it will check the status of the app. If the app is running on the foreground (the app is open) it will generate the notification sound and display the notification sound.
If the app is running on the background then it will check the notification body to find the image URL. If the image URL absent notification will generate without an image. otherwise, it will generate the notification with the image.
Step 5
Next, create a class called NotificationUtils.java and insert the following code.
package com.example.fcmnotification;
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.Date;
import java.util.List;
import com.example.fcmnotification.R;
import com.example.fcmnotification.Config;
public class NotificationUtils {
private static String TAG = NotificationUtils.class.getSimpleName();
private Context mContext;
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();
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
+ "://" + mContext.getPackageName() + "/raw/notification");
Ringtone r = RingtoneManager.getRingtone(mContext, 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(Context context) {
NotificationManager notificationManager = (NotificationManager) context.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;
}
}
In this class, it contains the functionalities to display the notification with title, body, image and the timestamp.
Step 6
Modify the MainActivity as follows
package com.example.fcmnotification;
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.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.firebase.messaging.FirebaseMessaging;
import com.example.fcmnotification.R;
import com.example.fcmnotification.Config;
import com.example.fcmnotification.NotificationUtils;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private BroadcastReceiver mRegistrationBroadcastReceiver;
private TextView txtMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtMessage = (TextView) findViewById(R.id.txt_push_message);
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
FirebaseMessaging.getInstance().subscribeToTopic(Config.TOPIC_GLOBAL);
} else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
// new push notification is received
String message = intent.getStringExtra("message");
Toast.makeText(getApplicationContext(), "Push notification: " + message, Toast.LENGTH_LONG).show();
txtMessage.setText(message);
}
}
};
}
@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));
// clear the notification area when the app is opened
NotificationUtils.clearNotifications(getApplicationContext());
}
@Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
}
Step 7
Now the implementing is done. we need to design the interface for the home screen. add the following code to the activity_main.xml as below
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.fcmnotification.MainActivity"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@mipmap/ic_launcher"
/>
<TextView
android:id="@+id/txt_push_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:gravity="center_horizontal"
android:textColor="@color/colorPrimary"
android:textSize="26dp" />
</RelativeLayout>
Step 8
This is the final step of this tutorial. you have to add a service to the AndroidManifest.xml in order to work the app correctly.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fcmnotification">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="FirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="FirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
Finally, the coding part is done. Now we need to send a notification. Go to the Firebase Cloud messaging. As shown below enter the message and enter the Review button. You can see a notification on your device.
If there any problem you face you can download the source code of this project. Feel free to comment below if there is any question. As shown in the below, you can see the notification popping up at the time you send a notification from the Firebase. Enjoy the app.
Nice tutorial, i was wondering is it possible to send push notifications from 2 different firebases? The reason I am asking I want to package a library where I can send push notifications to those who use my SDK while the developers still can use their firebase and their own push too