diff options
7 files changed, 368 insertions, 167 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index a1fff9412eb2..4d7eb758f733 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -124,7 +124,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @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 {} @@ -138,6 +139,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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; @@ -469,7 +471,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (userRemovedNotif) { return handleDismissalInterception(entry); } - return false; } }); @@ -740,18 +741,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ 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); } @@ -761,11 +762,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @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); } /** @@ -870,7 +876,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ @MainThread void removeBubble(NotificationEntry entry, int reason) { - if (mBubbleData.hasBubbleWithKey(entry.getKey())) { + if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { mBubbleData.notificationEntryRemoved(entry, reason); } } @@ -885,7 +891,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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) { @@ -924,7 +930,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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) { @@ -934,6 +940,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } + 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() { @@ -956,36 +975,36 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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()); } } } @@ -1034,7 +1053,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } Log.d(TAG, "\n[BubbleData] overflow:"); Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(), - null)); + null) + "\n"); } } }; @@ -1053,21 +1072,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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"); @@ -1096,11 +1113,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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 a1393cddc9f6..35a4811110a8 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 @@ public class BubbleData { 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 @@ public class BubbleData { 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 @@ public class BubbleData { 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 @@ public class BubbleData { } 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 @@ public class BubbleData { * 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 @@ public class BubbleData { } bubble = new Bubble(entry, mSuppressionListener); mPendingBubbles.add(bubble); - } else { - bubble.setEntry(entry); } return bubble; } @@ -269,7 +276,7 @@ public class BubbleData { 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 @@ public class BubbleData { } 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 @@ public class BubbleData { } 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); - } - 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); + Log.d(TAG, "Overflow full. Remove: " + oldest); } + mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED); + mOverflowBubbles.remove(oldest); } } @@ -764,7 +786,17 @@ public class BubbleData { @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 @@ public class BubbleData { } /** + * 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 2060391d38a3..a888bd57c699 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 @@ public class BubbleExperimentConfig { 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 c9069316028c..d870c1191512 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 @@ public class BubbleStackView extends FrameLayout { 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 @@ public class BubbleStackView extends FrameLayout { 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 @@ public class BubbleStackView extends FrameLayout { 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 @@ public class BubbleStackView extends FrameLayout { // 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 @@ public class BubbleStackView extends FrameLayout { 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 3ef693a1ec98..c2b35069a4ad 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 @@ public class BubbleControllerTest extends SysuiTestCase { private NotificationTestHelper mNotificationTestHelper; private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; + private ExpandableNotificationRow mRow3; private ExpandableNotificationRow mNonBubbleNotifRow; @Mock @@ -232,6 +233,7 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { @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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { } @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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { // 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { mRow.getEntry())); mTestableLooper.processAllMessages(); - assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); + assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @Test @@ -812,28 +821,73 @@ public class BubbleControllerTest extends SysuiTestCase { } @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 + // Dismiss the bubble into overflow. mBubbleController.removeBubble( mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE); 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, 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()); + + assertTrue(mBubbleController.hasBubbles()); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mRow.getEntry())); + + mBubbleController.removeBubble( + mRow.getEntry(), BubbleController.DISMISS_NO_LONGER_BUBBLE); + assertFalse(mBubbleController.hasBubbles()); + + boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( + mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL); + + // 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 @@ public class BubbleControllerTest extends SysuiTestCase { // 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 @@ public class BubbleControllerTest extends SysuiTestCase { // 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 d2f912770577..66f119a082a6 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.android.systemui.statusbar.NotificationEntryHelper.modifyRanki 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 @@ public class BubbleDataTest extends SysuiTestCase { 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 @@ public class BubbleDataTest extends SysuiTestCase { } /** + * 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 8e6fc8a24b31..23dfb7c2a3a5 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mRow.getEntry())); mTestableLooper.processAllMessages(); - assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); + assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @Test @@ -679,7 +700,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { } @Test - public void removeBubble_succeeds_userDismissBubble_userDimissNotif() { + public void removeBubble_dismissIntoOverflow_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); mBubbleController.updateBubble(mRow.getEntry()); @@ -695,7 +716,28 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { 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())); |