Android Q Bubbles

Filed Under: Android

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

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();
By default you need to click the bubble for it to show the content.
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.

NotificationChannel has a method 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

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="http://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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages