From 0761c4b3d4a9b9954563657134d67db5675981d7 Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Tue, 28 Jun 2022 16:00:41 -0400 Subject: [PATCH] Inline notif pipeline flag into BubblesManager This change is a no-op; the flag is now enabled-by-default, so all removed code paths here are effectively dead. Bug: 200269355 Test: atest SystemUITests Change-Id: I5ccc8d1926a5973ba02bfc20c565a18ec100d805 --- .../systemui/dagger/SystemUIModule.java | 6 - .../systemui/wmshell/BubblesManager.java | 156 +- .../android/systemui/wmshell/BubblesTest.java | 418 ++--- .../wmshell/NewNotifPipelineBubblesTest.java | 1401 ----------------- 4 files changed, 138 insertions(+), 1843 deletions(-) delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 366ef2651a92e..ba1e057716f8a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -55,8 +55,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.QsFrameTranslateModule; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; @@ -220,11 +218,9 @@ public abstract class SystemUIModule { ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, - NotifPipelineFlags notifPipelineFlags, DumpManager dumpManager, @Main Executor sysuiMainExecutor) { return Optional.ofNullable(BubblesManager.create(context, @@ -240,11 +236,9 @@ public abstract class SystemUIModule { zenModeController, notifUserManager, groupManager, - entryManager, notifCollection, notifPipeline, sysUiState, - notifPipelineFlags, dumpManager, sysuiMainExecutor)); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index e90775de591a4..bca2a24ff12db 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -21,14 +21,10 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; -import static android.service.notification.NotificationListenerService.REASON_CLICK; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; -import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -55,7 +51,6 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; @@ -63,9 +58,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationChannelHelper; -import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -114,7 +107,6 @@ public class BubblesManager implements Dumpable { private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final NotificationLockscreenUserManager mNotifUserManager; private final NotificationGroupManagerLegacy mNotificationGroupManager; - private final NotificationEntryManager mNotificationEntryManager; private final CommonNotifCollection mCommonNotifCollection; private final NotifPipeline mNotifPipeline; private final Executor mSysuiMainExecutor; @@ -142,11 +134,9 @@ public class BubblesManager implements Dumpable { ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, - NotifPipelineFlags notifPipelineFlags, DumpManager dumpManager, Executor sysuiMainExecutor) { if (bubblesOptional.isPresent()) { @@ -163,11 +153,9 @@ public class BubblesManager implements Dumpable { zenModeController, notifUserManager, groupManager, - entryManager, notifCollection, notifPipeline, sysUiState, - notifPipelineFlags, dumpManager, sysuiMainExecutor); } else { @@ -189,11 +177,9 @@ public class BubblesManager implements Dumpable { ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, - NotifPipelineFlags notifPipelineFlags, DumpManager dumpManager, Executor sysuiMainExecutor) { mContext = context; @@ -205,7 +191,6 @@ public class BubblesManager implements Dumpable { mNotificationInterruptStateProvider = interruptionStateProvider; mNotifUserManager = notifUserManager; mNotificationGroupManager = groupManager; - mNotificationEntryManager = entryManager; mCommonNotifCollection = notifCollection; mNotifPipeline = notifPipeline; mSysuiMainExecutor = sysuiMainExecutor; @@ -215,11 +200,7 @@ public class BubblesManager implements Dumpable { ServiceManager.getService(Context.STATUS_BAR_SERVICE)) : statusBarService; - if (notifPipelineFlags.isNewPipelineEnabled()) { - setupNotifPipeline(); - } else { - setupNEM(); - } + setupNotifPipeline(); dumpManager.registerDumpable(TAG, this); @@ -438,141 +419,6 @@ public class BubblesManager implements Dumpable { mBubbles.setSysuiProxy(mSysuiProxy); } - private void setupNEM() { - mNotificationEntryManager.addNotificationEntryListener( - new NotificationEntryListener() { - @Override - public void onPendingEntryAdded(NotificationEntry entry) { - BubblesManager.this.onEntryAdded(entry); - } - - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - BubblesManager.this.onEntryUpdated(entry); - } - - @Override - public void onEntryRemoved( - NotificationEntry entry, - @Nullable NotificationVisibility visibility, - boolean removedByUser, - int reason) { - BubblesManager.this.onEntryRemoved(entry); - } - - @Override - public void onNotificationRankingUpdated(RankingMap rankingMap) { - BubblesManager.this.onRankingUpdate(rankingMap); - } - - @Override - public void onNotificationChannelModified( - String pkgName, - UserHandle user, - NotificationChannel channel, - int modificationType) { - BubblesManager.this.onNotificationChannelModified(pkgName, - user, - channel, - modificationType); - } - }); - - // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator - mNotificationEntryManager.addNotificationRemoveInterceptor( - (key, entry, dismissReason) -> { - final boolean isClearAll = dismissReason == REASON_CANCEL_ALL; - final boolean isUserDismiss = dismissReason == REASON_CANCEL - || dismissReason == REASON_CLICK; - final boolean isAppCancel = dismissReason == REASON_APP_CANCEL - || dismissReason == REASON_APP_CANCEL_ALL; - final boolean isSummaryCancel = - dismissReason == REASON_GROUP_SUMMARY_CANCELED; - - // Need to check for !appCancel here because the notification may have - // previously been dismissed & entry.isRowDismissed would still be true - boolean userRemovedNotif = - (entry != null && entry.isRowDismissed() && !isAppCancel) - || isClearAll || isUserDismiss || isSummaryCancel; - - if (userRemovedNotif) { - return handleDismissalInterception(entry); - } - return false; - }); - - mNotificationGroupManager.registerGroupChangeListener( - new NotificationGroupManagerLegacy.OnGroupChangeListener() { - @Override - public void onGroupSuppressionChanged( - NotificationGroupManagerLegacy.NotificationGroup group, - boolean suppressed) { - // More notifications could be added causing summary to no longer - // be suppressed -- in this case need to remove the key. - final String groupKey = group.summary != null - ? group.summary.getSbn().getGroupKey() - : null; - if (!suppressed && groupKey != null) { - mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null); - } - } - }); - - addNotifCallback(new NotifCallback() { - @Override - public void removeNotification(NotificationEntry entry, - DismissedByUserStats dismissedByUserStats, int reason) { - mNotificationEntryManager.performRemoveNotification(entry.getSbn(), - dismissedByUserStats, reason); - } - - @Override - public void invalidateNotifications(String reason) { - mNotificationEntryManager.updateNotifications(reason); - } - - @Override - public void maybeCancelSummary(NotificationEntry entry) { - // Check if removed bubble has an associated suppressed group summary that needs - // to be removed now. - final String groupKey = entry.getSbn().getGroupKey(); - mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> { - final NotificationEntry summary = - mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey); - if (summary != null) { - mNotificationEntryManager.performRemoveNotification( - summary.getSbn(), - getDismissedByUserStats(summary, false), - UNDEFINED_DISMISS_REASON); - } - }, mSysuiMainExecutor); - - // Check if we still need to remove the summary from NoManGroup because the summary - // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above. - // For example: - // 1. Bubbled notifications (group) is posted to shade and are visible bubbles - // 2. User expands bubbles so now their respective notifications in the shade are - // hidden, including the group summary - // 3. User removes all bubbles - // 4. We expect all the removed bubbles AND the summary (note: the summary was - // never added to the suppressedSummary list in BubbleData, so we add this check) - NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry); - if (summary != null) { - ArrayList summaryChildren = - mNotificationGroupManager.getLogicalChildren(summary.getSbn()); - boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey()); - if (!isSummaryThisNotif && (summaryChildren == null - || summaryChildren.isEmpty())) { - mNotificationEntryManager.performRemoveNotification( - summary.getSbn(), - getDismissedByUserStats(summary, false), - UNDEFINED_DISMISS_REASON); - } - } - } - }); - } - private void setupNotifPipeline() { mNotifPipeline.addCollectionListener(new NotifCollectionListener() { @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 7d4e27fa2580f..2e58fc4f93fa5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,9 @@ import static android.app.PendingIntent.FLAG_MUTABLE; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL; import static com.google.common.truth.Truth.assertThat; @@ -62,7 +59,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.face.FaceManager; import android.os.Handler; import android.os.PowerManager; import android.os.UserHandle; @@ -90,18 +86,16 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; @@ -118,7 +112,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; @@ -140,8 +133,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; -import com.google.common.collect.ImmutableList; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -155,22 +146,17 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; -/** - * Tests the NotificationEntryManager setup with BubbleController. - * The {@link NotifPipeline} setup with BubbleController is tested in - * {@link NewNotifPipelineBubblesTest}. - */ @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BubblesTest extends SysuiTestCase { - @Mock - private NotificationEntryManager mNotificationEntryManager; @Mock private CommonNotifCollection mCommonNotifCollection; @Mock private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock + private BubblesManager.NotifCallback mNotifCallback; + @Mock private WindowManager mWindowManager; @Mock private IActivityManager mActivityManager; @@ -183,8 +169,6 @@ public class BubblesTest extends SysuiTestCase { @Mock private ZenModeConfig mZenModeConfig; @Mock - private FaceManager mFaceManager; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private SysuiStatusBarStateController mStatusBarStateController; @@ -196,15 +180,17 @@ public class BubblesTest extends SysuiTestCase { private FloatingContentCoordinator mFloatingContentCoordinator; @Mock private BubbleDataRepository mDataRepository; + @Mock + private NotificationShadeWindowView mNotificationShadeWindowView; + @Mock + private AuthController mAuthController; private SysUiState mSysUiState; private boolean mSysUiStateBubblesExpanded; private boolean mSysUiStateBubblesManageMenuExpanded; @Captor - private ArgumentCaptor mEntryListenerCaptor; - @Captor - private ArgumentCaptor mRemoveInterceptorCaptor; + private ArgumentCaptor mNotifListenerCaptor; @Captor private ArgumentCaptor> mBubbleListCaptor; @Captor @@ -212,22 +198,16 @@ public class BubblesTest extends SysuiTestCase { @Captor private ArgumentCaptor mBroadcastReceiverArgumentCaptor; - private BubblesManager mBubblesManager; - // TODO(178618782): Move tests on the controller directly to the shell private TestableBubbleController mBubbleController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; - private NotificationEntryListener mEntryListener; - private NotificationRemoveInterceptor mRemoveInterceptor; - + private NotifCollectionListener mEntryListener; private NotificationTestHelper mNotificationTestHelper; private NotificationEntry mRow; private NotificationEntry mRow2; - private NotificationEntry mRow3; private ExpandableNotificationRow mNonBubbleNotifRow; private BubbleEntry mBubbleEntry; private BubbleEntry mBubbleEntry2; - private BubbleEntry mBubbleEntry3; private BubbleEntry mBubbleEntryUser11; private BubbleEntry mBubbleEntry2User11; @@ -245,12 +225,8 @@ public class BubblesTest extends SysuiTestCase { @Mock private NotifPipeline mNotifPipeline; @Mock - private NotifPipelineFlags mNotifPipelineFlags; - @Mock private DumpManager mDumpManager; @Mock - private NotificationShadeWindowView mNotificationShadeWindowView; - @Mock private IStatusBarService mStatusBarService; @Mock private NotificationVisibilityProvider mVisibilityProvider; @@ -269,8 +245,6 @@ public class BubblesTest extends SysuiTestCase { @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock - private AuthController mAuthController; - @Mock private TaskViewTransitions mTaskViewTransitions; @Mock private Optional mOneHandedOptional; @@ -290,7 +264,6 @@ public class BubblesTest extends SysuiTestCase { // For the purposes of this test, just run everything synchronously ShellExecutor syncExecutor = new SyncExecutor(); - mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, @@ -308,11 +281,9 @@ public class BubblesTest extends SysuiTestCase { TestableLooper.get(this)); mRow = mNotificationTestHelper.createBubble(mDeleteIntent); mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); - mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow); mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2); - mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3); UserHandle handle = mock(UserHandle.class); when(handle.getIdentifier()).thenReturn(11); @@ -321,9 +292,6 @@ public class BubblesTest extends SysuiTestCase { mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry( mNotificationTestHelper.createBubble(handle)); - // Return non-null notification data from the CommonNotifCollection - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); @@ -336,7 +304,6 @@ public class BubblesTest extends SysuiTestCase { (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0; }); - // TODO: Fix mPositioner = new TestableBubblePositioner(mContext, mWindowManager); mPositioner.setMaxBubbles(5); mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor); @@ -355,8 +322,6 @@ public class BubblesTest extends SysuiTestCase { mock(NotifPipelineFlags.class), mock(KeyguardNotificationVisibilityProvider.class) ); - - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false); when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); mBubbleController = new TestableBubbleController( mContext, @@ -396,23 +361,17 @@ public class BubblesTest extends SysuiTestCase { mZenModeController, mLockscreenUserManager, mNotificationGroupManager, - mNotificationEntryManager, mCommonNotifCollection, mNotifPipeline, mSysUiState, - mNotifPipelineFlags, mDumpManager, syncExecutor); + mBubblesManager.addNotifCallback(mNotifCallback); - // XXX: Does *this* need to be changed? // Get a reference to the BubbleController's entry listener - verify(mNotificationEntryManager, atLeastOnce()) - .addNotificationEntryListener(mEntryListenerCaptor.capture()); - mEntryListener = mEntryListenerCaptor.getValue(); - // And the remove interceptor - verify(mNotificationEntryManager, atLeastOnce()) - .addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture()); - mRemoveInterceptor = mRemoveInterceptorCaptor.getValue(); + verify(mNotifPipeline, atLeastOnce()) + .addCollectionListener(mNotifListenerCaptor.capture()); + mEntryListener = mNotifListenerCaptor.getValue(); } @Test @@ -433,90 +392,75 @@ public class BubblesTest extends SysuiTestCase { @Test public void testRemoveBubble() { mBubbleController.updateBubble(mBubbleEntry); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())); + assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertTrue(mBubbleController.hasBubbles()); - verify(mNotificationEntryManager).updateNotifications(any()); + verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); mBubbleController.removeBubble( mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); + verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); } @Test - public void testPromoteBubble_autoExpand() throws Exception { - mBubbleController.updateBubble(mBubbleEntry2); + public void testRemoveBubble_withDismissedNotif_inOverflow() { + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getKey()); - assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b)); - verify(mNotificationEntryManager, never()).performRemoveNotification( - eq(mRow.getSbn()), any(), anyInt()); - assertThat(mRow.isBubble()).isFalse(); - - Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getKey()); - assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2); - - mBubbleController.promoteBubbleFromOverflow(b); - - assertThat(b.isBubble()).isTrue(); - assertThat(b.shouldAutoExpand()).isTrue(); - int flags = Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE - | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; - verify(mStatusBarService, times(1)).onNotificationBubbleChanged( - eq(b.getKey()), eq(true), eq(flags)); - } - - @Test - public void testCancelOverflowBubble() { - mBubbleController.updateBubble(mBubbleEntry2); - mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */ - false, /* showInShade */ true); - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - - mBubbleController.removeBubble( - mRow.getKey(), DISMISS_NOTIF_CANCEL); - verify(mNotificationEntryManager, times(1)).performRemoveNotification( - eq(mRow.getSbn()), any(), anyInt()); - assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); - assertFalse(mRow.isBubble()); - } - - @Test - public void testUserChange_doesNotRemoveNotif() { - mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); + // Make it look like dismissed notif + mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); + + // Now remove the bubble mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_CHANGED); - verify(mNotificationEntryManager, never()).performRemoveNotification( - eq(mRow.getSbn()), any(), anyInt()); + mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); + assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())); + + // We don't remove the notification since the bubble is still in overflow. + verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt()); + assertFalse(mBubbleController.hasBubbles()); + } + + @Test + public void testRemoveBubble_withDismissedNotif_notInOverflow() { + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + + assertTrue(mBubbleController.hasBubbles()); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); + + // Make it look like dismissed notif + mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); + + // Now remove the bubble + mBubbleController.removeBubble( + mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); + assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.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), + any(), anyInt()); assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - assertTrue(mRow.isBubble()); } @Test public void testDismissStack() { mBubbleController.updateBubble(mBubbleEntry); - verify(mNotificationEntryManager, times(1)).updateNotifications(any()); + verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); mBubbleController.updateBubble(mBubbleEntry2); - verify(mNotificationEntryManager, times(2)).updateNotifications(any()); + verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); assertTrue(mBubbleController.hasBubbles()); mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - verify(mNotificationEntryManager, times(3)).updateNotifications(any()); + verify(mNotifCallback, times(3)).invalidateNotifications(anyString()); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); @@ -528,7 +472,7 @@ public class BubblesTest extends SysuiTestCase { assertStackCollapsed(); // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed @@ -536,7 +480,6 @@ public class BubblesTest extends SysuiTestCase { assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Expand the stack - BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); @@ -556,8 +499,8 @@ public class BubblesTest extends SysuiTestCase { @Ignore("Currently broken.") public void testCollapseAfterChangingExpandedBubble() { // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); - mEntryListener.onPendingEntryAdded(mRow2); + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry2); @@ -593,6 +536,7 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( true, mRow.getKey()); + // Collapse mBubbleController.collapseStack(); assertStackCollapsed(); @@ -602,7 +546,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testExpansionRemovesShowInShadeAndDot() { // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed @@ -627,7 +571,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed @@ -649,7 +593,7 @@ public class BubblesTest extends SysuiTestCase { assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); // Send update - mEntryListener.onPreEntryUpdated(mRow); + mEntryListener.onEntryUpdated(mRow); // Nothing should have changed // Notif is suppressed after expansion @@ -661,8 +605,8 @@ public class BubblesTest extends SysuiTestCase { @Test public void testRemoveLastExpanded_collapses() { // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); - mEntryListener.onPendingEntryAdded(mRow2); + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry2); @@ -707,7 +651,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testRemoveLastExpandedEmptyOverflow_collapses() { // Mark it as a bubble and add it explicitly - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // Expand @@ -732,6 +676,7 @@ public class BubblesTest extends SysuiTestCase { assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); } + @Test public void testAutoExpand_fails_noFlag() { assertStackCollapsed(); @@ -739,7 +684,7 @@ public class BubblesTest extends SysuiTestCase { Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */); // Add the auto expand bubble - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // Expansion shouldn't change @@ -755,7 +700,7 @@ public class BubblesTest extends SysuiTestCase { Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */); // Add the auto expand bubble - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // Expansion should change @@ -771,7 +716,7 @@ public class BubblesTest extends SysuiTestCase { Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); // Add the suppress notif bubble - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground @@ -804,23 +749,9 @@ public class BubblesTest extends SysuiTestCase { assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); } - @Test - public void testExpandStackAndSelectBubble_removedFirst() { - mEntryListener.onPendingEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // Simulate notification cancellation. - mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_APP_CANCEL); - - mBubbleController.expandStackAndSelectBubble(mBubbleEntry); - - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - } - @Test public void testMarkNewNotificationAsShowInShade() { - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); @@ -829,8 +760,8 @@ public class BubblesTest extends SysuiTestCase { @Test public void testAddNotif_notBubble() { - mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry()); - mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry()); + mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry()); + mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry()); assertThat(mBubbleController.hasBubbles()).isFalse(); } @@ -868,48 +799,33 @@ public class BubblesTest extends SysuiTestCase { NotificationListenerService.Ranking ranking = new RankingBuilder( mRow.getRanking()).setCanBubble(false).build(); mRow.setRanking(ranking); - mEntryListener.onPreEntryUpdated(mRow); + mEntryListener.onEntryUpdated(mRow); assertFalse(mBubbleController.hasBubbles()); verify(mDeleteIntent, never()).send(); } - @Test - public void testRemoveBubble_succeeds_appCancel() { - mEntryListener.onPendingEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - - boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_APP_CANCEL); - - // Cancels always remove so no need to intercept - assertFalse(intercepted); - } - @Test public void testRemoveBubble_entryListenerRemove() { - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); // Removes the notification - mEntryListener.onEntryRemoved(mRow, null, false, REASON_APP_CANCEL); + mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL); assertFalse(mBubbleController.hasBubbles()); } @Test - public void removeBubble_clearAllIntercepted() { - mEntryListener.onPendingEntryAdded(mRow); + public void removeBubble_intercepted() { + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_CANCEL_ALL); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); // Intercept! assertTrue(intercepted); @@ -918,99 +834,51 @@ public class BubblesTest extends SysuiTestCase { } @Test - public void removeBubble_userDismissNotifIntercepted() { - mEntryListener.onPendingEntryAdded(mRow); + public void removeBubble_dismissIntoOverflow_intercepted() { + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_CANCEL); - - // Intercept! - assertTrue(intercepted); - // Should update show in shade state - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - } - - @Test - public void removeNotif_inOverflow_intercepted() { - // Get bubble with notif in shade. - mEntryListener.onPendingEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Dismiss the bubble into overflow. - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); + // Dismiss the bubble + mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); assertFalse(mBubbleController.hasBubbles()); - boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_CANCEL); + // Dismiss the notification + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); - // Notif is no longer a bubble, but still in overflow, so we intercept removal. + // Intercept dismissal since bubble is going into overflow assertTrue(intercepted); } @Test - public void removeNotif_notInOverflow_notIntercepted() { - // Get bubble with notif in shade. - mEntryListener.onPendingEntryAdded(mRow); + public void removeBubble_notIntercepted() { + mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); + // Dismiss the bubble + mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); assertFalse(mBubbleController.hasBubbles()); - boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_CANCEL); + // Dismiss the notification + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); - // Notif is no longer a bubble, so we should not intercept removal. + // Not a bubble anymore so we don't intercept dismissal. assertFalse(intercepted); } - @Test - public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() { - mBubbleController.updateBubble( - mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false); - mBubbleController.updateBubble( - mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false); - mBubbleController.updateBubble( - mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false); - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); - when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3); - assertEquals(mBubbleData.getBubbles().size(), 3); - - mBubbleData.setMaxOverflowBubbles(1); - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - assertEquals(mBubbleData.getBubbles().size(), 2); - assertEquals(mBubbleData.getOverflowBubbles().size(), 1); - - mBubbleController.removeBubble( - mRow2.getKey(), Bubbles.DISMISS_USER_GESTURE); - // Overflow max of 1 is reached; mRow is oldest, so it gets removed - verify(mNotificationEntryManager, times(1)).performRemoveNotification( - eq(mRow.getSbn()), any(), eq(REASON_CANCEL)); - assertEquals(mBubbleData.getBubbles().size(), 1); - assertEquals(mBubbleData.getOverflowBubbles().size(), 1); - } - @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - mRemoveInterceptor.onNotificationRemoveRequested( - mRow.getKey(), mRow, REASON_CANCEL); + mBubblesManager.handleDismissalInterception(mRow); // Should update show in shade state assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -1022,7 +890,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - mEntryListener.onPendingEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); @@ -1042,9 +910,9 @@ public class BubblesTest extends SysuiTestCase { // GIVEN a group summary with a bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); + mEntryListener.onEntryAdded(groupedBubble.getEntry()); when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); - mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); @@ -1054,10 +922,10 @@ public class BubblesTest extends SysuiTestCase { // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), - groupSummary.getEntry().getSbn().getGroupKey())); + groupedBubble.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), - groupSummary.getEntry().getSbn().getGroupKey())); + groupedBubble.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -1066,7 +934,7 @@ public class BubblesTest extends SysuiTestCase { // GIVEN a group summary with a bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); + mEntryListener.onEntryAdded(groupedBubble.getEntry()); when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); @@ -1076,7 +944,7 @@ public class BubblesTest extends SysuiTestCase { mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app - mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL); + mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL); // THEN the summary and its children are removed from bubble data assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); @@ -1085,14 +953,14 @@ public class BubblesTest extends SysuiTestCase { } @Test - public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren() + public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren() throws Exception { // GIVEN a group summary with two (non-bubble) children and one bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); + mEntryListener.onEntryAdded(groupedBubble.getEntry()); when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); - mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); // WHEN the summary is dismissed @@ -1100,16 +968,15 @@ public class BubblesTest extends SysuiTestCase { // THEN only the NON-bubble children are dismissed List childrenRows = groupSummary.getAttachedChildren(); - verify(mNotificationEntryManager, times(1)).performRemoveNotification( - eq(childrenRows.get(0).getEntry().getSbn()), any(), - eq(REASON_GROUP_SUMMARY_CANCELED)); - verify(mNotificationEntryManager, times(1)).performRemoveNotification( - eq(childrenRows.get(1).getEntry().getSbn()), any(), - eq(REASON_GROUP_SUMMARY_CANCELED)); - verify(mNotificationEntryManager, never()).performRemoveNotification( - eq(groupedBubble.getEntry().getSbn()), any(), anyInt()); + verify(mNotifCallback, times(1)).removeNotification( + eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); + verify(mNotifCallback, times(1)).removeNotification( + eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); + verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), + any(), anyInt()); - // THEN the bubble child is suppressed from the shade + // THEN the bubble child still exists as a bubble and is suppressed from the shade + assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getSbn().getGroupKey())); @@ -1117,33 +984,16 @@ public class BubblesTest extends SysuiTestCase { groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getSbn().getGroupKey())); - // THEN the summary is removed from GroupManager - verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); + // THEN the summary is also suppressed from the shade + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + groupSummary.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupSummary.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); } - /** - * Verifies that when a non visually interruptive update occurs for a bubble in the overflow, - * the that bubble does not get promoted from the overflow. - */ - @Test - public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() { - // Setup - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - assertTrue(mBubbleController.hasBubbles()); - - // Overflow it - mBubbleData.dismissBubbleWithKey(mRow.getKey(), - Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse(); - assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue(); - - // Test - mBubbleController.updateBubble(mBubbleEntry); - assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse(); - } - /** * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't * test the loading from the repository which would be a nice thing to add. @@ -1185,15 +1035,17 @@ public class BubblesTest extends SysuiTestCase { */ @Test public void testOverflowLoadedOnce() { - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse(); + // XXX + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL); - mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL); + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); + assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty(); + + mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL); + mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL); assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); verify(mDataRepository, times(1)).loadBubbles(anyInt(), any()); @@ -1376,6 +1228,7 @@ public class BubblesTest extends SysuiTestCase { assertStackCollapsed(); } + @Test public void testRegisterUnregisterBroadcastListener() { spyOn(mContext); @@ -1455,7 +1308,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testSetShouldAutoExpand_notifiesFlagChanged() { - mEntryListener.onPendingEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()); @@ -1551,7 +1404,7 @@ public class BubblesTest extends SysuiTestCase { } /** - * Sets the bubble metadata flags for this entry. These ]flags are normally set by + * Sets the bubble metadata flags for this entry. These flags are normally set by * NotificationManagerService when the notification is sent, however, these tests do not * go through that path so we set them explicitly when testing. */ @@ -1570,12 +1423,15 @@ public class BubblesTest extends SysuiTestCase { private Notification.BubbleMetadata getMetadata() { Intent target = new Intent(mContext, BubblesTestActivity.class); PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, FLAG_MUTABLE); - - return new Notification.BubbleMetadata.Builder(bubbleIntent, - Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) + return new Notification.BubbleMetadata.Builder( + bubbleIntent, + Icon.createWithResource( + mContext, + com.android.wm.shell.R.drawable.bubble_ic_create_bubble)) .build(); } + /** * Asserts that the bubble stack is expanded and also validates the cached state is updated. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java deleted file mode 100644 index a6327b9daed5e..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.wmshell; - -import static android.app.Notification.FLAG_BUBBLE; -import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; -import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; -import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.IActivityManager; -import android.app.INotificationManager; -import android.app.Notification; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.LauncherApps; -import android.content.pm.UserInfo; -import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; -import android.os.PowerManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.service.dreams.IDreamManager; -import android.service.notification.NotificationListenerService; -import android.service.notification.ZenModeConfig; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.util.Pair; -import android.util.SparseArray; -import android.view.View; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; -import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.TaskViewTransitions; -import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.bubbles.Bubble; -import com.android.wm.shell.bubbles.BubbleData; -import com.android.wm.shell.bubbles.BubbleDataRepository; -import com.android.wm.shell.bubbles.BubbleEntry; -import com.android.wm.shell.bubbles.BubbleLogger; -import com.android.wm.shell.bubbles.BubbleStackView; -import com.android.wm.shell.bubbles.Bubbles; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.FloatingContentCoordinator; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.draganddrop.DragAndDropController; -import com.android.wm.shell.onehanded.OneHandedController; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.HashMap; -import java.util.List; -import java.util.Optional; - -/** - * Tests the NotifPipeline setup with BubbleController. - * The NotificationEntryManager setup with BubbleController is tested in - * {@link BubblesTest}. - */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class NewNotifPipelineBubblesTest extends SysuiTestCase { - @Mock - private NotificationEntryManager mNotificationEntryManager; - @Mock - private CommonNotifCollection mCommonNotifCollection; - @Mock - private NotificationGroupManagerLegacy mNotificationGroupManager; - @Mock - private BubblesManager.NotifCallback mNotifCallback; - @Mock - private WindowManager mWindowManager; - @Mock - private IActivityManager mActivityManager; - @Mock - private DozeParameters mDozeParameters; - @Mock - private ConfigurationController mConfigurationController; - @Mock - private ZenModeController mZenModeController; - @Mock - private ZenModeConfig mZenModeConfig; - @Mock - private NotificationLockscreenUserManager mLockscreenUserManager; - @Mock - private SysuiStatusBarStateController mStatusBarStateController; - @Mock - private KeyguardViewMediator mKeyguardViewMediator; - @Mock - private KeyguardBypassController mKeyguardBypassController; - @Mock - private FloatingContentCoordinator mFloatingContentCoordinator; - @Mock - private BubbleDataRepository mDataRepository; - @Mock - private NotificationShadeWindowView mNotificationShadeWindowView; - @Mock - private AuthController mAuthController; - - private SysUiState mSysUiState; - private boolean mSysUiStateBubblesExpanded; - private boolean mSysUiStateBubblesManageMenuExpanded; - - @Captor - private ArgumentCaptor mNotifListenerCaptor; - @Captor - private ArgumentCaptor> mBubbleListCaptor; - @Captor - private ArgumentCaptor mFilterArgumentCaptor; - @Captor - private ArgumentCaptor mBroadcastReceiverArgumentCaptor; - - private BubblesManager mBubblesManager; - private TestableBubbleController mBubbleController; - private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; - private NotifCollectionListener mEntryListener; - private NotificationTestHelper mNotificationTestHelper; - private NotificationEntry mRow; - private NotificationEntry mRow2; - private ExpandableNotificationRow mNonBubbleNotifRow; - private BubbleEntry mBubbleEntry; - private BubbleEntry mBubbleEntry2; - - private BubbleEntry mBubbleEntryUser11; - private BubbleEntry mBubbleEntry2User11; - - @Mock - private Bubbles.BubbleExpandListener mBubbleExpandListener; - @Mock - private PendingIntent mDeleteIntent; - @Mock - private SysuiColorExtractor mColorExtractor; - @Mock - ColorExtractor.GradientColors mGradientColors; - @Mock - private ShadeController mShadeController; - @Mock - private NotifPipeline mNotifPipeline; - @Mock - private NotifPipelineFlags mNotifPipelineFlags; - @Mock - private DumpManager mDumpManager; - @Mock - private IStatusBarService mStatusBarService; - @Mock - private NotificationVisibilityProvider mVisibilityProvider; - @Mock - private LauncherApps mLauncherApps; - @Mock - private WindowManagerShellWrapper mWindowManagerShellWrapper; - @Mock - private BubbleLogger mBubbleLogger; - @Mock - private TaskStackListenerImpl mTaskStackListener; - @Mock - private ShellTaskOrganizer mShellTaskOrganizer; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private ScreenOffAnimationController mScreenOffAnimationController; - @Mock - private TaskViewTransitions mTaskViewTransitions; - @Mock - private Optional mOneHandedOptional; - - private TestableBubblePositioner mPositioner; - - private BubbleData mBubbleData; - - private TestableLooper mTestableLooper; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mTestableLooper = TestableLooper.get(this); - - // For the purposes of this test, just run everything synchronously - ShellExecutor syncExecutor = new SyncExecutor(); - - when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - - mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, - mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, - mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, - mColorExtractor, mDumpManager, mKeyguardStateController, - mScreenOffAnimationController, mAuthController); - mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); - mNotificationShadeWindowController.attach(); - - // Need notifications for bubbles - mNotificationTestHelper = new NotificationTestHelper( - mContext, - mDependency, - TestableLooper.get(this)); - mRow = mNotificationTestHelper.createBubble(mDeleteIntent); - mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); - mNonBubbleNotifRow = mNotificationTestHelper.createRow(); - mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow); - mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2); - - UserHandle handle = mock(UserHandle.class); - when(handle.getIdentifier()).thenReturn(11); - mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry( - mNotificationTestHelper.createBubble(handle)); - mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry( - mNotificationTestHelper.createBubble(handle)); - - mZenModeConfig.suppressedVisualEffects = 0; - when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - - mSysUiState = new SysUiState(); - mSysUiState.addCallback(sysUiFlags -> { - mSysUiStateBubblesManageMenuExpanded = - (sysUiFlags - & QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0; - mSysUiStateBubblesExpanded = - (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0; - }); - - mPositioner = new TestableBubblePositioner(mContext, mWindowManager); - mPositioner.setMaxBubbles(5); - mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor); - - TestableNotificationInterruptStateProviderImpl interruptionStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), - mock(PowerManager.class), - mock(IDreamManager.class), - mock(AmbientDisplayConfiguration.class), - mock(NotificationFilter.class), - mock(StatusBarStateController.class), - mock(BatteryController.class), - mock(HeadsUpManager.class), - mock(NotificationInterruptLogger.class), - mock(Handler.class), - mock(NotifPipelineFlags.class), - mock(KeyguardNotificationVisibilityProvider.class) - ); - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true); - when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); - mBubbleController = new TestableBubbleController( - mContext, - mBubbleData, - mFloatingContentCoordinator, - mDataRepository, - mStatusBarService, - mWindowManager, - mWindowManagerShellWrapper, - mock(UserManager.class), - mLauncherApps, - mBubbleLogger, - mTaskStackListener, - mShellTaskOrganizer, - mPositioner, - mock(DisplayController.class), - mOneHandedOptional, - mock(DragAndDropController.class), - syncExecutor, - mock(Handler.class), - mTaskViewTransitions, - mock(SyncTransactionQueue.class)); - mBubbleController.setExpandListener(mBubbleExpandListener); - spyOn(mBubbleController); - - mBubblesManager = new BubblesManager( - mContext, - mBubbleController.asBubbles(), - mNotificationShadeWindowController, - mock(KeyguardStateController.class), - mShadeController, - mConfigurationController, - mStatusBarService, - mock(INotificationManager.class), - mVisibilityProvider, - interruptionStateProvider, - mZenModeController, - mLockscreenUserManager, - mNotificationGroupManager, - mNotificationEntryManager, - mCommonNotifCollection, - mNotifPipeline, - mSysUiState, - mNotifPipelineFlags, - mDumpManager, - syncExecutor); - mBubblesManager.addNotifCallback(mNotifCallback); - - // Get a reference to the BubbleController's entry listener - verify(mNotifPipeline, atLeastOnce()) - .addCollectionListener(mNotifListenerCaptor.capture()); - mEntryListener = mNotifListenerCaptor.getValue(); - } - - @Test - public void testAddBubble() { - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testHasBubbles() { - assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testRemoveBubble() { - mBubbleController.updateBubble(mBubbleEntry); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - assertTrue(mBubbleController.hasBubbles()); - verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); - - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); - - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testRemoveBubble_withDismissedNotif_inOverflow() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Make it look like dismissed notif - mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); - - // Now remove the bubble - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())); - - // We don't remove the notification since the bubble is still in overflow. - verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt()); - assertFalse(mBubbleController.hasBubbles()); - } - - @Test - public void testRemoveBubble_withDismissedNotif_notInOverflow() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Make it look like dismissed notif - mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); - - // Now remove the bubble - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); - assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.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), - any(), anyInt()); - assertFalse(mBubbleController.hasBubbles()); - } - - @Test - public void testDismissStack() { - mBubbleController.updateBubble(mBubbleEntry); - verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - mBubbleController.updateBubble(mBubbleEntry2); - verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); - assertTrue(mBubbleController.hasBubbles()); - - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - verify(mNotifCallback, times(3)).invalidateNotifications(anyString()); - assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); - - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testExpandCollapseStack() { - assertStackCollapsed(); - - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // We should have bubbles & their notifs should not be suppressed - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Expand the stack - mBubbleData.setExpanded(true); - assertStackExpanded(); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Make sure the notif is suppressed - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - - // Collapse - mBubbleController.collapseStack(); - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - @Ignore("Currently broken.") - public void testCollapseAfterChangingExpandedBubble() { - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mEntryListener.onEntryAdded(mRow2); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - - // We should have bubbles & their notifs should not be suppressed - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2); - - // Expand - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - assertStackExpanded(); - verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( - true, mRow2.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Last added is the one that is expanded - assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey()); - assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); - - // Switch which bubble is expanded - mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( - mRow.getKey())); - mBubbleData.setExpanded(true); - assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey()); - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - - // collapse for previous bubble - verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( - false, mRow2.getKey()); - // expand for selected bubble - verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( - true, mRow.getKey()); - - - // Collapse - mBubbleController.collapseStack(); - assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testExpansionRemovesShowInShadeAndDot() { - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // We should have bubbles & their notifs should not be suppressed - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - mTestableLooper.processAllMessages(); - assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - - // Expand - mBubbleData.setExpanded(true); - assertStackExpanded(); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Notif is suppressed after expansion - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - // Notif shouldn't show dot after expansion - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - } - - @Test - public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // We should have bubbles & their notifs should not be suppressed - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - mTestableLooper.processAllMessages(); - assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - - // Expand - mBubbleData.setExpanded(true); - assertStackExpanded(); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Notif is suppressed after expansion - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - // Notif shouldn't show dot after expansion - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - - // Send update - mEntryListener.onEntryUpdated(mRow); - - // Nothing should have changed - // Notif is suppressed after expansion - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - // Notif shouldn't show dot after expansion - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - } - - @Test - public void testRemoveLastExpanded_collapses() { - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mEntryListener.onEntryAdded(mRow2); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - - // Expand - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - assertStackExpanded(); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey()); - - // Last added is the one that is expanded - assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey()); - assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); - - // Dismiss currently expanded - mBubbleController.removeBubble( - mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey(), - Bubbles.DISMISS_USER_GESTURE); - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getKey()); - - // Make sure first bubble is selected - assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - - // Dismiss that one - mBubbleController.removeBubble( - mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey(), - Bubbles.DISMISS_USER_GESTURE); - - // We should be collapsed - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testRemoveLastExpandedEmptyOverflow_collapses() { - // Mark it as a bubble and add it explicitly - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // Expand - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - assertStackExpanded(); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - - // Block the bubble so it won't be in the overflow - mBubbleController.removeBubble( - mBubbleData.getBubbleInStackWithKey( - stackView.getExpandedBubble().getKey()).getKey(), - Bubbles.DISMISS_BLOCKED); - - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - - // We should be collapsed - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - - @Test - public void testAutoExpand_fails_noFlag() { - assertStackCollapsed(); - setMetadataFlags(mRow, - Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */); - - // Add the auto expand bubble - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // Expansion shouldn't change - verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, - mRow.getKey()); - assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testAutoExpand_succeeds_withFlag() { - setMetadataFlags(mRow, - Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */); - - // Add the auto expand bubble - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // Expansion should change - verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, - mRow.getKey()); - assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testSuppressNotif_onInitialNotif() { - setMetadataFlags(mRow, - Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - - // Add the suppress notif bubble - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - // Notif should be suppressed because we were foreground - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - // Dot + flyout is hidden because notif is suppressed - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testSuppressNotif_onUpdateNotif() { - mBubbleController.updateBubble(mBubbleEntry); - - // Should not be suppressed - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - // Should show dot - assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - - // Update to suppress notif - setMetadataFlags(mRow, - Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - mBubbleController.updateBubble(mBubbleEntry); - - // Notif should be suppressed - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - // Dot + flyout is hidden because notif is suppressed - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testMarkNewNotificationAsShowInShade() { - mEntryListener.onEntryAdded(mRow); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - mTestableLooper.processAllMessages(); - assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); - } - - @Test - public void testAddNotif_notBubble() { - mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry()); - mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry()); - - assertThat(mBubbleController.hasBubbles()).isFalse(); - } - - @Test - public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_AGED); - verify(mDeleteIntent, never()).send(); - } - - @Test - public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.removeBubble( - mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - verify(mDeleteIntent, times(1)).send(); - } - - @Test - public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - verify(mDeleteIntent, times(2)).send(); - } - - @Test - public void testRemoveBubble_noLongerBubbleAfterUpdate() - throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - - mRow.getSbn().getNotification().flags &= ~FLAG_BUBBLE; - NotificationListenerService.Ranking ranking = new RankingBuilder( - mRow.getRanking()).setCanBubble(false).build(); - mRow.setRanking(ranking); - mEntryListener.onEntryUpdated(mRow); - - assertFalse(mBubbleController.hasBubbles()); - verify(mDeleteIntent, never()).send(); - } - - @Test - public void testRemoveBubble_entryListenerRemove() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - - // Removes the notification - mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL); - assertFalse(mBubbleController.hasBubbles()); - } - - @Test - public void removeBubble_intercepted() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); - - // Intercept! - assertTrue(intercepted); - // Should update show in shade state - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - } - - @Test - public void removeBubble_dismissIntoOverflow_intercepted() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Dismiss the bubble - mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); - assertFalse(mBubbleController.hasBubbles()); - - // Dismiss the notification - boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); - - // Intercept dismissal since bubble is going into overflow - assertTrue(intercepted); - } - - @Test - public void removeBubble_notIntercepted() { - mEntryListener.onEntryAdded(mRow); - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - // Dismiss the bubble - mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); - assertFalse(mBubbleController.hasBubbles()); - - // Dismiss the notification - boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); - - // Not a bubble anymore so we don't intercept dismissal. - assertFalse(intercepted); - } - - @Test - public void testNotifyShadeSuppressionChange_notificationDismiss() { - mEntryListener.onEntryAdded(mRow); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - mBubblesManager.handleDismissalInterception(mRow); - - // Should update show in shade state - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - - // Should notify delegate that shade state changed - verify(mBubbleController).onBubbleMetadataFlagChanged( - mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - } - - @Test - public void testNotifyShadeSuppressionChange_bubbleExpanded() { - mEntryListener.onEntryAdded(mRow); - - assertTrue(mBubbleController.hasBubbles()); - assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); - - mBubbleData.setExpanded(true); - - // Once a bubble is expanded the notif is suppressed - assertBubbleNotificationSuppressedFromShade(mBubbleEntry); - - // Should notify delegate that shade state changed - verify(mBubbleController).onBubbleMetadataFlagChanged( - mBubbleData.getBubbleInStackWithKey(mRow.getKey())); - } - - @Test - public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception { - // GIVEN a group summary with a bubble child - ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); - ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) - .thenReturn(groupedBubble.getEntry()); - groupSummary.addChildNotification(groupedBubble); - assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); - - // WHEN the summary is dismissed - mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); - - // THEN the summary and bubbled child are suppressed from the shade - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry().getKey(), - groupedBubble.getEntry().getSbn().getGroupKey())); - assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry().getKey(), - groupedBubble.getEntry().getSbn().getGroupKey())); - assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); - } - - @Test - public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception { - // GIVEN a group summary with a bubble child - ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); - ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) - .thenReturn(groupedBubble.getEntry()); - groupSummary.addChildNotification(groupedBubble); - assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); - - // GIVEN the summary is dismissed - mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); - - // WHEN the summary is cancelled by the app - mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL); - - // THEN the summary and its children are removed from bubble data - assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); - assertFalse(mBubbleData.isSummarySuppressed( - groupSummary.getEntry().getSbn().getGroupKey())); - } - - @Test - public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren() - throws Exception { - // GIVEN a group summary with two (non-bubble) children and one bubble child - ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); - ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) - .thenReturn(groupedBubble.getEntry()); - groupSummary.addChildNotification(groupedBubble); - - // WHEN the summary is dismissed - mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); - - // THEN only the NON-bubble children are dismissed - List childrenRows = groupSummary.getAttachedChildren(); - verify(mNotifCallback, times(1)).removeNotification( - eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); - verify(mNotifCallback, times(1)).removeNotification( - eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); - verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), - any(), anyInt()); - - // THEN the bubble child still exists as a bubble and is suppressed from the shade - assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry().getKey(), - groupedBubble.getEntry().getSbn().getGroupKey())); - assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry().getKey(), - groupedBubble.getEntry().getSbn().getGroupKey())); - - // THEN the summary is also suppressed from the shade - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupSummary.getEntry().getKey(), - groupSummary.getEntry().getSbn().getGroupKey())); - assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( - groupSummary.getEntry().getKey(), - groupSummary.getEntry().getSbn().getGroupKey())); - } - - - /** - * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't - * test the loading from the repository which would be a nice thing to add. - */ - @Test - public void testOnUserChanged_overflowState() { - int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(); - int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier(); - - mBubbleController.updateBubble(mBubbleEntry); - mBubbleController.updateBubble(mBubbleEntry2); - assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - - // Verify these are in the overflow - assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey())).isNotNull(); - assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull(); - - // Switch users - mBubbleController.onUserChanged(secondUserId); - assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); - - // Give this user some bubbles - mBubbleController.updateBubble(mBubbleEntryUser11); - mBubbleController.updateBubble(mBubbleEntry2User11); - assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - - // Verify these are in the overflow - assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull(); - assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull(); - - // Would have loaded bubbles twice because of user switch - verify(mDataRepository, times(2)).loadBubbles(anyInt(), any()); - } - - /** - * Verifies we only load the overflow data once. - */ - @Test - public void testOverflowLoadedOnce() { - // XXX - when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); - when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); - - mEntryListener.onEntryAdded(mRow); - mEntryListener.onEntryAdded(mRow2); - mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty(); - - mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL); - mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL); - assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); - - verify(mDataRepository, times(1)).loadBubbles(anyInt(), any()); - } - - /** - * Verifies that shortcut deletions triggers that bubble being removed from XML. - */ - @Test - public void testDeleteShortcutsDeletesXml() throws Exception { - ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId"); - BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry()); - mBubbleController.updateBubble(shortcutBubbleEntry); - - mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(), - Bubbles.DISMISS_SHORTCUT_REMOVED); - - verify(mDataRepository, atLeastOnce()).removeBubbles(anyInt(), mBubbleListCaptor.capture()); - assertThat(mBubbleListCaptor.getValue().get(0).getKey()).isEqualTo( - shortcutBubbleEntry.getKey()); - } - - @Test - public void testShowManageMenuChangesSysuiState() { - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - - // Expand the stack - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Show the menu - stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); - } - - @Test - public void testHideManageMenuChangesSysuiState() { - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - - // Expand the stack - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Show the menu - stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); - - // Hide the menu - stackView.showManageMenu(false); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testCollapseBubbleManageMenuChangesSysuiState() { - mBubbleController.updateBubble(mBubbleEntry); - assertTrue(mBubbleController.hasBubbles()); - - // Expand the stack - BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleData.setExpanded(true); - assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); - - // Show the menu - stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); - - // Collapse the stack - mBubbleData.setExpanded(false); - - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); - } - - @Test - public void testNotificationChannelModified_channelUpdated_removesOverflowBubble() - throws Exception { - // Setup - ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId"); - NotificationEntry entry = row.getEntry(); - entry.getChannel().setConversationId( - row.getEntry().getChannel().getParentChannelId(), - "shortcutId"); - mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry())); - assertTrue(mBubbleController.hasBubbles()); - - // Overflow it - mBubbleData.dismissBubbleWithKey(entry.getKey(), - Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue(); - - // Test - entry.getChannel().setDeleted(true); - mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(), - entry.getSbn().getUser(), - entry.getChannel(), - NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); - assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); - } - - @Test - public void testNotificationChannelModified_channelDeleted_removesOverflowBubble() - throws Exception { - // Setup - ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId"); - NotificationEntry entry = row.getEntry(); - entry.getChannel().setConversationId( - row.getEntry().getChannel().getParentChannelId(), - "shortcutId"); - mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry())); - assertTrue(mBubbleController.hasBubbles()); - - // Overflow it - mBubbleData.dismissBubbleWithKey(entry.getKey(), - Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue(); - - // Test - entry.getChannel().setDeleted(true); - mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(), - entry.getSbn().getUser(), - entry.getChannel(), - NOTIFICATION_CHANNEL_OR_GROUP_DELETED); - assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); - } - - @Test - public void testStackViewOnBackPressed_updatesBubbleDataExpandState() { - mBubbleController.updateBubble(mBubbleEntry); - - // Expand the stack - mBubbleData.setExpanded(true); - assertStackExpanded(); - - // Hit back - BubbleStackView stackView = mBubbleController.getStackView(); - stackView.onBackPressed(); - - // Make sure we're collapsed - assertStackCollapsed(); - } - - - @Test - public void testRegisterUnregisterBroadcastListener() { - spyOn(mContext); - mBubbleController.updateBubble(mBubbleEntry); - verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), - mFilterArgumentCaptor.capture()); - assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo( - Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo( - Intent.ACTION_SCREEN_OFF); - - mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL); - // TODO: not certain why this isn't called normally when tests are run, perhaps because - // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe - mBubbleController.onAllBubblesAnimatedOut(); - - verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue())); - } - - @Test - public void testBroadcastReceiverCloseDialogs_notGestureNav() { - spyOn(mContext); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleData.setExpanded(true); - verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), - mFilterArgumentCaptor.capture()); - Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); - - assertStackExpanded(); - } - - @Test - public void testBroadcastReceiverCloseDialogs_reasonGestureNav() { - spyOn(mContext); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleData.setExpanded(true); - - verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), - mFilterArgumentCaptor.capture()); - Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - i.putExtra("reason", "gestureNav"); - mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); - assertStackCollapsed(); - } - - @Test - public void testBroadcastReceiver_screenOff() { - spyOn(mContext); - mBubbleController.updateBubble(mBubbleEntry); - mBubbleData.setExpanded(true); - - verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), - mFilterArgumentCaptor.capture()); - - Intent i = new Intent(Intent.ACTION_SCREEN_OFF); - mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); - assertStackCollapsed(); - } - - @Test - public void testOnStatusBarStateChanged() { - mBubbleController.updateBubble(mBubbleEntry); - mBubbleData.setExpanded(true); - assertStackExpanded(); - BubbleStackView stackView = mBubbleController.getStackView(); - assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE); - - mBubbleController.onStatusBarStateChanged(false); - - assertStackCollapsed(); - assertThat(stackView.getVisibility()).isEqualTo(View.INVISIBLE); - - mBubbleController.onStatusBarStateChanged(true); - assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void testSetShouldAutoExpand_notifiesFlagChanged() { - mBubbleController.updateBubble(mBubbleEntry); - - assertTrue(mBubbleController.hasBubbles()); - Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()); - assertThat(b.shouldAutoExpand()).isFalse(); - - // Set it to the same thing - b.setShouldAutoExpand(false); - - // Verify it doesn't notify - verify(mBubbleController, never()).onBubbleMetadataFlagChanged(any()); - - // Set it to something different - b.setShouldAutoExpand(true); - verify(mBubbleController).onBubbleMetadataFlagChanged(b); - } - - @Test - public void testUpdateBubble_skipsDndSuppressListNotifs() { - mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(), - mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */, - mRow.shouldSuppressPeek()); - mBubbleEntry.getBubbleMetadata().setFlags( - Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); - - mBubbleController.updateBubble(mBubbleEntry); - - Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey()); - assertThat(b.shouldAutoExpand()).isFalse(); - assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull(); - } - - @Test - public void testOnRankingUpdate_DndSuppressListNotif() { - // It's in the stack - mBubbleController.updateBubble(mBubbleEntry); - assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue(); - - // Set current user profile - SparseArray userInfos = new SparseArray<>(); - userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(), - mock(UserInfo.class)); - mBubbleController.onCurrentProfilesChanged(userInfos); - - // Send ranking update that the notif is suppressed from the list. - HashMap> entryDataByKey = new HashMap<>(); - mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(), - mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */, - mRow.shouldSuppressPeek()); - Pair pair = new Pair(mBubbleEntry, true); - entryDataByKey.put(mBubbleEntry.getKey(), pair); - - NotificationListenerService.RankingMap rankingMap = - mock(NotificationListenerService.RankingMap.class); - when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() }); - mBubbleController.onRankingUpdated(rankingMap, entryDataByKey); - - // Should no longer be in the stack - assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse(); - } - - /** - * Sets the bubble metadata flags for this entry. These flags are normally set by - * NotificationManagerService when the notification is sent, however, these tests do not - * go through that path so we set them explicitly when testing. - */ - private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) { - Notification.BubbleMetadata bubbleMetadata = - entry.getSbn().getNotification().getBubbleMetadata(); - int flags = bubbleMetadata.getFlags(); - if (enableFlag) { - flags |= flag; - } else { - flags &= ~flag; - } - bubbleMetadata.setFlags(flags); - } - - /** - * Asserts that the bubble stack is expanded and also validates the cached state is updated. - */ - private void assertStackExpanded() { - assertTrue(mBubbleController.isStackExpanded()); - assertTrue(mBubbleController.getImplCachedState().isStackExpanded()); - } - - /** - * Asserts that the bubble stack is collapsed and also validates the cached state is updated. - */ - private void assertStackCollapsed() { - assertFalse(mBubbleController.isStackExpanded()); - assertFalse(mBubbleController.getImplCachedState().isStackExpanded()); - } - - /** - * Asserts that a bubble notification is suppressed from the shade and also validates the cached - * state is updated. - */ - private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) { - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - entry.getKey(), entry.getGroupKey())); - assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( - entry.getKey(), entry.getGroupKey())); - } - - /** - * Asserts that a bubble notification is not suppressed from the shade and also validates the - * cached state is updated. - */ - private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) { - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - entry.getKey(), entry.getGroupKey())); - assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( - entry.getKey(), entry.getGroupKey())); - } - - /** - * Asserts that the system ui states associated to bubbles are in the correct state. - */ - private void assertSysuiStates(boolean stackExpanded, boolean manageMenuExpanded) { - assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded); - assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); - } -}