Skip to content

Grouped or Stacked notifications across all Android versions.

Notifications are the essential part of an android app. A notification is a message you display to the user outside of your app’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. In Google I/O 2017, UX team of Android stated that multiple notifications from the same app will make a big mess among various types of users. You can watch the full video here.

In Android N, the team came up with a solution called Bundled/Stacked notification. When creating notifications for a handheld device, you should always aggregate similar notifications into a single summary notification. And in Android Oreo, they came up with new changes in notifications including notification channels. For devices of version greater than Nougat, Stacked notifications will work fine and you can easily group the notifications and define the click event also.

Stacked Notifications on Post Nougat Android devices.
But in practical ways, If your projects’ minimum target is less than Android N, Things will be hard to implement grouped notifications. There is no one method solution to solve this problem. In our appEntri, we tried to implement stacked notifications for the forum and discussion feature inside the app. And we came up with a solution of handling stacking notifications separately for devices.

In our Entri app there is a forum feature, where users can ask question and will get the answers for the question. Other users can upvote and downvote the same question. We made some GCM(Planned for a FCM upgrade) notification triggers in the backend. While app receives notifications related to forum activity, we used to stack those notifications and will show it to the user in a single bundled notification.

To implement bundled notifications in devices of Nougat and later below code is used.

 /* Bundled notification on devices of Nougat and later */

 //Version check.
 if (android.os.Build.VERSION.SDK_INT >=Build.VERSION_CODES.N) {


 /**
 * createNougatNotification() will create individual notification with a group key
 * which is a constant string value for identifying and grouping notifications
 * together.
 **/

 NotificationCompat.Builder builder;

 builder  =createNougatNotification(getContext(),desc,title,name);

 builder.setContentIntent(intent);

 builder.setAutoCancel(true); //Cancels notification on click.


 /**
 * Code to handle on click of the individual notifications in
 * notification bundle.
 **/

 Intent targetIntent = new Intent(getContext().getApplicationContext(),
                       ForumDetails.class);

 targetIntent.putExtra("id",id); //Extras for the activity launch

 Intent parent = new Intent(getContext().getApplicationContext(),
                 ForumHome.class);

 parent.putExtra("history","ForumFragment");  //Extras for the activity launch

 TaskStackBuilder stackBuilder = TaskStackBuilder
                     .create(getContext().getApplicationContext());

 stackBuilder.addNextIntent(parent);

 stackBuilder.addNextIntent(targetIntent);

 PendingIntent intent = stackBuilder.getPendingIntent(0,
                        PendingIntent.FLAG_UPDATE_CURRENT);

 /**
 * Creates the notification and adds properties.
 * In Oreo onwards, you should set NotificationChannels.
 * For simplicity i am avoiding those codes.
 * for more
 * https://developer.android.com/reference/android/app/NotificationChannel.html
 **/

 Notification notification=builder.build();

 notification.sound = Uri.parse("android.resource://" +
                       context.getPackageName() + "/" + R.raw.notif);

 notification.defaults = Notification.DEFAULT_LIGHTS;

 //getNotificationId(Context) will return a varying int id to grouping purpose

 getNotificationManager(context).notify(getNotificationId(context)
                       ,notification);
 /**
 * As I mentioned, we have to create summary notification.
 * SetGroupSummary(true) for identifying it as a summary notifications
 **/

 NotificationCompat.Builder summary=createSummaryForNougat(context);

 TaskStackBuilder summaryStackBuilder = TaskStackBuilder.create
                                 (context.getApplicationContext());
 Intent summaryParent = new Intent(context.getApplicationContext(),
                        ForumHome.class);

 summaryParent.putExtra("history","ForumFragment");

 summaryStackBuilder.addNextIntent(summaryParent);

 summaryStackBuilder.addNextIntent(parent);

 PendingIntent summaryIntent = summaryStackBuilder.getPendingIntent(0,
                               PendingIntent.FLAG_UPDATE_CURRENT);

 summary.setContentIntent(summaryIntent);

 summary.setAutoCancel(true);

 getNotificationManager(context).notify
               (getSummaryNotificationId(context),summary.build());
 }

 /**
 * Helper methods
 **/

 /**
 * Creates individual notification to the devices of nougat and later.
 * @return notificaionCompat.Builder class object to build notification.
 */

 public NotificationCompat.Builder createNougatNotification

 (Context context,String description,String title,String url){

 NotificationCompat.Builder builder=new NotificationCompat.Builder(context);

 builder.setContentText(description);

 builder.setContentTitle(title);

 builder.setLargeIcon(getBitmapFromURL(url));


 builder.setColor(ContextCompat.getColor(context, R.color.brand_blue));

 builder.setSmallIcon(R.drawable.ic_notification_small);

 builder.setAutoCancel(true);

 builder.setGroup(GROUP_KEY_NOTIFICATION);

 return  builder;
 }

 /**
 * Creates summary notification to the devices of nougat and later.
 * @param context Context of the Service class.
 * @return notificaionCompat.Builder class object to build notification.
 */

public NotificationCompat.Builder createSummaryForNougat(Context context){

    NotificationCompat.Builder builder=new NotificationCompat.Builder(context);

    builder.setContentTitle("Click to view the notifications");

    builder.setSmallIcon(R.drawable.ic_notification_small);

    builder.setGroup(GROUP_KEY_NOTIFICATION);

    builder.setAutoCancel(true);

    builder.setGroupSummary(true); //Don't forget this method call.

    return  builder;
}

To create a stack, call setGroup() for each notification you want in the stack and specify a group key. Then call notify() to send it to the device. If we use same group key, we can stack the notification with the old available notification. We have to create a summary notification with the same notification group key and make it as the summary notification by calling setGroupSummary(true).

As an addition we can define the targetIntent class to each individual notification and to the summary notification also. So if the user clicks on the total single bundled notification, the target intent of summary notification will be launched. In our case, if the user clicks on the summary, (ie, total single notification) App will launch to the forum activity home screen. But if the user expands the notification and clicks on the single notification, it will launch the related forum thread’s activity. Simple and easy logic.

But in the version earlier Nougat, we cannot simply set up a stacked notification by calling setGroup() method. It won’t make any change in the previous versions. From many stack overflow threads, we concluded that by setting InboxMessage() style, we can achieve the grouped notifications. But there lies another issue. You can add notifications as a line to the single summary notification. But if we uses GCM or FCM and the notifications are not sent at a single time but an unknown time period recurringly, those method won’t work. Notifications may not group together but all notifications are displayed as separate notifications.(ie, You cannot add a new notification to already existing notification) In order to solve this issue, we can rely on many strategies.

1.Developers can tell the backend team to
 send notifications as string array in order to
 keep them bundled.
2.Handle it in the front-end.

For the first solution, it might not work well all the time. Simply, it won’t work out all the time. But the second solution can be done easily using our Shared Preference class and Gson library. Below code structure will explain the tricks to you. FYI, this is the exact behaviour we have seen in famous apps like Gmail, Whatsapp and Hike etc,.

    /*  Bundled notification on devices of Marshmallow and earlier */

    //Version check
    if (android.os.Build.VERSION.SDK_INT <=Build.VERSION_CODES.M) {

      /**
      * Only summary notification is implemented.
      * You can add all individual notification descriptions
      * as a line in the summary notification.
      * Since notification over riding won't work in this method,
      * we are saving notification description strings in an arraylist
      * and saving the arraylist object in sharedpref as a single string
      * by the help of Gson class.
      * when user clicks on appropirate notification, that sharedpref
      * key-value is cleared. Thus we can obtain the same behaviour
      * of stacked notification.
      **/

      /**
      * processStringDatas() method will return all the
      * saved strings in the SharedPreference which will
      * added as a line in the grouped notification.
      **/

      ArrayList<string> notificationDescObject=
                          processStringDatas(context,title,desc);


      NotificationCompat.Builder summary=getSummaryBuilder(context);

      summary.setContentTitle(title);

      summary.setContentText(desc);

      summary.setColor(ContextCompat.getColor
                      (context, R.color.brand_blue));

      summary.setLargeIcon(getBitmapFromURL(image_url));

      summary.setGroupSummary(true); //Don't forget to add this line.

      Intent targetIntent = new Intent
                (context.getApplicationContext(), DoubtsDetail.class);

      targetIntent.putExtra("id",id);

      Intent summaryParent = new Intent(context.getApplicationContext(),
                             ForumHome.class);

      summaryParent.putExtra("history","ForumFragment");

      TaskStackBuilder summaryStackBuilder = TaskStackBuilder
                                  .create(context.getApplicationContext());

      /**
      * Setting inboxStyle to notification to add each individual notification's
      * description.
      **/

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

      summary.setStyle(inboxStyle);

      inboxStyle.setBigContentTitle(title);

      /**
      * Magic happens here!
      * All the strings of our notificationDescObject is added
      * and grouping done.
      **/

      for(String string:notificationDescObject){

          inboxStyle.addLine(string);

      }

      //Can set no.of messages as a summary.

      inboxStyle.setSummaryText(String.valueOf
                    (notificationObject.size())+" Messages");

      summaryParent.putExtra("history","ForumFragment");

      summaryStackBuilder.addNextIntent(summaryParent);

      summaryStackBuilder.addNextIntent(targetIntent);

      PendingIntent summaryIntent = summaryStackBuilder
                    .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

      summary.setContentIntent(summaryIntent);

      summary.setAutoCancel(true);

      getNotificationManager(context).notify(getConstantId(context),notification);

      }

      /**
      * Helper method to save the strings in shared preference.
      **/

      /**
      * This method will save all the notifications on shared pereference
      * and will return the String array list which will be processed.
      * @param context context of current service
      * @param title   title of notification. Not used in this demo mode.
      * @param desc    String to save in SP.
      * @return        String value of ArrayList.class object.
      */

      private ArrayList<string> processStringDatas
        (Context context, String title, String desc) {

       SharedPreferences sp=getSharedPreferece(context);

       ArrayList<string> notificationObject;

       String notif_content=sp.getString("notif_content",null);

       if(notif_content!=null){

           notificationObject=new Gson()
           .fromJson(notif_content,ArrayList.class);

       }else {

           notificationObject=new ArrayList<>();
       }

       notificationObject.add(desc);

       SharedPreferences.Editor editor =sp.edit();

       editor.putString("notif_content", new Gson().toJson(notificationObject));

       editor.apply();

       return notificationObject;
   }

Below pictures are the one to show how stacked notifications are rendered in devices of pre Lollipop and post Lollipop. Always remember to clear the shared preference key value pair when user clicks on the notifications. Now stacked notification behaviour can be achieved across all the devices in Android by the method i have described.

Stacked notifications on different Android versions
Happy Coding!
Published inAndroid

One Comment

  1. Jithin Jithin

    Was looking for a post like this. So helpfull

Leave a Reply

Your email address will not be published. Required fields are marked *