Android Push Notification Using Firebase Cloud Messaging (FCM)

Android Push Notification Using Firebase Cloud Messaging (FCM)

    The 2016 Google I/O announced major improvements to Firebase – a cloud platform with a lot of amazing features for mobile app developers. One of them is Firebase Cloud Messaging (FCM) — a cross-platform messaging solution that lets users reliably deliver messages at no cost. Yes, FCM is a free service from Google. Comparing to the earlier Google Cloud Messaging (GCM), FCM is much more developer-friendly because you don't even need to see any of the server code involved.

    Through my previous post, you have know about Firebase authentication. So, in this is a tutorial which talking about other features: sending push notifications to Android devices, based on the new release of Firebase this year (2016). This tutorial shows how to setup the skeleton for sending and receiving push notifications via FCM with instructions on server code.

Setting up Firebase project

    Go to Firebase console page, login with your Google account and and start a new Android project by click "Create New Project". Filling your project information with appearing Dialog:
    After that, choose Android platform with your project/app:
    Filling your app information (package name, SHA-1 key (optional)):
    Now, your configuration in the console page is completed and google-service.json file have been downloaded to your computer.
    Start your Android Studio project, make sure that it has same package name with project in the console page (here is info.devexchanges.firebasenotification). Copying google-serivce.json file to app folder in your project:
    Adding this rules to your project-level build.gradle file, to include the google-services plugin:
buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.gms:google-services:3.0.0'
    }
}
Then, in your module Gradle file (usually the app/build.gradle), add the apply plugin line at the bottom of the file to enable the Gradle plugin:
apply plugin: 'com.android.application'

android {
  // ...
}

dependencies {
  // ...
  compile 'com.google.firebase:firebase-messaging:9.4.0'
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'
    Up to now, the project configuration process is finished!

Creating Firebase Service

The first Java class is a Service. This class is extended from FirebaseInstanceIdService and will be used to get the refreshed token and to store it on the server if needed. Here is full code:
CustomFirebaseInstanceIDService.java
package info.devexchanges.firebasenotification;

import android.util.Log;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;

public class CustomFirebaseInstanceIDService extends FirebaseInstanceIdService {

    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the InstanceID token
     * is initially generated so this is where you would retrieve the token.
     */

    private static String TAG = CustomFirebaseInstanceIDService.class.getSimpleName();

    @Override
    public void onTokenRefresh() {
        // Get updated InstanceID token.
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Refreshed token: " + refreshedToken);

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(refreshedToken);
    }

    /**
     * Persist token to third-party servers.
     * * Modify this method to associate the user's FCM InstanceID token with any server-side account
     * maintained by your application.
     *
     * @param token The new token.
     */
    private void sendRegistrationToServer(String token) {
    }
}
    The second Serivce file is extended from FirebaseMessagingService. It will receive the messages from the firebase server and generate the notifications on the device. The notifications will be build normally by NotificationCompat.Builder:
CustomFirebaseMessagingService.java
package info.devexchanges.firebasenotification;

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.support.v4.app.NotificationCompat;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class CustomFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        //Calling method to generate notification
        sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
    }

    private void sendNotification(String title, String messageBody) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0, notificationBuilder.build());
    }
}
The last important step is register your 2 Services to your AndroidManifest.xml. Never forget to adding Internet permission, too:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.devexchanges.firebasenotification">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".CustomFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>

        <service
            android:name=".CustomFirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
            </intent-filter>
        </service>

    </application>

</manifest>

Running application

    To running any application which using Firebase, you should take a glance at the prerequisites in Firebase oficial website:
  • An Android device running Google Play services 9.0.0 or later 
  •  The Google Play services SDK from the Android SDK Manager 
  •  Android Studio 1.5 or higher 
  • An Android Studio project and it's package name.
    In an understandable way, you should run this app in your real device, which usually installed Google Play services. This is output in my Asus Zenfone Go (main screen):
    Now, go to Firebase console page, select Notification entry in the left pane:
    Click at "Send your first message" and filling your message content:
    After click at "Send Message", your Android device will receive a notification:

Conclusions

    Now, I have presented all basic steps to integrating FCM to an Android application. I hope you will like the article and it will definitely help you to make your apps more productive. Now, for more details, you should read official doc of Firebase notification to deep understanding this topic.
    Some related links:

Android Basic Training Course: Alerting users by Notifications System

Android Basic Training Course: Alerting users by Notifications System

    A notification is a message you can display to the user outside of your application's normal UI. When you tell the system to issue a notification, it first appears as an icon in the notification area. To see the details of the notification, the user opens the notification drawer. Both the notification area and the notification drawer are system-controlled areas that the user can view at any time.
    Notifications, as an important part of the Android user interface, have their own design guidelines. Big style notification was introduced on Android version 4.1.x. If you are building app that supports older android phones like android 2.3 or above, in such cases you may like to go for  NotificationCompat.Builder for showing notifications.

Creating a Notification and set properties

    By using a Notification Builder instance, you can easily create a Notification object though this simple code:
Notification notification = new NotificationCompat.Builder(this)
    A Notification has some necessary attributes which you must declared like:
  • A small icon: set by setSmallIcon()
  • A title, set by setContentTitle()
  • Detail text, set by setContentText()
    And this is full code for this creating Notification process though  NotificationCompat.Builder:
Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_notify) // icon of notification
                .setContentTitle(notificationTitle) // title of the notification
                .setContentText(notificationMessage) // content of the notification
                .setContentIntent(pi) // content Intent
                .setAutoCancel(true) // auto dismiss notification when user clicked
                .build();

Attach action with Intent

    This is an optional part and required if you want to attach an action with the notification. An action allows users to go directly from the notification to an destination (received)  Activity  in your application, where they can look at one or more events or do further work.
    The action is defined by a  PendingIntent  containing an  Intent  that starts an  Activity  in your application:
        Intent intent = new Intent(this, ReceiveNotifyActivity.class);
        intent.putExtra("Title", notificationTitle);
        intent.putExtra("Body", notificationMessage);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    In this example, I use putExtra() method of Intent to put data (notification title and body message) to the receiving Activity and show these details here.

Invoking the Notification

    Finally, you pass the Notification object to the system by calling NotificationManager.notify()  to send your notification:
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //initializing a NotificationManager object
notificationManager.notify(0, notification);
    Over here, adding some code, we have full code for "pushing notification" Activity:
package devexchanges.info.notificationsample;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class CreateNotifyActivity extends AppCompatActivity {

    private View btnCreateNotify;
    private EditText txtTitle;
    private EditText txtMsg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create);

        txtMsg = (EditText)findViewById(R.id.msg);
        txtTitle = (EditText)findViewById(R.id.title);

        btnCreateNotify = findViewById(R.id.btn_create);
        btnCreateNotify.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                createNotification(txtTitle.getText().toString(), txtMsg.getText().toString());
                finish(); // In this example, I finish current activity after push a notify
            }
        });

    }

    private void createNotification(String notificationTitle, String notificationMessage) {
        Intent intent = new Intent(this, ReceiveNotifyActivity.class);
        intent.putExtra("Title", notificationTitle);
        intent.putExtra("Body", notificationMessage);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_notify) // icon of notification
                .setContentTitle(notificationTitle) // title of the notification
                .setContentText(notificationMessage) // content of the notification
                .setContentIntent(pi) // content Intent
                .setAutoCancel(true) // auto dismiss notification when user clicked
                .build();

        //Building notification
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(0, notification);
    }
}
    Provide a layout for it by a xml file:
    This screen after running and typing some texts to EditTexts :

Making a destination Activity

   Like all of Android apps, you always must have a receiving notification Activity which appear when user click at each Notification object. With the above code, I put the Notification title and message to the destination Activity, so you get them through Intent, source code for it is so simple:
package devexchanges.info.notificationsample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class ReceiveNotifyActivity extends AppCompatActivity {

    private TextView notificationTitle;
    private TextView notificationMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_receive);

        notificationMessage = (TextView) findViewById(R.id.msg);
        notificationTitle = (TextView) findViewById(R.id.title);

        Bundle extras = getIntent().getExtras();

        notificationMessage.setText("Notification content: " + extras.getString("Body"));
        notificationTitle.setText("Notification title: " + extras.getString("Title"));
    }
}
    And this is it's layout (xml file):
    Running program, after hit "create Notification" button at the first screen, you will have a notification appear at notification area:
    Clicking at it, your app will open the receiving notification Activity and show it's details:

Conclusions

    Through this post, I provided a simple project to create and showing the notification. Further, you can find out your shelf the way to create it with sound and some advanced options (for example: create notification with multiline). The notification system is very flexible, but also easy when you just need a simple notification displayed. Your users will benefit from notifications by seeing what is going on in the background and being reminded about the application.




Building Actionbar notifications count Icon

    In some app like Google, we see number of new notifications is shown in ActionBar like this:
It's so intutive for users and convenient for actions. In this sample post, I will present the simple way to make an application which has similar UI like above.

1. Start Android Studio and create a new project (min sdk I used is 14).
2. Create an Activity which sending information (ForwardActivity)

    In this Activity, we will send data to NotificationActivity based on CheckBoxes check/unchecked. It's layout (declaring in xml file):
activity_notification.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_margin="10dp"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:textSize="22sp"
        android:text="@string/choose"
        android:textColor="@android:color/holo_green_dark"
        android:layout_margin="10dp"
        android:layout_height="wrap_content" />

    <CheckBox
        android:id="@+id/chk_android"
        android:text="@string/android"
        android:textColor="#003399"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <CheckBox
        android:textColor="#003399"
        android:id="@+id/chk_ios"
        android:text="@string/ios"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <CheckBox
        android:textColor="#003399"
        android:id="@+id/chk_wp"
        android:text="@string/wp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <CheckBox
        android:textColor="#003399"
        android:id="@+id/chk_other"
        android:text="@string/other"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_go"
        android:text="@string/go"
        android:layout_margin="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
    Programmatically code: sending data (List of Strings) after click the Button:

private View.OnClickListener onClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isChecked(chkAndroid, "Android");
                isChecked(chkIOS, "IOS");
                isChecked(chkWP, "Window Phone");
                isChecked(chkOther, "Other Mobile Platform");

                //go to activity and put string array
                Intent intent = new Intent(ForwardActivity.this, NotificationActivity.class);
                intent.putStringArrayListExtra("notification", intentStrings);
                startActivity(intent);
                intentStrings.clear();
            }
        };
    }

    /**
     * Append String when checkbox checked
     * @param checkBox
     * @param s
     */
    private void isChecked(CheckBox checkBox, String s) {
        if (checkBox.isChecked()) {
            intentStrings.add(s);
        }
    }

3. Create an activity which show notifications

- Firstly, create a menu_notification.xml in res/menu folder:
menu_notification.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.blogspot.hongthaiit.notification.NotificationActivity">

    <item
        android:id="@+id/badge"
        android:actionLayout="@layout/feed_update_count"
        android:icon="@drawable/shape_notification"
        android:showAsAction="always">
    </item>
</menu>
- Action layout (feed_update_count.xml) in res/layout folder for this menu:
feed_update_count.xml
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/notif_count"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minWidth="32dp"
    android:minHeight="32dp"
    android:background="@drawable/shape_notification"
    android:text="0"
    android:textSize="16sp"
    android:textColor="@android:color/white"
    android:gravity="center"
    android:padding="5dp"
    android:singleLine="true">

    </Button>
- Layout for this Activity: a TextView to show notification content:
activity_notification.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/notifi_text"
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="22sp"
        android:textColor="#990000"
        android:layout_margin="10dp"
        android:gravity="center"
        android:layout_height="wrap_content" />

</RelativeLayout>
    In code, inflating menu in onCreateOptionsMenu() method, open NotificationActivity and paste this code:
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_notification, menu);

        View count = menu.findItem(R.id.badge).getActionView();
        btnNotifCount = (Button) count.findViewById(R.id.notif_count);
        btnNotifCount.setText(String.valueOf(intentList.size()));

        //show list of notifications when menu button clicked
        btnNotifCount.setOnClickListener(onClickListener());

        return super.onCreateOptionsMenu(menu);
    }
    Handle click event of badge icon method (show a PopupMenu):
private View.OnClickListener onClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Creating the instance of PopupMenu
                PopupMenu popup = new PopupMenu(NotificationActivity.this, btnNotifCount);
                //Inflating the Popup using xml file
                popup.getMenuInflater()
                        .inflate(R.menu.menu_popup, popup.getMenu());

                createPopupMenuItems(popup);
                popup.show();
            }
        };
    }

    private void createPopupMenuItems(final PopupMenu popup) {
        if (intentList.size() > 0) {
            for (int i = 0; i < intentList.size(); i++) {
                popup.getMenu().add(intentList.get(i));
            }

            popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {

                    textView.setText("You selected " + item.getTitle() + " at MainActivity");
                    intentList.remove(item.getTitle()); //remove notification after clicked (read)
                    setBtnNotifCount(intentList.size()); //update notifications count
                    popup.dismiss();

                    return true;
                }
            });
        }
    }
    Add some important methods, we have full NotificationActivity code:
NotificationActivity.java
package com.blogspot.hongthaiit.notification;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.TextView;

import java.util.ArrayList;


public class NotificationActivity extends Activity {

    private Button btnNotifCount;
    private TextView textView;
    private int mNotifCount = 0;
    private ArrayList<String> intentList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notification);
        textView = (TextView) findViewById(R.id.notifi_text);

        getDataFromIntent();
    }

    private void getDataFromIntent() {
        intentList = getIntent().getStringArrayListExtra("notification");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_notification, menu);

        View count = menu.findItem(R.id.badge).getActionView();
        btnNotifCount = (Button) count.findViewById(R.id.notif_count);
        btnNotifCount.setText(String.valueOf(intentList.size()));

        //show list of notifications when menu button clicked
        btnNotifCount.setOnClickListener(onClickListener());

        return super.onCreateOptionsMenu(menu);
    }

    private void setBtnNotifCount(int count){
        mNotifCount = count;
        invalidateOptionsMenu();
    }

    private View.OnClickListener onClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Creating the instance of PopupMenu
                PopupMenu popup = new PopupMenu(NotificationActivity.this, btnNotifCount);
                //Inflating the Popup using xml file
                popup.getMenuInflater()
                        .inflate(R.menu.menu_popup, popup.getMenu());

                createPopupMenuItems(popup);
                popup.show();
            }
        };
    }

    private void createPopupMenuItems(final PopupMenu popup) {
        if (intentList.size() > 0) {
            for (int i = 0; i < intentList.size(); i++) {
                popup.getMenu().add(intentList.get(i));
            }

            popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {

                    textView.setText("You selected " + item.getTitle() + " at MainActivity");
                    intentList.remove(item.getTitle()); //remove notification after clicked (read)
                    setBtnNotifCount(intentList.size()); //update notifications count
                    popup.dismiss();

                    return true;
                }
            });
        }
    }
}

4. Some necessary files

- Shaping notification corner icon for make it better looking (res/drawable/shape_notification.xml):
shape_notification.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <stroke android:color="#22000000" android:width="2dp" />
    <corners android:radius="5dp" />
    <solid android:color="#CC0001" />
</shape>
- Strings resource:
strings.xml
<resources>
    <string name="app_name">Notification ActionBar</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="android">Android</string>
    <string name="ios">iOS</string>
    <string name="go">Go to Notification Activity</string>
    <string name="choose">Please choose a mobile platform</string>
    <string name="wp">Window Phone</string>
    <string name="other">Other platform</string>
    <string name="note">Note</string>
    <string name="title_activity_notification">NotificationActivity</string>
</resources>

5. Running application, we see our result like this



(Sorry for ads)