blob: d22f0265abb56facf81bf7bee2023c272894791d [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.notifications;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static com.android.cts.verifier.notifications.BubbleActivity.EXTRA_INSETS;
import static com.android.cts.verifier.notifications.BubbleActivity.EXTRA_TEST_NAME;
import static com.android.cts.verifier.notifications.BubbleActivity.TEST_MAX_HEIGHT;
import static com.android.cts.verifier.notifications.BubbleActivity.TEST_MIN_HEIGHT;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.ArraySet;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.widget.Button;
import android.widget.TextView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import java.util.ArrayList;
import java.util.Set;
/**
* Bubble notification tests: This test checks the behaviour of notifications that have a bubble.
*/
public class BubblesVerifierActivity extends PassFailButtons.Activity {
private static final String CHANNEL_ID = "BubblesVerifierChannel";
private static final int NOTIFICATION_ID = 1;
private static final int NUM_SHORTCUTS = 3;
private static final String SHORTCUT_CATEGORY =
"com.android.cts.verifier.notifications.SHORTCUT_CATEGORY";
private NotificationManager mNotificationManager;
private ArrayList<ShortcutInfo> mShortcuts = new ArrayList<>();
private boolean mIsLargeScreen;
private TextView mTestTitle;
private TextView mTestDescription;
private Button mTestAction;
private ViewGroup mTestStepButtonLayout;
private TextView mTestState;
private Button mTestStepPassed;
private Button mTestStepFailed;
private ArrayList<BubblesTestStep> mTests = new ArrayList<>();
private int mCurrentTestIndex = -1; // gets incremented first time
private int mStepFailureCount = 0;
private boolean mShowingSummary = false;
private Handler mHandler = new Handler();
private Runnable mRunnable;
private abstract class BubblesTestStep {
/** The title of the test step. */
public abstract int getTestTitle();
/** What the tester should do & look for to verify this step was successful. */
public abstract int getTestDescription();
/**
* Text of the button that performs the action for this test (e.g. send bubble). If no
* button is necessary this will return 0.
*/
public int getButtonText() {
return 0;
}
/** Code to run when the button is activated; only used if {@link #getButtonText()} != 0 */
public void performTestAction() {
// optional
}
/** Checks device state to see if the test passed. */
public boolean verify() {
return true;
}
/** Return true if the test has stuff to check in {@link #verify()}. **/
public boolean waitForVerify() {
return false;
}
}
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.bubbles_main);
mTestTitle = findViewById(R.id.bubble_test_title);
mTestDescription = findViewById(R.id.bubble_test_description);
mTestAction = findViewById(R.id.bubble_test_button);
mTestStepButtonLayout = findViewById(R.id.button_layout);
mTestState = findViewById(R.id.bubble_test_state);
mTestStepPassed = findViewById(R.id.test_step_passed);
mTestStepFailed = findViewById(R.id.test_step_failed);
mTestStepPassed.setOnClickListener((v) -> runNextTestOrShowSummary());
mTestStepFailed.setOnClickListener((v) -> {
mStepFailureCount++;
runNextTestOrShowSummary();
});
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (am.isLowRamDevice()) {
// Bubbles don't occur on low ram, instead they just show as notifs so test that
mTests.add(new LowRamBubbleTest());
} else if (!Resources.getSystem()
.getBoolean(com.android.internal.R.bool.config_supportsBubble)) {
// Bubbles don't occur on bubble disabled devices, only test notifications.
mTests.add(new BubbleDisabledTest());
} else {
//
// Behavior around settings at the device level and on the app settings page.
//
mTests.add(new CheckDefaultsTest());
mTests.add(new BubbleButtonTest());
mTests.add(new BubblePreferenceSelected());
mTests.add(new DisableFeature());
mTests.add(new EnableBubblesTest());
//
// Behavior around visibility of the bubble, the notification, and the dot
// on the bubble for 'new messages'.
//
mTests.add(new SendBubbleTest());
mTests.add(new SuppressNotifTest());
mTests.add(new AddNotifTest());
mTests.add(new RemoveMetadataTest());
mTests.add(new AddMetadataTest());
mTests.add(new ExpandBubbleTest());
mTests.add(new DismissBubbleTest());
mTests.add(new DismissNotificationTest());
mTests.add(new AutoExpandBubbleTest());
//
// Behavior around the bubble overflow
//
mTests.add(new CheckOverflowExists());
mTests.add(new DismissBubbleShowsInOverflow());
mTests.add(new PromoteBubbleFromOverflow());
mTests.add(new CancelRemovesBubblesInOverflow());
mTests.add(new TapNotifWithOverflowBubble());
//
// Expanded view appearance
//
// At the moment, PC devices do not support rotation
if (!getPackageManager().hasSystemFeature(FEATURE_PC)) {
mTests.add(new PortraitAndLandscape());
}
mTests.add(new ScrimBehindExpandedView());
if (getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS)) {
mTests.add(new ImeInsetsExpandedView());
}
mTests.add(new MinHeightExpandedView());
mTests.add(new MaxHeightExpandedView());
}
setPassFailButtonClickListeners();
// Pass is are enabled when all the steps are done & succeeded
getPassButton().setEnabled(false);
// Sets the text in the dialog
setInfoResources(R.string.bubbles_test_title, R.string.bubbles_test_info, -1);
mIsLargeScreen = getResources().getConfiguration().smallestScreenWidthDp >= 600;
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(
new NotificationChannel(CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT));
createShortcuts();
runNextTestOrShowSummary();
restoreStateFromBundle(savedState);
}
@Override
protected void onSaveInstanceState(Bundle savedState) {
super.onSaveInstanceState(savedState);
savedState.putInt("mCurrentTestIndex", mCurrentTestIndex);
savedState.putInt("mStepFailureCount", mStepFailureCount);
savedState.putBoolean("mShowingSummary", mShowingSummary);
savedState.putBoolean("mTestStepPassed", mTestStepPassed.isEnabled());
}
private void restoreStateFromBundle(Bundle savedState) {
if (savedState != null) {
mCurrentTestIndex = savedState.getInt("mCurrentTestIndex", mCurrentTestIndex);
mStepFailureCount = savedState.getInt("mStepFailureCount", mStepFailureCount);
mShowingSummary = savedState.getBoolean("mShowingSummary", mShowingSummary);
if (mShowingSummary) {
updateViewForCompletionSummary();
} else {
BubblesTestStep currentTest = mTests.get(mCurrentTestIndex);
updateViewForTest(currentTest);
mTestStepPassed.setEnabled(savedState.getBoolean("mTestStepPassed", false));
}
}
}
private void runNextTestOrShowSummary() {
if (mCurrentTestIndex + 1 >= mTests.size()) {
updateViewForCompletionSummary();
} else {
mCurrentTestIndex++;
BubblesTestStep currentTest = mTests.get(mCurrentTestIndex);
updateViewForTest(currentTest);
}
}
/** Populates the UI based on the provided test step */
private void updateViewForTest(BubblesTestStep test) {
mTestStepButtonLayout.setVisibility(VISIBLE);
mTestTitle.setText(test.getTestTitle());
mTestDescription.setText(test.getTestDescription());
if (mRunnable != null) {
mHandler.removeCallbacks(mRunnable);
}
if (test.waitForVerify()) {
mTestState.setVisibility(VISIBLE);
if (test.verify()) {
mTestStepPassed.setEnabled(true);
mTestState.setText(R.string.bubble_test_state_correct);
} else {
mTestState.setText(R.string.bubble_test_state_waiting);
mRunnable = new Runnable() {
@Override
public void run() {
if (test.verify()) {
mTestStepPassed.setEnabled(true);
mTestState.setText(R.string.bubble_test_state_correct);
} else {
mHandler.postDelayed(this, 3000);
}
}
};
mHandler.postDelayed(mRunnable, 1000);
}
} else {
mTestState.setVisibility(GONE);
}
if (test.getButtonText() != 0) {
// Can't pass until test action has run
mTestStepPassed.setEnabled(false);
// Set up test action
mTestAction.setOnClickListener((v) -> {
test.performTestAction();
mTestStepPassed.setEnabled(true);
});
mTestAction.setText(test.getButtonText());
mTestAction.setVisibility(VISIBLE);
} else {
// No test action required
mTestAction.setOnClickListener(null);
mTestAction.setVisibility(INVISIBLE);
mTestStepPassed.setEnabled(true);
}
}
/** Populates the UI indicating results of test & updates test buttons as needed */
private void updateViewForCompletionSummary() {
mShowingSummary = true;
// No longer need any of these buttons
mTestStepButtonLayout.setVisibility(INVISIBLE);
mTestAction.setVisibility(INVISIBLE);
boolean didEverythingSucceed = mStepFailureCount == 0;
int totalTests = mTests.size();
int totalPasses = totalTests - mStepFailureCount;
mTestTitle.setText(R.string.bubbles_test_summary_title);
mTestDescription.setText(getString(R.string.bubbles_test_summary, totalPasses, totalTests));
if (didEverythingSucceed) {
getPassButton().setEnabled(true);
}
// Clean up
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
shortcutManager.removeAllDynamicShortcuts();
mNotificationManager.cancelAll();
}
private class CheckDefaultsTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_check_defaults_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_check_defaults_verify;
}
@Override
public boolean waitForVerify() {
return true;
}
@Override
public boolean verify() {
return mNotificationManager.getBubblePreference() == BUBBLE_PREFERENCE_NONE
&& mNotificationManager.areBubblesEnabled();
}
}
private class BubbleButtonTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_bubble_button_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_bubble_button_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_bubble_button_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(getBubbleBuilder().build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class BubblePreferenceSelected extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_app_selected_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_app_selected_verify;
}
@Override
public boolean waitForVerify() {
return true;
}
@Override
public boolean verify() {
return mNotificationManager.getBubblePreference()
== BUBBLE_PREFERENCE_SELECTED;
}
}
private class DisableFeature extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_disable_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_disable_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_disable_button;
}
@Override
public void performTestAction() {
final Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
public boolean waitForVerify() {
return true;
}
@Override
public boolean verify() {
return !mNotificationManager.areBubblesEnabled();
}
}
private class EnableBubblesTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_enable_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_enable_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_enable_button;
}
@Override
public void performTestAction() {
final String packageName = getApplicationContext().getPackageName();
final int appUid = getApplicationInfo().uid;
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
intent.putExtra(Settings.EXTRA_APP_UID, appUid);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
public boolean waitForVerify() {
return true;
}
@Override
public boolean verify() {
return mNotificationManager.getBubblePreference() == BUBBLE_PREFERENCE_ALL
&& mNotificationManager.areBubblesEnabled();
}
}
private class SendBubbleTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_send_bubble_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_send_bubble_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_send_bubble_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(getBubbleBuilder().build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class SuppressNotifTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_suppress_notif_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_suppress_notif_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_suppress_notif_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder()
.setSuppressNotification(true)
.build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class AddNotifTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_add_notif_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_add_notif_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_add_notif_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder()
.setSuppressNotification(false)
.build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class RemoveMetadataTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_remove_metadata_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_remove_metadata_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_remove_metadata_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class AddMetadataTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_add_metadata_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_add_metadata_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_add_metadata_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class ExpandBubbleTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_expand_bubble_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_expand_bubble_verify;
}
}
private class DismissBubbleTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_dismiss_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_dismiss_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_dismiss_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class DismissNotificationTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_dismiss_notif_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_dismiss_notif_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_dismiss_notif_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class AutoExpandBubbleTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_autoexpand_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_autoexpand_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_autoexpand_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata =
getBubbleBuilder(mShortcuts.get(0)).setAutoExpandBubble(true).build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class CheckOverflowExists extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_overflow_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_overflow_verify;
}
}
private class DismissBubbleShowsInOverflow extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_bubbles_in_overflow_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_bubbles_in_overflow_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_bubbles_in_overflow_button;
}
@Override
public void performTestAction() {
Notification.Builder builder =
getConversationNotif(getString(getTestTitle()), mShortcuts.get(0));
builder.setBubbleMetadata(getBubbleBuilder(mShortcuts.get(0)).build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
builder = getConversationNotif(getString(getTestTitle()), mShortcuts.get(1));
builder.setBubbleMetadata(getBubbleBuilder(mShortcuts.get(1)).build());
mNotificationManager.notify(NOTIFICATION_ID + 1, builder.build());
builder = getConversationNotif(getString(getTestTitle()), mShortcuts.get(2));
builder.setBubbleMetadata(getBubbleBuilder(mShortcuts.get(2)).build());
mNotificationManager.notify(NOTIFICATION_ID + 2, builder.build());
}
}
private class PromoteBubbleFromOverflow extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_add_from_overflow_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_add_from_overflow_verify;
}
}
private class CancelRemovesBubblesInOverflow extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_cancel_overflow_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_cancel_overflow_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_cancel_overflow_button;
}
@Override
public void performTestAction() {
// Make sure one is a bubble
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
// Cancel the others
mNotificationManager.cancel(NOTIFICATION_ID + 1);
mNotificationManager.cancel(NOTIFICATION_ID + 2);
}
}
private class TapNotifWithOverflowBubble extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_tap_notif_with_overflow_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_tap_notif_with_overflow_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_tap_notif_with_overflow_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
Notification.BubbleMetadata metadata = getBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class PortraitAndLandscape extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_expanded_position_title;
}
@Override
public int getTestDescription() {
return mIsLargeScreen
? R.string.bubbles_test_expanded_position_largescreen_verify
: R.string.bubbles_test_expanded_position_phone_verify;
}
}
private class ScrimBehindExpandedView extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_expanded_scrim_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_expanded_scrim_verify;
}
}
private class ImeInsetsExpandedView extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_ime_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_ime_verify;
}
}
private class MinHeightExpandedView extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_min_height_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_min_height_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_min_height_button;
}
@Override
public void performTestAction() {
mNotificationManager.cancelAll();
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
intent.putExtra(EXTRA_TEST_NAME, TEST_MIN_HEIGHT);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent,
PendingIntent.FLAG_MUTABLE);
Notification.BubbleMetadata.Builder b = new Notification.BubbleMetadata.Builder(
pendingIntent,
Icon.createWithResource(getApplicationContext(), R.drawable.ic_android));
b.setDesiredHeight(0);
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(b.build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class MaxHeightExpandedView extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_max_height_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_max_height_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_max_height_button;
}
@Override
public void performTestAction() {
WindowMetrics windowMetrics =
getSystemService(WindowManager.class).getCurrentWindowMetrics();
WindowInsets metricInsets = windowMetrics.getWindowInsets();
Insets insets = metricInsets.getInsetsIgnoringVisibility(
WindowInsets.Type.navigationBars()
| WindowInsets.Type.statusBars()
| WindowInsets.Type.displayCutout());
mNotificationManager.cancelAll();
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
Bundle extras = new Bundle();
extras.putString(EXTRA_TEST_NAME, TEST_MAX_HEIGHT);
// Pass the insets because the bubble'd activity window won't have them.
extras.putParcelable(EXTRA_INSETS, insets);
intent.putExtras(extras);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 2, intent,
PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
Notification.BubbleMetadata.Builder b = new Notification.BubbleMetadata.Builder(
pendingIntent,
Icon.createWithResource(getApplicationContext(), R.drawable.ic_android));
b.setDesiredHeight(Short.MAX_VALUE);
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(b.build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class LowRamBubbleTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_lowram_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_lowram_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_lowram_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(getBubbleBuilder().build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
private class BubbleDisabledTest extends BubblesTestStep {
@Override
public int getTestTitle() {
return R.string.bubbles_test_disable_config_title;
}
@Override
public int getTestDescription() {
return R.string.bubbles_test_disable_config_verify;
}
@Override
public int getButtonText() {
return R.string.bubbles_test_disable_config_button;
}
@Override
public void performTestAction() {
Notification.Builder builder = getConversationNotif(getTestTitle());
builder.setBubbleMetadata(getBubbleBuilder().build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
/** Creates a shortcut to use for the notifications to be considered conversations */
private void createShortcuts() {
mShortcuts.clear();
for (int i = 0; i < NUM_SHORTCUTS; i++) {
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
intent.setAction(ACTION_VIEW);
intent.putExtra("CONVERSATION", i);
String name = "BubbleBot" + i;
Person person = new Person.Builder()
.setBot(false)
.setIcon(Icon.createWithResource(context, R.drawable.ic_android))
.setName(name)
.setImportant(true)
.build();
Set<String> categorySet = new ArraySet<>();
categorySet.add(SHORTCUT_CATEGORY);
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, name)
.setShortLabel(name)
.setLongLived(true)
.setPerson(person)
.setCategories(categorySet)
.setIcon(Icon.createWithResource(context, R.drawable.ic_android))
.setIntent(intent)
.build();
mShortcuts.add(shortcut);
}
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
shortcutManager.addDynamicShortcuts(mShortcuts);
}
/** Creates a minimally filled out {@link android.app.Notification.BubbleMetadata.Builder} */
private Notification.BubbleMetadata.Builder getBubbleBuilder() {
return getBubbleBuilder(mShortcuts.get(0));
}
/** Creates a minimally filled out {@link android.app.Notification.BubbleMetadata.Builder} */
private Notification.BubbleMetadata.Builder getBubbleBuilder(@Nullable ShortcutInfo info) {
Notification.BubbleMetadata.Builder b;
if (info == null) {
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_MUTABLE);
b = new Notification.BubbleMetadata.Builder(pendingIntent,
Icon.createWithResource(getApplicationContext(),
R.drawable.ic_android));
} else {
b = new Notification.BubbleMetadata.Builder(info.getId());
}
b.setDesiredHeight(Short.MAX_VALUE);
return b;
}
/** Creates a {@link Notification.Builder} that is a conversation. */
private Notification.Builder getConversationNotif(int contentResId) {
return getConversationNotif(getString(contentResId), mShortcuts.get(0));
}
/** Creates a {@link Notification.Builder} that is a conversation. */
private Notification.Builder getConversationNotif(@NonNull CharSequence content,
@NonNull ShortcutInfo shortcutInfo) {
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_MUTABLE);
Person person = new Person.Builder()
.setName(shortcutInfo.getShortLabel())
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(), PendingIntent.FLAG_MUTABLE);
Icon icon = Icon.createWithResource(getApplicationContext(), R.drawable.ic_android);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
inputIntent).addRemoteInput(remoteInput)
.build();
return new Notification.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_android)
.setContentTitle("Bubble Notification")
.setContentText(content)
.setColor(Color.GREEN)
.setShortcutId(shortcutInfo.getId())
.setContentIntent(pendingIntent)
.setActions(replyAction)
.setStyle(new Notification.MessagingStyle(person)
.setConversationTitle("Bubble Chat")
.addMessage(content,
SystemClock.currentThreadTimeMillis(), person)
);
}
}