I’ve been working on implementing bundled notifications in my app, which is by far one of my favourite features about Android 7.0 Nougat. While the official guide is good enough for understanding how they work, I’ve constantly found myself running into behaviours that were not easy to find in the documentation. This post is an attempt to summarise those findings.
Implementing bundled notifications is slightly difficult because ensuring that they work-as-intended across multiple Android versions can involve a fair amount of cognitive overhead. To start with, we require two things for creating a bundle:
- One summary notification - N bundled child notifications
On Nougat and above, Android will display the summary notification when collapsed and the child notifications when expanded. On older versions however, Android will only show the summary notification. This essentially means that we also need to correctly choose a style (
InboxStyle, etc.) for the summary notification’s style depending on the number of items to show. So the list of things required actually looks like this:
- One summary notification - BigTextStyle, for 1 item (e.g., message) - InboxStyle, for N items - N bundled child notifications
One interesting behavior of the summary notification is that it gets ignored on Nougat. Android picks up some of its properties like the color, timestamp, icon, etc. and generates its own summary notification with a style that looks and behaves similar to
new Notification.Builder(context) .setContentTitle() // Ignored .setContentText() // Ignored .setSmallIcon(R.drawable.new_mail) // Picked .setLargeIcon() // Picked .setStyle(new InboxStyle()) // Ignored .build();
On versions before Nougat, the summary notification is displayed without any modifications.
If your notifications are actionable, you will also want to update their state upon user interaction. For example, when a message notification is marked as “read”, you might want to dismiss the notification before updating the message’s state in the database. Or a more complex example would be “direct reply” where Android requires the notification to be reset for dismissing the reply field once the reply has been sent.
Another important thing here to keep in mind is that the notifications are only bundled “visually” and Android treats them as separate notifications functionality wise. What this means is that,
- Each notification in the bundle can have its own tone and vibration. So if you accidentally end up setting a tone to each one of them, Android will play all the tones… at the same time.
- A bundle is not replaceable. When a new bundle is posted with the same group key, Android will replace the summary, but the new child notifications will get merged with the previously visible child notifications instead of replacing them entirely.
- The summary notification can exist even when all the bundled notifications are dismissed.
Problem #1 can be solved easily by only setting the tone and vibration only on the summary notification. However, problem #2 and #3 will require some effort which will vary depending upon the use-case.
Here’s a simple example:
1. Get all “unread” messages from DB. 2. Display bundled notifications if we have unreads. Otherwise, dismiss any stale notification. 3. When a notification is marked as read: a) Dismiss it using NotificationManager#cancel(notifId). b) Mark it as read in DB. 4. To ensure that the summary notification is not the only remaining notification, goto step #1 again.
This “diff” illustration from Android Developer’s blog can be referred to for understanding the visual changes done in Nougat.
Starting from Nougat, Android applies a darker tint to the color passed to
Notification.Builder#setColor() to increase contrast and legibility. This behaviour is intentional so you should not try to balance this by supplying a lighter color.
To be able to correctly update notifications (for e.g., dismissing direct reply field), make sure to use stable IDs for them. That is, your logic for generating notification IDs should be pure. Not doing so will result in duplicate notifications.
Cancelling summary notification
Cancelling a summary notification also cancels all its child notifications. Dan Lew has written a detailed blog post to workaround this problem.
Photo credits: Material.io