5

I've been experimenting with the new Bubbles API recently. No matter what I do, the notifications that I expect to appear as a bubble always appear in the system tray as a normal notification.

I've written my own toy app, which I'll add here. I have also pulled down a couple of other apps from tutorials (here and here) that I have studied. In every single case, no bubble, just a system tray notification.

Since the sample apps assert that they can present bubbles, I assume that the problem must be somewhere in my emulator environment. I'm running an emulator that uses Android API R. And I have enabled bubbles in the developer options:

enter image description here

Here is the relevant code from the app that I have developed:

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bubbles">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".BubbleActivity"
            android:allowEmbedded="true"
            android:documentLaunchMode="always"
            android:resizeableActivity="true" />
        <activity
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

MainActivity.kt

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {

    private lateinit var bubbleViewModel: BubbleViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bubbleViewModel = ViewModelProvider(
            this, BubbleViewModelFactory(this.application))
            .get(BubbleViewModel::class.java)
    }

    fun blowBubble(view: View) {
        bubbleViewModel.showBubble()
    }
}

BubbleViewModel.kt

class BubbleViewModel(application: Application): AndroidViewModel(application) {

    private val notificationHelper = NotificationHelper(getApplication())

    init {
        notificationHelper.createNotificationChannels()
    }

    fun showBubble() {
        viewModelScope.launch{
            withContext(Dispatchers.Main) {
                with (notificationHelper) {
                    if (canBubble())
                        showNotification()
                }
            }
        }
    }
}

NotificationHelper.kt

class NotificationHelper(private val context: Context) {

    private val notificationManager = context.getSystemService(NotificationManager::class.java)
    private lateinit var channel: NotificationChannel

    fun createNotificationChannels() {

        channel = NotificationChannel(
            CHANNEL_NEW_MESSAGES,
            context.getString(R.string.channel_name),
            NotificationManager.IMPORTANCE_HIGH)

        with(channel) {
            enableVibration(true)
            lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
            description = context.getString(R.string.channel_description)
            setAllowBubbles(true)
        }

        Log.d("bubble", "Can Bubble: $channel.canBubble()")
        notificationManager?.let {
            it.createNotificationChannel(channel)
        }
    }

    @WorkerThread
    fun showNotification() {

        val bubbleIntent = PendingIntent.getActivity(
            context,
            REQUEST_BUBBLE,
            Intent(context, BubbleActivity::class.java).setAction(Intent.ACTION_VIEW),
            PendingIntent.FLAG_UPDATE_CURRENT)

        val bubbleMetaData = Notification.BubbleMetadata.Builder()
            .setDesiredHeight(600)
            .createIntentBubble(bubbleIntent, Icon.createWithResource(context, R.drawable.baseball))
            .setAutoExpandBubble(false)
            .setSuppressNotification(false)
            .build()

        val person = Person.Builder()
            .setIcon(Icon.createWithResource(context, R.drawable.baseball))
            .setName("Bubbles...")
            .setImportant(true)
            .build()

        val style = Notification.MessagingStyle(person)
            .addMessage("...are the Best!", System.currentTimeMillis(), person)

        val builder = Notification.Builder(context, CHANNEL_NEW_MESSAGES)
            .setBubbleMetadata(bubbleMetaData)
            .setContentIntent(bubbleIntent)
//            .setContentTitle("Title")
//            .setContentText("Hello this is a notification")
            .setSmallIcon(R.drawable.baseball)
            .setShowWhen(true)
            .setAutoCancel(true)
            .setStyle(style)
//            .addPerson(person.uri)


        notificationManager?.notify(0, builder.build())
    }

    fun canBubble(): Boolean {
        notificationManager?.let {
            val channel = it.getNotificationChannel(CHANNEL_NEW_MESSAGES)
            return it.areBubblesAllowed() && channel.canBubble()
        }
        return false
    }

    companion object {
        private const val CHANNEL_NEW_MESSAGES = "new_messages"
        const val REQUEST_BUBBLE = 2
    }
}

And finally, the destination activity, which I don't think really matters too much since it only fires if the bubble were available to click: BubbleActivity.kt

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class BubbleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_bubble)
    }
}

And that's really all there is to this. But when run this and click the button to display a bubble, I get this result:

enter image description here

AndroidDev
  • 20,466
  • 42
  • 148
  • 239
  • Coming straight from the docs - Note: The Bubbles feature is in Developer Preview and is for developer use only. It should not be used in production. **Bubbles are disabled by default.** Developers can enable Bubbles in the developer options settings. _Maybe? Just maybe?_ :) – ashu Mar 05 '20 at 20:28
  • Should have mentioned that. Yes, they are enabled. I updated the post with that information. Good question. – AndroidDev Mar 05 '20 at 20:32

2 Answers2

5

@AndroidDev your code works fine in API level 29 and shows the notifications as bubbles but it does not work in Android R. I guess you should not worry about that as it is in preview stage and definitely not stable.

So if you run your code in an emulator with API level 29, it should work fine.

This issue is a regression from Android 10 and is reported here

Niladree
  • 526
  • 3
  • 14
2

I was struggling with the same issue, but when I ran Google's example app People it worked just fine.

From https://developer.android.com/guide/topics/ui/bubbles:

If an app targets Android 11 or higher, a notification doesn't appear as a bubble unless it meets the conversation requirements.

Those requirements mentions this part that it looks like you are missing:

Only notifications with an associated shortcut are able to bubble.

How to set up the shortcut (and everything else) can be seen in their mentioned example: https://github.com/android/user-interface-samples/blob/main/People/app/src/main/java/com/example/android/people/data/NotificationHelper.kt

Roy Solberg
  • 18,133
  • 12
  • 49
  • 76