diff options
| author | 2024-08-16 13:58:23 +0000 | |
|---|---|---|
| committer | 2024-08-19 18:45:15 +0000 | |
| commit | ddf249118d35308d0a137de4b018b99bc3c83b73 (patch) | |
| tree | af83d8f6c63237f56fe3da52a0cd3fc7372e184b | |
| parent | b5508fff1f099fa155ced5170fec299ac55971d5 (diff) | |
[flexiglass] hide and cancel AlternateBouncer when device sleeps
After the device goes to sleep, the alternate bouncer should
immediately hide and any dismiss actions should be reset and
dismiss callbacks should receive an onDismissCancelled call.
Previously, the dismiss registry callbacks would miss success
and cancelled calls. This CL will call onDismissCancelled
when the device goes to sleep from the alternate or
primary bouncer and will call onDismissSucceeded when the
device becomes unlocked.
Fixes: 353955910
Flag: com.android.systemui.scene_container
Test: atest SceneContainerStartableTest
KeyguardDismissActionInteractorTest
Test: look at DismissCallbackRegistry logs from logcat and observe
the cancellation and success calls from the alt/primary bouncer
on AlternateBouncer => sleeping, PrimaryBouncer => sleeping,
AlternateBouncer => Primary Bouncer
Test: Tap on a notification with an intent to bring up the alternate
bouncer. Press the power button to put the device to sleep. Wake
the device and use the FP sensor to authenticate. Observe that the
device enters and does NOT open the stale notification intent.
Change-Id: Ifd6c1a1a1c36da584eaa8a96e73a3848bf943244
6 files changed, 127 insertions, 26 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 2dfff3ffc72b..5533a8b90056 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -28,9 +28,7 @@ import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.uiEventLoggerFake import com.android.internal.policy.IKeyguardDismissCallback import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository -import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.bouncerInteractor @@ -331,6 +329,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) + runCurrent() assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings) assertThat(alternateBouncerVisible).isFalse() @@ -481,6 +480,33 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test + fun hideAlternateBouncerAndNotifyDismissCancelledWhenDeviceSleeps() = + testScope.runTest { + val alternateBouncerVisible by + collectLastValue(bouncerRepository.alternateBouncerVisible) + val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + prepareState( + isDeviceUnlocked = false, + initialSceneKey = Scenes.Shade, + ) + assertThat(currentSceneKey).isEqualTo(Scenes.Shade) + bouncerRepository.setAlternateVisible(true) + underTest.start() + + // run all pending dismiss succeeded/cancelled calls from setup: + kosmos.fakeExecutor.runAllReady() + + val dismissCallback: IKeyguardDismissCallback = mock() + kosmos.dismissCallbackRegistry.addCallback(dismissCallback) + powerInteractor.setAsleepForTest() + runCurrent() + kosmos.fakeExecutor.runAllReady() + + assertThat(alternateBouncerVisible).isFalse() + verify(dismissCallback).onDismissCancelled() + } + + @Test fun switchToLockscreenWhenDeviceSleepsLocked() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) @@ -1618,19 +1644,27 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test - fun notifyKeyguardDismissCallbacks_whenUnlocking_onDismissSucceeded() = + fun notifyKeyguardDismissCallbacks_whenUnlockingFromBouncer_onDismissSucceeded() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) - prepareState() + val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + prepareState( + authenticationMethod = AuthenticationMethodModel.Pin, + isDeviceUnlocked = false, + initialSceneKey = Scenes.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) underTest.start() + + // run all pending dismiss succeeded/cancelled calls from setup: + kosmos.fakeExecutor.runAllReady() + val dismissCallback: IKeyguardDismissCallback = mock() kosmos.dismissCallbackRegistry.addCallback(dismissCallback) - // Switch to bouncer and unlock device: - sceneInteractor.changeScene(Scenes.Bouncer, "") - assertThat(currentScene).isEqualTo(Scenes.Bouncer) - kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) - assertThat(currentScene).isEqualTo(Scenes.Gone) + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() kosmos.fakeExecutor.runAllReady() verify(dismissCallback).onDismissSucceeded() @@ -1639,19 +1673,26 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun notifyKeyguardDismissCallbacks_whenLeavingBouncer_onDismissCancelled() = testScope.runTest { + val isUnlocked by collectLastValue(kosmos.deviceEntryInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene) prepareState() underTest.start() + + // run all pending dismiss succeeded/cancelled calls from setup: + kosmos.fakeExecutor.runAllReady() + val dismissCallback: IKeyguardDismissCallback = mock() kosmos.dismissCallbackRegistry.addCallback(dismissCallback) // Switch to bouncer: sceneInteractor.changeScene(Scenes.Bouncer, "") assertThat(currentScene).isEqualTo(Scenes.Bouncer) + runCurrent() - // Return to lockscreen: + // Return to lockscreen when isUnlocked=false: sceneInteractor.changeScene(Scenes.Lockscreen, "") assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isUnlocked).isFalse() runCurrent() kosmos.fakeExecutor.runAllReady() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt index 1c445a703bc4..7801c00d83b8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt @@ -17,15 +17,16 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone -import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolver import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolver @@ -61,6 +62,8 @@ constructor( deviceEntryInteractor: DeviceEntryInteractor, quickSettingsSceneFamilyResolver: QuickSettingsSceneFamilyResolver, notifShadeSceneFamilyResolver: NotifShadeSceneFamilyResolver, + powerInteractor: PowerInteractor, + alternateBouncerInteractor: AlternateBouncerInteractor, ) { val dismissAction: Flow<DismissAction> = repository.dismissAction @@ -124,10 +127,12 @@ constructor( scene = Scenes.Bouncer, stateWithoutSceneContainer = PRIMARY_BOUNCER ), - transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER), + alternateBouncerInteractor.isVisible, isOnShadeWhileUnlocked, - ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked -> - !isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked + powerInteractor.isAsleep, + ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked, isAsleep -> + (!isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked) || + isAsleep } .filter { it } .sampleFilter(dismissAction) { it !is DismissAction.None } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index e73664d43952..cc46216b6e43 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -152,7 +152,7 @@ constructor( hydrateBackStack() resetShadeSessions() handleKeyguardEnabledness() - notifyKeyguardDismissCallbacks() + notifyKeyguardDismissCancelledCallbacks() refreshLockscreenEnabled() } else { sceneLogger.logFrameworkEnabled( @@ -379,8 +379,10 @@ constructor( when { isAlternateBouncerVisible -> { // When the device becomes unlocked when the alternate bouncer is - // showing, always hide the alternate bouncer... + // showing, always hide the alternate bouncer and notify dismiss + // succeeded alternateBouncerInteractor.hide() + dismissCallbackRegistry.notifyDismissSucceeded() // ... and go to Gone or stay on the current scene if ( @@ -394,9 +396,11 @@ constructor( null } } - isOnPrimaryBouncer -> + isOnPrimaryBouncer -> { // When the device becomes unlocked in primary Bouncer, + // notify dismiss succeeded and // go to previous scene or Gone. + dismissCallbackRegistry.notifyDismissSucceeded() if ( previousScene.value == Scenes.Lockscreen || !statusBarStateController.leaveOpenOnKeyguardHide() @@ -410,6 +414,7 @@ constructor( "device was unlocked with primary bouncer showing," + " from sceneKey=$prevScene" } + } isOnLockscreen -> // The lockscreen should be dismissed automatically in 2 scenarios: // 1. When face auth bypass is enabled and authentication happens while @@ -468,6 +473,9 @@ constructor( applicationScope.launch { powerInteractor.isAsleep.collect { isAsleep -> if (isAsleep) { + alternateBouncerInteractor.hide() + dismissCallbackRegistry.notifyDismissCancelled() + switchToScene( targetSceneKey = Scenes.Lockscreen, loggingReason = "device is starting to sleep", @@ -771,15 +779,23 @@ constructor( } } - private fun notifyKeyguardDismissCallbacks() { + private fun notifyKeyguardDismissCancelledCallbacks() { applicationScope.launch { - sceneInteractor.currentScene.pairwise().collect { (from, to) -> - when { - from != Scenes.Bouncer -> Unit - to == Scenes.Gone -> dismissCallbackRegistry.notifyDismissSucceeded() - else -> dismissCallbackRegistry.notifyDismissCancelled() + combine( + deviceEntryInteractor.isUnlocked, + sceneInteractor.currentScene.pairwise(), + ) { isUnlocked, (from, to) -> + when { + from != Scenes.Bouncer -> false + to != Scenes.Gone && !isUnlocked -> true + else -> false + } + } + .collect { notifyKeyguardDismissCancelled -> + if (notifyKeyguardDismissCancelled) { + dismissCallbackRegistry.notifyDismissCancelled() + } } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index de4d14d31da3..0f93ff2b70ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -545,6 +545,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @VisibleForTesting void consumeFromAlternateBouncerTransitionSteps(TransitionStep step) { + SceneContainerFlag.assertInLegacyMode(); hideAlternateBouncer(false); } @@ -554,6 +555,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ @VisibleForTesting void consumeKeyguardAuthenticatedBiometricsHandled(Unit handled) { + SceneContainerFlag.assertInLegacyMode(); if (mAlternateBouncerInteractor.isVisibleState()) { hideAlternateBouncer(false); } @@ -981,7 +983,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else { showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset); } - if (hideBouncerWhenShowing) { + if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing) { hideAlternateBouncer(true); } mKeyguardUpdateManager.sendKeyguardReset(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index 32d059b2f037..a0fe538bdd2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer @@ -29,6 +30,10 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.fakePowerRepository +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition @@ -82,6 +87,8 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { deviceEntryInteractor = kosmos.deviceEntryInteractor, quickSettingsSceneFamilyResolver = kosmos.quickSettingsSceneFamilyResolver, notifShadeSceneFamilyResolver = kosmos.notifShadeSceneFamilyResolver, + powerInteractor = kosmos.powerInteractor, + alternateBouncerInteractor = kosmos.alternateBouncerInteractor, ) } @@ -234,6 +241,32 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { } @Test + fun resetDismissAction_onBouncer_OnAsleep() = + testScope.runTest { + kosmos.setSceneTransition(Idle(Scenes.Bouncer)) + kosmos.fakeAuthenticationRepository.setAuthenticationMethod( + AuthenticationMethodModel.None + ) + val resetDismissAction by collectLastValue(underTest.resetDismissAction) + keyguardRepository.setDismissAction( + DismissAction.RunAfterKeyguardGone( + dismissAction = {}, + onCancelAction = {}, + message = "message", + willAnimateOnLockscreen = true, + ) + ) + assertThat(resetDismissAction).isNull() + kosmos.fakePowerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.TIMEOUT, + powerButtonLaunchGestureTriggered = false, + ) + assertThat(resetDismissAction).isEqualTo(Unit) + } + + @Test fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() = testScope.runTest { val dismissAction by collectLastValue(underTest.dismissAction) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt index 957f092c188a..27eadb147055 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt @@ -16,10 +16,12 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver @@ -37,5 +39,7 @@ val Kosmos.keyguardDismissActionInteractor by deviceEntryInteractor = deviceEntryInteractor, quickSettingsSceneFamilyResolver = quickSettingsSceneFamilyResolver, notifShadeSceneFamilyResolver = notifShadeSceneFamilyResolver, + powerInteractor = powerInteractor, + alternateBouncerInteractor = alternateBouncerInteractor, ) } |