Add Bubble as an option in NotificationInfo longpress menu

- If the content is able to bubble, the bubble option will show in
  the menu
- If that notif is able to bubble (either via bubble metadata or via
  notification contents), then show the menu
- Adds tests for the bubble menu option, also tests selection for alert
  and silence buttons; removes unused test code

Test: atest NotificationInfoTest NotificationGutsManagerTest
Test: manual - post a bubble with bubbles test app, longpress on notif
               and demote it via menu => bubble is gone
             - long press on notif and promote it via menu => bubble is
               created
Bug: 138116133
Bug: 143173197
Change-Id: I2f2767ec12c49e5d3a8d2a7b86db5457d062275c
This commit is contained in:
Mady Mellor
2019-10-22 17:12:59 -07:00
parent ec3c03ea24
commit 53162c13a3
9 changed files with 485 additions and 74 deletions

View File

@@ -3203,6 +3203,14 @@ public class Notification implements Parcelable
return mBubbleMetadata;
}
/**
* Sets the {@link BubbleMetadata} for this notification.
* @hide
*/
public void setBubbleMetadata(BubbleMetadata data) {
mBubbleMetadata = data;
}
/**
* Returns whether the platform is allowed (by the app developer) to generate contextual actions
* for this notification.

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9c0,1.39 0.32,2.69 0.88,3.86l1.53,-1.53C5.15,13.6 5,12.82 5,12c0,-3.86 3.14,-7 7,-7s7,3.14 7,7s-3.14,7 -7,7c-0.83,0 -1.62,-0.15 -2.35,-0.42l-1.53,1.53C9.3,20.67 10.61,21 12,21c4.97,0 9,-4.03 9,-9C21,7.03 16.97,3 12,3z"
android:fillColor="#000000"/>
<path
android:pathData="M12.99,15.99l2,0l0,-7l-7,0l0,2l3.59,0l-8.79,8.8l1.41,1.41l8.79,-8.79z"
android:fillColor="#000000"/>
</vector>

View File

@@ -219,6 +219,58 @@ asked for it -->
android:gravity="center"
android:orientation="vertical">
<com.android.systemui.statusbar.notification.row.ButtonLinearLayout
android:id="@+id/bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/notification_importance_button_padding"
android:layout_marginBottom="@dimen/notification_importance_button_separation"
android:clickable="true"
android:focusable="true"
android:background="@drawable/notification_guts_priority_button_bg"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
>
<ImageView
android:id="@+id/bubble_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_create_bubble"
android:background="@android:color/transparent"
android:tint="@color/notification_guts_priority_contents"
android:clickable="false"
android:focusable="false"/>
<TextView
android:id="@+id/bubble_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_importance_drawable_padding"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:clickable="false"
android:focusable="false"
android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
android:text="@string/notification_bubble_title"/>
</LinearLayout>
<TextView
android:id="@+id/bubble_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
android:visibility="gone"
android:text="@string/notification_channel_summary_bubble"
android:clickable="false"
android:focusable="false"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
<com.android.systemui.statusbar.notification.row.ButtonLinearLayout
android:id="@+id/alert"
android:layout_width="match_parent"

View File

@@ -1717,12 +1717,18 @@
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_alert_title">Alerting</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_bubble_title">Bubble</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
<string name="notification_channel_summary_low">Helps you focus without sound or vibration.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
<string name="notification_channel_summary_default">Gets your attention with sound or vibration.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
<string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>

View File

@@ -319,7 +319,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
packageName,
row.getEntry().getChannel(),
row.getUniqueChannels(),
sbn,
row.getEntry(),
mCheckSaveListener,
onSettingsClick,
onAppSettingsClick,

View File

@@ -65,7 +65,10 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleExperimentConfig;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.lang.annotation.Retention;
@@ -99,6 +102,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
// standard controls
private static final int ACTION_ALERT = 5;
private TextView mBubbleDescriptionView;
private TextView mPriorityDescriptionView;
private TextView mSilentDescriptionView;
@@ -116,6 +120,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
private boolean mStartedAsBubble;
private boolean mWasShownHighPriority;
private boolean mPressedApply;
private boolean mPresentingChannelEditorDialog = false;
@@ -125,8 +130,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
* level; non-null once the user takes an action which indicates an explicit preference.
*/
@Nullable private Integer mChosenImportance;
/**
* The last bubble setting chosen by the user. Null if the user has not chosen a bubble level;
* non-null once the user takes an action which indicates an explicit preference.
*/
@Nullable private Boolean mChosenBubbleEnabled;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
private boolean mIsBubbleable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private AnimatorSet mExpandAnimation;
private boolean mIsDeviceProvisioned;
@@ -137,18 +149,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private NotificationGuts mGutsContainer;
private Drawable mPkgIcon;
private BubbleController mBubbleController;
/** Whether this view is being shown as part of the blocking helper. */
private boolean mIsForBlockingHelper;
@VisibleForTesting
boolean mSkipPost = false;
/**
* String that describes how the user exit or quit out of this view, also used as a counter tag.
*/
private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
// used by standard ui
private OnClickListener mOnAlert = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
mChosenImportance = IMPORTANCE_DEFAULT;
if (mStartedAsBubble) {
mChosenBubbleEnabled = false;
}
applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
};
@@ -156,9 +177,19 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private OnClickListener mOnSilent = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
mChosenImportance = IMPORTANCE_LOW;
if (mStartedAsBubble) {
mChosenBubbleEnabled = false;
}
applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
};
/** Used by standard ui (in an experiment) {@see BubbleExperimentConfig#allowNotifBubbleMenu} */
private OnClickListener mOnBubble = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
mChosenBubbleEnabled = true;
applyAlertingBehavior(BEHAVIOR_BUBBLE, true /* userTriggered */);
};
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
mPressedApply = true;
@@ -224,6 +255,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
protected void onFinishInflate() {
super.onFinishInflate();
mBubbleDescriptionView = findViewById(R.id.bubble_summary);
mPriorityDescriptionView = findViewById(R.id.alert_summary);
mSilentDescriptionView = findViewById(R.id.silence_summary);
}
@@ -251,7 +283,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
final String pkg,
final NotificationChannel notificationChannel,
final Set<NotificationChannel> uniqueChannelsInRow,
final StatusBarNotification sbn,
final NotificationEntry entry,
final CheckSaveListener checkSaveListener,
final OnSettingsClickListener onSettingsClick,
final OnAppSettingsClickListener onAppSettingsClick,
@@ -261,7 +293,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
boolean wasShownHighPriority)
throws RemoteException {
bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel,
uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
uniqueChannelsInRow, entry, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */,
importance, wasShownHighPriority);
@@ -274,7 +306,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
String pkg,
NotificationChannel notificationChannel,
Set<NotificationChannel> uniqueChannelsInRow,
StatusBarNotification sbn,
NotificationEntry entry,
CheckSaveListener checkSaveListener,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
@@ -288,10 +320,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mMetricsLogger = Dependency.get(MetricsLogger.class);
mVisualStabilityManager = visualStabilityManager;
mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
mBubbleController = Dependency.get(BubbleController.class);
mPackageName = pkg;
mUniqueChannelsInRow = uniqueChannelsInRow;
mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
mSbn = sbn;
mEntry = entry;
mSbn = entry.getSbn();
mPm = pm;
mAppSettingsClickListener = onAppSettingsClick;
mAppName = mPackageName;
@@ -318,6 +352,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
&& numTotalChannels == 1;
}
mIsBubbleable = mEntry.getBubbleMetadata() != null;
mStartedAsBubble = mEntry.isBubble();
bindHeader();
bindChannelDetails();
@@ -365,6 +402,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
findViewById(R.id.non_configurable_text).setVisibility(GONE);
findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
findViewById(R.id.bubble).setVisibility(mIsBubbleable ? VISIBLE : GONE);
}
View turnOffButton = findViewById(R.id.turn_off_notifications);
@@ -378,12 +416,17 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
View silent = findViewById(R.id.silence);
View alert = findViewById(R.id.alert);
View bubble = findViewById(R.id.bubble);
silent.setOnClickListener(mOnSilent);
alert.setOnClickListener(mOnAlert);
bubble.setOnClickListener(mOnBubble);
applyAlertingBehavior(
mWasShownHighPriority ? BEHAVIOR_ALERTING : BEHAVIOR_SILENT,
false /* userTriggered */);
int behavior = mStartedAsBubble
? BEHAVIOR_BUBBLE
: mWasShownHighPriority
? BEHAVIOR_ALERTING
: BEHAVIOR_SILENT;
applyAlertingBehavior(behavior, false /* userTriggered */);
}
private void bindHeader() {
@@ -544,6 +587,14 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
}
if (mChosenBubbleEnabled != null && mStartedAsBubble != mChosenBubbleEnabled) {
if (mChosenBubbleEnabled) {
mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
} else {
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
}
}
Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
bgHandler.post(
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
@@ -553,6 +604,16 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
}
@Override
public boolean post(Runnable action) {
if (mSkipPost) {
action.run();
return true;
} else {
return super.post(action);
}
}
private void applyAlertingBehavior(@AlertingBehavior int behavior, boolean userTriggered) {
if (userTriggered) {
TransitionSet transition = new TransitionSet();
@@ -569,6 +630,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
TransitionManager.beginDelayedTransition(this, transition);
}
View bubble = findViewById(R.id.bubble);
View alert = findViewById(R.id.alert);
View silence = findViewById(R.id.silence);
@@ -576,33 +638,53 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
case BEHAVIOR_ALERTING:
mPriorityDescriptionView.setVisibility(VISIBLE);
mSilentDescriptionView.setVisibility(GONE);
mBubbleDescriptionView.setVisibility(GONE);
post(() -> {
alert.setSelected(true);
silence.setSelected(false);
bubble.setSelected(false);
});
break;
case BEHAVIOR_SILENT:
case BEHAVIOR_SILENT:
mSilentDescriptionView.setVisibility(VISIBLE);
mPriorityDescriptionView.setVisibility(GONE);
mBubbleDescriptionView.setVisibility(GONE);
post(() -> {
alert.setSelected(false);
silence.setSelected(true);
bubble.setSelected(false);
});
break;
case BEHAVIOR_BUBBLE:
mBubbleDescriptionView.setVisibility(VISIBLE);
mSilentDescriptionView.setVisibility(GONE);
mPriorityDescriptionView.setVisibility(GONE);
post(() -> {
alert.setSelected(false);
silence.setSelected(false);
bubble.setSelected(true);
});
break;
default:
throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
}
boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
boolean isABubbleChange = mStartedAsBubble != (behavior == BEHAVIOR_BUBBLE);
TextView done = findViewById(R.id.done);
done.setText(isAChange ? R.string.inline_ok_button : R.string.inline_done_button);
done.setText((isAChange || isABubbleChange)
? R.string.inline_ok_button
: R.string.inline_done_button);
}
private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
switch (action) {
case ACTION_UNDO:
mChosenImportance = mStartingChannelImportance;
mChosenBubbleEnabled = mStartedAsBubble;
break;
case ACTION_DELIVER_SILENTLY:
mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
@@ -685,6 +767,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
if (mChosenImportance != null) {
mStartingChannelImportance = mChosenImportance;
}
if (mChosenBubbleEnabled != null) {
mStartedAsBubble = mChosenBubbleEnabled;
}
mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
if (mIsForBlockingHelper) {
@@ -884,8 +969,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
@Retention(SOURCE)
@IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
@IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE})
private @interface AlertingBehavior {}
private static final int BEHAVIOR_ALERTING = 0;
private static final int BEHAVIOR_SILENT = 1;
private static final int BEHAVIOR_BUBBLE = 2;
}

View File

@@ -33,6 +33,7 @@ public class SbnBuilder {
private int mUid;
private int mInitialPid;
private Notification mNotification = new Notification();
private Notification.BubbleMetadata mBubbleMetadata;
private UserHandle mUser = UserHandle.of(0);
private String mOverrideGroupKey;
private long mPostTime;
@@ -54,6 +55,9 @@ public class SbnBuilder {
}
public StatusBarNotification build() {
if (mBubbleMetadata != null) {
mNotification.setBubbleMetadata(mBubbleMetadata);
}
return new StatusBarNotification(
mPkg,
mOpPkg,
@@ -116,4 +120,9 @@ public class SbnBuilder {
mPostTime = postTime;
return this;
}
public SbnBuilder setBubbleMetadata(Notification.BubbleMetadata data) {
mBubbleMetadata = data;
return this;
}
}

View File

@@ -321,6 +321,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -331,7 +332,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
eq(statusBarNotification),
eq(entry),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -352,6 +353,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -362,7 +364,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
eq(statusBarNotification),
eq(entry),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -385,6 +387,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
row.getEntry().setIsHighPriority(true);
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -395,7 +398,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
eq(statusBarNotification),
eq(entry),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -416,6 +419,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
NotificationEntry entry = row.getEntry();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -427,7 +432,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
eq(statusBarNotification),
eq(entry),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -448,6 +453,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -458,7 +464,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
eq(statusBarNotification),
eq(entry),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -49,10 +50,13 @@ import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -72,7 +76,12 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.After;
import org.junit.Before;
@@ -106,6 +115,9 @@ public class NotificationInfoTest extends SysuiTestCase {
private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private StatusBarNotification mBubbleSbn;
private NotificationEntry mBubbleEntry;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -119,6 +131,8 @@ public class NotificationInfoTest extends SysuiTestCase {
private NotificationBlockingHelperManager mBlockingHelperManager;
@Mock
private VisualStabilityManager mVisualStabilityManager;
@Mock
private BubbleController mBubbleController;
@Before
public void setUp() throws Exception {
@@ -126,13 +140,18 @@ public class NotificationInfoTest extends SysuiTestCase {
NotificationBlockingHelperManager.class,
mBlockingHelperManager);
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(BubbleController.class, mBubbleController);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
null);
mNotificationInfo.setGutsParent(mock(NotificationGuts.class));
// Our view is never attached to a window so the View#post methods in NotificationInfo never
// get called. Setting this will skip the post and do the action immediately.
mNotificationInfo.mSkipPost = true;
// PackageManager must return a packageInfo and applicationInfo.
final PackageInfo packageInfo = new PackageInfo();
@@ -164,6 +183,16 @@ public class NotificationInfoTest extends SysuiTestCase {
mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0,
new Intent(mContext, BubblesTestActivity.class), 0);
mBubbleSbn = new SbnBuilder(mSbn).setBubbleMetadata(
new Notification.BubbleMetadata.Builder()
.setIntent(bubbleIntent)
.setIcon(Icon.createWithResource(mContext, R.drawable.android)).build())
.build();
mBubbleEntry = new NotificationEntryBuilder().setSbn(mBubbleSbn).build();
Settings.Secure.putInt(mContext.getContentResolver(),
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
@@ -182,17 +211,6 @@ public class NotificationInfoTest extends SysuiTestCase {
() -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
}
private void ensureNoUndoButton() {
PollingCheck.waitFor(1000,
() -> GONE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()
&& !mNotificationInfo.isAnimating());
}
private void waitForStopButton() {
PollingCheck.waitFor(1000,
() -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
}
@Test
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
@@ -203,7 +221,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -228,7 +246,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -249,7 +267,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -273,6 +291,7 @@ public class NotificationInfoTest extends SysuiTestCase {
applicationInfo);
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build();
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -280,7 +299,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
entry,
null,
null,
null,
@@ -304,7 +323,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -331,7 +350,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -353,7 +372,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -374,7 +393,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
mDefaultNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -399,7 +418,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
mDefaultNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -420,7 +439,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -441,7 +460,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
mock(NotificationInfo.OnSettingsClickListener.class),
null,
@@ -468,7 +487,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -495,7 +514,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -517,7 +536,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -540,7 +559,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -555,7 +574,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
(View v, NotificationChannel c, int appUid) -> { },
null,
@@ -576,7 +595,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -600,7 +619,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -625,7 +644,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -647,7 +666,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mVisualStabilityManager,
TEST_PACKAGE_NAME, mNotificationChannel,
createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
mSbn,
mEntry,
null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
@@ -675,7 +694,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
mSbn,
mEntry,
null,
null,
null,
@@ -698,7 +717,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
mSbn,
mEntry,
null,
null,
null,
@@ -721,7 +740,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -737,6 +756,202 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility());
}
@Test
public void testBindNotification_alertIsSelected() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
}
@Test
public void testBindNotification_silenceIsSelected() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
false);
assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
}
@Test
public void testBindNotification_bubbleIsSelected() throws Exception {
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
assertEquals(View.VISIBLE, bubbleView.getVisibility());
assertTrue(bubbleView.isSelected());
}
@Test
public void testBindNotification_whenCanBubble() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
assertEquals(View.VISIBLE, bubbleView.getVisibility());
assertFalse(bubbleView.isSelected());
}
@Test
public void testBindNotification_whenCantBubble() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
assertEquals(View.GONE, bubbleView.getVisibility());
}
@Test
public void testBubble_promotesBubble() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
assertFalse(mBubbleEntry.isBubble());
// Promote it
mNotificationInfo.findViewById(R.id.bubble).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
mNotificationInfo.handleCloseControls(true, false);
verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry);
}
@Test
public void testAlert_demotesBubble() throws Exception {
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
assertTrue(mBubbleEntry.isBubble());
// Demote it
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
mNotificationInfo.handleCloseControls(true, false);
verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
}
@Test
public void testSilence_demotesBubble() throws Exception {
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mBubbleEntry,
null,
null,
null,
true,
false,
IMPORTANCE_DEFAULT,
true);
assertTrue(mBubbleEntry.isBubble());
// Demote it
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
mNotificationInfo.handleCloseControls(true, false);
verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
}
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(
@@ -746,7 +961,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -769,7 +984,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -795,7 +1010,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -821,7 +1036,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -848,7 +1063,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -878,7 +1093,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel /* notificationChannel */,
createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
mSbn,
mEntry,
listener /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
@@ -917,7 +1132,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel /* notificationChannel */,
createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
mSbn,
mEntry,
listener /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
@@ -945,7 +1160,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel /* notificationChannel */,
createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
mSbn,
mEntry,
listener /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
@@ -970,7 +1185,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
mSbn,
mEntry,
null /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
@@ -999,7 +1214,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
mSbn,
mEntry,
null /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
@@ -1033,7 +1248,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1064,7 +1279,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1094,7 +1309,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1127,7 +1342,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1161,7 +1376,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1195,7 +1410,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1232,7 +1447,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1268,7 +1483,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1295,7 +1510,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1325,7 +1540,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1358,7 +1573,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
null,
null,
null,
@@ -1386,7 +1601,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
},
@@ -1421,7 +1636,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
},
@@ -1449,7 +1664,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
mSbn,
mEntry,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
},