Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt +10 −0 Original line number Original line Diff line number Diff line Loading @@ -73,6 +73,11 @@ abstract class VisualInterruptionCondition( override val uiEventId: UiEventEnum? = null, override val uiEventId: UiEventEnum? = null, override val eventLogData: EventLogData? = null override val eventLogData: EventLogData? = null ) : VisualInterruptionSuppressor { ) : VisualInterruptionSuppressor { constructor( types: Set<VisualInterruptionType>, reason: String ) : this(types, reason, /* uiEventId = */ null) /** @return true if these interruptions should be suppressed right now. */ /** @return true if these interruptions should be suppressed right now. */ abstract fun shouldSuppress(): Boolean abstract fun shouldSuppress(): Boolean } } Loading @@ -84,6 +89,11 @@ abstract class VisualInterruptionFilter( override val uiEventId: UiEventEnum? = null, override val uiEventId: UiEventEnum? = null, override val eventLogData: EventLogData? = null override val eventLogData: EventLogData? = null ) : VisualInterruptionSuppressor { ) : VisualInterruptionSuppressor { constructor( types: Set<VisualInterruptionType>, reason: String ) : this(types, reason, /* uiEventId = */ null) /** /** * @param entry the notification to consider suppressing * @param entry the notification to consider suppressing * @return true if these interruptions should be suppressed for this notification right now * @return true if these interruptions should be suppressed for this notification right now Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +68 −1 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,9 @@ package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE; import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED; import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED; import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; Loading @@ -29,6 +32,8 @@ import android.util.Log; import android.util.Slog; import android.util.Slog; import android.view.View; import android.view.View; import androidx.annotation.NonNull; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; import com.android.systemui.InitController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton; Loading @@ -54,7 +59,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; Loading @@ -63,6 +71,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Set; import javax.inject.Inject; import javax.inject.Inject; @SysUISingleton @SysUISingleton Loading Loading @@ -163,7 +173,14 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu initController.addPostInitTask(() -> { initController.addPostInitTask(() -> { mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); if (VisualInterruptionRefactor.isEnabled()) { visualInterruptionDecisionProvider.addCondition(mAlertsDisabledCondition); visualInterruptionDecisionProvider.addCondition(mVrModeCondition); visualInterruptionDecisionProvider.addFilter(mNeedsRedactionFilter); visualInterruptionDecisionProvider.addCondition(mPanelsDisabledCondition); } else { visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor); visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor); } mLockscreenUserManager.setUpWithPresenter(this); mLockscreenUserManager.setUpWithPresenter(this); mGutsManager.setUpWithPresenter( mGutsManager.setUpWithPresenter( this, mNotifListContainer, mOnSettingsClickListener); this, mNotifListContainer, mOnSettingsClickListener); Loading Loading @@ -306,4 +323,54 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); } } }; }; private final VisualInterruptionCondition mAlertsDisabledCondition = new VisualInterruptionCondition(Set.of(PEEK, PULSE, BUBBLE), "notification alerts disabled") { @Override public boolean shouldSuppress() { return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); } }; private final VisualInterruptionCondition mVrModeCondition = new VisualInterruptionCondition(Set.of(PEEK, BUBBLE), "device is in VR mode") { @Override public boolean shouldSuppress() { return isDeviceInVrMode(); } }; private final VisualInterruptionFilter mNeedsRedactionFilter = new VisualInterruptionFilter(Set.of(PEEK), "needs redaction on public lockscreen") { @Override public boolean shouldSuppress(@NonNull NotificationEntry entry) { if (!mKeyguardStateController.isOccluded()) { return false; } if (!mLockscreenUserManager.needsRedaction(entry)) { return false; } final int currentUserId = mLockscreenUserManager.getCurrentUserId(); final boolean currentUserPublic = mLockscreenUserManager.isLockscreenPublicMode( currentUserId); final int notificationUserId = entry.getSbn().getUserId(); final boolean notificationUserPublic = mLockscreenUserManager.isLockscreenPublicMode(notificationUserId); // TODO(b/135046837): we can probably relax this with dynamic privacy return currentUserPublic || notificationUserPublic; } }; private final VisualInterruptionCondition mPanelsDisabledCondition = new VisualInterruptionCondition(Set.of(PEEK), "disabled panel") { @Override public boolean shouldSuppress() { return !mCommandQueue.panelsEnabled(); } }; } } packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +236 −85 Original line number Original line Diff line number Diff line Loading @@ -16,22 +16,28 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.Notification; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.StatusBarManager; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.systemui.Flags; import com.android.systemui.InitController; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; Loading @@ -55,7 +61,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; Loading @@ -64,10 +73,14 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor; import java.util.List; import java.util.Set; @SmallTest @SmallTest @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @RunWithLooper() @RunWithLooper() Loading @@ -76,18 +89,23 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider = private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider = mock(VisualInterruptionDecisionProvider.class); mock(VisualInterruptionDecisionProvider.class); private NotificationInterruptSuppressor mInterruptSuppressor; private NotificationInterruptSuppressor mInterruptSuppressor; private VisualInterruptionCondition mAlertsDisabledCondition; private VisualInterruptionCondition mVrModeCondition; private VisualInterruptionFilter mNeedsRedactionFilter; private VisualInterruptionCondition mPanelsDisabledCondition; private CommandQueue mCommandQueue; private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private final ShadeController mShadeController = mock(ShadeController.class); private final ShadeController mShadeController = mock(ShadeController.class); private final NotificationAlertsInteractor mNotificationAlertsInteractor = private final NotificationAlertsInteractor mNotificationAlertsInteractor = mock(NotificationAlertsInteractor.class); mock(NotificationAlertsInteractor.class); private final KeyguardStateController mKeyguardStateController = private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); mock(KeyguardStateController.class); private final InitController mInitController = new InitController(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @Before @Before public void setup() { public void setup() { mMetricsLogger = new FakeMetricsLogger(); mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); mDependency.injectTestDependency(StatusBarStateController.class, mDependency.injectTestDependency(StatusBarStateController.class, mock(SysuiStatusBarStateController.class)); mock(SysuiStatusBarStateController.class)); Loading @@ -95,15 +113,182 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); NotificationShadeWindowView notificationShadeWindowView = when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true); } @Test public void testInit_refactorDisabled() { ensureRefactorDisabledState(); } @Test public void testInit_refactorEnabled() { ensureRefactorEnabledState(); } @Test public void testNoSuppressHeadsUp_default_refactorDisabled() { ensureRefactorDisabledState(); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testNoSuppressHeadsUp_default_refactorEnabled() { ensureRefactorEnabledState(); assertFalse(mAlertsDisabledCondition.shouldSuppress()); assertFalse(mVrModeCondition.shouldSuppress()); assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry())); assertFalse(mAlertsDisabledCondition.shouldSuppress()); } @Test public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() { ensureRefactorDisabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() { ensureRefactorEnabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", mPanelsDisabledCondition.shouldSuppress()); } @Test public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() { ensureRefactorDisabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade disabled", mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() { ensureRefactorEnabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade disabled", mPanelsDisabledCondition.shouldSuppress()); } @Test public void testPanelsDisabledConditionSuppressesPeek() { ensureRefactorEnabledState(); final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertFalse(types.contains(BUBBLE)); } @Test public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() { ensureRefactorDisabledState(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry())); } @Test public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() { ensureRefactorEnabledState(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry())); final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertFalse(types.contains(BUBBLE)); } @Test public void testSuppressInterruptions_vrMode_refactorDisabled() { ensureRefactorDisabledState(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry())); } @Test public void testSuppressInterruptions_vrMode_refactorEnabled() { ensureRefactorEnabledState(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress()); final Set<VisualInterruptionType> types = mVrModeCondition.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertTrue(types.contains(BUBBLE)); } @Test public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() { ensureRefactorDisabledState(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(createNotificationEntry())); } @Test public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() { ensureRefactorEnabledState(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mAlertsDisabledCondition.shouldSuppress()); final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes(); assertTrue(types.contains(PEEK)); assertTrue(types.contains(PULSE)); assertTrue(types.contains(BUBBLE)); } private void createPresenter() { final ShadeViewController shadeViewController = mock(ShadeViewController.class); final NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); mock(NotificationShadeWindowView.class); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); NotificationStackScrollLayoutController stackScrollLayoutController = NotificationStackScrollLayoutController stackScrollLayoutController = mock(NotificationStackScrollLayoutController.class); mock(NotificationStackScrollLayoutController.class); when(stackScrollLayoutController.getView()).thenReturn( when(stackScrollLayoutController.getView()).thenReturn( mock(NotificationStackScrollLayout.class)); mock(NotificationStackScrollLayout.class)); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); ShadeViewController shadeViewController = mock(ShadeViewController.class); final InitController initController = new InitController(); mStatusBarNotificationPresenter = new StatusBarNotificationPresenter( mStatusBarNotificationPresenter = new StatusBarNotificationPresenter( mContext, mContext, shadeViewController, shadeViewController, Loading @@ -125,110 +310,76 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotifShadeEventSource.class), mock(NotifShadeEventSource.class), mock(NotificationMediaManager.class), mock(NotificationMediaManager.class), mock(NotificationGutsManager.class), mock(NotificationGutsManager.class), mInitController, initController, mVisualInterruptionDecisionProvider, mVisualInterruptionDecisionProvider, mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.Callback.class), mock(NotificationRemoteInputManager.Callback.class), mock(NotificationListContainer.class)); mock(NotificationListContainer.class)); mInitController.executePostInitTasks(); ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = initController.executePostInitTasks(); ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture()); mInterruptSuppressor = suppressorCaptor.getValue(); } } @Test private void verifyAndCaptureSuppressors() { public void testNoSuppressHeadsUp_default() { mInterruptSuppressor = null; Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") .setTag("a") .setNotification(n) .build(); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor = ArgumentCaptor.forClass(VisualInterruptionCondition.class); verify(mVisualInterruptionDecisionProvider, times(3)).addCondition( conditionCaptor.capture()); final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues(); mAlertsDisabledCondition = conditions.get(0); mVrModeCondition = conditions.get(1); mPanelsDisabledCondition = conditions.get(2); final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor = ArgumentCaptor.forClass(VisualInterruptionFilter.class); verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture()); mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue(); } } @Test private void verifyAndCaptureLegacySuppressor() { public void testSuppressHeadsUp_disabledStatusBar() { mAlertsDisabledCondition = null; Notification n = new Notification.Builder(getContext(), "a").build(); mVrModeCondition = null; NotificationEntry entry = new NotificationEntryBuilder() mNeedsRedactionFilter = null; .setPkg("a") mPanelsDisabledCondition = null; .setOpPkg("a") .setTag("a") .setNotification(n) .build(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = mInterruptSuppressor.suppressAwakeHeadsUp(entry)); ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture()); mInterruptSuppressor = suppressorCaptor.getValue(); } } @Test private void ensureRefactorDisabledState() { public void testSuppressHeadsUp_disabledNotificationShade() { mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); Notification n = new Notification.Builder(getContext(), "a").build(); createPresenter(); NotificationEntry entry = new NotificationEntryBuilder() verifyAndCaptureLegacySuppressor(); .setPkg("a") } .setOpPkg("a") .setTag("a") .setNotification(n) .build(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade " private void ensureRefactorEnabledState() { + "disabled", mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); mInterruptSuppressor.suppressAwakeHeadsUp(entry)); createPresenter(); verifyAndCaptureSuppressors(); } } @Test private NotificationEntry createNotificationEntry() { public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard() { return new NotificationEntryBuilder() Notification n = new Notification.Builder(getContext(), "a") .setFullScreenIntent(mock(PendingIntent.class), true) .build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setPkg("a") .setOpPkg("a") .setOpPkg("a") .setTag("a") .setTag("a") .setNotification(n) .setNotification(new Notification.Builder(getContext(), "a").build()) .build(); .build(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); } } @Test private NotificationEntry createFsiNotificationEntry() { public void testSuppressInterruptions_vrMode() { final Notification notification = new Notification.Builder(getContext(), "a") Notification n = new Notification.Builder(getContext(), "a").build(); .setFullScreenIntent(mock(PendingIntent.class), true) NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") .setTag("a") .setNotification(n) .build(); .build(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mInterruptSuppressor.suppressAwakeInterruptions(entry)); } @Test return new NotificationEntryBuilder() public void testSuppressInterruptions_statusBarAlertsDisabled() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setPkg("a") .setOpPkg("a") .setOpPkg("a") .setTag("a") .setTag("a") .setNotification(n) .setNotification(notification) .build(); .build(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(entry)); } } } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt +10 −0 Original line number Original line Diff line number Diff line Loading @@ -73,6 +73,11 @@ abstract class VisualInterruptionCondition( override val uiEventId: UiEventEnum? = null, override val uiEventId: UiEventEnum? = null, override val eventLogData: EventLogData? = null override val eventLogData: EventLogData? = null ) : VisualInterruptionSuppressor { ) : VisualInterruptionSuppressor { constructor( types: Set<VisualInterruptionType>, reason: String ) : this(types, reason, /* uiEventId = */ null) /** @return true if these interruptions should be suppressed right now. */ /** @return true if these interruptions should be suppressed right now. */ abstract fun shouldSuppress(): Boolean abstract fun shouldSuppress(): Boolean } } Loading @@ -84,6 +89,11 @@ abstract class VisualInterruptionFilter( override val uiEventId: UiEventEnum? = null, override val uiEventId: UiEventEnum? = null, override val eventLogData: EventLogData? = null override val eventLogData: EventLogData? = null ) : VisualInterruptionSuppressor { ) : VisualInterruptionSuppressor { constructor( types: Set<VisualInterruptionType>, reason: String ) : this(types, reason, /* uiEventId = */ null) /** /** * @param entry the notification to consider suppressing * @param entry the notification to consider suppressing * @return true if these interruptions should be suppressed for this notification right now * @return true if these interruptions should be suppressed for this notification right now Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +68 −1 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,9 @@ package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE; import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED; import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED; import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; Loading @@ -29,6 +32,8 @@ import android.util.Log; import android.util.Slog; import android.util.Slog; import android.view.View; import android.view.View; import androidx.annotation.NonNull; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; import com.android.systemui.InitController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton; Loading @@ -54,7 +59,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; Loading @@ -63,6 +71,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Set; import javax.inject.Inject; import javax.inject.Inject; @SysUISingleton @SysUISingleton Loading Loading @@ -163,7 +173,14 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu initController.addPostInitTask(() -> { initController.addPostInitTask(() -> { mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); if (VisualInterruptionRefactor.isEnabled()) { visualInterruptionDecisionProvider.addCondition(mAlertsDisabledCondition); visualInterruptionDecisionProvider.addCondition(mVrModeCondition); visualInterruptionDecisionProvider.addFilter(mNeedsRedactionFilter); visualInterruptionDecisionProvider.addCondition(mPanelsDisabledCondition); } else { visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor); visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor); } mLockscreenUserManager.setUpWithPresenter(this); mLockscreenUserManager.setUpWithPresenter(this); mGutsManager.setUpWithPresenter( mGutsManager.setUpWithPresenter( this, mNotifListContainer, mOnSettingsClickListener); this, mNotifListContainer, mOnSettingsClickListener); Loading Loading @@ -306,4 +323,54 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); } } }; }; private final VisualInterruptionCondition mAlertsDisabledCondition = new VisualInterruptionCondition(Set.of(PEEK, PULSE, BUBBLE), "notification alerts disabled") { @Override public boolean shouldSuppress() { return !mNotificationAlertsInteractor.areNotificationAlertsEnabled(); } }; private final VisualInterruptionCondition mVrModeCondition = new VisualInterruptionCondition(Set.of(PEEK, BUBBLE), "device is in VR mode") { @Override public boolean shouldSuppress() { return isDeviceInVrMode(); } }; private final VisualInterruptionFilter mNeedsRedactionFilter = new VisualInterruptionFilter(Set.of(PEEK), "needs redaction on public lockscreen") { @Override public boolean shouldSuppress(@NonNull NotificationEntry entry) { if (!mKeyguardStateController.isOccluded()) { return false; } if (!mLockscreenUserManager.needsRedaction(entry)) { return false; } final int currentUserId = mLockscreenUserManager.getCurrentUserId(); final boolean currentUserPublic = mLockscreenUserManager.isLockscreenPublicMode( currentUserId); final int notificationUserId = entry.getSbn().getUserId(); final boolean notificationUserPublic = mLockscreenUserManager.isLockscreenPublicMode(notificationUserId); // TODO(b/135046837): we can probably relax this with dynamic privacy return currentUserPublic || notificationUserPublic; } }; private final VisualInterruptionCondition mPanelsDisabledCondition = new VisualInterruptionCondition(Set.of(PEEK), "disabled panel") { @Override public boolean shouldSuppress() { return !mCommandQueue.panelsEnabled(); } }; } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +236 −85 Original line number Original line Diff line number Diff line Loading @@ -16,22 +16,28 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.Notification; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.StatusBarManager; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.systemui.Flags; import com.android.systemui.InitController; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; Loading @@ -55,7 +61,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; Loading @@ -64,10 +73,14 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor; import java.util.List; import java.util.Set; @SmallTest @SmallTest @RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class) @RunWithLooper() @RunWithLooper() Loading @@ -76,18 +89,23 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider = private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider = mock(VisualInterruptionDecisionProvider.class); mock(VisualInterruptionDecisionProvider.class); private NotificationInterruptSuppressor mInterruptSuppressor; private NotificationInterruptSuppressor mInterruptSuppressor; private VisualInterruptionCondition mAlertsDisabledCondition; private VisualInterruptionCondition mVrModeCondition; private VisualInterruptionFilter mNeedsRedactionFilter; private VisualInterruptionCondition mPanelsDisabledCondition; private CommandQueue mCommandQueue; private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private final ShadeController mShadeController = mock(ShadeController.class); private final ShadeController mShadeController = mock(ShadeController.class); private final NotificationAlertsInteractor mNotificationAlertsInteractor = private final NotificationAlertsInteractor mNotificationAlertsInteractor = mock(NotificationAlertsInteractor.class); mock(NotificationAlertsInteractor.class); private final KeyguardStateController mKeyguardStateController = private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); mock(KeyguardStateController.class); private final InitController mInitController = new InitController(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @Before @Before public void setup() { public void setup() { mMetricsLogger = new FakeMetricsLogger(); mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); mDependency.injectTestDependency(StatusBarStateController.class, mDependency.injectTestDependency(StatusBarStateController.class, mock(SysuiStatusBarStateController.class)); mock(SysuiStatusBarStateController.class)); Loading @@ -95,15 +113,182 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); NotificationShadeWindowView notificationShadeWindowView = when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true); } @Test public void testInit_refactorDisabled() { ensureRefactorDisabledState(); } @Test public void testInit_refactorEnabled() { ensureRefactorEnabledState(); } @Test public void testNoSuppressHeadsUp_default_refactorDisabled() { ensureRefactorDisabledState(); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testNoSuppressHeadsUp_default_refactorEnabled() { ensureRefactorEnabledState(); assertFalse(mAlertsDisabledCondition.shouldSuppress()); assertFalse(mVrModeCondition.shouldSuppress()); assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry())); assertFalse(mAlertsDisabledCondition.shouldSuppress()); } @Test public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() { ensureRefactorDisabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() { ensureRefactorEnabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", mPanelsDisabledCondition.shouldSuppress()); } @Test public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() { ensureRefactorDisabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade disabled", mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() { ensureRefactorEnabledState(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade disabled", mPanelsDisabledCondition.shouldSuppress()); } @Test public void testPanelsDisabledConditionSuppressesPeek() { ensureRefactorEnabledState(); final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertFalse(types.contains(BUBBLE)); } @Test public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() { ensureRefactorDisabledState(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry())); } @Test public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() { ensureRefactorEnabledState(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry())); final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertFalse(types.contains(BUBBLE)); } @Test public void testSuppressInterruptions_vrMode_refactorDisabled() { ensureRefactorDisabledState(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry())); } @Test public void testSuppressInterruptions_vrMode_refactorEnabled() { ensureRefactorEnabledState(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress()); final Set<VisualInterruptionType> types = mVrModeCondition.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); assertTrue(types.contains(BUBBLE)); } @Test public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() { ensureRefactorDisabledState(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(createNotificationEntry())); } @Test public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() { ensureRefactorEnabledState(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mAlertsDisabledCondition.shouldSuppress()); final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes(); assertTrue(types.contains(PEEK)); assertTrue(types.contains(PULSE)); assertTrue(types.contains(BUBBLE)); } private void createPresenter() { final ShadeViewController shadeViewController = mock(ShadeViewController.class); final NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); mock(NotificationShadeWindowView.class); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); NotificationStackScrollLayoutController stackScrollLayoutController = NotificationStackScrollLayoutController stackScrollLayoutController = mock(NotificationStackScrollLayoutController.class); mock(NotificationStackScrollLayoutController.class); when(stackScrollLayoutController.getView()).thenReturn( when(stackScrollLayoutController.getView()).thenReturn( mock(NotificationStackScrollLayout.class)); mock(NotificationStackScrollLayout.class)); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); ShadeViewController shadeViewController = mock(ShadeViewController.class); final InitController initController = new InitController(); mStatusBarNotificationPresenter = new StatusBarNotificationPresenter( mStatusBarNotificationPresenter = new StatusBarNotificationPresenter( mContext, mContext, shadeViewController, shadeViewController, Loading @@ -125,110 +310,76 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotifShadeEventSource.class), mock(NotifShadeEventSource.class), mock(NotificationMediaManager.class), mock(NotificationMediaManager.class), mock(NotificationGutsManager.class), mock(NotificationGutsManager.class), mInitController, initController, mVisualInterruptionDecisionProvider, mVisualInterruptionDecisionProvider, mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.Callback.class), mock(NotificationRemoteInputManager.Callback.class), mock(NotificationListContainer.class)); mock(NotificationListContainer.class)); mInitController.executePostInitTasks(); ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = initController.executePostInitTasks(); ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture()); mInterruptSuppressor = suppressorCaptor.getValue(); } } @Test private void verifyAndCaptureSuppressors() { public void testNoSuppressHeadsUp_default() { mInterruptSuppressor = null; Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") .setTag("a") .setNotification(n) .build(); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor = ArgumentCaptor.forClass(VisualInterruptionCondition.class); verify(mVisualInterruptionDecisionProvider, times(3)).addCondition( conditionCaptor.capture()); final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues(); mAlertsDisabledCondition = conditions.get(0); mVrModeCondition = conditions.get(1); mPanelsDisabledCondition = conditions.get(2); final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor = ArgumentCaptor.forClass(VisualInterruptionFilter.class); verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture()); mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue(); } } @Test private void verifyAndCaptureLegacySuppressor() { public void testSuppressHeadsUp_disabledStatusBar() { mAlertsDisabledCondition = null; Notification n = new Notification.Builder(getContext(), "a").build(); mVrModeCondition = null; NotificationEntry entry = new NotificationEntryBuilder() mNeedsRedactionFilter = null; .setPkg("a") mPanelsDisabledCondition = null; .setOpPkg("a") .setTag("a") .setNotification(n) .build(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress heads up while disabled", final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = mInterruptSuppressor.suppressAwakeHeadsUp(entry)); ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture()); mInterruptSuppressor = suppressorCaptor.getValue(); } } @Test private void ensureRefactorDisabledState() { public void testSuppressHeadsUp_disabledNotificationShade() { mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); Notification n = new Notification.Builder(getContext(), "a").build(); createPresenter(); NotificationEntry entry = new NotificationEntryBuilder() verifyAndCaptureLegacySuppressor(); .setPkg("a") } .setOpPkg("a") .setTag("a") .setNotification(n) .build(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertTrue("The panel should suppress interruptions while notification shade " private void ensureRefactorEnabledState() { + "disabled", mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); mInterruptSuppressor.suppressAwakeHeadsUp(entry)); createPresenter(); verifyAndCaptureSuppressors(); } } @Test private NotificationEntry createNotificationEntry() { public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard() { return new NotificationEntryBuilder() Notification n = new Notification.Builder(getContext(), "a") .setFullScreenIntent(mock(PendingIntent.class), true) .build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setPkg("a") .setOpPkg("a") .setOpPkg("a") .setTag("a") .setTag("a") .setNotification(n) .setNotification(new Notification.Builder(getContext(), "a").build()) .build(); .build(); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); } } @Test private NotificationEntry createFsiNotificationEntry() { public void testSuppressInterruptions_vrMode() { final Notification notification = new Notification.Builder(getContext(), "a") Notification n = new Notification.Builder(getContext(), "a").build(); .setFullScreenIntent(mock(PendingIntent.class), true) NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") .setTag("a") .setNotification(n) .build(); .build(); mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mInterruptSuppressor.suppressAwakeInterruptions(entry)); } @Test return new NotificationEntryBuilder() public void testSuppressInterruptions_statusBarAlertsDisabled() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setPkg("a") .setOpPkg("a") .setOpPkg("a") .setTag("a") .setTag("a") .setNotification(n) .setNotification(notification) .build(); .build(); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(entry)); } } } }