Bubbles & Conversations in Android 11: Getting Started

Learn how to use Bubbles in Android to add notifications and create conversational shortcuts for Direct Share. By Jenn Bailey.

Leave a rating/review
Download materials
Save for later
Share
Update note: Jenn Bailey updated this tutorial for Android 11. She also wrote the original.

The most important thing an Android phone does is connect people. Wouldn’t it be nice if you could feel like the people you want to talk to are right there with you?

Android 11 has spectacular new features to help developers and users do just that. These features allow quick access to conversations with conversation shortcuts and adds a new Conversations section in the notification shade. Bubbles also gives you sophisticated sharing and ranking capabilities, as well as improved multitasking.

These changes reflect the goal of the platform: It will continue to evolve to focus on people and conversations as part of a multi-year Android initiative.

Happy android expressing joy with hearts.

In this tutorial, you’ll learn how to add these features to an app called Bubble Affirmations, which simulates chats with inspirational characters who provide a quick fix of ongoing encouragement at your fingertips.

In the process, you’ll learn how to:

  • Make notifications appear in the Conversations section
  • Create conversation shortcuts
  • Allow direct share through the shortcuts
  • Add metadata to bubbles
  • Detect user permission for bubbles

Perhaps you know Android but are unfamiliar with Kotlin. If that’s the case, take a look at Kotlin For Android: An Introduction.

Just in case you’d like a quick primer on notifications, take a peek at Android Notifications Tutorial: Getting Started.

Note: This tutorial assumes you have basic knowledge of Kotlin and Android. If you’re new to Android, check out our Android tutorials.

Perhaps you know Android but are unfamiliar with Kotlin. If that’s the case, take a look at Kotlin For Android: An Introduction.

Just in case you’d like a quick primer on notifications, take a peek at Android Notifications Tutorial: Getting Started.

Now, it’s time to dive in!

Getting Started

Download the project materials by using the Download Materials button at the top or bottom of this tutorial. Open the Starter project in Android Studio. After the project finishes loading, build and run.

The starter app displaying a list of affirmational quote categories

Select a category of affirmations to see a random quote from that category:
Example quote

Tap the bell icon in the top-right corner, and your selected quote will appear as a notification:
A plain notification displaying the quote.

This notification could be more inspiring. In the next step, you’ll promote it to the Conversations section of the drawer.

Showing Notifications in the Conversations Section

Many phones have a reserved section at the top of the notification shade for real-time conversations. Notifications that appear in this section have a stronger visual emphasis on the avatar, highlighting who the conversation is with. Long-pressing notifications in this area shows you conversation-specific actions you can take.

To appear in this section, the notification must use MessagingStyle and associate the message with a Person. You’ll see how to do this next.

Adding a Person to the Conversation

Open NotificationHelper.kt and add the method below inside NotificationHelper:

private fun createPerson(icon: Icon, category: Category): Person {
  return Person.Builder()
    .setName(category.name)
    .setIcon(icon)
    .build()
}

This code generates a person to associate with the notification, making it a Person Notification.

Now, in showNotification, add the following above the line of code that creates the notification:

val person = createPerson(icon, quoteAndCategory.category)

The code above creates the person. Now, update the line of code that creates the notification to:

val notification = createNotification(quoteAndCategory, icon, person)

This passes the person to createNotification.

Then, replace createNotification with:

private fun createNotification(
  quoteAndCategory: QuoteAndCategory,
  icon: Icon,
  person: Person
): Notification.Builder {
  return Notification.Builder(context, CHANNEL_QUOTES)
    // 1
    .setContentTitle(quoteAndCategory.category.name)
    .setSmallIcon(icon)
    // 2
    .setShortcutId(quoteAndCategory.category.shortcutId)
    .setLocusId(LocusId(quoteAndCategory.category.shortcutId))
    .setCategory(Notification.CATEGORY_MESSAGE)
    // 3
    .setStyle(Notification.MessagingStyle(person)
      .setGroupConversation(false)
      .addMessage(quoteAndCategory.quote.quoteText,
                  currentTimeMillis(), person)
    )
    // 4
    .setShowWhen(true)
    .setContentIntent(createPendingMainIntent(
      REQUEST_CONTENT,
      quoteAndCategory.category)
    )
}

createNotification configures and returns Notification.Builder to send the notification. Here’s what’s going on inside:

  1. First, you set the title and icon for the notification.
  2. Then you use setShortcutId or setShortcutInfo to associate the notification with a long-lived dynamic or cached sharing shortcut when targeting Android 11. You also set the LocusId to improve ranking accuracy.
  3. In Android 11, a conversation notification must use MessagingStyle.
  4. Finally, you include the time of the notification and set an intent to open when the user taps the notification.

For better ranking, a messaging app can include a URI to a contact.

Note: Using LocusId helps the on-device intelligence figure out which conversations interest the user most. Ranking is based on how recent and how frequent the conversations are.

For better ranking, a messaging app can include a URI to a contact.

Understanding Fallback Behavior

If you want a conversation to appear in the Conversations section in Android 11, you must associate it with a valid, long-lived, dynamic or cached sharing shortcut and use MessagingStyle. The user can demote the conversation by altering the notification channel settings.

Apps that target Android 10 can make messages appear in the notification space, but the requirements are different. If the app uses MessagingStyle, it isn’t necessary to provide a shortcut in Android 10. However, if you don’t provide a shortcut, users won’t be able to see conversation-specific functions inline by long-pressing the notification.

If you set the category to CATEGORY_MESSAGE and the platform recognizes the app as a messaging app, it will still show in the Conversations section, but it will be in the pre-Android 11 style with no conversation-specific functions.

Now that you’ve associated the notification with a person, build and run:

Notification in person style, but not Conversations section.

The notification looks better now, and the sender’s avatar is more visible. For the notification to show up in the conversation area, however, you have to associate it with a valid shortcut. You’ll create the shortcuts next.

Creating Conversation Shortcuts

When targeting Android 11 or higher, you have to give a shortcut to any notification you want to show in the conversation space. Shortcuts are a handy way for a user to jump right into a conversation. Be sure to use AdaptiveIconDrawable for your shortcut icons. Otherwise, the avatar might not look as intended.

Note: To learn more, follow this shortcut imagery guide.

Add the following function inside NotificationHelper.kt:

private fun createShortcutIcon(category: Category): Icon {
  return Icon.createWithAdaptiveBitmap(
    context.resources.assets.open(
      "${category.name.toLowerCase(Locale.ROOT)}.png").use {
        input ->
          BitmapFactory.decodeStream(input)
       })
}

This code provides an adaptive bitmap so you can add the shortcuts. To do this, first add a new Android resource file to res called shortcuts.xml. Type shortcuts as the root element and select XML as the resource type and xml as the directory name.

Adding a shortcuts.xml file with XML

In shortcuts.xml, inside the shortcut tag, add the following code:

<share-target android:targetClass=
  "com.raywenderlich.android.bubblesaffirmations.ui.main.MainActivity">
  <data android:mimeType="text/plain" />
  <category android:name=
    "com.example.android.bubbles.category.TEXT_SHARE_TARGET" />
</share-target>

The share-target element holds information about which class will handle the intent, the type of data it can accept from sharing and the category that the direct share target uses.

Next, open AndroidManifest.xml and add the following code above the closing activity tag for MainActivity:

<meta-data
  android:name="android.app.shortcuts"
  android:resource="@xml/shortcuts" />

The code above associates MainActivity with the dynamic shortcut’s share-target in shortcuts.xml.

Next, open NotificationHelper.kt and add the following inside NotificationHelper:

private fun createDynamicShortcutIntent(category: Category): Intent =
  Intent(context, MainActivity::class.java)
    .setAction(Intent.ACTION_VIEW)
    .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    .setData(
      Uri.parse(
        "https://raywenderlich.android.bubblesaffirmations.com"
        + "/quote/${category.categoryId}"
      )
    )

createDynamicShortcutIntent creates the intent to launch from this shortcut. It uses a URI that passes categoryId to link directly to a specific conversation in the app via the shortcut.

Still in NotificationHelper.kt, add:

private fun createShortcuts() = categories.map { category ->
  ShortcutInfo.Builder(context, category.shortcutId)
    // 1
    .setLocusId(LocusId(category.shortcutId))
    .setActivity(ComponentName(context, MainActivity::class.java))
    .setShortLabel(category.name)
    .setIcon(createShortcutIcon(category))
    // 2
    .setLongLived(true)
    // 3
    .setCategories(
      setOf("com.example.android.bubbles.category.TEXT_SHARE_TARGET")
    )
    .setIntent(createDynamicShortcutIntent(category))
    // 4
    .setPerson(createPerson(createIcon(category), category)
  ).build()
}

Here’s what the code above does:

  1. LocusId helps the system accurately rank the conversation based on app usage.
  2. Conversation shortcuts must be long-lived. If the conversation no longer exists, the app can delete the shortcut with removeLongLivedShortcuts. Don’t remove shortcuts unless absolutely necessary — for example, when the user blocks a contact.
  3. Sets the category to the share-target defined in shortcuts.xml. Categories describe what type of data the user can share to the shortcut.
  4. Creates a person with an avatar and associates the person with the shortcut. Conversational shortcuts always have an associated person object.