Notification removal with overflow bubbles
Intercept dismissal for overflow bubbles.
Remove notifications for inactive (not in stack and overflow)
bubbles cancelled or suppressed from shade.
Set "isBubble" SysUI flag
- false for inactive bubbles still in shade,
- true for bubbles promoted from overflow.
Account for overflow bubbles in event handling
- onEntryUpdated: remove overflow bubble if no longer a bubble
- onRankingUpdated: remove overflow bubble if blocked
- expandStackAndSelectBubble: promote overflow bubble then expand
- isBubbleNotificationSuppressedFromShade: check overflow bubbles
Update tests
Enable overflow flag
----------------
Fixes: 151104690
Test: manual: cancel oldest bubble => oldest overflow bubble removed
Test: atest SystemUITests
Change-Id: I44bb623f99f9473055787cf10693f7a3bfd1c768
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e488cf2..4b50378 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -123,7 +123,8 @@
@Retention(SOURCE)
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
- DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT})
+ DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
+ DISMISS_OVERFLOW_MAX_REACHED})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
@@ -137,6 +138,7 @@
static final int DISMISS_USER_CHANGED = 8;
static final int DISMISS_GROUP_CANCELLED = 9;
static final int DISMISS_INVALID_INTENT = 10;
+ static final int DISMISS_OVERFLOW_MAX_REACHED = 11;
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
@@ -465,7 +467,6 @@
if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
-
return false;
}
});
@@ -736,18 +737,18 @@
*/
public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
String key = entry.getKey();
- boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
- && !mBubbleData.getBubbleWithKey(key).showInShade();
+ boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
+ && !mBubbleData.getAnyBubbleWithkey(key).showInShade());
String groupKey = entry.getSbn().getGroupKey();
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
-
- return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
+ return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
void promoteBubbleFromOverflow(Bubble bubble) {
bubble.setInflateSynchronously(mInflateSynchronously);
+ setIsBubble(bubble, /* isBubble */ true);
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
}
@@ -757,11 +758,16 @@
* @param notificationKey the notification key for the bubble to be selected
*/
public void expandStackAndSelectBubble(String notificationKey) {
- Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
- if (bubble != null) {
+ Bubble bubble = mBubbleData.getBubbleInStackWithKey(notificationKey);
+ if (bubble == null) {
+ bubble = mBubbleData.getOverflowBubbleWithKey(notificationKey);
+ if (bubble != null) {
+ mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
+ }
+ } else if (bubble.getEntry().isBubble()){
mBubbleData.setSelectedBubble(bubble);
- mBubbleData.setExpanded(true);
}
+ mBubbleData.setExpanded(true);
}
/**
@@ -856,7 +862,7 @@
*/
@MainThread
void removeBubble(NotificationEntry entry, int reason) {
- if (mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
mBubbleData.notificationEntryRemoved(entry, reason);
}
}
@@ -871,7 +877,7 @@
private void onEntryUpdated(NotificationEntry entry) {
boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry);
- if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
@@ -910,7 +916,7 @@
String key = orderedKeys[i];
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
rankingMap.getRanking(key, mTmpRanking);
- boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+ boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
@@ -920,6 +926,19 @@
}
}
+ private void setIsBubble(Bubble b, boolean isBubble) {
+ if (isBubble) {
+ b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
+ } else {
+ b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ }
+ try {
+ mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ }
+
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@@ -942,36 +961,36 @@
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
mStackView.removeBubble(bubble);
+
// If the bubble is removed for user switching, leave the notification in place.
- if (reason != DISMISS_USER_CHANGED) {
- if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
- && !bubble.showInShade()) {
+ if (reason == DISMISS_USER_CHANGED) {
+ continue;
+ }
+ if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+ if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
+ && (!bubble.showInShade()
+ || reason == DISMISS_NOTIF_CANCEL
+ || reason == DISMISS_GROUP_CANCELLED)) {
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
}
} else {
- // Update the flag for SysUI
- bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ if (bubble.getEntry().isBubble() && bubble.showInShade()) {
+ setIsBubble(bubble, /* isBubble */ false);
+ }
if (bubble.getEntry().getRow() != null) {
bubble.getEntry().getRow().updateBubbleButton();
}
-
- // Update the state in NotificationManagerService
- try {
- mBarService.onNotificationBubbleChanged(bubble.getKey(),
- false /* isBubble */, 0 /* flags */);
- } catch (RemoteException e) {
- }
}
- final String groupKey = bubble.getEntry().getSbn().getGroupKey();
- if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(bubble.getEntry());
- }
+ }
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(bubble.getEntry());
}
}
}
@@ -1020,7 +1039,7 @@
}
Log.d(TAG, "\n[BubbleData] overflow:");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
- null));
+ null) + "\n");
}
}
};
@@ -1039,21 +1058,19 @@
if (entry == null) {
return false;
}
-
- final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey())
- && entry.isBubble();
- final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry);
-
- if (interceptSummaryDismissal) {
+ if (isSummaryOfBubbles(entry)) {
handleSummaryDismissalInterception(entry);
- } else if (interceptBubbleDismissal) {
- Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey());
+ } else {
+ Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey());
+ if (bubble == null || !entry.isBubble()) {
+ bubble = mBubbleData.getOverflowBubbleWithKey(entry.getKey());
+ }
+ if (bubble == null) {
+ return false;
+ }
bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */);
- } else {
- return false;
}
-
// Update the shade
for (NotifCallback cb : mCallbacks) {
cb.invalidateNotifications("BubbleController.handleDismissalInterception");
@@ -1082,11 +1099,11 @@
if (children != null) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
- if (mBubbleData.hasBubbleWithKey(child.getKey())) {
+ if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) {
// Suppress the bubbled child
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
- Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey());
+ Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index a1393cd..35a4811 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -123,7 +123,7 @@
private boolean mShowingOverflow;
private boolean mExpanded;
private final int mMaxBubbles;
- private final int mMaxOverflowBubbles;
+ private int mMaxOverflowBubbles;
// State tracked during an operation -- keeps track of what listener events to dispatch.
private Update mStateChange;
@@ -175,8 +175,16 @@
return mExpanded;
}
- public boolean hasBubbleWithKey(String key) {
- return getBubbleWithKey(key) != null;
+ public boolean hasAnyBubbleWithKey(String key) {
+ return hasBubbleInStackWithKey(key) || hasOverflowBubbleWithKey(key);
+ }
+
+ public boolean hasBubbleInStackWithKey(String key) {
+ return getBubbleInStackWithKey(key) != null;
+ }
+
+ public boolean hasOverflowBubbleWithKey(String key) {
+ return getOverflowBubbleWithKey(key) != null;
}
@Nullable
@@ -206,6 +214,8 @@
Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
}
moveOverflowBubbleToPending(bubble);
+ // Preserve new order for next repack, which sorts by last updated time.
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
bubble.inflate(
b -> {
notificationEntryUpdated(bubble, /* suppressFlyout */
@@ -221,8 +231,6 @@
}
private void moveOverflowBubbleToPending(Bubble b) {
- // Preserve new order for next repack, which sorts by last updated time.
- b.markUpdatedAt(mTimeSource.currentTimeMillis());
mOverflowBubbles.remove(b);
mPendingBubbles.add(b);
}
@@ -233,15 +241,16 @@
* for that.
*/
Bubble getOrCreateBubble(NotificationEntry entry) {
- Bubble bubble = getBubbleWithKey(entry.getKey());
- if (bubble == null) {
- for (int i = 0; i < mOverflowBubbles.size(); i++) {
- Bubble b = mOverflowBubbles.get(i);
- if (b.getKey().equals(entry.getKey())) {
- moveOverflowBubbleToPending(b);
- b.setEntry(entry);
- return b;
- }
+ String key = entry.getKey();
+ Bubble bubble = getBubbleInStackWithKey(entry.getKey());
+ if (bubble != null) {
+ bubble.setEntry(entry);
+ } else {
+ bubble = getOverflowBubbleWithKey(key);
+ if (bubble != null) {
+ moveOverflowBubbleToPending(bubble);
+ bubble.setEntry(entry);
+ return bubble;
}
// Check for it in pending
for (int i = 0; i < mPendingBubbles.size(); i++) {
@@ -253,8 +262,6 @@
}
bubble = new Bubble(entry, mSuppressionListener);
mPendingBubbles.add(bubble);
- } else {
- bubble.setEntry(entry);
}
return bubble;
}
@@ -269,7 +276,7 @@
Log.d(TAG, "notificationEntryUpdated: " + bubble);
}
mPendingBubbles.remove(bubble); // No longer pending once we're here
- Bubble prevBubble = getBubbleWithKey(bubble.getKey());
+ Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive();
if (prevBubble == null) {
@@ -422,6 +429,19 @@
}
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
+ if (hasOverflowBubbleWithKey(key)
+ && (reason == BubbleController.DISMISS_NOTIF_CANCEL
+ || reason == BubbleController.DISMISS_GROUP_CANCELLED
+ || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE
+ || reason == BubbleController.DISMISS_BLOCKED)) {
+
+ Bubble b = getOverflowBubbleWithKey(key);
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "Cancel overflow bubble: " + b);
+ }
+ mStateChange.bubbleRemoved(b, reason);
+ mOverflowBubbles.remove(b);
+ }
return;
}
Bubble bubbleToRemove = mBubbles.get(indexToRemove);
@@ -453,21 +473,23 @@
}
void overflowBubble(@DismissReason int reason, Bubble bubble) {
- if (reason == BubbleController.DISMISS_AGED
- || reason == BubbleController.DISMISS_USER_GESTURE) {
+ if (!(reason == BubbleController.DISMISS_AGED
+ || reason == BubbleController.DISMISS_USER_GESTURE)) {
+ return;
+ }
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "Overflowing: " + bubble);
+ }
+ mOverflowBubbles.add(0, bubble);
+ bubble.stopInflation();
+ if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
+ // Remove oldest bubble.
+ Bubble oldest = mOverflowBubbles.get(mOverflowBubbles.size() - 1);
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "Overflowing: " + bubble);
+ Log.d(TAG, "Overflow full. Remove: " + oldest);
}
- mOverflowBubbles.add(0, bubble);
- bubble.stopInflation();
- if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
- // Remove oldest bubble.
- if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "Overflow full. Remove: " + mOverflowBubbles.get(
- mOverflowBubbles.size() - 1));
- }
- mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
- }
+ mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED);
+ mOverflowBubbles.remove(oldest);
}
}
@@ -764,7 +786,17 @@
@VisibleForTesting(visibility = PRIVATE)
@Nullable
- Bubble getBubbleWithKey(String key) {
+ Bubble getAnyBubbleWithkey(String key) {
+ Bubble b = getBubbleInStackWithKey(key);
+ if (b == null) {
+ b = getOverflowBubbleWithKey(key);
+ }
+ return b;
+ }
+
+ @VisibleForTesting(visibility = PRIVATE)
+ @Nullable
+ Bubble getBubbleInStackWithKey(String key) {
for (int i = 0; i < mBubbles.size(); i++) {
Bubble bubble = mBubbles.get(i);
if (bubble.getKey().equals(key)) {
@@ -806,6 +838,15 @@
}
/**
+ * Set maximum number of bubbles allowed in overflow.
+ * This method should only be used in tests, not in production.
+ */
+ @VisibleForTesting
+ void setMaxOverflowBubbles(int maxOverflowBubbles) {
+ mMaxOverflowBubbles = maxOverflowBubbles;
+ }
+
+ /**
* Description of current bubble data state.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 2060391..a888bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -71,7 +71,7 @@
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
- private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
+ private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = true;
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c906931..d870c11 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -906,7 +906,7 @@
view -> {
showManageMenu(false /* show */);
final Bubble bubble = mBubbleData.getSelectedBubble();
- if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mUnbubbleConversationCallback.accept(bubble.getEntry());
}
});
@@ -915,7 +915,7 @@
view -> {
showManageMenu(false /* show */);
final Bubble bubble = mBubbleData.getSelectedBubble();
- if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
final Intent intent = bubble.getSettingsIntent();
collapseStack(() -> {
mContext.startActivityAsUser(
@@ -1756,14 +1756,13 @@
if (mIsExpanded) {
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView));
-
} else {
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
}
}
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
- if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mBubbleData.notificationEntryRemoved(
bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
}
@@ -2024,8 +2023,8 @@
// If available, update the manage menu's settings option with the expanded bubble's app
// name and icon.
- if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) {
- final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey());
+ if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
+ final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon());
mManageSettingsText.setText(getResources().getString(
R.string.bubbles_app_settings, bubble.getAppName()));
@@ -2241,7 +2240,7 @@
View child = mBubbleContainer.getChildAt(i);
if (child instanceof BadgedImageView) {
String key = ((BadgedImageView) child).getKey();
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ Bubble bubble = mBubbleData.getBubbleInStackWithKey(key);
bubbles.add(bubble);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 3ef693a..c2b3506 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -158,6 +158,7 @@
private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mRow;
private ExpandableNotificationRow mRow2;
+ private ExpandableNotificationRow mRow3;
private ExpandableNotificationRow mNonBubbleNotifRow;
@Mock
@@ -232,6 +233,7 @@
TestableLooper.get(this));
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
// Return non-null notification data from the NEM
@@ -311,7 +313,7 @@
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotificationEntryManager).updateNotifications(any());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -319,7 +321,7 @@
mBubbleController.removeBubble(
mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
@@ -335,8 +337,11 @@
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
+ verify(mNotificationEntryManager, never()).performRemoveNotification(
+ eq(mRow.getEntry().getSbn()), anyInt());
+ assertFalse(mRow.getEntry().isBubble());
- Bubble b2 = mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey());
+ Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey());
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2);
mBubbleController.promoteBubbleFromOverflow(b);
@@ -344,45 +349,49 @@
}
@Test
- public void testRemoveBubble_withDismissedNotif() {
- mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
-
- assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
-
- // Make it look like dismissed notif
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
-
- // Now remove the bubble
+ public void testCancelOverflowBubble() {
+ mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
+ false, /* showInShade */ true);
mBubbleController.removeBubble(
mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
- // Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
- // called to really remove the notif
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
- assertFalse(mBubbleController.hasBubbles());
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+ assertFalse(mRow.getEntry().isBubble());
+ }
+ @Test
+ public void testUserChange_doesNotRemoveNotif() {
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_USER_CHANGED);
+ verify(mNotificationEntryManager, never()).performRemoveNotification(
+ eq(mRow.getEntry().getSbn()), anyInt());
+ assertFalse(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
+ assertTrue(mRow.getEntry().isBubble());
}
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
verify(mNotificationEntryManager, times(1)).updateNotifications(any());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mRow2.getEntry());
verify(mNotificationEntryManager, times(2)).updateNotifications(any());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
- assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertFalse(mSysUiStateBubblesExpanded);
}
@@ -452,10 +461,10 @@
mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -483,7 +492,7 @@
mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
@@ -496,7 +505,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -511,7 +520,7 @@
mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
@@ -524,7 +533,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Send update
mEntryListener.onPreEntryUpdated(mRow.getEntry());
@@ -534,7 +543,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -557,24 +566,24 @@
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
assertEquals(mRow.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -639,8 +648,8 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
@@ -656,7 +665,7 @@
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Should show dot
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
@@ -667,8 +676,8 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
@@ -699,7 +708,7 @@
mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -812,7 +821,29 @@
}
@Test
- public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+ public void removeNotif_inOverflow_intercepted() {
+ // Get bubble with notif in shade.
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // Dismiss the bubble into overflow.
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ assertFalse(mBubbleController.hasBubbles());
+
+ boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
+
+ // Notif is no longer a bubble, but still in overflow, so we intercept removal.
+ assertTrue(intercepted);
+ }
+
+ @Test
+ public void removeNotif_notInOverflow_notIntercepted() {
+ // Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
@@ -820,20 +851,43 @@
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
- // Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
assertFalse(mBubbleController.hasBubbles());
- // Dismiss the notification
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
- // It's no longer a bubble so we shouldn't intercept
+ // Notif is no longer a bubble, so we should not intercept removal.
assertFalse(intercepted);
}
@Test
+ public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
+ mBubbleController.updateBubble(
+ mRow.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ mBubbleController.updateBubble(
+ mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ mBubbleController.updateBubble(
+ mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ assertEquals(mBubbleData.getBubbles().size(), 3);
+
+ mBubbleData.setMaxOverflowBubbles(1);
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ assertEquals(mBubbleData.getBubbles().size(), 2);
+ assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
+
+ mBubbleController.removeBubble(
+ mRow2.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ // Overflow max of 1 is reached; mRow is oldest, so it gets removed
+ verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+ mRow.getEntry().getSbn(), REASON_CANCEL);
+ assertEquals(mBubbleData.getBubbles().size(), 1);
+ assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
+ }
+
+ @Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
BubbleController.NotificationSuppressionChangedListener listener =
mock(BubbleController.NotificationSuppressionChangedListener.class);
@@ -854,7 +908,7 @@
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
}
@Test
@@ -877,7 +931,7 @@
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
}
@Test
@@ -887,7 +941,7 @@
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
@@ -905,7 +959,7 @@
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
@@ -914,7 +968,7 @@
mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
// THEN the summary and its children are removed from bubble data
- assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertFalse(mBubbleData.isSummarySuppressed(
groupSummary.getEntry().getSbn().getGroupKey()));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index d2f9127..66f119a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -20,8 +20,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -285,6 +288,25 @@
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
+ @Test
+ public void testOverflowBubble_maxReached_bubbleRemoved() {
+ // Setup
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.setMaxOverflowBubbles(1);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
+
+ // Overflow max of 1 is reached; A1 is oldest, so it gets removed
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
+ }
+
/**
* Verifies that new bubbles insert to the left when collapsed, carrying along grouped bubbles.
* <p>
@@ -473,6 +495,32 @@
}
/**
+ * Verifies that overflow bubbles are canceled on notif entry removal.
+ */
+ @Test
+ public void test_removeOverflowBubble_forCanceledNotif() {
+ // Setup
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+ sendUpdatedEntryAtTime(mEntryB3, 6000); // [A2, A3, B1, B2, B3], overflow: [A1]
+ sendUpdatedEntryAtTime(mEntryC1, 7000); // [A3, B1, B2, B3, C1], overflow: [A2, A1]
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
+
+ // Test
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_GROUP_CANCELLED);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of());
+ }
+
+ /**
* Verifies that when the selected bubble is removed with the stack in the collapsed state,
* the selection moves to the next most-recently updated bubble.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 8e6fc8a..23dfb7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -283,20 +283,20 @@
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
}
@Test
- public void testRemoveBubble_withDismissedNotif() {
+ public void testRemoveBubble_withDismissedNotif_inOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
@@ -304,13 +304,34 @@
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
// Make it look like dismissed notif
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
- // Since the notif is dismissed, once the bubble is removed, removeNotification gets
- // called to really remove the notif
+ // We don't remove the notification since the bubble is still in overflow.
+ verify(mNotifCallback, never()).removeNotification(eq(mRow.getEntry()), anyInt());
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testRemoveBubble_withDismissedNotif_notInOverflow() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+ // Make it look like dismissed notif
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
+
+ // Now remove the bubble
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
+ assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
+
+ // Since the notif is dismissed and not in overflow, once the bubble is removed,
+ // removeNotification gets called to really remove the notif
verify(mNotifCallback, times(1)).removeNotification(eq(mRow.getEntry()), anyInt());
assertFalse(mBubbleController.hasBubbles());
}
@@ -319,17 +340,17 @@
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mRow2.getEntry());
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
- assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
}
@Test
@@ -389,10 +410,10 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -417,7 +438,7 @@
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
@@ -428,7 +449,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -443,7 +464,7 @@
mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
@@ -454,7 +475,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Send update
mEntryListener.onEntryUpdated(mRow.getEntry());
@@ -464,7 +485,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -485,24 +506,24 @@
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
assertEquals(mRow.getEntry(),
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -561,8 +582,8 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
@@ -576,7 +597,7 @@
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Should show dot
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
@@ -587,8 +608,8 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
@@ -601,7 +622,7 @@
mRow.getEntry()));
mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@Test
@@ -679,7 +700,7 @@
}
@Test
- public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+ public void removeBubble_dismissIntoOverflow_intercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
@@ -695,7 +716,28 @@
// Dismiss the notification
boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
- // It's no longer a bubble so we shouldn't intercept
+ // Intercept dismissal since bubble is going into overflow
+ assertTrue(intercepted);
+ }
+
+ @Test
+ public void removeBubble_notIntercepted() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // Dismiss the bubble
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
+ assertFalse(mBubbleController.hasBubbles());
+
+ // Dismiss the notification
+ boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+ // Not a bubble anymore so we don't intercept dismissal.
assertFalse(intercepted);
}
@@ -719,7 +761,7 @@
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
}
@Test
@@ -742,7 +784,7 @@
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
}
@Test
@@ -752,7 +794,7 @@
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
@@ -770,7 +812,7 @@
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
@@ -779,7 +821,7 @@
mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0);
// THEN the summary and its children are removed from bubble data
- assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertFalse(mBubbleData.isSummarySuppressed(
groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -805,7 +847,7 @@
verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), anyInt());
// THEN the bubble child still exists as a bubble and is suppressed from the shade
- assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry()));