In this tutorial, we’ll look at a new feature introduced with Android Q which is Bubbles. We’ll be implementing it in our Android Application today.
Android Q Bubbles
We’ve all seen how Facebook chat bubbles work. Come Android Q, and now we have a built-in notification system which opens a preview of the application screen from the notification bubble. Moreover, we can multi-task and switch between different bubbles.
When the device is locked, Bubbles won’t be displayed. Only the normal notification would be displayed.
Bubbles are an opt-in feature. When presented for the first time, we have an option to allow/disable bubbles.
Otherwise, we can do the same from the settings.
Bubble displays content in floating windows.

Android Q Notification Bubble Permissions
How are Bubbles implemented?
In order to implement Bubbles in our Notification, we have to set the Bubble Metadata using the Builder and set it on the Notification with setBubbleMetadata()
.
For the activity to open from the bubble, we need to define it in the Manifest file as:
<activity
android:name=".BubbleActivity"
android:label="@string/title_activity_bubble"
android:allowEmbedded="true"
android:documentLaunchMode="always"
android:resizeableActivity="true"
android:theme="@style/AppTheme.NoActionBar"/>
Creating a Bubble Metadata:
Notification.BubbleMetadata bubbleData =
new Notification.BubbleMetadata.Builder()
.setDesiredHeight(600)
.setIntent(bubbleIntent)
.setAutoExpandBubble(true)
.setSuppressInitialNotification(true)
.build();
But you can always auto expand the bubble by setting the methods
setAutoExpand(true)
and setSuppressInitialNotification(true)
The lifecycle of the activity presented in the Bubble is the same as the normal lifecycle.
Every time the bubble is dismissed, the activity gets killed.
For a bubble to be displayed, you must pass IMPORTANCE HIGH
in the NotificationChannel
.
canBubble()
method. You can use this to determine whether bubbles could be displayed for this notification channel or whether they are disabled.setAllowBubbles()
can be set to true on the NotificationChannel in order to allow bubbles to be displayed for this NotificationChannel group.
PRO-TIP
areBubblesAllowed()
is added in the NotificationManager class. This along with canBubble should be used to check whether bubbles are allowed or not.
In the following section, we’ll be creating a simple application that demonstrates Android Bubbles.
We’ll be using AndroidX.
Project Structure

Android Q Bubble Project Structure
Code
The code for the activity_main.xml
layout is given below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_height="match_parent">
<Button
android:id="@+id/btnBubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Create Bubble" />
<Button
android:id="@+id/btnBubble2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Another Bubble" />
</LinearLayout>
The code for the MainActivity.java class is given below:
package com.journaldev.androidqbubbles;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btnBubble, btnBubble2;
NotificationManager notificationManager;
Notification.Builder builder;
NotificationChannel channel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBubble = findViewById(R.id.btnBubble);
btnBubble2 = findViewById(R.id.btnBubble2);
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence name = "My Channel";
String description = "xyz";
int importance = NotificationManager.IMPORTANCE_HIGH;
channel = new NotificationChannel("1", name, importance);
channel.setDescription(description);
channel.setAllowBubbles(true);
btnBubble.setOnClickListener(this);
btnBubble2.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId())
{
case R.id.btnBubble:
Intent target = new Intent(MainActivity.this, BubbleActivity.class);
PendingIntent bubbleIntent =
PendingIntent.getActivity(MainActivity.this, 0, target, PendingIntent.FLAG_UPDATE_CURRENT /* flags */);
// Create bubble metadata
Notification.BubbleMetadata bubbleData =
new Notification.BubbleMetadata.Builder()
.setDesiredHeight(600)
.setIcon(Icon.createWithResource(MainActivity.this, R.mipmap.ic_launcher))
.setIntent(bubbleIntent)
.build();
builder = new Notification.Builder(MainActivity.this, channel.getId())
.setSmallIcon(R.mipmap.ic_launcher)
.setBubbleMetadata(bubbleData);
notificationManager.createNotificationChannel(channel);
notificationManager.notify(1, builder.build());
break;
case R.id.btnBubble2:
target = new Intent(MainActivity.this, BubbleActivity.class);
target.putExtra("key","This is the second bubble");
bubbleIntent =
PendingIntent.getActivity(MainActivity.this, 0, target, PendingIntent.FLAG_UPDATE_CURRENT);
// Create bubble metadata
bubbleData = new Notification.BubbleMetadata.Builder()
.setDesiredHeight(600)
.setIcon(Icon.createWithResource(MainActivity.this, R.mipmap.ic_launcher))
.setIntent(bubbleIntent)
.build();
builder = new Notification.Builder(MainActivity.this, channel.getId())
.setContentTitle("Second Bubble")
.setSmallIcon(R.mipmap.ic_launcher)
.setBubbleMetadata(bubbleData);
notificationManager.createNotificationChannel(channel);
notificationManager.notify(2, builder.build());
break;
}
}
}
Note: While writing this tutorial, the emulator is unable to display the Icons. This should be rectified in upcoming updates of Android Q after the Beta 2 soon.
We’ve created another activity using the Basic Activity template.
The code for the BubbleActivity.java is given below:
package com.journaldev.androidqbubbles;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
public class BubbleActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bubble);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
textView = findViewById(R.id.textView);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
@Override
protected void onResume() {
super.onResume();
if (getIntent() != null && getIntent().getExtras() != null) {
String value = getIntent().getStringExtra("key");
textView.setText(value);
}
}
}
Here we update the TextView based on the PendingIntent results from the Notification.
Note: Do not forget to add the BubbleActivity to the Manifest with the correct attributes as discussed at the beginning of this tutorial.
The output of the above application in action is given below:

Android Q Notification Bubble Output
As you can see, we are able to perform all actions in the BubbleActivity that’s displayed as a floating window.
That brings an end to this tutorial. You can download the AndroidQBubbles tutorial from the link below or visit our Github Repository for the same.
Bubble is getting collapsed after clicking anywhere on expanded bubble activity. I am not to interact with the Bubble Activity. Any idea why is it happening?