Exploring Android P: Enhanced Notifications

With Android P now out in the wild, there are a bunch of new features and APIs which have become available for us to make use of in our applications. Some of these new features fall under the notification APIs, allowing us to create more contextual and detailed notifications for our applications. In this post, I want to dive into the new notification enhancements that have been added to the Notification APIs.


This enhancement for notifications is focused around the display of messages within the notification content. The first part that we’re going to look at is the display of the some author when it comes to these messages. To display messages within your notifications you may have previously used the addMessage() function — this function allowed you to pass in a sender name, displaying a textual representation of who the message in the notification has come from. This function is now deprecated and has been replaced with a new addMessage() function — this replacement allows us to pass in an instance of a Person which will be used to tie that message to a specific person and use those details for contextual representation within the notification content. This can allow us to provide more useful content to our notifications, improving the experience here for our user.

Now, when it comes to building a notification, you’ll still it in the same way:

val notificationBuilder = NotificationCompat.Builder(this, ...)
.setSmallIcon(...)
.setContentTitle(...)
.setContentText(...)
.setPriority(...)

We’ll need that reference later, but we’ll leave it as it is for now — let’s go ahead and create an instance of the Person class that we previously mentioned. We’ll use the build of this class to create a simple Person instance that has a name assigned to it:

val sender = Person.Builder()
.setName(R.string.user_name)
.build()

We can then go ahead and use the addMessage() function, passing in our text to be shown in the notification, along with the time and the sender which the message is bound to. Remember, you need to use the setBuilder() function here to reference the notification builder that we previously defined:

NotificationCompat.MessagingStyle(sender)
.addMessage("Check this out!", Date().time, sender)
.setBuilder(notificationBuilder)

Finally, you can go ahead and show your notification just like you previously would have:

NotificationManagerCompat.from(this)
.notify(..., notificationBuilder.build())

When this notification is displayed, you’ll notice that our message is shown, along with the sender name and the default icon for the Person instance:

Whilst this is pretty neat, we can do a little better here! One of the other properties which we can set on a Person is the icon to be used, this is done by using the setIcon() function which takes an instance of an Icon:

val sender = Person.Builder()
.setName(R.string.user_name)
.setIcon(Icon.createWithBitmap(bitmap))
.build()

If we go ahead and show the notification again then you’ll notice that we now have a nice visual representation of the person who is tied to that notification message:

The person class also has a collection of other properties which we can apply using the following functions:

  • setImportant() — Specify whether or not this is a person of importance, using a boolean value. This can be if it is someone who the owner of the device regularly interacts with — should only need to be set if there is no URI provided for this Person instance.
  • setKey() —Set a unique string identifier for this Person instance. This can be useful if the display name of the person is not a unique representation — such as a shortened name.
  • setUri() — A string representation of a ContactsContract.Contacts.CONTENT_LOOKUP_URI which connects this Person to a contact stored on the users device. If an Icon is set on this Person instance, then the icon from this URI will be ignored.
  • setBot() — Pass in a boolean to specify whether or not this person instance is a machine or human.

Now that the sender of our notification is looking all nice and shiny, what can we do with the message content itself? A neat new piece of functionality here is the support for inline images within notifications — to achieve this we can simply use the setData() function on our message instance to provide a URI to the image which is to be used:

val message = Notification.MessagingStyle.Message(message, 
time, sender)
.setData("image/", chickenUri)

And then just like before, pass our message in using the addMessage() function:

Notification.MessagingStyle(sender)
.addMessage(message)
.setBuilder(mBuilder)

When our notification is shown, you can see that we have a nice inline image tied to the message being shown:

Now at this point, say if I was to send another message to this conversation then we can simply continue to chain them on using the addMessage() function:

Notification.MessagingStyle(sender)
.addMessage(message)
.addMessage(messageTwo)
.addMessage(messageThree)
.setBuilder(mBuilder)

If the Person instance in a message is the same as the previous messages` Person, then that message will simply be chained onto the previous messages content:

And if the Person instance of the message is not the same as the Person of the previous message, then the message will be treated as a new entity and the Person will be displayed alongside the message to depict the change of context:


Another property that we can set on our notification is the isGroupNotification property, which can be done using the setGroupConversation() function when building our MessagingStyle:

Notification.MessagingStyle(sender)
.addMessage(message)
.setGroupConversation(true)
.setBuilder(mBuilder)

Setting this property will identify the messages in your notification as part of a group conversation. It’s also important to note that on Android P this property must be set to true if your notification wishes to display a large icon using the setLargeIcon() function on the notification builder. When set though, the large icon will be shown on the right hand side of the notification when collapsed:


Come Android P we now also have the ability to assign semantic meaning to our notification actions, whilst this doesn’t change the look or feel of notifications it allows us to assign a pre-defined reference for a semantic value to our notification actions. When building your notification actions you can use the setSemanticAction() function to do this. So let’s say we had a notification action that would call the person who sent the last message, then we would use the SEMANTIC_ACTION_CALL for its semantic meaning:

val action = NotificationCompat.Action.Builder(...)
.setSemanticAction(SEMANTIC_ACTION_CALL)
.build()

Currently the following semantic action values are supported:


Finally, there are a couple of other small changes which we may wish to make use of in our applications. First off is the ability to retrieve draft input content from a notification that was previously from a closed message notification. Here we can use the EXTRA_REMOTE_INPUT_DRAFT key to retrieve this value and assign it as the contentInput for the remote input message of our notification — allowing our user to easily carry on where they left off.

Another change comes in the setChoices() function. Whilst this has been available to us to use since API level 20, these choices will always be shown when the device is running at least Android P (API level 28).


In this post we’ve taken a look into some of the enhancements that have been introduced for notifications in Android P. I hope this has given some insight into how we can make our notifications more contextual and visual for our users. Are you using these new notification APIs in your apps, or do you have any questions before you get started? Please feel free to reach out 🙂