Android Bundled Notifications

Filed Under: Android
Android Bundled Notifications

In this tutorial, we’ll be implementing Bundled Notifications in our Android Application. We’ve seen and implemented Direct Reply for Android Nougat. If you aren’t aware about Notifications and Pending Intents do go through the tutorial before proceeding.

Android Bundled Notifications

Android Bundled Notifications are handy when you have too many notifications stacked vertically. Bundled Notifications collapse them all into a single notification with a group summary set on the group notification. This saves us from scrolling through all the notifications. Also, if you receive many single notifications at once, instead of getting multiple noisy notification sounds, bundled notifications would give a single notification sound.

A Bundle Notification is of the format:

  • 1 Summary Notification
  • N enclosed Single Notifications

We can set a separate tone for every single notification. Every notification of a bundle would have the same group key.

When a new notification is added, it gets merged in the bundle with the matching group key.

We can add a separate action on each notification click as well as a separate action on bundle notification click.
The Bundle Notification can exist without any enclosed notifications.

Creating a Bundle Notification

A Bundle Notification is defined in the following way.


NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    NotificationChannel groupChannel = new NotificationChannel("bundle_channel_id", "bundle_channel_name", NotificationManager.IMPORTANCE_LOW);
                    notificationManager.createNotificationChannel(groupChannel);
                }


NotificationCompat.Builder summaryNotificationBuilder = new NotificationCompat.Builder(this, "bundle_channel_id")
                        .setGroup(bundle_notification_id)
                        .setGroupSummary(true)
                        .setContentTitle("Bundled Notification. " + bundleNotificationId)
                        .setContentText("Content Text for bundle notification")
                        .setSmallIcon(R.mipmap.ic_launcher);

                notificationManager.notify(bundleNotificationId, summaryNotificationBuilder.build());

A Bundle Notification must have the setGroupSummary() set to true.

The group key set in the setGroup() method must be set in all the notifications that are to be enclosed in the bundle.

Note: A bundle_channel_id needs to defined since Android Oreo. We’ve discussed Notification Channels at length in a separate tutorial.

We’ve set the importance of the bundle notification channel to LOW to avoid simultaneous sounds from the Bundle and single notifications. We’ll create a separate channel for single notifications.

In the sample application, we’ll be implementing the Bundle Notifications feature. We’ll be using a Button to create a new Bundle and another button to add new Single Notifications in the current Bundle.

On a notification click, we’ll cancel the notification and display it’s ID in a Toast.

We’ll not be focusing on push notifications from the backend in this tutorial. Our goal is to understand the Bundle Notifications feature only.

Android Bundled Notifications Project Structure

android bundled notifications project structure

A Single Activity application.

Android Bundled Notifications Code

The code for the activity_main.xml layout class is given below.


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.journaldev.bundlednotificationsexample.MainActivity">

    <Button
        android:id="@+id/btnBundleNotification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Bundle Notification"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnSingleNotification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Notification to Bundle"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnBundleNotification" />

</android.support.constraint.ConstraintLayout>

The code for the MainActivity.java class is given below.


package com.journaldev.bundlednotificationsexample;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
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.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button btnBundleNotification, btnSingleNotification;
    NotificationManager notificationManager;
    int bundleNotificationId = 100;
    int singleNotificationId = 100;
    NotificationCompat.Builder summaryNotificationBuilder;

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

        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        btnBundleNotification = findViewById(R.id.btnBundleNotification);
        btnSingleNotification = findViewById(R.id.btnSingleNotification);
        btnBundleNotification.setOnClickListener(this);
        btnSingleNotification.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnBundleNotification:


                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    NotificationChannel groupChannel = new NotificationChannel("bundle_channel_id", "bundle_channel_name", NotificationManager.IMPORTANCE_LOW);
                    notificationManager.createNotificationChannel(groupChannel);
                }
                bundleNotificationId += 100;
                singleNotificationId = bundleNotificationId;
                String bundle_notification_id = "bundle_notification_" + bundleNotificationId;
                Intent resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Summary Notification Clicked");
                resultIntent.putExtra("notification_id", bundleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);


                summaryNotificationBuilder = new NotificationCompat.Builder(this, "bundle_channel_id")
                        .setGroup(bundle_notification_id)
                        .setGroupSummary(true)
                        .setContentTitle("Bundled Notification. " + bundleNotificationId)
                        .setContentText("Content Text for bundle notification")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentIntent(resultPendingIntent);

                notificationManager.notify(bundleNotificationId, summaryNotificationBuilder.build());

                break;

            case R.id.btnSingleNotification:


                bundle_notification_id = "bundle_notification_" + bundleNotificationId;

                resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Summary Notification Clicked");
                resultIntent.putExtra("notification_id", bundleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

                //We need to update the bundle notification every time a new notification comes up.
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    if (notificationManager.getNotificationChannels().size() < 2) {
                        NotificationChannel groupChannel = new NotificationChannel("bundle_channel_id", "bundle_channel_name", NotificationManager.IMPORTANCE_LOW);
                        notificationManager.createNotificationChannel(groupChannel);
                        NotificationChannel channel = new NotificationChannel("channel_id", "channel_name", NotificationManager.IMPORTANCE_DEFAULT);
                        notificationManager.createNotificationChannel(channel);
                    }
                }
                summaryNotificationBuilder = new NotificationCompat.Builder(this, "bundle_channel_id")
                        .setGroup(bundle_notification_id)
                        .setGroupSummary(true)
                        .setContentTitle("Bundled Notification " + bundleNotificationId)
                        .setContentText("Content Text for group summary")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentIntent(resultPendingIntent);


                if (singleNotificationId == bundleNotificationId)
                    singleNotificationId = bundleNotificationId + 1;
                else
                    singleNotificationId++;

                resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Single notification clicked");
                resultIntent.putExtra("notification_id", singleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);


                NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel_id")
                        .setGroup(bundle_notification_id)
                        .setContentTitle("New Notification " + singleNotificationId)
                        .setContentText("Content for the notification")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setGroupSummary(false)
                        .setContentIntent(resultPendingIntent);

                notificationManager.notify(singleNotificationId, notification.build());
                notificationManager.notify(bundleNotificationId, summaryNotificationBuilder.build());
                break;

        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Bundle extras = intent.getExtras();
        if (extras != null) {
            int notification_id = extras.getInt("notification_id");
            Toast.makeText(getApplicationContext(), "Notification with ID " + notification_id + " is cancelled", Toast.LENGTH_LONG).show();
            notificationManager.cancel(notification_id);
        }
    }
}
  1. btnSingleNotification adds a Single Notification in the current bundle notification. If the current bundle notification doesn’t exist it creates one first.
  2. btnBundleNotification creates a new Bundle Notification and updates the group key by incrementing the id.
  3. We set a bundleNotificationId equal to the singleNotificationId initially.
  4. Every time a new bundle notification is created, we increment the bundleNotificationId by 100.
  5. Every time a single notification is created inside the bundle notification, we increment the singleNotificationId by 1 on the current bundleNotificationId.
  6. We’ve created separate channels for Bundle and Single Notifications.
  7. onNewIntent is triggered on Notification click. It gets the notification id from the intent data and cancels the respective notification.

Android Bundled Notifications App Output

Let’s see the output from the above code.
android bundled notifications app output

Do note that in the above output, clicking any notification cancels only the last notification added.

Why?

Because PendingIntent updates the data to the latest and we’re using request code as 0 for each of the PendingIntents. So it returns the latest data only.

We need to set a different request code for each notification.

Let’s set the request code as bundleNotificationId for bundle notifications and singleNotificationId for the single ones.


@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnBundleNotification:


                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    NotificationChannel groupChannel = new NotificationChannel("bundle_channel_id", "bundle_channel_name", NotificationManager.IMPORTANCE_LOW);
                    notificationManager.createNotificationChannel(groupChannel);
                }
                bundleNotificationId += 100;
                singleNotificationId = bundleNotificationId;
                String bundle_notification_id = "bundle_notification_" + bundleNotificationId;
                Intent resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Summary Notification Clicked");
                resultIntent.putExtra("notification_id", bundleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                PendingIntent resultPendingIntent = PendingIntent.getActivity(this, bundleNotificationId, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);


                summaryNotificationBuilder = new NotificationCompat.Builder(this, "bundle_channel_id")
                        .setGroup(bundle_notification_id)
                        .setGroupSummary(true)
                        .setContentTitle("Bundled Notification. " + bundleNotificationId)
                        .setContentText("Content Text for bundle notification")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentIntent(resultPendingIntent);

                notificationManager.notify(bundleNotificationId, summaryNotificationBuilder.build());

                break;

            case R.id.btnSingleNotification:


                bundle_notification_id = "bundle_notification_" + bundleNotificationId;

                resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Summary Notification Clicked");
                resultIntent.putExtra("notification_id", bundleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                resultPendingIntent = PendingIntent.getActivity(this, bundleNotificationId, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

                //We need to update the bundle notification every time a new notification comes up.
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    if (notificationManager.getNotificationChannels().size() < 2) {
                        NotificationChannel groupChannel = new NotificationChannel("bundle_channel_id", "bundle_channel_name", NotificationManager.IMPORTANCE_LOW);
                        notificationManager.createNotificationChannel(groupChannel);
                        NotificationChannel channel = new NotificationChannel("channel_id", "channel_name", NotificationManager.IMPORTANCE_DEFAULT);
                        notificationManager.createNotificationChannel(channel);
                    }
                }
                summaryNotificationBuilder = new NotificationCompat.Builder(this, "bundle_channel_id")
                        .setGroup(bundle_notification_id)
                        .setGroupSummary(true)
                        .setContentTitle("Bundled Notification " + bundleNotificationId)
                        .setContentText("Content Text for group summary")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentIntent(resultPendingIntent);


                if (singleNotificationId == bundleNotificationId)
                    singleNotificationId = bundleNotificationId + 1;
                else
                    singleNotificationId++;

                resultIntent = new Intent(this, MainActivity.class);
                resultIntent.putExtra("notification", "Single notification clicked");
                resultIntent.putExtra("notification_id", singleNotificationId);
                resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                resultPendingIntent = PendingIntent.getActivity(this, singleNotificationId, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); //singleNotificationId is set as the request code.


                NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel_id")
                        .setGroup(bundle_notification_id)
                        .setContentTitle("New Notification " + singleNotificationId)
                        .setContentText("Content for the notification")
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setGroupSummary(false)
                        .setContentIntent(resultPendingIntent);

                notificationManager.notify(singleNotificationId, notification.build());
                notificationManager.notify(bundleNotificationId, summaryNotificationBuilder.build());
                break;

        }
    }

Let’s see the output now.
android bundled notifications example

Works!. This brings an end to android bundled notifications tutorial. You can download the Android BundledNotificationsExample Project from the link below.

Comments

  1. Deep Dave says:

    Same code of bundled Notification pasted in firebase message on message received method but it overwrites previous notification… Any Idea What could be missing link?

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages