diff options
5 files changed, 226 insertions, 45 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 d924d88d1e59..92d5c26148e4 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 @@ -74,6 +74,16 @@ sealed interface ObservableTransitionState { */ val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState + + fun isIdle(scene: SceneKey?): Boolean { + return this is Idle && (scene == null || this.currentScene == scene) + } + + fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean { + return this is Transition && + (from == null || this.fromScene == from) && + (to == null || this.toScene == to) + } } /** 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 bb2eeb77969d..dc35e4311d25 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 @@ -16,11 +16,16 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockMode import com.android.systemui.keyguard.shared.model.KeyguardState 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.Scenes import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,6 +47,7 @@ constructor( fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor, fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor, notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, + sceneInteractor: SceneInteractor, ) { private val defaultSurfaceBehindVisibility = transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible) @@ -103,21 +109,42 @@ constructor( * animation. This is used to keep the RemoteAnimationTarget alive until we're done using it. */ val usingKeyguardGoingAwayAnimation: Flow<Boolean> = - combine( - transitionInteractor.isInTransitionToState(KeyguardState.GONE), - transitionInteractor.finishedKeyguardState, - surfaceBehindInteractor.isAnimatingSurface, - notificationLaunchAnimationInteractor.isLaunchAnimationRunning, - ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning -> - // Using the animation if we're animating it directly, or if the - // ActivityLaunchAnimator is in the process of animating it. - val animationsRunning = isAnimatingSurface || notifLaunchRunning - // We may still be animating the surface after the keyguard is fully GONE, since - // some animations (like the translation spring) are not tied directly to the - // transition step amount. - isInTransitionToGone || (finishedState == KeyguardState.GONE && animationsRunning) - } - .distinctUntilChanged() + if (SceneContainerFlag.isEnabled) { + combine( + sceneInteractor.transitionState, + surfaceBehindInteractor.isAnimatingSurface, + notificationLaunchAnimationInteractor.isLaunchAnimationRunning, + ) { transition, isAnimatingSurface, isLaunchAnimationRunning -> + // Using the animation if we're animating it directly, or if the + // ActivityLaunchAnimator is in the process of animating it. + val isAnyAnimationRunning = isAnimatingSurface || isLaunchAnimationRunning + // We may still be animating the surface after the keyguard is fully GONE, since + // some animations (like the translation spring) are not tied directly to the + // transition step amount. + transition.isTransitioning(to = Scenes.Gone) || + (isAnyAnimationRunning && + (transition.isIdle(Scenes.Gone) || + transition.isTransitioning(from = Scenes.Gone))) + } + .distinctUntilChanged() + } else { + combine( + transitionInteractor.isInTransitionToState(KeyguardState.GONE), + transitionInteractor.finishedKeyguardState, + surfaceBehindInteractor.isAnimatingSurface, + notificationLaunchAnimationInteractor.isLaunchAnimationRunning, + ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning -> + // Using the animation if we're animating it directly, or if the + // ActivityLaunchAnimator is in the process of animating it. + val animationsRunning = isAnimatingSurface || notifLaunchRunning + // We may still be animating the surface after the keyguard is fully GONE, since + // some animations (like the translation spring) are not tied directly to the + // transition step amount. + isInTransitionToGone || + (finishedState == KeyguardState.GONE && animationsRunning) + } + .distinctUntilChanged() + } /** * Whether the lockscreen is visible, from the Window Manager (WM) perspective. @@ -127,28 +154,44 @@ constructor( * want to know if the AOD/clock/notifs/etc. are visible. */ val lockscreenVisibility: Flow<Boolean> = - transitionInteractor.currentKeyguardState - .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair) - .map { (currentState, startedWithPrev) -> - val startedFromStep = startedWithPrev?.previousValue - val startedStep = startedWithPrev?.newValue - val returningToGoneAfterCancellation = - startedStep?.to == KeyguardState.GONE && - startedFromStep?.transitionState == TransitionState.CANCELED && - startedFromStep.from == KeyguardState.GONE + if (SceneContainerFlag.isEnabled) { + sceneInteractor.transitionState + .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen)) + .map { (prevTransitionState, transitionState) -> + val isReturningToGoneAfterCancellation = + prevTransitionState.isTransitioning(from = Scenes.Gone) && + transitionState.isTransitioning(to = Scenes.Gone) + val isNotOnGone = + !transitionState.isTransitioning(from = Scenes.Gone) && + !transitionState.isIdle(Scenes.Gone) - if (!returningToGoneAfterCancellation) { - // By default, apply the lockscreen visibility of the current state. - KeyguardState.lockscreenVisibleInState(currentState) - } else { - // If we're transitioning to GONE after a prior canceled transition from GONE, - // then this is the camera launch transition from an asleep state back to GONE. - // We don't want to show the lockscreen since we're aborting the lock and going - // back to GONE. - KeyguardState.lockscreenVisibleInState(KeyguardState.GONE) + isNotOnGone && !isReturningToGoneAfterCancellation } - } - .distinctUntilChanged() + .distinctUntilChanged() + } else { + transitionInteractor.currentKeyguardState + .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair) + .map { (currentState, startedWithPrev) -> + val startedFromStep = startedWithPrev?.previousValue + val startedStep = startedWithPrev?.newValue + val returningToGoneAfterCancellation = + startedStep?.to == KeyguardState.GONE && + startedFromStep?.transitionState == TransitionState.CANCELED && + startedFromStep.from == KeyguardState.GONE + + if (!returningToGoneAfterCancellation) { + // By default, apply the lockscreen visibility of the current state. + KeyguardState.lockscreenVisibleInState(currentState) + } else { + // If we're transitioning to GONE after a prior canceled transition from + // GONE, then this is the camera launch transition from an asleep state back + // to GONE. We don't want to show the lockscreen since we're aborting the + // lock and going back to GONE. + KeyguardState.lockscreenVisibleInState(KeyguardState.GONE) + } + } + .distinctUntilChanged() + } /** * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index b1a8dd1d3fdc..a77169e74de5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -18,20 +18,29 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState 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.sceneContainerRepository +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -57,14 +66,22 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { .thenReturn(surfaceBehindIsAnimatingFlow) } - private val underTest = kosmos.windowManagerLockscreenVisibilityInteractor + private val underTest = lazy { kosmos.windowManagerLockscreenVisibilityInteractor } private val testScope = kosmos.testScope private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + @Before + fun setUp() { + // lazy value needs to be called here otherwise flow collection misbehaves + underTest.value + kosmos.sceneContainerRepository.setTransitionState(sceneTransitions) + } + @Test + @DisableSceneContainer fun surfaceBehindVisibility_switchesToCorrectFlow() = testScope.runTest { - val values by collectValues(underTest.surfaceBehindVisibility) + val values by collectValues(underTest.value.surfaceBehindVisibility) // Start on LOCKSCREEN. transitionRepository.sendTransitionStep( @@ -170,9 +187,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun testUsingGoingAwayAnimation_duringTransitionToGone() = testScope.runTest { - val values by collectValues(underTest.usingKeyguardGoingAwayAnimation) + val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation) // Start on LOCKSCREEN. transitionRepository.sendTransitionStep( @@ -230,9 +248,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() = testScope.runTest { - val values by collectValues(underTest.usingKeyguardGoingAwayAnimation) + val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation) // Start on LOCKSCREEN. transitionRepository.sendTransitionStep( @@ -319,9 +338,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun lockscreenVisibility_visibleWhenGone() = testScope.runTest { - val values by collectValues(underTest.lockscreenVisibility) + val values by collectValues(underTest.value.lockscreenVisibility) // Start on LOCKSCREEN. transitionRepository.sendTransitionStep( @@ -385,9 +405,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun testLockscreenVisibility_usesFromState_ifCanceled() = testScope.runTest { - val values by collectValues(underTest.lockscreenVisibility) + val values by collectValues(underTest.value.lockscreenVisibility) transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, @@ -486,9 +507,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { * state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true). */ @Test + @DisableSceneContainer fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() = testScope.runTest { - val values by collectValues(underTest.lockscreenVisibility) + val values by collectValues(underTest.value.lockscreenVisibility) transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, @@ -587,11 +609,11 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { ) } - /** */ @Test + @DisableSceneContainer fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() = testScope.runTest { - val values by collectValues(underTest.lockscreenVisibility) + val values by collectValues(underTest.value.lockscreenVisibility) transitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, @@ -702,4 +724,109 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { values ) } + + @Test + @EnableSceneContainer + fun sceneContainer_lockscreenVisibility_visibleWhenNotGone() = + testScope.runTest { + val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility) + + sceneTransitions.value = lsToGone + assertThat(lockscreenVisibility).isTrue() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = goneToLs + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen) + assertThat(lockscreenVisibility).isTrue() + } + + @Test + @EnableSceneContainer + fun sceneContainer_lockscreenVisibility_notVisibleWhenReturningToGone() = + testScope.runTest { + val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility) + + sceneTransitions.value = goneToLs + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = lsToGone + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = goneToLs + assertThat(lockscreenVisibility).isFalse() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen) + assertThat(lockscreenVisibility).isTrue() + } + + @Test + @EnableSceneContainer + fun sceneContainer_usingGoingAwayAnimation_duringTransitionToGone() = + testScope.runTest { + val usingKeyguardGoingAwayAnimation by + collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation) + + sceneTransitions.value = lsToGone + assertThat(usingKeyguardGoingAwayAnimation).isTrue() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + assertThat(usingKeyguardGoingAwayAnimation).isFalse() + } + + @Test + @EnableSceneContainer + fun sceneContainer_usingGoingAwayAnimation_surfaceBehindIsAnimating() = + testScope.runTest { + val usingKeyguardGoingAwayAnimation by + collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation) + + sceneTransitions.value = lsToGone + surfaceBehindIsAnimatingFlow.emit(true) + assertThat(usingKeyguardGoingAwayAnimation).isTrue() + + sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + assertThat(usingKeyguardGoingAwayAnimation).isTrue() + + sceneTransitions.value = goneToLs + assertThat(usingKeyguardGoingAwayAnimation).isTrue() + + surfaceBehindIsAnimatingFlow.emit(false) + assertThat(usingKeyguardGoingAwayAnimation).isFalse() + } + + companion object { + private val progress = MutableStateFlow(0f) + + private val sceneTransitions = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.Lockscreen) + ) + + private val lsToGone = + ObservableTransitionState.Transition( + Scenes.Lockscreen, + Scenes.Gone, + flowOf(Scenes.Lockscreen), + progress, + false, + flowOf(false) + ) + + private val goneToLs = + ObservableTransitionState.Transition( + Scenes.Gone, + Scenes.Lockscreen, + flowOf(Scenes.Lockscreen), + progress, + false, + flowOf(false) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 56e5e293c799..aac36405e9b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -138,7 +138,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt index 29167d64d1f1..b38acc8a46dc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor val Kosmos.windowManagerLockscreenVisibilityInteractor by @@ -29,5 +30,6 @@ val Kosmos.windowManagerLockscreenVisibilityInteractor by fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor, fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor, notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor, + sceneInteractor = sceneInteractor, ) } |