Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stack Firebase Cloud Messaging notifications when the application is not running?

I am using Firebase Cloud Messaging to send push notifications from my server to my android application.

When the application is running, the notifications are stacked because I set them to a group in my FirebaseMessagingService. Which is nice.

However, when the application is not running, the notifications are not stacked and each one appears individually. Which is not nice.

How to make sure notifications are stacked even when the application is not running?

This is how my FirebaseMessagingService looks like:

public class MyFcmListenerService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        RemoteMessage.Notification notification = remoteMessage.getNotification();

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)

                .setSmallIcon(R.drawable.notif_white)
                .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.notif_white))
                .setContentTitle(getResources().getString(R.string.app_name))
                .setContentText(notification.getBody())
                .setAutoCancel(true)
                .setPriority(2)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent)
                .setGroup("1")
                .setGroupSummary(true)
                .setOnlyAlertOnce(true);


        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0 , notificationBuilder.build());

        }
    }
like image 523
The Cook Avatar asked Feb 16 '17 12:02

The Cook


1 Answers

To stack two or more notifications(specified in messages list) and to make them appear like GMail style notifications, you can add inbox style for your notification, like below:-

    private void showNotification(Context mContext, String title, List messages, String timeStamp, PendingIntent resultPendingIntent, Uri alarmSound) {

    final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
            mContext);

    NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();

    for(int i=0;i<messages.size();i++)
        inboxStyle.addLine(messages.get(i));

    Notification notification;
    notification = mBuilder.setTicker(title)
            .setAutoCancel(true)
            .setContentTitle(title)
            .setContentIntent(resultPendingIntent)
            .setSound(alarmSound)
            .setStyle(inboxStyle)
            .setWhen(getTimeMilliSec(timeStamp))
            .setSmallIcon(R.drawable.notification_small_icon)
            .setLargeIcon(R.drawable.notification_large_icon)
            .setDeleteIntent(PendingIntent.getBroadcast(mContext,101,new Intent(mContext, NotificationDismissedReceiver.class),PendingIntent.FLAG_CANCEL_CURRENT))
            .build();

    NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(NConfig.NOTIFICATION_ID, notification);
    }

If you notice, i have also added delete intent for my notification which triggers NotificationDismissedReceiver(BroadcastReceiver) whose main job is to clear notifications messages that have been dismissed by swipe gesture, so that next time only new notification messages get stack together.

public class NotificationDismissedReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    // TODO: This method is called when the BroadcastReceiver is receiving
    // an Intent broadcast.
    messages.clear();
}
}

The main logic is to collect all unread/unswiped notifications inside a list i.e messages, below is onMessageReceive() of FirebaseMessagingService :-

    public void onMessageReceived(RemoteMessage remoteMessage) {
    Log.e(TAG, "From: " + remoteMessage.getFrom());

    if (remoteMessage == null)
        return;

    if (remoteMessage.getData()!=null && remoteMessage.getData().size() > 0) 
    {

        try {
            JSONObject json = new JSONObject(remoteMessage.getData().toString());
            Log.e(TAG, "Notification Data: " + json);
            Title = json.get("title").toString();
            Message = json.get("body").toString();
            messages.add(Message);

        } catch (Exception e) {
            Log.e(TAG, "Exception: " + e.getMessage());
        }
     }

       showNotification(...);
     }

When the application is in foreground, the above onMessageReceive() of FirebaseMessagingService executes fine but when your application is in background or killed it doesn't execute. To make it execute you have to omit notification part from the JSON message sent from server side and only include the data part, as shown below:-

       var data = new
       {
           to = token,
        //   notification = new
        //   {
        //       body = messageBody,   //Omitting notification part of data
        //       title = messageTitle,
        //       icon = "myicon",
        //},
        data = new
        {
               body = messageBody,  // adding all notification information inside data
               title = messageTitle,
               icon = "myicon",
           }
       };

By doing this your message now becomes data message only which means that it will always execute onMessageReceive() of FirebaseMessagingService irrespective of whether your app is in background or foreground.

Hope this much explanation helps.

like image 50
Rishabh Avatar answered Sep 21 '22 12:09

Rishabh