diff options
11 files changed, 752 insertions, 1064 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 9613f76c2b48..2c488e3a7242 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -28,7 +28,6 @@ import com.android.systemui.log.logcatLogBuffer import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips -import com.android.systemui.statusbar.notification.HeadsUpManagerPhone import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -52,6 +51,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback import com.android.systemui.statusbar.phone.NotificationGroupTestHelper +import com.android.systemui.statusbar.policy.BaseHeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor @@ -101,7 +101,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) - private val headsUpManager: HeadsUpManagerPhone = mock() + private val headsUpManager: BaseHeadsUpManager = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt index 9a862fc6a18f..c5eed7365d92 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt @@ -24,11 +24,19 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer +import com.android.systemui.plugins.statusbar.statusBarStateController +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun +import com.android.systemui.statusbar.phone.keyguardBypassController import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry +import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.settings.FakeGlobalSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -48,6 +56,7 @@ import org.mockito.junit.MockitoRule @RunWith(AndroidJUnit4::class) @EnableFlags(NotificationThrottleHun.FLAG_NAME) class AvalancheControllerTest : SysuiTestCase() { + private val kosmos = testKosmos() // For creating mocks @get:Rule var rule: MockitoRule = MockitoJUnit.rule() @@ -61,7 +70,6 @@ class AvalancheControllerTest : SysuiTestCase() { @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null private val mUiEventLoggerFake = UiEventLoggerFake() @Mock private lateinit var mHeadsUpManagerLogger: HeadsUpManagerLogger - @Mock private lateinit var mBgHandler: Handler private val mLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer())) @@ -76,26 +84,33 @@ class AvalancheControllerTest : SysuiTestCase() { Mockito.`when`( mAccessibilityMgr!!.getRecommendedTimeoutMillis( ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .then { i: InvocationOnMock -> i.getArgument(0) } // Initialize AvalancheController and TestableHeadsUpManager during setUp instead of // declaration, where mocks are null - mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake, - mHeadsUpManagerLogger, mBgHandler) + mAvalancheController = + AvalancheController(dumpManager, mUiEventLoggerFake, mHeadsUpManagerLogger, mBgHandler) testableHeadsUpManager = TestableHeadsUpManager( mContext, mLogger, + kosmos.statusBarStateController, + kosmos.keyguardBypassController, + GroupMembershipManagerImpl(), + kosmos.visualStabilityProvider, + kosmos.configurationController, mExecutor, mGlobalSettings, mSystemClock, mAccessibilityMgr, mUiEventLoggerFake, - mAvalancheController + JavaAdapter(kosmos.testScope), + kosmos.shadeInteractor, + mAvalancheController, ) } @@ -270,7 +285,6 @@ class AvalancheControllerTest : SysuiTestCase() { assertThat(mAvalancheController.headsUpEntryShowing).isEqualTo(nextEntry) } - @Test fun testDelete_deleteSecondToLastEntry_showingEntryKeyBecomesPreviousHunKey() { mAvalancheController.previousHunKey = "" @@ -305,7 +319,7 @@ class AvalancheControllerTest : SysuiTestCase() { mAvalancheController.delete(showingEntry, runnableMock!!, "testLabel") // Next entry is shown - assertThat(mAvalancheController.previousHunKey).isEqualTo(""); + assertThat(mAvalancheController.previousHunKey).isEqualTo("") } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 89aa670e5f0c..abb3e6e0c1f2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -35,6 +35,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; + import android.app.Notification; import android.app.PendingIntent; import android.app.Person; @@ -49,15 +51,21 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.res.R; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.time.FakeSystemClock; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,7 +81,10 @@ import java.util.List; @SmallTest @TestableLooper.RunWithLooper @RunWith(ParameterizedAndroidJunit4.class) +// TODO(b/378142453): Merge this with BaseHeadsUpManagerTest. public class BaseHeadsUpManagerTest extends SysuiTestCase { + protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + @Rule public MockitoRule rule = MockitoJUnit.rule(); @@ -85,6 +96,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer())); @Mock private Handler mBgHandler; @Mock private DumpManager dumpManager; + @Mock private ShadeInteractor mShadeInteractor; private AvalancheController mAvalancheController; @Mock private AccessibilityManagerWrapper mAccessibilityMgr; @@ -108,8 +120,22 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { } private BaseHeadsUpManager createHeadsUpManager() { - return new TestableHeadsUpManager(mContext, mLogger, mExecutor, mGlobalSettings, - mSystemClock, mAccessibilityMgr, mUiEventLoggerFake, mAvalancheController); + return new TestableHeadsUpManager( + mContext, + mLogger, + mKosmos.getStatusBarStateController(), + mKosmos.getKeyguardBypassController(), + new GroupMembershipManagerImpl(), + mKosmos.getVisualStabilityProvider(), + mKosmos.getConfigurationController(), + mExecutor, + mGlobalSettings, + mSystemClock, + mAccessibilityMgr, + mUiEventLoggerFake, + new JavaAdapter(mKosmos.getTestScope()), + mShadeInteractor, + mAvalancheController); } private NotificationEntry createStickyEntry(int id) { @@ -152,6 +178,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { super.SysuiSetup(); mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mLogger, mBgHandler); + when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(true)); + when(mKosmos.getKeyguardBypassController().getBypassEnabled()).thenReturn(false); } @Test @@ -298,46 +326,6 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry)); } - @Test - public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); - final NotificationEntry notifEntry = - HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext); - - // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned - hum.showNotification(notifEntry); - - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( - notifEntry.getKey()); - headsUpEntry.mWasUnpinned = false; - - assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry)); - } - - @Test - public void testShouldHeadsUpBecomePinned_wasUnpinned_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); - final NotificationEntry notifEntry = - HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext); - - // Add notifEntry to ANM mAlertEntries map and make it unpinned - hum.showNotification(notifEntry); - - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( - notifEntry.getKey()); - headsUpEntry.mWasUnpinned = true; - - assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry)); - } - - @Test - public void testShouldHeadsUpBecomePinned_noFSI_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); - final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - - assertFalse(hum.shouldHeadsUpBecomePinned(entry)); - } - @Test public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt index 1915e8ede7e7..8ebdbaaa6bf0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.statusbar.policy -import android.content.Context import android.os.Handler import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization @@ -26,27 +25,19 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.flags.andSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.FakeStatusBarStateController import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.ConfigurationControllerImpl -import com.android.systemui.statusbar.notification.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.testKosmos -import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.kotlin.JavaAdapter -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.settings.GlobalSettings -import com.android.systemui.util.time.SystemClock import com.google.common.truth.Truth.assertThat import junit.framework.Assert import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -78,7 +69,7 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @Mock private lateinit var mVSProvider: VisualStabilityProvider - @Mock private lateinit var mStatusBarStateController: StatusBarStateController + val statusBarStateController = FakeStatusBarStateController() @Mock private lateinit var mBypassController: KeyguardBypassController @@ -97,61 +88,16 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @Mock private lateinit var mBgHandler: Handler - private class TestableHeadsUpManagerPhone( - context: Context, - headsUpManagerLogger: HeadsUpManagerLogger, - groupManager: GroupMembershipManager, - visualStabilityProvider: VisualStabilityProvider, - statusBarStateController: StatusBarStateController, - keyguardBypassController: KeyguardBypassController, - configurationController: ConfigurationController, - globalSettings: GlobalSettings, - systemClock: SystemClock, - executor: DelayableExecutor, - accessibilityManagerWrapper: AccessibilityManagerWrapper, - uiEventLogger: UiEventLogger, - javaAdapter: JavaAdapter, - shadeInteractor: ShadeInteractor, - avalancheController: AvalancheController - ) : - HeadsUpManagerPhone( - context, - headsUpManagerLogger, - statusBarStateController, - keyguardBypassController, - groupManager, - visualStabilityProvider, - configurationController, - mockExecutorHandler(executor), - globalSettings, - systemClock, - executor, - accessibilityManagerWrapper, - uiEventLogger, - javaAdapter, - shadeInteractor, - avalancheController - ) { - init { - mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME - mAutoDismissTime = TEST_AUTO_DISMISS_TIME - } - - /** Wrapper for [BaseHeadsUpManager.shouldHeadsUpBecomePinned] for testing */ - fun shouldHeadsUpBecomePinnedWrapper(entry: NotificationEntry): Boolean { - return shouldHeadsUpBecomePinned(entry) - } - } - - private fun createHeadsUpManagerPhone(): HeadsUpManagerPhone { - return TestableHeadsUpManagerPhone( + private fun createHeadsUpManagerPhone(): BaseHeadsUpManager { + return BaseHeadsUpManager( mContext, mHeadsUpManagerLogger, + statusBarStateController, + mBypassController, mGroupManager, mVSProvider, - mStatusBarStateController, - mBypassController, mConfigurationController, + mockExecutorHandler(mExecutor), mGlobalSettings, mSystemClock, mExecutor, @@ -159,20 +105,22 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager mUiEventLogger, mJavaAdapter, mShadeInteractor, - mAvalancheController + mAvalancheController, ) } @Before fun setUp() { whenever(mShadeInteractor.isAnyExpanded).thenReturn(MutableStateFlow(false)) + whenever(mShadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false)) + whenever(mBypassController.bypassEnabled).thenReturn(false) whenever(mVSProvider.isReorderingAllowed).thenReturn(true) val accessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper::class.java) whenever( accessibilityMgr.getRecommendedTimeoutMillis( ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .thenReturn(TEST_AUTO_DISMISS_TIME) @@ -205,7 +153,7 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager hmp.removeNotification( entry.key, /* releaseImmediately= */ false, - /* reason= */ "swipe out" + /* reason= */ "swipe out", ) Assert.assertTrue(removedImmediately) Assert.assertFalse(hmp.isHeadsUpEntry(entry.key)) @@ -245,6 +193,7 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME + hmp.mExtensionTime / 2).toLong()) Assert.assertTrue(hmp.isHeadsUpEntry(entry.key)) } + @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) fun testShowNotification_removeWhenReorderingAllowedTrue() { @@ -253,7 +202,7 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) hmp.showNotification(notifEntry) - assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue(); + assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue() } @Test @@ -264,7 +213,7 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) hmp.showNotification(notifEntry) - assertThat(notifEntry.isSeenInShade).isTrue(); + assertThat(notifEntry.isSeenInShade).isTrue() } @Test @@ -275,197 +224,136 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) hmp.showNotification(notifEntry) - assertThat(notifEntry.isSeenInShade).isFalse(); + assertThat(notifEntry.isSeenInShade).isFalse() } @Test + fun testShouldHeadsUpBecomePinned_noFSI_false() = + testScope.runTest { + val hum = createHeadsUpManagerPhone() + statusBarStateController.setState(StatusBarState.KEYGUARD) + + val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) + + Assert.assertFalse(hum.shouldHeadsUpBecomePinned(entry)) + } + + @Test + fun testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() = + testScope.runTest { + val hum = createHeadsUpManagerPhone() + statusBarStateController.setState(StatusBarState.KEYGUARD) + + val notifEntry = + HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext) + + // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned + hum.showNotification(notifEntry) + + val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key) + headsUpEntry!!.mWasUnpinned = false + + Assert.assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry)) + } + + @Test + fun testShouldHeadsUpBecomePinned_wasUnpinned_false() = + testScope.runTest { + val hum = createHeadsUpManagerPhone() + statusBarStateController.setState(StatusBarState.KEYGUARD) + + val notifEntry = + HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext) + + // Add notifEntry to ANM mAlertEntries map and make it unpinned + hum.showNotification(notifEntry) + + val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key) + headsUpEntry!!.mWasUnpinned = true + + Assert.assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry)) + } + + @Test fun shouldHeadsUpBecomePinned_shadeNotExpanded_true() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() whenever(mShadeInteractor.isAnyFullyExpanded).thenReturn(MutableStateFlow(false)) - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(StatusBarState.SHADE) runCurrent() // THEN - Assert.assertTrue(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertTrue(hmp.shouldHeadsUpBecomePinned(entry)) } @Test fun shouldHeadsUpBecomePinned_shadeLocked_false() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(StatusBarState.SHADE_LOCKED) runCurrent() // THEN - Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry)) } @Test fun shouldHeadsUpBecomePinned_shadeUnknown_false() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(1207) runCurrent() // THEN - Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry)) } @Test fun shouldHeadsUpBecomePinned_keyguardWithBypassOn_true() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() whenever(mBypassController.bypassEnabled).thenReturn(true) - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(StatusBarState.KEYGUARD) runCurrent() // THEN - Assert.assertTrue(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertTrue(hmp.shouldHeadsUpBecomePinned(entry)) } @Test fun shouldHeadsUpBecomePinned_keyguardWithBypassOff_false() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() whenever(mBypassController.bypassEnabled).thenReturn(false) - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(StatusBarState.KEYGUARD) runCurrent() // THEN - Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry)) } @Test fun shouldHeadsUpBecomePinned_shadeExpanded_false() = testScope.runTest { // GIVEN - val statusBarStateController = FakeStatusBarStateController() whenever(mShadeInteractor.isAnyExpanded).thenReturn(MutableStateFlow(true)) - val hmp = - TestableHeadsUpManagerPhone( - mContext, - mHeadsUpManagerLogger, - mGroupManager, - mVSProvider, - statusBarStateController, - mBypassController, - mConfigurationController, - mGlobalSettings, - mSystemClock, - mExecutor, - mAccessibilityManagerWrapper, - mUiEventLogger, - mJavaAdapter, - mShadeInteractor, - mAvalancheController - ) + val hmp = createHeadsUpManagerPhone() val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) statusBarStateController.setState(StatusBarState.SHADE) runCurrent() // THEN - Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry)) + Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry)) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java index 3efabd743141..59987f413ad6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler; import static org.mockito.Mockito.spy; @@ -28,8 +27,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.logging.UiEventLogger; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.SystemClock; @@ -37,16 +42,39 @@ class TestableHeadsUpManager extends BaseHeadsUpManager { private HeadsUpEntry mLastCreatedEntry; - TestableHeadsUpManager(Context context, + TestableHeadsUpManager( + Context context, HeadsUpManagerLogger logger, + StatusBarStateController statusBarStateController, + KeyguardBypassController bypassController, + GroupMembershipManager groupMembershipManager, + VisualStabilityProvider visualStabilityProvider, + ConfigurationController configurationController, DelayableExecutor executor, GlobalSettings globalSettings, SystemClock systemClock, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger, + JavaAdapter javaAdapter, + ShadeInteractor shadeInteractor, AvalancheController avalancheController) { - super(context, logger, mockExecutorHandler(executor), globalSettings, systemClock, - executor, accessibilityManagerWrapper, uiEventLogger, avalancheController); + super( + context, + logger, + statusBarStateController, + bypassController, + groupMembershipManager, + visualStabilityProvider, + configurationController, + mockExecutorHandler(executor), + globalSettings, + systemClock, + executor, + accessibilityManagerWrapper, + uiEventLogger, + javaAdapter, + shadeInteractor, + avalancheController); mTouchAcceptanceDelay = BaseHeadsUpManagerTest.TEST_TOUCH_ACCEPTANCE_TIME; mMinimumDisplayTime = BaseHeadsUpManagerTest.TEST_MINIMUM_DISPLAY_TIME; @@ -61,11 +89,6 @@ class TestableHeadsUpManager extends BaseHeadsUpManager { return mLastCreatedEntry; } - @Override - public int getContentFlag() { - return FLAG_CONTENT_VIEW_CONTRACTED; - } - // The following are only implemented by HeadsUpManagerPhone. If you need them, use that. @Override public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java deleted file mode 100644 index 0299ebcdf043..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright (C) 2024 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.statusbar.notification; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Region; -import android.os.Handler; -import android.util.ArrayMap; -import android.util.Pools; - -import androidx.collection.ArraySet; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener; -import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener; -import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; -import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; -import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository; -import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; -import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; -import com.android.systemui.statusbar.policy.AnimationStateHandler; -import com.android.systemui.statusbar.policy.AvalancheController; -import com.android.systemui.statusbar.policy.BaseHeadsUpManager; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; -import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange; -import com.android.systemui.util.concurrency.DelayableExecutor; -import com.android.systemui.util.kotlin.JavaAdapter; -import com.android.systemui.util.settings.GlobalSettings; -import com.android.systemui.util.time.SystemClock; - -import kotlinx.coroutines.flow.Flow; -import kotlinx.coroutines.flow.MutableStateFlow; -import kotlinx.coroutines.flow.StateFlow; -import kotlinx.coroutines.flow.StateFlowKt; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.Stack; - -import javax.inject.Inject; - -/** A implementation of HeadsUpManager for phone. */ -@SysUISingleton -public class HeadsUpManagerPhone extends BaseHeadsUpManager implements - HeadsUpRepository, OnHeadsUpChangedListener { - private static final String TAG = "HeadsUpManagerPhone"; - - @VisibleForTesting - public final int mExtensionTime; - private final KeyguardBypassController mBypassController; - private final GroupMembershipManager mGroupMembershipManager; - private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>(); - private final VisualStabilityProvider mVisualStabilityProvider; - - private AvalancheController mAvalancheController; - - // TODO(b/328393698) move the topHeadsUpRow logic to an interactor - private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow = - StateFlowKt.MutableStateFlow(null); - private final MutableStateFlow<Set<HeadsUpRowRepository>> mHeadsUpNotificationRows = - StateFlowKt.MutableStateFlow(new HashSet<>()); - private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway = - StateFlowKt.MutableStateFlow(false); - private boolean mReleaseOnExpandFinish; - private boolean mTrackingHeadsUp; - private final HashSet<String> mSwipedOutKeys = new HashSet<>(); - private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>(); - @VisibleForTesting - public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed - = new ArraySet<>(); - private boolean mIsShadeOrQsExpanded; - private boolean mIsQsExpanded; - private int mStatusBarState; - private AnimationStateHandler mAnimationStateHandler; - - private int mHeadsUpInset; - - // Used for determining the region for touch interaction - private final Region mTouchableRegion = new Region(); - - private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() { - private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>(); - - @Override - public HeadsUpEntryPhone acquire() { - NotificationThrottleHun.assertInLegacyMode(); - if (!mPoolObjects.isEmpty()) { - return mPoolObjects.pop(); - } - return new HeadsUpEntryPhone(); - } - - @Override - public boolean release(@NonNull HeadsUpEntryPhone instance) { - NotificationThrottleHun.assertInLegacyMode(); - mPoolObjects.push(instance); - return true; - } - }; - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: - @Inject - public HeadsUpManagerPhone( - @NonNull final Context context, - HeadsUpManagerLogger logger, - StatusBarStateController statusBarStateController, - KeyguardBypassController bypassController, - GroupMembershipManager groupMembershipManager, - VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController, - @Main Handler handler, - GlobalSettings globalSettings, - SystemClock systemClock, - @Main DelayableExecutor executor, - AccessibilityManagerWrapper accessibilityManagerWrapper, - UiEventLogger uiEventLogger, - JavaAdapter javaAdapter, - ShadeInteractor shadeInteractor, - AvalancheController avalancheController) { - super(context, logger, handler, globalSettings, systemClock, executor, - accessibilityManagerWrapper, uiEventLogger, avalancheController); - Resources resources = mContext.getResources(); - mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); - statusBarStateController.addCallback(mStatusBarStateListener); - mBypassController = bypassController; - mGroupMembershipManager = groupMembershipManager; - mVisualStabilityProvider = visualStabilityProvider; - mAvalancheController = avalancheController; - updateResources(); - configurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onDensityOrFontScaleChanged() { - updateResources(); - } - - @Override - public void onThemeChanged() { - updateResources(); - } - }); - javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), - this::onShadeOrQsExpanded); - if (SceneContainerFlag.isEnabled()) { - javaAdapter.alwaysCollectFlow(shadeInteractor.isQsExpanded(), - this::onQsExpanded); - } - if (NotificationThrottleHun.isEnabled()) { - mVisualStabilityProvider.addPersistentReorderingBannedListener( - mOnReorderingBannedListener); - mVisualStabilityProvider.addPersistentReorderingAllowedListener( - mOnReorderingAllowedListener); - } - } - - public void setAnimationStateHandler(AnimationStateHandler handler) { - mAnimationStateHandler = handler; - } - - private void updateResources() { - Resources resources = mContext.getResources(); - mHeadsUpInset = SystemBarUtils.getStatusBarHeight(mContext) - + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Public methods: - - /** - * Add a listener to receive callbacks {@link #setHeadsUpAnimatingAway(boolean)} - */ - @Override - public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) { - mHeadsUpPhoneListeners.add(listener); - } - - /** - * Gets the touchable region needed for heads up notifications. Returns null if no touchable - * region is required (ie: no heads up notification currently exists). - */ - // TODO(b/347007367): With scene container enabled this method may report outdated regions - @Override - public @Nullable Region getTouchableRegion() { - NotificationEntry topEntry = getTopEntry(); - - // This call could be made in an inconsistent state while the pinnedMode hasn't been - // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's - // therefore also check if the topEntry is null. - if (!hasPinnedHeadsUp() || topEntry == null) { - return null; - } else { - if (topEntry.rowIsChildInGroup()) { - final NotificationEntry groupSummary = - mGroupMembershipManager.getGroupSummary(topEntry); - if (groupSummary != null) { - topEntry = groupSummary; - } - } - ExpandableNotificationRow topRow = topEntry.getRow(); - int[] tmpArray = new int[2]; - topRow.getLocationOnScreen(tmpArray); - int minX = tmpArray[0]; - int maxX = tmpArray[0] + topRow.getWidth(); - int height = topRow.getIntrinsicHeight(); - final boolean stretchToTop = tmpArray[1] <= mHeadsUpInset; - mTouchableRegion.set(minX, stretchToTop ? 0 : tmpArray[1], maxX, tmpArray[1] + height); - return mTouchableRegion; - } - } - - /** - * Decides whether a click is invalid for a notification, i.e it has not been shown long enough - * that a user might have consciously clicked on it. - * - * @param key the key of the touched notification - * @return whether the touch is invalid and should be discarded - */ - @Override - public boolean shouldSwallowClick(@NonNull String key) { - BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); - return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime; - } - - @Override - public void releaseAfterExpansion() { - if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; - onExpandingFinished(); - } - - public void onExpandingFinished() { - if (mReleaseOnExpandFinish) { - releaseAllImmediately(); - mReleaseOnExpandFinish = false; - } else { - for (NotificationEntry entry: getAllEntries().toList()) { - entry.setSeenInShade(true); - } - for (NotificationEntry entry : mEntriesToRemoveAfterExpand) { - if (isHeadsUpEntry(entry.getKey())) { - // Maybe the heads-up was removed already - removeEntry(entry.getKey(), "onExpandingFinished"); - } - } - } - mEntriesToRemoveAfterExpand.clear(); - } - - /** - * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry - * from the list even after a Heads Up Notification is gone. - */ - public void setTrackingHeadsUp(boolean trackingHeadsUp) { - mTrackingHeadsUp = trackingHeadsUp; - } - - private void onShadeOrQsExpanded(Boolean isExpanded) { - if (isExpanded != mIsShadeOrQsExpanded) { - mIsShadeOrQsExpanded = isExpanded; - if (!SceneContainerFlag.isEnabled() && isExpanded) { - mHeadsUpAnimatingAway.setValue(false); - } - } - } - - private void onQsExpanded(Boolean isQsExpanded) { - if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; - if (isQsExpanded != mIsQsExpanded) mIsQsExpanded = isQsExpanded; - } - - /** - * Set that we are exiting the headsUp pinned mode, but some notifications might still be - * animating out. This is used to keep the touchable regions in a reasonable state. - */ - @Override - public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { - if (headsUpAnimatingAway != mHeadsUpAnimatingAway.getValue()) { - for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) { - listener.onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway); - } - mHeadsUpAnimatingAway.setValue(headsUpAnimatingAway); - } - } - - @Override - public void unpinAll(boolean userUnPinned) { - super.unpinAll(userUnPinned); - } - - /** - * Notifies that a remote input textbox in notification gets active or inactive. - * - * @param entry The entry of the target notification. - * @param remoteInputActive True to notify active, False to notify inactive. - */ - public void setRemoteInputActive( - @NonNull NotificationEntry entry, boolean remoteInputActive) { - HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey()); - if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) { - headsUpEntry.mRemoteInputActive = remoteInputActive; - if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) { - headsUpEntry.mRemoteInputActivatedAtLeastOnce = true; - } - if (remoteInputActive) { - headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)"); - } else { - headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)"); - } - onEntryUpdated(headsUpEntry); - } - } - - /** - * Sets whether an entry's guts are exposed and therefore it should stick in the heads up - * area if it's pinned until it's hidden again. - */ - public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) { - HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); - if (!(headsUpEntry instanceof HeadsUpEntryPhone)) return; - HeadsUpEntryPhone headsUpEntryPhone = (HeadsUpEntryPhone)headsUpEntry; - if (entry.isRowPinned() || !gutsShown) { - headsUpEntryPhone.setGutsShownPinned(gutsShown); - } - } - - /** - * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts - * longer. - */ - public void extendHeadsUp() { - HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone(); - if (topEntry == null) { - return; - } - topEntry.extendPulse(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpManager public methods overrides and overloads: - - @Override - public boolean isTrackingHeadsUp() { - return mTrackingHeadsUp; - } - - @Override - public void snooze() { - super.snooze(); - mReleaseOnExpandFinish = true; - } - - public void addSwipedOutNotification(@NonNull String key) { - mSwipedOutKeys.add(key); - } - - @Override - public boolean removeNotification(@NonNull String key, boolean releaseImmediately, - boolean animate, @NonNull String reason) { - if (animate) { - return removeNotification(key, releaseImmediately, - "removeNotification(animate: true), reason: " + reason); - } else { - mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); - final boolean removed = removeNotification(key, releaseImmediately, - "removeNotification(animate: false), reason: " + reason); - mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true); - return removed; - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Dumpable overrides: - - @Override - public void dump(PrintWriter pw, String[] args) { - pw.println("HeadsUpManagerPhone state:"); - dumpInternal(pw, args); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // OnReorderingAllowedListener: - - private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> { - if (NotificationThrottleHun.isEnabled()) { - mAvalancheController.setEnableAtRuntime(true); - if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) { - return; - } - } - mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); - for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { - if (entry != null && isHeadsUpEntry(entry.getKey())) { - // Maybe the heads-up was removed already - removeEntry(entry.getKey(), "mOnReorderingAllowedListener"); - } - } - mEntriesToRemoveWhenReorderingAllowed.clear(); - mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true); - }; - - private final OnReorderingBannedListener mOnReorderingBannedListener = () -> { - if (mAvalancheController != null) { - // In open shade the first HUN is pinned, and visual stability logic prevents us from - // unpinning this first HUN as long as the shade remains open. AvalancheController only - // shows the next HUN when the currently showing HUN is unpinned, so we must disable - // throttling here so that the incoming HUN stream is not forever paused. This is reset - // when reorder becomes allowed. - mAvalancheController.setEnableAtRuntime(false); - - // Note that we cannot do the above when - // 1) The remove runnable runs because its delay means it may not run before shade close - // 2) Reordering is allowed again (when shade closes) because the HUN appear animation - // will have started by then - } - }; - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpManager utility (protected) methods overrides: - - @NonNull - @Override - protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) { - if (NotificationThrottleHun.isEnabled()) { - return new HeadsUpEntryPhone(entry); - } else { - HeadsUpEntryPhone headsUpEntry = mEntryPool.acquire(); - headsUpEntry.setEntry(entry); - return headsUpEntry; - } - } - - @Override - protected void onEntryAdded(HeadsUpEntry headsUpEntry) { - super.onEntryAdded(headsUpEntry); - updateTopHeadsUpFlow(); - updateHeadsUpFlow(); - } - - @Override - protected void onEntryUpdated(HeadsUpEntry headsUpEntry) { - super.onEntryUpdated(headsUpEntry); - // no need to update the list here - updateTopHeadsUpFlow(); - } - - @Override - protected void onEntryRemoved(HeadsUpEntry headsUpEntry) { - super.onEntryRemoved(headsUpEntry); - if (!NotificationThrottleHun.isEnabled()) { - mEntryPool.release((HeadsUpEntryPhone) headsUpEntry); - } - updateTopHeadsUpFlow(); - updateHeadsUpFlow(); - if (NotificationThrottleHun.isEnabled()) { - if (headsUpEntry.mEntry != null) { - if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) { - mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry); - } - } - } - } - - private void updateTopHeadsUpFlow() { - mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry()); - } - - private void updateHeadsUpFlow() { - mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values())); - } - - @Override - protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) { - boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded; - if (SceneContainerFlag.isEnabled()) { - pin |= mIsQsExpanded; - } - if (mBypassController.getBypassEnabled()) { - pin |= mStatusBarState == StatusBarState.KEYGUARD; - } - return pin || super.shouldHeadsUpBecomePinned(entry); - } - - @Override - protected void dumpInternal(PrintWriter pw, String[] args) { - super.dumpInternal(pw, args); - pw.print(" mBarState="); - pw.println(mStatusBarState); - pw.print(" mTouchableRegion="); - pw.println(mTouchableRegion); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Private utility methods: - - @NonNull - private ArrayMap<String, HeadsUpEntryPhone> getHeadsUpEntryPhoneMap() { - //noinspection unchecked - return (ArrayMap<String, HeadsUpEntryPhone>) ((ArrayMap) mHeadsUpEntryMap); - } - - @Nullable - private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) { - return (HeadsUpEntryPhone) mHeadsUpEntryMap.get(key); - } - - @Nullable - private HeadsUpEntryPhone getTopHeadsUpEntryPhone() { - if (SceneContainerFlag.isEnabled()) { - return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue(); - } else { - return (HeadsUpEntryPhone) getTopHeadsUpEntry(); - } - } - - @Override - public boolean canRemoveImmediately(@NonNull String key) { - if (mSwipedOutKeys.contains(key)) { - // We always instantly dismiss views being manually swiped out. - mSwipedOutKeys.remove(key); - return true; - } - - HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key); - HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone(); - - return headsUpEntry == null || headsUpEntry != topEntry || super.canRemoveImmediately(key); - } - - @Override - @NonNull - public Flow<HeadsUpRowRepository> getTopHeadsUpRow() { - return mTopHeadsUpRow; - } - - @Override - @NonNull - public Flow<Set<HeadsUpRowRepository>> getActiveHeadsUpRows() { - return mHeadsUpNotificationRows; - } - - @Override - @NonNull - public StateFlow<Boolean> isHeadsUpAnimatingAway() { - return mHeadsUpAnimatingAway; - } - - @Override - public boolean isHeadsUpAnimatingAwayValue() { - return mHeadsUpAnimatingAway.getValue(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpEntryPhone: - - protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry implements - HeadsUpRowRepository { - - private boolean mGutsShownPinned; - private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false); - - /** - * If the time this entry has been on was extended - */ - private boolean extended; - - @Override - public boolean isSticky() { - return super.isSticky() || mGutsShownPinned; - } - - public HeadsUpEntryPhone() { - super(); - } - - public HeadsUpEntryPhone(NotificationEntry entry) { - super(entry); - } - - @Override - @NonNull - public String getKey() { - return requireEntry().getKey(); - } - - @Override - @NonNull - public StateFlow<Boolean> isPinned() { - return mIsPinned; - } - - @Override - protected void setRowPinned(boolean pinned) { - // TODO(b/327624082): replace this super call with a ViewBinder - super.setRowPinned(pinned); - mIsPinned.setValue(pinned); - } - - @Override - protected void setEntry(@androidx.annotation.NonNull NotificationEntry entry, - @androidx.annotation.Nullable Runnable removeRunnable) { - super.setEntry(entry, removeRunnable); - - if (NotificationThrottleHun.isEnabled()) { - mEntriesToRemoveWhenReorderingAllowed.add(entry); - if (!mVisualStabilityProvider.isReorderingAllowed()) { - entry.setSeenInShade(true); - } - } - } - - @Override - protected Runnable createRemoveRunnable(NotificationEntry entry) { - return () -> { - if (!NotificationThrottleHun.isEnabled() - && !mVisualStabilityProvider.isReorderingAllowed() - // We don't want to allow reordering while pulsing, but headsup need to - // time out anyway - && !entry.showingPulsing()) { - mEntriesToRemoveWhenReorderingAllowed.add(entry); - mVisualStabilityProvider.addTemporaryReorderingAllowedListener( - mOnReorderingAllowedListener); - } else if (mTrackingHeadsUp) { - mEntriesToRemoveAfterExpand.add(entry); - mLogger.logRemoveEntryAfterExpand(entry); - } else if (mVisualStabilityProvider.isReorderingAllowed() - || entry.showingPulsing()) { - removeEntry(entry.getKey(), "createRemoveRunnable"); - } - }; - } - - @Override - public void updateEntry(boolean updatePostTime, String reason) { - super.updateEntry(updatePostTime, reason); - - if (mEntriesToRemoveAfterExpand.contains(mEntry)) { - mEntriesToRemoveAfterExpand.remove(mEntry); - } - if (!NotificationThrottleHun.isEnabled()) { - if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) { - mEntriesToRemoveWhenReorderingAllowed.remove(mEntry); - } - } - } - - @Override - public void setExpanded(boolean expanded) { - if (this.mExpanded == expanded) { - return; - } - - this.mExpanded = expanded; - if (expanded) { - cancelAutoRemovalCallbacks("setExpanded(true)"); - } else { - updateEntry(false /* updatePostTime */, "setExpanded(false)"); - } - } - - public void setGutsShownPinned(boolean gutsShownPinned) { - if (mGutsShownPinned == gutsShownPinned) { - return; - } - - mGutsShownPinned = gutsShownPinned; - if (gutsShownPinned) { - cancelAutoRemovalCallbacks("setGutsShownPinned(true)"); - } else { - updateEntry(false /* updatePostTime */, "setGutsShownPinned(false)"); - } - } - - @Override - public void reset() { - super.reset(); - mGutsShownPinned = false; - extended = false; - } - - private void extendPulse() { - if (!extended) { - extended = true; - updateEntry(false, "extendPulse()"); - } - } - - @Override - protected long calculateFinishTime() { - return super.calculateFinishTime() + (extended ? mExtensionTime : 0); - } - - @Override - @NonNull - public Object getElementKey() { - return requireEntry().getRow(); - } - - private NotificationEntry requireEntry() { - /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode(); - return Objects.requireNonNull(mEntry); - } - } - - private final StateListener mStatusBarStateListener = new StateListener() { - @Override - public void onStateChanged(int newState) { - boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD; - boolean isKeyguard = newState == StatusBarState.KEYGUARD; - mStatusBarState = newState; - - if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) { - ArrayList<String> keysToRemove = new ArrayList<>(); - for (HeadsUpEntry entry : getHeadsUpEntryList()) { - if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) { - keysToRemove.add(entry.mEntry.getKey()); - } - } - for (String key : keysToRemove) { - removeEntry(key, "mStatusBarStateListener"); - } - } - } - - @Override - public void onDozingChanged(boolean isDozing) { - if (!isDozing) { - // Let's make sure all huns we got while dozing time out within the normal timeout - // duration. Otherwise they could get stuck for a very long time - for (HeadsUpEntry entry : getHeadsUpEntryList()) { - entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)"); - } - } - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt index 63c9e8be9ead..cf4fb25f638e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt @@ -16,16 +16,11 @@ package com.android.systemui.statusbar.notification.data import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository -import com.android.systemui.statusbar.notification.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.BaseHeadsUpManager import dagger.Binds import dagger.Module -@Module( - includes = - [ - NotificationSettingsRepositoryModule::class, - ] -) +@Module(includes = [NotificationSettingsRepositoryModule::class]) interface NotificationDataLayerModule { - @Binds fun bindHeadsUpNotificationRepository(impl: HeadsUpManagerPhone): HeadsUpRepository + @Binds fun bindHeadsUpNotificationRepository(impl: BaseHeadsUpManager): HeadsUpRepository } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt index 7919c84152b2..83551e9b8294 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt @@ -17,12 +17,12 @@ package com.android.systemui.statusbar.phone import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.notification.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.BaseHeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Binds import dagger.Module @Module interface HeadsUpModule { - @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager + @Binds @SysUISingleton fun bindsHeadsUpManager(hum: BaseHeadsUpManager): HeadsUpManager } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index 4284c1911b33..f6f567f17077 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -16,33 +16,48 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Region; import android.os.Handler; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Pools; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.internal.policy.SystemBarUtils; import com.android.systemui.EventLogTags; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener; +import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener; +import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository; +import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.SystemClock; @@ -50,14 +65,27 @@ import org.jetbrains.annotations.NotNull; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.Stack; import java.util.stream.Stream; +import javax.inject.Inject; + +import kotlinx.coroutines.flow.Flow; +import kotlinx.coroutines.flow.MutableStateFlow; +import kotlinx.coroutines.flow.StateFlow; +import kotlinx.coroutines.flow.StateFlowKt; + /** * A manager which handles heads up notifications which is a special mode where * they simply peek from the top of the screen. */ -public abstract class BaseHeadsUpManager implements HeadsUpManager { +@SysUISingleton +public class BaseHeadsUpManager + implements HeadsUpManager, HeadsUpRepository, OnHeadsUpChangedListener { private static final String TAG = "BaseHeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; @@ -74,7 +102,11 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { private final AccessibilityManagerWrapper mAccessibilityMgr; private final UiEventLogger mUiEventLogger; - private final AvalancheController mAvalancheController; + private AvalancheController mAvalancheController; + private final KeyguardBypassController mBypassController; + private final GroupMembershipManager mGroupMembershipManager; + private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>(); + private final VisualStabilityProvider mVisualStabilityProvider; protected final SystemClock mSystemClock; protected final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>(); @@ -84,6 +116,53 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { protected int mAutoDismissTime; protected DelayableExecutor mExecutor; + @VisibleForTesting + public final int mExtensionTime; + + // TODO(b/328393698) move the topHeadsUpRow logic to an interactor + private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow = + StateFlowKt.MutableStateFlow(null); + private final MutableStateFlow<Set<HeadsUpRowRepository>> mHeadsUpNotificationRows = + StateFlowKt.MutableStateFlow(new HashSet<>()); + private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway = + StateFlowKt.MutableStateFlow(false); + private final HashSet<String> mSwipedOutKeys = new HashSet<>(); + private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>(); + @VisibleForTesting + public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed + = new ArraySet<>(); + + private boolean mReleaseOnExpandFinish; + private boolean mTrackingHeadsUp; + private boolean mIsShadeOrQsExpanded; + private boolean mIsQsExpanded; + private int mStatusBarState; + private AnimationStateHandler mAnimationStateHandler; + private int mHeadsUpInset; + + // Used for determining the region for touch interaction + private final Region mTouchableRegion = new Region(); + + private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<>() { + private final Stack<HeadsUpEntry> mPoolObjects = new Stack<>(); + + @Override + public HeadsUpEntry acquire() { + NotificationThrottleHun.assertInLegacyMode(); + if (!mPoolObjects.isEmpty()) { + return mPoolObjects.pop(); + } + return new HeadsUpEntry(); + } + + @Override + public boolean release(@NonNull HeadsUpEntry instance) { + NotificationThrottleHun.assertInLegacyMode(); + mPoolObjects.push(instance); + return true; + } + }; + /** * Enum entry for notification peek logged from this class. */ @@ -100,14 +179,23 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } } - public BaseHeadsUpManager(@NonNull final Context context, + @Inject + public BaseHeadsUpManager( + @NonNull final Context context, HeadsUpManagerLogger logger, + StatusBarStateController statusBarStateController, + KeyguardBypassController bypassController, + GroupMembershipManager groupMembershipManager, + VisualStabilityProvider visualStabilityProvider, + ConfigurationController configurationController, @Main Handler handler, GlobalSettings globalSettings, SystemClock systemClock, @Main DelayableExecutor executor, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger, + JavaAdapter javaAdapter, + ShadeInteractor shadeInteractor, AvalancheController avalancheController) { mLogger = logger; mExecutor = executor; @@ -117,12 +205,16 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mUiEventLogger = uiEventLogger; mAvalancheController = avalancheController; mAvalancheController.setBaseEntryMapStr(this::getEntryMapStr); + mBypassController = bypassController; + mGroupMembershipManager = groupMembershipManager; + mVisualStabilityProvider = visualStabilityProvider; Resources resources = context.getResources(); mMinimumDisplayTime = NotificationThrottleHun.isEnabled() ? 500 : resources.getInteger(R.integer.heads_up_notification_minimum_time); mStickyForSomeTimeAutoDismissTime = resources.getInteger( R.integer.sticky_heads_up_notification_time); mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay); + mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); mSnoozedPackages = new ArrayMap<>(); int defaultSnoozeLengthMs = @@ -145,11 +237,38 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), /* notifyForDescendants = */ false, settingsObserver); + + statusBarStateController.addCallback(mStatusBarStateListener); + updateResources(); + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onDensityOrFontScaleChanged() { + updateResources(); + } + + @Override + public void onThemeChanged() { + updateResources(); + } + }); + javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), + this::onShadeOrQsExpanded); + if (SceneContainerFlag.isEnabled()) { + javaAdapter.alwaysCollectFlow(shadeInteractor.isQsExpanded(), + this::onQsExpanded); + } + if (NotificationThrottleHun.isEnabled()) { + mVisualStabilityProvider.addPersistentReorderingBannedListener( + mOnReorderingBannedListener); + mVisualStabilityProvider.addPersistentReorderingAllowedListener( + mOnReorderingAllowedListener); + } } /** * Adds an OnHeadUpChangedListener to observe events. */ + @Override public void addListener(@NonNull OnHeadsUpChangedListener listener) { mListeners.addIfAbsent(listener); } @@ -157,11 +276,31 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { /** * Removes the OnHeadUpChangedListener from the observer list. */ + @Override public void removeListener(@NonNull OnHeadsUpChangedListener listener) { mListeners.remove(listener); } /** + * Add a listener to receive callbacks {@link #setHeadsUpAnimatingAway(boolean)} + */ + @Override + public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) { + mHeadsUpPhoneListeners.add(listener); + } + + @Override + public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) { + mAnimationStateHandler = handler; + } + + private void updateResources() { + Resources resources = mContext.getResources(); + mHeadsUpInset = SystemBarUtils.getStatusBarHeight(mContext) + + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding); + } + + /** * Called when posting a new notification that should appear on screen. * Adds the notification to be managed. * @param entry entry to show @@ -188,14 +327,24 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mAvalancheController.update(headsUpEntry, runnable, "showNotification"); } - /** - * Try to remove the notification. May not succeed if the notification has not been shown long - * enough and needs to be kept around. - * @param key the key of the notification to remove - * @param releaseImmediately force a remove regardless of earliest removal time - * @param reason reason for removing the notification - * @return true if notification is removed, false otherwise - */ + @Override + public boolean removeNotification( + @NonNull String key, + boolean releaseImmediately, + boolean animate, + @NonNull String reason) { + if (animate) { + return removeNotification(key, releaseImmediately, + "removeNotification(animate: true), reason: " + reason); + } else { + mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); + final boolean removed = removeNotification(key, releaseImmediately, + "removeNotification(animate: false), reason: " + reason); + mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true); + return removed; + } + } + @Override public boolean removeNotification(@NotNull String key, boolean releaseImmediately, @NonNull String reason) { @@ -261,6 +410,42 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } } + @Override + public void setTrackingHeadsUp(boolean trackingHeadsUp) { + mTrackingHeadsUp = trackingHeadsUp; + } + + @Override + public boolean shouldSwallowClick(@NonNull String key) { + BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); + return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime; + } + + @Override + public void releaseAfterExpansion() { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; + onExpandingFinished(); + } + + @Override + public void onExpandingFinished() { + if (mReleaseOnExpandFinish) { + releaseAllImmediately(); + mReleaseOnExpandFinish = false; + } else { + for (NotificationEntry entry : getAllEntries().toList()) { + entry.setSeenInShade(true); + } + for (NotificationEntry entry : mEntriesToRemoveAfterExpand) { + if (isHeadsUpEntry(entry.getKey())) { + // Maybe the heads-up was removed already + removeEntry(entry.getKey(), "onExpandingFinished"); + } + } + } + mEntriesToRemoveAfterExpand.clear(); + } + /** * Clears all managed notifications. */ @@ -339,10 +524,19 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { return 0; } + @VisibleForTesting protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) { - if (entry == null) { - return false; + boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded; + if (SceneContainerFlag.isEnabled()) { + pin |= mIsQsExpanded; + } + if (mBypassController.getBypassEnabled()) { + pin |= mStatusBarState == StatusBarState.KEYGUARD; + } + if (pin) { + return true; } + final HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); if (headsUpEntry == null) { // This should not happen since shouldHeadsUpBecomePinned is always called after adding @@ -392,10 +586,6 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } } - public @InflationFlag int getContentFlag() { - return FLAG_CONTENT_VIEW_HEADS_UP; - } - /** * Manager-specific logic that should occur when an entry is added. * @param headsUpEntry entry added @@ -410,6 +600,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, true); } + updateTopHeadsUpFlow(); + updateHeadsUpFlow(); } /** @@ -466,11 +658,60 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, false); } + if (!NotificationThrottleHun.isEnabled()) { + mEntryPool.release(headsUpEntry); + } + updateTopHeadsUpFlow(); + updateHeadsUpFlow(); + if (NotificationThrottleHun.isEnabled()) { + if (headsUpEntry.mEntry != null) { + if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) { + mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry); + } + } + } + } + + private void updateTopHeadsUpFlow() { + mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry()); + } + + private void updateHeadsUpFlow() { + mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values())); + } + + @Override + @NonNull + public Flow<HeadsUpRowRepository> getTopHeadsUpRow() { + return mTopHeadsUpRow; + } + + @Override + @NonNull + public Flow<Set<HeadsUpRowRepository>> getActiveHeadsUpRows() { + return mHeadsUpNotificationRows; + } + + @Override + @NonNull + public StateFlow<Boolean> isHeadsUpAnimatingAway() { + return mHeadsUpAnimatingAway; + } + + @Override + public boolean isHeadsUpAnimatingAwayValue() { + return mHeadsUpAnimatingAway.getValue(); + } + + @NonNull + private ArrayMap<String, HeadsUpEntry> getHeadsUpEntryPhoneMap() { + return mHeadsUpEntryMap; } /** * Called to notify the listeners that the HUN animating away animation has ended. */ + @Override public void onEntryAnimatingAwayEnded(@NonNull NotificationEntry entry) { for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpAnimatingAwayEnded(entry); @@ -484,6 +725,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { * @param headsUpEntry entry updated */ protected void onEntryUpdated(HeadsUpEntry headsUpEntry) { + // no need to update the list here + updateTopHeadsUpFlow(); } protected void updatePinnedMode() { @@ -521,6 +764,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { /** * Snoozes all current Heads Up Notifications. */ + @Override public void snooze() { List<String> keySet = new ArrayList<>(mHeadsUpEntryMap.keySet()); keySet.addAll(mAvalancheController.getWaitingKeys()); @@ -534,6 +778,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mLogger.logPackageSnoozed(snoozeKey); mSnoozedPackages.put(snoozeKey, mSystemClock.elapsedRealtime() + mSnoozeLengthMs); } + mReleaseOnExpandFinish = true; } @NonNull @@ -541,6 +786,11 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { return user + "," + packageName; } + @Override + public void addSwipedOutNotification(@NonNull String key) { + mSwipedOutKeys.add(key); + } + @Nullable protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) { if (mHeadsUpEntryMap.containsKey(key)) { @@ -597,6 +847,59 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } @Override + public @Nullable Region getTouchableRegion() { + NotificationEntry topEntry = getTopEntry(); + + // This call could be made in an inconsistent state while the pinnedMode hasn't been + // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's + // therefore also check if the topEntry is null. + if (!hasPinnedHeadsUp() || topEntry == null) { + return null; + } else { + if (topEntry.rowIsChildInGroup()) { + final NotificationEntry groupSummary = + mGroupMembershipManager.getGroupSummary(topEntry); + if (groupSummary != null) { + topEntry = groupSummary; + } + } + ExpandableNotificationRow topRow = topEntry.getRow(); + int[] tmpArray = new int[2]; + topRow.getLocationOnScreen(tmpArray); + int minX = tmpArray[0]; + int maxX = tmpArray[0] + topRow.getWidth(); + int height = topRow.getIntrinsicHeight(); + final boolean stretchToTop = tmpArray[1] <= mHeadsUpInset; + mTouchableRegion.set(minX, stretchToTop ? 0 : tmpArray[1], maxX, tmpArray[1] + height); + return mTouchableRegion; + } + } + + @Override + public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { + if (headsUpAnimatingAway != mHeadsUpAnimatingAway.getValue()) { + for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) { + listener.onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway); + } + mHeadsUpAnimatingAway.setValue(headsUpAnimatingAway); + } + } + + private void onShadeOrQsExpanded(Boolean isExpanded) { + if (isExpanded != mIsShadeOrQsExpanded) { + mIsShadeOrQsExpanded = isExpanded; + if (!SceneContainerFlag.isEnabled() && isExpanded) { + mHeadsUpAnimatingAway.setValue(false); + } + } + } + + private void onQsExpanded(Boolean isQsExpanded) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; + if (isQsExpanded != mIsQsExpanded) mIsQsExpanded = isQsExpanded; + } + + @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("HeadsUpManager state:"); dumpInternal(pw, args); @@ -616,6 +919,10 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); } + pw.print(" mBarState="); + pw.println(mStatusBarState); + pw.print(" mTouchableRegion="); + pw.println(mTouchableRegion); } /** @@ -639,6 +946,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { * Unpins all pinned Heads Up Notifications. * @param userUnPinned The unpinned action is trigger by user real operation. */ + @Override public void unpinAll(boolean userUnPinned) { for (String key : mHeadsUpEntryMap.keySet()) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); @@ -662,13 +970,59 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } } - /** - * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as - * well. - */ + @Override + public void setRemoteInputActive( + @NonNull NotificationEntry entry, boolean remoteInputActive) { + HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(entry.getKey()); + if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) { + headsUpEntry.mRemoteInputActive = remoteInputActive; + if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) { + headsUpEntry.mRemoteInputActivatedAtLeastOnce = true; + } + if (remoteInputActive) { + headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)"); + } else { + headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)"); + } + onEntryUpdated(headsUpEntry); + } + } + + @Nullable + private HeadsUpEntry getHeadsUpEntryPhone(@NonNull String key) { + return mHeadsUpEntryMap.get(key); + } + + @Override + public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); + if (headsUpEntry == null) return; + if (entry.isRowPinned() || !gutsShown) { + headsUpEntry.setGutsShownPinned(gutsShown); + } + } + + @Override + public void extendHeadsUp() { + HeadsUpEntry topEntry = getTopHeadsUpEntryPhone(); + if (topEntry == null) { + return; + } + topEntry.extendPulse(); + } + + @Nullable + private HeadsUpEntry getTopHeadsUpEntryPhone() { + if (SceneContainerFlag.isEnabled()) { + return (HeadsUpEntry) mTopHeadsUpRow.getValue(); + } else { + return getTopHeadsUpEntry(); + } + } + + @Override public boolean isTrackingHeadsUp() { - // Might be implemented in subclass. - return false; + return mTrackingHeadsUp; } /** @@ -724,11 +1078,23 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { */ @Override public boolean canRemoveImmediately(@NonNull String key) { - HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); - if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) { + if (mSwipedOutKeys.contains(key)) { + // We always instantly dismiss views being manually swiped out. + mSwipedOutKeys.remove(key); + return true; + } + + HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(key); + HeadsUpEntry topEntry = getTopHeadsUpEntryPhone(); + + if (headsUpEntry == null || headsUpEntry != topEntry) { return true; } - return headsUpEntry == null || headsUpEntry.wasShownLongEnough() + + if (headsUpEntry.mUserActionMayIndirectlyRemove) { + return true; + } + return headsUpEntry.wasShownLongEnough() || (headsUpEntry.mEntry != null && headsUpEntry.mEntry.isRowDismissed()); } @@ -747,7 +1113,13 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { @NonNull protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) { - return new HeadsUpEntry(entry); + if (NotificationThrottleHun.isEnabled()) { + return new HeadsUpEntry(entry); + } else { + HeadsUpEntry headsUpEntry = mEntryPool.acquire(); + headsUpEntry.setEntry(entry); + return headsUpEntry; + } } /** @@ -763,12 +1135,79 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { && Notification.CATEGORY_CALL.equals(n.category)); } + private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> { + if (NotificationThrottleHun.isEnabled()) { + mAvalancheController.setEnableAtRuntime(true); + if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) { + return; + } + } + mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); + for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { + if (entry != null && isHeadsUpEntry(entry.getKey())) { + // Maybe the heads-up was removed already + removeEntry(entry.getKey(), "mOnReorderingAllowedListener"); + } + } + mEntriesToRemoveWhenReorderingAllowed.clear(); + mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true); + }; + + private final OnReorderingBannedListener mOnReorderingBannedListener = () -> { + if (mAvalancheController != null) { + // In open shade the first HUN is pinned, and visual stability logic prevents us from + // unpinning this first HUN as long as the shade remains open. AvalancheController only + // shows the next HUN when the currently showing HUN is unpinned, so we must disable + // throttling here so that the incoming HUN stream is not forever paused. This is reset + // when reorder becomes allowed. + mAvalancheController.setEnableAtRuntime(false); + + // Note that we cannot do the above when + // 1) The remove runnable runs because its delay means it may not run before shade close + // 2) Reordering is allowed again (when shade closes) because the HUN appear animation + // will have started by then + } + }; + + private final StatusBarStateController.StateListener + mStatusBarStateListener = new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD; + boolean isKeyguard = newState == StatusBarState.KEYGUARD; + mStatusBarState = newState; + + if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) { + ArrayList<String> keysToRemove = new ArrayList<>(); + for (HeadsUpEntry entry : getHeadsUpEntryList()) { + if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) { + keysToRemove.add(entry.mEntry.getKey()); + } + } + for (String key : keysToRemove) { + removeEntry(key, "mStatusBarStateListener"); + } + } + } + + @Override + public void onDozingChanged(boolean isDozing) { + if (!isDozing) { + // Let's make sure all huns we got while dozing time out within the normal timeout + // duration. Otherwise they could get stuck for a very long time + for (HeadsUpEntry entry : getHeadsUpEntryList()) { + entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)"); + } + } + } + }; + /** * This represents a notification and how long it is in a heads up mode. It also manages its * lifecycle automatically when created. This class is public because it is exposed by methods * of AvalancheController that take it as param. */ - public class HeadsUpEntry implements Comparable<HeadsUpEntry> { + public class HeadsUpEntry implements Comparable<HeadsUpEntry>, HeadsUpRowRepository { public boolean mRemoteInputActivatedAtLeastOnce; public boolean mRemoteInputActive; public boolean mUserActionMayIndirectlyRemove; @@ -784,6 +1223,14 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { @Nullable private Runnable mCancelRemoveRunnable; + private boolean mGutsShownPinned; + private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false); + + /** + * If the time this entry has been on was extended + */ + private boolean extended; + public HeadsUpEntry() { NotificationThrottleHun.assertInLegacyMode(); } @@ -794,19 +1241,50 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { setEntry(entry, createRemoveRunnable(entry)); } + @Override + @NonNull + public String getKey() { + return requireEntry().getKey(); + } + + @Override + @NonNull + public Object getElementKey() { + return requireEntry().getRow(); + } + + private NotificationEntry requireEntry() { + /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode(); + return Objects.requireNonNull(mEntry); + } + + @Override + @NonNull + public StateFlow<Boolean> isPinned() { + return mIsPinned; + } + /** Attach a NotificationEntry. */ public void setEntry(@NonNull final NotificationEntry entry) { NotificationThrottleHun.assertInLegacyMode(); setEntry(entry, createRemoveRunnable(entry)); } - protected void setEntry(@NonNull final NotificationEntry entry, + protected void setEntry( + @NonNull final NotificationEntry entry, @Nullable Runnable removeRunnable) { mEntry = entry; mRemoveRunnable = removeRunnable; mPostTime = calculatePostTime(); updateEntry(true /* updatePostTime */, "setEntry"); + + if (NotificationThrottleHun.isEnabled()) { + mEntriesToRemoveWhenReorderingAllowed.add(entry); + if (!mVisualStabilityProvider.isReorderingAllowed()) { + entry.setSeenInShade(true); + } + } } protected boolean isRowPinned() { @@ -815,6 +1293,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { protected void setRowPinned(boolean pinned) { if (mEntry != null) mEntry.setRowPinned(pinned); + mIsPinned.setValue(pinned); } /** @@ -870,6 +1349,22 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { // Notify the manager, that the posted time has changed. onEntryUpdated(this); + + if (mEntriesToRemoveAfterExpand.contains(mEntry)) { + mEntriesToRemoveAfterExpand.remove(mEntry); + } + if (!NotificationThrottleHun.isEnabled()) { + if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) { + mEntriesToRemoveWhenReorderingAllowed.remove(mEntry); + } + } + } + + private void extendPulse() { + if (!extended) { + extended = true; + updateEntry(false, "extendPulse()"); + } } /** @@ -878,6 +1373,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { * @return true if the notification is sticky */ public boolean isSticky() { + if (mGutsShownPinned) return true; + if (mEntry == null) return false; if (ExpandHeadsUpOnInlineReply.isEnabled()) { @@ -989,7 +1486,29 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } public void setExpanded(boolean expanded) { + if (this.mExpanded == expanded) { + return; + } + this.mExpanded = expanded; + if (expanded) { + cancelAutoRemovalCallbacks("setExpanded(true)"); + } else { + updateEntry(false /* updatePostTime */, "setExpanded(false)"); + } + } + + public void setGutsShownPinned(boolean gutsShownPinned) { + if (mGutsShownPinned == gutsShownPinned) { + return; + } + + mGutsShownPinned = gutsShownPinned; + if (gutsShownPinned) { + cancelAutoRemovalCallbacks("setGutsShownPinned(true)"); + } else { + updateEntry(false /* updatePostTime */, "setGutsShownPinned(false)"); + } } public void reset() { @@ -999,6 +1518,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mRemoveRunnable = null; mExpanded = false; mRemoteInputActive = false; + mGutsShownPinned = false; + extended = false; } /** @@ -1074,7 +1595,23 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { /** Creates a runnable to remove this notification from the alerting entries. */ protected Runnable createRemoveRunnable(NotificationEntry entry) { - return () -> removeEntry(entry.getKey(), "createRemoveRunnable"); + return () -> { + if (!NotificationThrottleHun.isEnabled() + && !mVisualStabilityProvider.isReorderingAllowed() + // We don't want to allow reordering while pulsing, but headsup need to + // time out anyway + && !entry.showingPulsing()) { + mEntriesToRemoveWhenReorderingAllowed.add(entry); + mVisualStabilityProvider.addTemporaryReorderingAllowedListener( + mOnReorderingAllowedListener); + } else if (mTrackingHeadsUp) { + mEntriesToRemoveAfterExpand.add(entry); + mLogger.logRemoveEntryAfterExpand(entry); + } else if (mVisualStabilityProvider.isReorderingAllowed() + || entry.showingPulsing()) { + removeEntry(entry.getKey(), "createRemoveRunnable"); + } + }; } /** @@ -1098,7 +1635,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { requestedTimeOutMs = mAvalancheController.getDurationMs(this, mAutoDismissTime); } final long duration = getRecommendedHeadsUpTimeoutMs(requestedTimeOutMs); - return mPostTime + duration; + return mPostTime + duration + (extended ? mExtensionTime : 0); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt index 04fe6b3e9eb7..b37194b8b7a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt @@ -59,6 +59,7 @@ interface HeadsUpManager : Dumpable { * Gets the touchable region needed for heads up notifications. Returns null if no touchable * region is required (ie: no heads up notification currently exists). */ + // TODO(b/347007367): With scene container enabled this method may report outdated regions fun getTouchableRegion(): Region? /** @@ -83,6 +84,10 @@ interface HeadsUpManager : Dumpable { /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */ fun isSticky(key: String?): Boolean + /** + * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as + * well. + */ fun isTrackingHeadsUp(): Boolean fun onExpandingFinished() @@ -115,7 +120,7 @@ interface HeadsUpManager : Dumpable { key: String, releaseImmediately: Boolean, animate: Boolean, - reason: String + reason: String, ): Boolean /** Clears all managed notifications. */ @@ -149,6 +154,10 @@ interface HeadsUpManager : Dumpable { */ fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) + /** + * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry + * from the list even after a Heads Up Notification is gone. + */ fun setTrackingHeadsUp(tracking: Boolean) /** Sets the current user. */ @@ -260,7 +269,7 @@ class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager { key: String, releaseImmediately: Boolean, animate: Boolean, - reason: String + reason: String, ) = false override fun setAnimationStateHandler(handler: AnimationStateHandler) {} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 63e6eb6c0ef5..3cd613b21f4b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -71,14 +71,17 @@ import com.android.systemui.shade.shadeController import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository +import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.keyguardBypassController import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor +import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor import com.android.systemui.statusbar.ui.viewmodel.keyguardStatusBarViewModel @@ -105,6 +108,7 @@ class KosmosJavaAdapter() { val testScope by lazy { kosmos.testScope } val fakeExecutor by lazy { kosmos.fakeExecutor } val fakeExecutorHandler by lazy { kosmos.fakeExecutorHandler } + val configurationController by lazy { kosmos.configurationController } val configurationRepository by lazy { kosmos.fakeConfigurationRepository } val configurationInteractor by lazy { kosmos.configurationInteractor } val bouncerRepository by lazy { kosmos.bouncerRepository } @@ -115,6 +119,7 @@ class KosmosJavaAdapter() { val seenNotificationsInteractor by lazy { kosmos.seenNotificationsInteractor } val keyguardRepository by lazy { kosmos.fakeKeyguardRepository } val keyguardBouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository } + val keyguardBypassController by lazy { kosmos.keyguardBypassController } val keyguardInteractor by lazy { kosmos.keyguardInteractor } val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository } val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor } @@ -158,6 +163,7 @@ class KosmosJavaAdapter() { val shadeRepository by lazy { kosmos.shadeRepository } val shadeInteractor by lazy { kosmos.shadeInteractor } val notificationShadeWindowModel by lazy { kosmos.notificationShadeWindowModel } + val visualStabilityProvider by lazy { kosmos.visualStabilityProvider } val wifiInteractor by lazy { kosmos.wifiInteractor } val fakeWifiRepository by lazy { kosmos.fakeWifiRepository } val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor } |