Notifications are important! I can't recall using an app that didn't have notifications excluding, of course, those apps that don't serve a purpose like this one

Notifications play a vital role in your app success. They give you a platform to reach out to your users, interact with them, give them updates, update the app data and much more.

While notification is an important part of the whole app development process, it somehow is typical to integrate as compared to the normal android APIs. The reason? Well, while normal APIs need configuration inside the app only, notifications need more than that -- outside configuration like handling user tokens and sending notifications using those tokens.

Okay, enough of fact-checking. Let's see how we can incorporate notifications in our app -- handling tokens, sending them to the server, receiving notification data and showing them to users. We will assume you have a good understanding of common Android APIs.

We will use an example Android project, My Notifications App. Be sure to make changes wherever it's required as per your application's needs.

To make notifications work, we first need to make sure our app can connect to the internet. Adding Internet and Access Network  State permissions to our manifest will give it the ability to connect to the internet.

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

Now, we need to get our application registered with Firebase in order to proceed forward and generate token. Go to Firebase Console and add a project like this,


Enter the details and hit create a project. Once done, a new window will pop up asking you to download the google-services.json file. Download the JSON file and put it inside your project's app folder. FCM's internal framework on Android uses this JSON file to validate your application as a registered application and thus generating token for every app user. Tap continue.


Follow the gradle instructions and add the gms dependency and gms plugin in correct gradle files. After adding gradle sync your project. Now go back to the console. You will be greeted with an overview to add apps to the new created project to get started. Select Android, and you will see this window pop up,


Entering your application's package name is a must since it's the unique key firebase uses to verify your application's tokens. You can optionally add a nickname for your app as well. While adding a signing certificate (SHA1) is optional it's highly recommended that you add it. Register app and we're done with the Firebase Console part. Next is the application setup.

Add the following to build.gradle (Module: app) and sync.

compile 'com.google.firebase:firebase-messaging:10.2.6'

This will add the firebase messaging module to our app. Now we need to generate a token using FirebaseInstanceIdService class and send it to the server to be stored. Create a new class InstanceIdService and extend it with FirebaseInstanceIdService. We now need to add a constructor and onTokenRefresh methods inside this class. We will take help of predefined tools in Android Studio. Click inside the class block and go to Code in the toolbar and select override methods. Select the desired methods and click ok. After the methods are added, we need to get the generated token everytime onTokenRefresh method is called by the framework.

String token = FirebaseInstanceId.getInstance().getToken();

This will get us the token. And we can now send it to our server and store it for further use.

package com.mynotificationsapp.android;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by Nilesh Singh -- Context Neutral.
*/
public class InstanceIdService extends FirebaseInstanceIdService {
public InstanceIdService() {
super();
}
@Override
public void onTokenRefresh() {
super.onTokenRefresh();
String token = FirebaseInstanceId.getInstance().getToken();
//sends this token to the server
sendToServer(token);
}
private void sendToServer(String token) {
try {
URL url = new URL("https://www.whatsthatlambda.com/store");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
dos.writeBytes("token=" + token);
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// Do whatever you want after the
// token is successfully stored on the server
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

The sendToServer(...) method can be used to send our token to the server. What we are doing here is use the server endpoint for a post request (the URL) and open an HTTP connection with that. We specify that we want to send a post request by adding,

connection.setRequestMethod("POST");

Now, in order to attach a post body, we first need to get the connection's output stream and open a DataOutputStream to write our post body to the connection.

DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
dos.writeBytes("token=" + token);

Make sure you close the DataOutputStream after the write is done. We have used a token as the key for our token value. We will use this key on the server side to retrieve the token value.  It's time for us to handle the messages. Create a new class MessageReceiver and extend it with FirebaseMessagingService and add the constructor and onMessageReceived method. The latter handles the data you send from your server or Firebase Console.

package com.mynotificationsapp.android;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
/**
* Created by Nilesh Singh -- Context Neutral.
*/
public class MessageReceiver extends FirebaseMessagingService {
private static final int REQUEST_CODE = 1;
private static final int NOTIFICATION_ID = 6578;
public MessageReceiver() {
super();
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
final String title = remoteMessage.getData().get("title");
final String message = remoteMessage.getData().get("body");
showNotifications(title, message);
}
private void showNotifications(String title, String msg) {
Intent i = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, REQUEST_CODE,
i, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this)
.setContentText(msg)
.setContentTitle(title)
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher_round)
.build();
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_ID, notification);
}
}
view raw MessageReceiver.java hosted with ❤ by GitHub

The following example exracts the data sent from a remote server (where the tokens are stored). If you wish to send data from the Firebase Console instead, you need to get the title and message body by calling remoteMessage's getNotification method.

String title = remoteMessage.getNotification().getTitle();
String message = remoteMessage.getNotification().getBody();

FCM messages are of two types, Notification messages, and Data messages. Notification messages are generally used for displaying normal notifications with a minimal set of data -- body, title, icon, sound. FCM handles notification messages in two ways. If the app is in the background, the notification is sent to the Notification Tray and the app doesn't need to handle the notification. The app receives notification data when the user taps on the notification to open it. If the app was in the foreground, the notification is sent to the app and onMessageReceived(...) is invoked with data. A Notification message looks something like this,

"notification" : { "body" : "Message body", "title" : "Notifications for Android", "icon" : "appIcon" }

For a custom message to qualify as a Notification message, it's mandatory to include notification key. If you are adding more data to notification messages like an icon or sound, you need to add files for them in your app. For icon, app icon, appIcon.png should be added to the drawable folder. In the case of sound, a sound file should reside inside the raw folder. 

Data messages, on the other hand, are custom messages with custom data. onMessageReceived(...) is always invoked and the app handles the data.

Once the data is received using either method, a simple notification can be shown to the user using the data. We need Notification and NotificationManager API to build and notify the user with the notification. The example above contains a pretty basic Notification builder with title and content text. The small icon is mandatory for building a notification so be sure to add a small icon.

The NotificationManager class is a system service that takes in notification object and a unique notification id to display the notification. The above example also makes use of ContentIntent. A content intent uses pending intent and is the intent that will be fired when the notification is tapped with the purpose to see more of the notification. It's always a good idea to add a pending intent to your notification.

PendingIntent pendingIntent = PendingIntent.getActivity(this, REQUEST_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT);

The pending intent requires an intent to jump from the current state to the desired activity. Create a new Intent object and pass it to the Pending Intent along with Context, the request code, and Pending Intent flag. Here we use FLAG_UPDATE_CURRENT as we wish to update the already existing (if) pending intent with a new one. You can find more flags and their description here.

Lastly, we need to update the application's manifest to reflect changes and FCM to work. We need to add both the classes (services) we just created to the manifest.

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

We are done. Run the app and go to Firebase Console > Notifications and opt to send a new message. Enter a message, select the app in the target section and in advanced section enter the title and send. You should be able to see a Notification like the one below. If not, go through the steps one more time and see where it went wrong.


That's it. We can now use the Firebase Console to send notifications. If you need more control over what to send and whom to send, it's better to send notifications using your own server. We will discuss server side handling of tokens and sending notifications, both batch and topic wise in some other post. Do share your issues in the comments section below.