Firebase for Android: Notifications (Firebase Cloud Messaging)

Firebase for Android: Notifications (Firebase Cloud Messaging)

Firebase is a cloud service provider, provided by Google, which provides mobile backend as a service (MBaaS). Firebase Cloud Messaging (FCM) is the new version of GCM. Firebase Cloud Messaging provides cross-platform messaging solution that allows reliable delivery of messages at no cost. Using this we can:

  • Send notification messages (2KB limit) or data messages (4KB limit). We can keep the user informed about events without draining the smartphone battery. When a user receives the notification, it appears, as a customized icon, in the status bar.
  • Distribute messages to a single device, groups of devices, or to devices subscribed to some topics.
  • Send acknowledgments, chats, and other messages from devices back to the server over FCM’s reliable and battery-efficient connection channel.
android_fcm.png

To configure the project to use the Firebase platform, open the Firebase Assistant window by clicking on Tools > Firebase in Android Studio. Now from the assistant go to Notifications and click Receive Notifications in your app. Next, press the Connect to Firebase button and make sure that the Create new Firebase project option is selected. Finaly, click Add Notifications.

android_firebase_notification_setup.png

Now you have connected Firebase platform with Notifications. Also you can manage your Firebase data from Firebase console.

Since we need to connect to the Network add the Internet permission in AndroidManifest.xml file.

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

Now we need to add a service that extends FirebaseMessagingService. This is the base class for communicating with Firebase Messaging. This FirebaseMessagingService extends Service class.

This class provides various functionalities:

  • automatically displaying notifications
  • message handling beyond receiving notifications on apps in the background.
  • receive notifications in foregrounded apps
  • receiving data payload
  • sending upstream messages
  • receiving errors in case of upstream message failures.

To handle any type of events required by the application, we need to override this base class methods. These methods are invoked on a background thread. To add this service include following to your manifest file :

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

Add an entry for a service that extends FirebaseInstanceIdService which will be used to handle the registration token lifecycle. This is required for sending messages to specific devices/device groups.

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

Create a new java class MyAndroidFirebaseMessagingService and add the following code. It is a service that extends FirebaseMessagingService. It performs all kind of message handling in the background and sends the push notification once it is available.

public class MyAndroidFirebaseMessagingService extends FirebaseMessagingService {
    private static final String TAG = "DBG";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (remoteMessage == null)
            return;

        // check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            Log.e(TAG, "Notification body: " + remoteMessage.getNotification().getBody());
            createNotification(remoteMessage.getNotification());
        }

        // check if message contains a data payload
        if (remoteMessage.getData().size() > 0) {
            Log.d(TAG, "From: " + remoteMessage.getFrom());
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        }
    }

    private void createNotification (RemoteMessage.Notification notification) {
        Intent intent = new Intent(this , MainActivity.class );
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent resultIntent = PendingIntent.getActivity( this , 0, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Uri notificationSoundURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder( this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(notification.getTitle())
                .setContentText(notification.getBody())
                .setAutoCancel( true )
                .setSound(notificationSoundURI)
                .setContentIntent(resultIntent);

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

        notificationManager.notify(0, mNotificationBuilder.build());
    }
}

On receiving a message onMessageReceived() is called. Inside this function we are logging the message to the LogCat console and calling the createNotification() with the message text. The createNotification() method will create a push notification in the android notification area as shown below.

We are using NotificationCompat.Builder to create a new notification with default notification sound and passing the MainActivity to the intent.

There's a lot of information that can be passed down with the RemoteMessage. However, most of the options are only available if you use the Firebase back-end API, rather than the console. From the Firebase console, you are able to set a title, a message body, and custom key/value pairs.

Logging all of the available data from a RemoteMessage can be done like so:

for (Map.Entry<String, String> entry : remoteMessage.getData().entrySet()) {
    Log.e("Test", "Key = " + entry.getKey() + ", Value = " + entry.getValue() );
}

Log.e(TAG, "collapsekey: " + remoteMessage.getCollapseKey());
Log.e(TAG, "from: " + remoteMessage.getFrom());
Log.e(TAG, "message id: " + remoteMessage.getMessageId());
Log.e(TAG, "message type:: " + remoteMessage.getMessageType());
Log.e(TAG, "to: " + remoteMessage.getTo());
Log.e(TAG, "send time: " + remoteMessage.getSentTime());
Log.e(TAG, "ttl: " + remoteMessage.getTtl());
Log.e(TAG, "title: " + remoteMessage.getNotification().getTitle());
Log.e(TAG, "body: " + remoteMessage.getNotification().getBody());
Log.e(TAG, "click action: " + remoteMessage.getNotification().getClickAction());
Log.e(TAG, "color: " + remoteMessage.getNotification().getColor());
Log.e(TAG, "icon: " + remoteMessage.getNotification().getIcon());

Create a java class MyAndroidFirebaseInstanceIdService and add the following code. It is a service that extends FirebaseInstanceIdService and handles the creation, rotation, and updating of registration tokens. It makes sure theat the given message is sent to specific devices/device groups.

public class MyAndroidFirebaseInstanceIdService extends FirebaseInstanceIdService {
    private static final String TAG = "DBG";

    @Override
    public void onTokenRefresh() {
        // get hold of the registration token
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        // lg the token
        Log.d(TAG, "Refreshed token: " + refreshedToken);
    }
    private void sendRegistrationToServer(String token) {
        // implement this method if you want to store the token on your server
    }
}

The onTokenRefresh() callback fires on generation of a new token. We are calling getToken() in the context of onTokenRefresh() to ensure that we are accessing a currently available registration token.

Now the app is complete, we will test the app by sending a notification from the Firebase Notifications panel.

  1. Go to Firebase console and select the app you created.
  2. From the left menu select Notification.
  3. Click on New message.
  4. Enter message, select Single device or User segment. Select Send message. For User segment use something like me.proft.firebasetest (value applicationId from build.gradle(Module)). For Single device you should use FCM registration token , see below how to get this token.

This will generate a new Notification message on your android device as shown below.

android_firebase_notification_example.png

Send notification from terminal

First of all, you'll need a Server key. Open Firebase console and select the app you created. Next, click on gear and select Project settings then Cloud messaging.

Second, we'll need a FCM registration token. I use following command

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

Finish, issue following command

curl https://fcm.googleapis.com/fcm/send -X POST \
--header "Authorization: key=APISERVER" \
--Header "Content-Type: application/json" \
 -d '{
   "to": "TOKEN"
   "notification":{
     "title": "New Notification!",
     "body": "Test"
   },
   "priority": 10
}'

Send notification from application using OkHttpClient and JSONObject

Also, we can send notification from application using OkHttpClient and JSONObject.

public void sendNotification(View v) {
    final Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run () {
            createNotification();

            handler.post(new Runnable(){
                @Override
                public void run(){
                    // do some UI related thing
                    // like update a progress bar or TextView
                }
            });
        }
    };

    new Thread(runnable).start();
}

public void createNotification() {
    String SERVER_KEY = "******";
    String TOKEN = "*******";

    OkHttpClient client = new OkHttpClient();
    JSONObject json = new JSONObject();
    JSONObject dataJson = new JSONObject();
    try {
        dataJson.put("body", "Test");
        dataJson.put("title", "New Notification!");
        json.put("notification", dataJson);
        json.put("to", TOKEN);

        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(JSON, json.toString());
        Request request = new Request.Builder()
                .header("Authorization","key=" + SERVER_KEY)
                .url("https://fcm.googleapis.com/fcm/send")
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        String finalResponse = response.body().string();
        Log.d("VVV", "map: " + finalResponse);
    } catch (JSONException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
comments powered by Disqus