diff options
| author | 2024-10-23 17:54:19 +0200 | |
|---|---|---|
| committer | 2024-11-04 19:57:56 +0100 | |
| commit | dac990d672f17ad6e245880ed121f23623a57afc (patch) | |
| tree | b41f86edef46530c5a9b00144e0879abcd2cac86 | |
| parent | 64daa1f40171230079c056c5319a31b71cc7c4f9 (diff) | |
[flexiglass] Rewrite lockscreenVisibility logic
lockscreenVisibility logic was driven by isDeviceNotEntered up until
now. This has several issues:
1. isDeviceNotEntered is not relyable as it is event based: b/374101548
2. The timings of isDeviceNotEntered are based on currentScene which
can be different based on transition type (gesture).
3. The timings of isDeviceNotEntered are not matching what we need.
Depending on the specific transition we need to be able to decide when
lockscreenVisibility is toggled, which always sends a signal to
WindowManager. b/353323341 flickers because WM is being told too early
that lockscreen is visible. This does two things: It starts a WM
transition that flickers a bunch of screens. This is actually expected
but it should happen when its all already covered by LS (at the end
of the transition, rather than the start in this case). Also it tells
WM to close open apps, it is important to not close them preemptively.
Addressing 1) the logic introduced in this CL does not rely on a "gating
state" being passed (an event that might be missed due to how flows
work) but rather covers the entire transition graph to essentially split
it in three distinct areas/states: true, false and defer to
isDeviceNotEntered for shade scenes. Since b/374101548 addresses the
computation of isDeviceEntered missing any number of states in between
would still result in the correct state being computed.
This CL already addresses `canWakeDirectlyToGone` but there are more
edge cases from the legacy variant that need to be adopted in
subsequent CLs such that we can track what fixes what.
Bug: b/353323341
Flag: EXEMPT bug fix
Test: Manual transition check
Change-Id: If8c69911e546b467921150fdbd70f40a80f76f33
4 files changed, 246 insertions, 176 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 077927dfe0a2..5bf77ae9b23c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -233,6 +233,12 @@ sealed interface ObservableTransitionState { (to == null || this.toContent == to) } + fun isTransitioningSets(from: Set<ContentKey>? = null, to: Set<ContentKey>? = null): Boolean { + return this is Transition && + (from == null || from.contains(this.fromContent)) && + (to == null || to.contains(this.toContent)) + } + /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */ fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean { return isTransitioning(from = content, to = other) || diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 6e16705b0739..7f313564e35a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -34,6 +34,8 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor @@ -115,9 +117,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // We should start with the surface invisible on LOCKSCREEN. + false // We should start with the surface invisible on LOCKSCREEN. ), - values + values, ) val lockscreenSpecificSurfaceVisibility = true @@ -134,13 +136,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // We started a transition from LOCKSCREEN, we should be using the value emitted by the // lockscreenSurfaceVisibilityFlow. - assertEquals( - listOf( - false, - lockscreenSpecificSurfaceVisibility, - ), - values - ) + assertEquals(listOf(false, lockscreenSpecificSurfaceVisibility), values) // Go back to LOCKSCREEN, since we won't emit 'true' twice in a row. transitionRepository.sendTransitionStep( @@ -166,7 +162,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { lockscreenSpecificSurfaceVisibility, false, // FINISHED (LOCKSCREEN) ), - values + values, ) val bouncerSpecificVisibility = true @@ -191,7 +187,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { false, bouncerSpecificVisibility, ), - values + values, ) } @@ -362,20 +358,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) - listOf( - Scenes.Shade, - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Gone, - ) - .forEach { scene -> - kosmos.setSceneTransition(ObservableTransitionState.Idle(scene)) - kosmos.sceneInteractor.changeScene(scene, "") - assertThat(currentScene).isEqualTo(scene) - assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"") - .that(isSurfaceBehindVisible) - .isTrue() - } + listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Gone).forEach { scene -> + kosmos.setSceneTransition(ObservableTransitionState.Idle(scene)) + kosmos.sceneInteractor.changeScene(scene, "") + assertThat(currentScene).isEqualTo(scene) + assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"") + .that(isSurfaceBehindVisible) + .isTrue() + } } @Test @@ -386,19 +376,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) - listOf( - Scenes.Shade, - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - ) - .forEach { scene -> - kosmos.setSceneTransition(ObservableTransitionState.Idle(scene)) - kosmos.sceneInteractor.changeScene(scene, "") - assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"") - .that(isSurfaceBehindVisible) - .isFalse() - } + listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen).forEach { + scene -> + kosmos.setSceneTransition(ObservableTransitionState.Idle(scene)) + kosmos.sceneInteractor.changeScene(scene, "") + assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"") + .that(isSurfaceBehindVisible) + .isFalse() + } } @Test @@ -427,9 +412,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Not using the animation when we're just sitting on LOCKSCREEN. + false // Not using the animation when we're just sitting on LOCKSCREEN. ), - values + values, ) surfaceBehindIsAnimatingFlow.emit(true) @@ -437,7 +422,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() @@ -446,7 +431,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { false, true, // Still true when we're FINISHED -> GONE, since we're still animating. ), - values + values, ) surfaceBehindIsAnimatingFlow.emit(false) @@ -458,7 +443,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // False once the animation ends. ), - values + values, ) } @@ -488,9 +473,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Not using the animation when we're just sitting on LOCKSCREEN. + false // Not using the animation when we're just sitting on LOCKSCREEN. ), - values + values, ) surfaceBehindIsAnimatingFlow.emit(true) @@ -509,7 +494,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { false, true, // We're happily animating while transitioning to gone. ), - values + values, ) // Oh no, we're still surfaceBehindAnimating=true, but no longer transitioning to GONE. @@ -536,7 +521,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // Despite the animator still running, this should be false. ), - values + values, ) surfaceBehindIsAnimatingFlow.emit(false) @@ -548,7 +533,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // The animator ending should have no effect. ), - values + values, ) } @@ -579,10 +564,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertEquals( listOf( - true, // Unsurprisingly, we should start with the lockscreen visible on + true // Unsurprisingly, we should start with the lockscreen visible on // LOCKSCREEN. ), - values + values, ) transitionRepository.sendTransitionStep( @@ -596,9 +581,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertEquals( listOf( - true, // Lockscreen remains visible while we're transitioning to GONE. + true // Lockscreen remains visible while we're transitioning to GONE. ), - values + values, ) transitionRepository.sendTransitionStep( @@ -615,7 +600,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // Once we're fully GONE, the lockscreen should not be visible. ), - values + values, ) } @@ -628,7 +613,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() @@ -640,7 +625,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // Then, false, since we finish in GONE. false, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -665,7 +650,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // Should remain false as we transition from GONE. false, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -693,7 +678,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // visibility of the from state (LS). true, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -706,14 +691,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { runCurrent() - assertEquals( - listOf( - true, - false, - true, - ), - values - ) + assertEquals(listOf(true, false, true), values) } /** @@ -730,7 +708,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() @@ -740,7 +718,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // Not visible since we're GONE. false, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -803,7 +781,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // STARTED to GONE after a CANCELED from GONE. false, ), - values + values, ) transitionRepository.sendTransitionSteps( @@ -820,7 +798,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // visible again once we're finished in LOCKSCREEN. true, ), - values + values, ) } @@ -833,7 +811,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() @@ -843,7 +821,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // Not visible when finished in GONE. false, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -869,7 +847,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // Still not visible during GONE -> AOD. false, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -886,9 +864,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // Visible now that we're FINISHED in AOD. - true + true, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -914,9 +892,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { true, false, // Remains visible from AOD during transition. - true + true, ), - values + values, ) transitionRepository.sendTransitionStep( @@ -934,15 +912,15 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { false, true, // Until we're finished in GONE again. - false + false, ), - values + values, ) } @Test @EnableSceneContainer - fun lockscreenVisibility() = + fun lockscreenVisibilityWithScenes() = testScope.runTest { val isDeviceUnlocked by collectLastValue( @@ -956,32 +934,69 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility) assertThat(lockscreenVisibility).isTrue() + kosmos.setSceneTransition(Idle(Scenes.Shade)) + kosmos.sceneInteractor.changeScene(Scenes.Shade, "") + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Transition(from = Scenes.Shade, to = Scenes.QuickSettings)) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Idle(Scenes.QuickSettings)) + kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "") + assertThat(currentScene).isEqualTo(Scenes.QuickSettings) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Transition(from = Scenes.QuickSettings, to = Scenes.Shade)) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Idle(Scenes.Shade)) + kosmos.sceneInteractor.changeScene(Scenes.Shade, "") + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Idle(Scenes.Bouncer)) kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(lockscreenVisibility).isTrue() + kosmos.setSceneTransition(Transition(from = Scenes.Bouncer, to = Scenes.Gone)) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Idle(Scenes.Gone)) kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) assertThat(isDeviceUnlocked).isTrue() kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(lockscreenVisibility).isFalse() + kosmos.setSceneTransition(Idle(Scenes.Shade)) kosmos.sceneInteractor.changeScene(Scenes.Shade, "") assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(lockscreenVisibility).isFalse() + kosmos.setSceneTransition(Transition(from = Scenes.Shade, to = Scenes.QuickSettings)) + assertThat(lockscreenVisibility).isFalse() + + kosmos.setSceneTransition(Idle(Scenes.QuickSettings)) kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "") assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(lockscreenVisibility).isFalse() + kosmos.setSceneTransition(Idle(Scenes.Shade)) kosmos.sceneInteractor.changeScene(Scenes.Shade, "") assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(lockscreenVisibility).isFalse() + kosmos.setSceneTransition(Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(lockscreenVisibility).isFalse() + kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen)) + assertThat(lockscreenVisibility).isFalse() + + kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "") assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(lockscreenVisibility).isTrue() @@ -1037,7 +1052,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { flowOf(Scenes.Lockscreen), progress, false, - flowOf(false) + flowOf(false), ) private val goneToLs = @@ -1047,7 +1062,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { flowOf(Scenes.Lockscreen), progress, false, - flowOf(false) + flowOf(false), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index a1f606740cd9..d648c25ced8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -18,7 +18,8 @@ package com.android.systemui.keyguard.domain.interactor -import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.ObservableTransitionState.Idle +import com.android.compose.animation.scene.ObservableTransitionState.Transition import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor @@ -30,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.device import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.util.kotlin.Utils.Companion.toTriple @@ -110,11 +112,8 @@ constructor( } .distinctUntilChanged() - private val isDeviceEntered: Flow<Boolean> by lazy { - deviceEntryInteractor.get().isDeviceEntered - } - - private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } } + private val isDeviceEntered by lazy { deviceEntryInteractor.get().isDeviceEntered } + private val isDeviceNotEntered by lazy { isDeviceEntered.map { !it } } /** * Surface visibility, which is either determined by the default visibility when not @@ -124,32 +123,24 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) val surfaceBehindVisibility: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { - sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState -> - when (transitionState) { - is ObservableTransitionState.Transition -> - when { - transitionState.fromContent == Scenes.Lockscreen && - transitionState.toContent == Scenes.Gone -> - sceneInteractor - .get() - .isTransitionUserInputOngoing - .flatMapLatestConflated { isUserInputOngoing -> - if (isUserInputOngoing) { - isDeviceEntered - } else { - flowOf(true) - } - } - transitionState.fromContent == Scenes.Bouncer && - transitionState.toContent == Scenes.Gone -> - transitionState.progress.map { progress -> - progress > - FromPrimaryBouncerTransitionInteractor - .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD - } - else -> isDeviceEntered + sceneInteractor.get().transitionState.flatMapLatestConflated { state -> + when { + state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) -> + (state as Transition).isUserInputOngoing.flatMapLatestConflated { + isUserInputOngoing -> + if (isUserInputOngoing) { + isDeviceEntered + } else { + flowOf(true) + } + } + state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) -> + (state as Transition).progress.map { progress -> + progress > + FromPrimaryBouncerTransitionInteractor + .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } - is ObservableTransitionState.Idle -> isDeviceEntered + else -> isDeviceEntered } } } else { @@ -219,6 +210,123 @@ constructor( } /** + * Scenes that are part of the keyguard and are shown when the device is locked or when the + * keyguard still needs to be dismissed. + */ + private val keyguardScenes = setOf(Scenes.Lockscreen, Scenes.Bouncer, Scenes.Communal) + + /** + * Scenes that don't belong in the keyguard family and cannot show when the device is locked or + * when the keyguard still needs to be dismissed. + */ + private val nonKeyguardScenes = setOf(Scenes.Gone) + + /** + * Scenes that can show regardless of device lock or keyguard dismissal states. Other sources of + * state need to be consulted to know whether the device has been entered or not. + */ + private val keyguardAgnosticScenes = + setOf( + Scenes.Shade, + Scenes.QuickSettings, + Overlays.NotificationsShade, + Overlays.QuickSettingsShade, + ) + + private val lockscreenVisibilityWithScenes = + combine( + sceneInteractor.get().transitionState.flatMapLatestConflated { + when (it) { + is Idle -> { + when (it.currentScene) { + in keyguardScenes -> flowOf(true) + in nonKeyguardScenes -> flowOf(false) + in keyguardAgnosticScenes -> isDeviceNotEntered + else -> + throw IllegalStateException("Unknown scene: ${it.currentScene}") + } + } + is Transition -> { + when { + it.isTransitioningSets(from = keyguardScenes) -> flowOf(true) + it.isTransitioningSets(from = nonKeyguardScenes) -> flowOf(false) + it.isTransitioningSets(from = keyguardAgnosticScenes) -> + isDeviceNotEntered + else -> + throw IllegalStateException("Unknown scene: ${it.fromContent}") + } + } + } + }, + wakeToGoneInteractor.canWakeDirectlyToGone, + ::Pair, + ) + .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) -> + lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone + } + + private val lockscreenVisibilityLegacy = + combine( + transitionInteractor.currentKeyguardState, + wakeToGoneInteractor.canWakeDirectlyToGone, + ::Pair, + ) + .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple) + .map { (currentState, canWakeDirectlyToGone, startedWithPrev) -> + val startedFromStep = startedWithPrev.previousValue + val startedStep = startedWithPrev.newValue + val returningToGoneAfterCancellation = + startedStep.to == KeyguardState.GONE && + startedFromStep.transitionState == TransitionState.CANCELED && + startedFromStep.from == KeyguardState.GONE + + val transitionInfo = + if (transitionRaceCondition()) { + transitionRepository.currentTransitionInfo + } else { + transitionRepository.currentTransitionInfoInternal.value + } + val wakingDirectlyToGone = + deviceIsAsleepInState(transitionInfo.from) && + transitionInfo.to == KeyguardState.GONE + + if (returningToGoneAfterCancellation || wakingDirectlyToGone) { + // GONE -> AOD/DOZING (cancel) -> GONE is the camera launch transition, + // which means we never want to show the lockscreen throughout the + // transition. Same for waking directly to gone, due to the lockscreen being + // disabled or because the device was woken back up before the lock timeout + // duration elapsed. + false + } else if (canWakeDirectlyToGone) { + // Never show the lockscreen if we can wake directly to GONE. This means + // that the lock timeout has not yet elapsed, or the keyguard is disabled. + // In either case, we don't show the activity lock screen until one of those + // conditions changes. + false + } else if ( + currentState == KeyguardState.DREAMING && + deviceEntryInteractor.get().isUnlocked.value + ) { + // Dreams dismiss keyguard and return to GONE if they can. + false + } else if ( + startedWithPrev.newValue.from == KeyguardState.OCCLUDED && + startedWithPrev.newValue.to == KeyguardState.GONE + ) { + // OCCLUDED -> GONE directly, without transiting a *_BOUNCER state, occurs + // when an app uses intent flags to launch over an insecure keyguard without + // dismissing it, and then manually requests keyguard dismissal while + // OCCLUDED. This transition is not user-visible; the device unlocks in the + // background and the app remains on top, while we're now GONE. In this case + // we should simply tell WM that the lockscreen is no longer visible, and + // *not* play the going away animation or related animations. + false + } else { + currentState != KeyguardState.GONE + } + } + + /** * Whether the lockscreen is visible, from the Window Manager (WM) perspective. * * Note: This may briefly be true even if the lockscreen UI has animated out (alpha = 0f), as we @@ -227,69 +335,11 @@ constructor( */ val lockscreenVisibility: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { - isDeviceNotEntered - } else { - combine( - transitionInteractor.currentKeyguardState, - wakeToGoneInteractor.canWakeDirectlyToGone, - ::Pair, - ) - .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple) - .map { (currentState, canWakeDirectlyToGone, startedWithPrev) -> - val startedFromStep = startedWithPrev.previousValue - val startedStep = startedWithPrev.newValue - val returningToGoneAfterCancellation = - startedStep.to == KeyguardState.GONE && - startedFromStep.transitionState == TransitionState.CANCELED && - startedFromStep.from == KeyguardState.GONE - - val transitionInfo = - if (transitionRaceCondition()) { - transitionRepository.currentTransitionInfo - } else { - transitionRepository.currentTransitionInfoInternal.value - } - val wakingDirectlyToGone = - deviceIsAsleepInState(transitionInfo.from) && - transitionInfo.to == KeyguardState.GONE - - if (returningToGoneAfterCancellation || wakingDirectlyToGone) { - // GONE -> AOD/DOZING (cancel) -> GONE is the camera launch transition, - // which means we never want to show the lockscreen throughout the - // transition. Same for waking directly to gone, due to the lockscreen being - // disabled or because the device was woken back up before the lock timeout - // duration elapsed. - false - } else if (canWakeDirectlyToGone) { - // Never show the lockscreen if we can wake directly to GONE. This means - // that the lock timeout has not yet elapsed, or the keyguard is disabled. - // In either case, we don't show the activity lock screen until one of those - // conditions changes. - false - } else if ( - currentState == KeyguardState.DREAMING && - deviceEntryInteractor.get().isUnlocked.value - ) { - // Dreams dismiss keyguard and return to GONE if they can. - false - } else if ( - startedWithPrev.newValue.from == KeyguardState.OCCLUDED && - startedWithPrev.newValue.to == KeyguardState.GONE - ) { - // OCCLUDED -> GONE directly, without transiting a *_BOUNCER state, occurs - // when an app uses intent flags to launch over an insecure keyguard without - // dismissing it, and then manually requests keyguard dismissal while - // OCCLUDED. This transition is not user-visible; the device unlocks in the - // background and the app remains on top, while we're now GONE. In this case - // we should simply tell WM that the lockscreen is no longer visible, and - // *not* play the going away animation or related animations. - false - } else { - currentState != KeyguardState.GONE - } - } - .distinctUntilChanged() - } + lockscreenVisibilityWithScenes + } else { + lockscreenVisibilityLegacy + } + .distinctUntilChanged() /** * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window 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 f3c6190d2f7b..1fbe8e2f21e5 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 @@ -213,7 +213,6 @@ constructor( /** Updates the visibility of the scene container. */ private fun hydrateVisibility() { applicationScope.launch { - // TODO(b/296114544): Combine with some global hun state to make it visible! deviceProvisioningInteractor.isDeviceProvisioned .flatMapLatest { isAllowedToBeVisible -> if (isAllowedToBeVisible) { |