diff options
3 files changed, 78 insertions, 10 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 152911a30524..af0274b1caaa 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 @@ -44,6 +44,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.bouncerInteractor import com.android.systemui.bouncer.shared.logging.BouncerUiEvent import com.android.systemui.classifier.FalsingCollector @@ -54,6 +55,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.flags.EnableSceneContainer @@ -114,6 +116,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent @@ -2356,6 +2359,58 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isLockscreenEnabled).isTrue() } + @Test + fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() = + testScope.runTest { + val transitionState = + prepareState( + isDeviceUnlocked = false, + initialSceneKey = Scenes.Lockscreen, + authenticationMethod = AuthenticationMethodModel.Pin, + ) + underTest.start() + + val isUnlocked by + collectLastValue( + kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } + ) + val currentScene by collectLastValue(sceneInteractor.currentScene) + val backStack by collectLastValue(sceneBackInteractor.backStack) + val isAlternateBouncerVisible by + collectLastValue(kosmos.alternateBouncerInteractor.isVisible) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Change to shade. + sceneInteractor.changeScene(Scenes.Shade, "") + transitionState.value = ObservableTransitionState.Idle(Scenes.Shade) + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Show the alternate bouncer. + kosmos.alternateBouncerInteractor.forceShow() + kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isTrue() + + // Trigger a fingerprint unlock. + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + assertThat(isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone) + assertThat(isAlternateBouncerVisible).isFalse() + } + private fun TestScope.emulateSceneTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toScene: SceneKey, 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 580a51a3dc0a..daeaaa52fd94 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 @@ -373,6 +373,7 @@ constructor( "device was unlocked with alternate bouncer showing" + " and shade didn't need to be left open" } else { + replaceLockscreenSceneOnBackStack() null } } @@ -391,16 +392,7 @@ constructor( val prevScene = previousScene.value val targetScene = prevScene ?: Scenes.Gone if (targetScene != Scenes.Gone) { - sceneBackInteractor.updateBackStack { stack -> - val list = stack.asIterable().toMutableList() - check(list.last() == Scenes.Lockscreen) { - "The bottommost/last SceneKey of the back stack isn't" + - " the Lockscreen scene like expected. The back" + - " stack is $stack." - } - list[list.size - 1] = Scenes.Gone - sceneStackOf(*list.toTypedArray()) - } + replaceLockscreenSceneOnBackStack() } targetScene to "device was unlocked with primary bouncer showing," + @@ -435,6 +427,20 @@ constructor( } } + /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */ + private fun replaceLockscreenSceneOnBackStack() { + sceneBackInteractor.updateBackStack { stack -> + val list = stack.asIterable().toMutableList() + check(list.last() == Scenes.Lockscreen) { + "The bottommost/last SceneKey of the back stack isn't" + + " the Lockscreen scene like expected. The back" + + " stack is $stack." + } + list[list.size - 1] = Scenes.Gone + sceneStackOf(*list.toTypedArray()) + } + } + private fun handlePowerState() { applicationScope.launch { powerInteractor.detailedWakefulness.collect { wakefulness -> 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 0c511aeae3e5..aef26dea3c0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -520,6 +520,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer.cancel(null); } mListenForCanShowAlternateBouncer = null; + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( mAlternateBouncerInteractor.getCanShowAlternateBouncer(), @@ -568,6 +569,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private void consumeCanShowAlternateBouncer(boolean canShow) { + if (SceneContainerFlag.isEnabled()) { + // When the scene framework is enabled, the alternative bouncer is hidden from the scene + // framework logic so there's no need for this logic here. + return; + } + // Hack: this is required to fix issues where // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never // reset. This is caused by usages of show()/forceShow() that only read this flow to set the |