Loading packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt→packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +173 −0 Original line number Diff line number Diff line Loading @@ -27,18 +27,22 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 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.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor 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 com.google.common.truth.Truth.assertWithMessage import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf Loading Loading @@ -191,6 +195,175 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { ) } @Test @EnableSceneContainer fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Before the transition, we start on Lockscreen so the surface should start invisible. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen)) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isFalse() // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) // Start the transition to Gone, the surface should become immediately visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Lockscreen, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf(0.3f), currentScene = flowOf(Scenes.Lockscreen), ) ) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isTrue() // Towards the end of the transition, the surface should continue to be visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Lockscreen, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf(0.9f), currentScene = flowOf(Scenes.Gone), ) ) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isTrue() // After the transition, settles on Gone. Surface behind should stay visible now. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(isSurfaceBehindVisible).isTrue() } @Test @EnableSceneContainer fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Before the transition, we start on Bouncer so the surface should start invisible. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer)) kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) // Start the transition to Gone, the surface should remain invisible prior to hitting // the // threshold. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Bouncer, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD ), currentScene = flowOf(Scenes.Bouncer), ) ) assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Once the transition passes the threshold, the surface should become visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Bouncer, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f ), currentScene = flowOf(Scenes.Gone), ) ) assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isTrue() // After the transition, settles on Gone. Surface behind should stay visible now. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(isSurfaceBehindVisible).isTrue() } @Test @EnableSceneContainer fun surfaceBehindVisibility_idleWhileUnlocked_alwaysTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) 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() } } @Test @EnableSceneContainer fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) 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() } } @Test @DisableSceneContainer fun testUsingGoingAwayAnimation_duringTransitionToGone() = Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +2 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ constructor( return@combine null } fromBouncerStep.value > 0.5f fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } .onStart { // Default to null ("don't care, use a reasonable default"). Loading Loading @@ -232,5 +232,6 @@ constructor( val TO_AOD_DURATION = DEFAULT_DURATION val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION val TO_DOZING_DURATION = DEFAULT_DURATION val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f } } packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +45 −16 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.shared.model.BiometricUnlockMode Loading @@ -29,6 +30,7 @@ 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.sample import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -84,14 +86,40 @@ constructor( } .distinctUntilChanged() private val isDeviceEntered: Flow<Boolean> by lazy { deviceEntryInteractor.get().isDeviceEntered } private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } } /** * Surface visibility, which is either determined by the default visibility in the FINISHED * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions. * Surface visibility, which is either determined by the default visibility when not * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used * during certain ongoing transitions. */ @OptIn(ExperimentalCoroutinesApi::class) val surfaceBehindVisibility: Flow<Boolean> = transitionInteractor.isInTransitionToAnyState .flatMapLatest { isInTransition -> if (SceneContainerFlag.isEnabled) { sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState -> when (transitionState) { is ObservableTransitionState.Transition -> when { transitionState.fromScene == Scenes.Lockscreen && transitionState.toScene == Scenes.Gone -> flowOf(true) transitionState.fromScene == Scenes.Bouncer && transitionState.toScene == Scenes.Gone -> transitionState.progress.map { progress -> progress > FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } else -> isDeviceEntered } is ObservableTransitionState.Idle -> isDeviceEntered } } } else { transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition -> if (!isInTransition) { defaultSurfaceBehindVisibility } else { Loading @@ -106,6 +134,7 @@ constructor( } } } } .distinctUntilChanged() /** Loading Loading @@ -162,7 +191,7 @@ constructor( */ val lockscreenVisibility: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { deviceEntryInteractor.get().isDeviceEntered.map { !it } isDeviceNotEntered } else { transitionInteractor.currentKeyguardState .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair) Loading Loading
packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt→packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +173 −0 Original line number Diff line number Diff line Loading @@ -27,18 +27,22 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 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.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor 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 com.google.common.truth.Truth.assertWithMessage import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf Loading Loading @@ -191,6 +195,175 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { ) } @Test @EnableSceneContainer fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Before the transition, we start on Lockscreen so the surface should start invisible. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen)) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isFalse() // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) // Start the transition to Gone, the surface should become immediately visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Lockscreen, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf(0.3f), currentScene = flowOf(Scenes.Lockscreen), ) ) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isTrue() // Towards the end of the transition, the surface should continue to be visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Lockscreen, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf(0.9f), currentScene = flowOf(Scenes.Gone), ) ) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) assertThat(isSurfaceBehindVisible).isTrue() // After the transition, settles on Gone. Surface behind should stay visible now. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(isSurfaceBehindVisible).isTrue() } @Test @EnableSceneContainer fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Before the transition, we start on Bouncer so the surface should start invisible. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer)) kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) // Start the transition to Gone, the surface should remain invisible prior to hitting // the // threshold. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Bouncer, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD ), currentScene = flowOf(Scenes.Bouncer), ) ) assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Once the transition passes the threshold, the surface should become visible. kosmos.setSceneTransition( ObservableTransitionState.Transition( fromScene = Scenes.Bouncer, toScene = Scenes.Gone, isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f ), currentScene = flowOf(Scenes.Gone), ) ) assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isSurfaceBehindVisible).isTrue() // After the transition, settles on Gone. Surface behind should stay visible now. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(isSurfaceBehindVisible).isTrue() } @Test @EnableSceneContainer fun surfaceBehindVisibility_idleWhileUnlocked_alwaysTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) // Unlocked with fingerprint. kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) 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() } } @Test @EnableSceneContainer fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) 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() } } @Test @DisableSceneContainer fun testUsingGoingAwayAnimation_duringTransitionToGone() = Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +2 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ constructor( return@combine null } fromBouncerStep.value > 0.5f fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } .onStart { // Default to null ("don't care, use a reasonable default"). Loading Loading @@ -232,5 +232,6 @@ constructor( val TO_AOD_DURATION = DEFAULT_DURATION val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION val TO_DOZING_DURATION = DEFAULT_DURATION val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f } }
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +45 −16 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.shared.model.BiometricUnlockMode Loading @@ -29,6 +30,7 @@ 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.sample import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -84,14 +86,40 @@ constructor( } .distinctUntilChanged() private val isDeviceEntered: Flow<Boolean> by lazy { deviceEntryInteractor.get().isDeviceEntered } private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } } /** * Surface visibility, which is either determined by the default visibility in the FINISHED * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions. * Surface visibility, which is either determined by the default visibility when not * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used * during certain ongoing transitions. */ @OptIn(ExperimentalCoroutinesApi::class) val surfaceBehindVisibility: Flow<Boolean> = transitionInteractor.isInTransitionToAnyState .flatMapLatest { isInTransition -> if (SceneContainerFlag.isEnabled) { sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState -> when (transitionState) { is ObservableTransitionState.Transition -> when { transitionState.fromScene == Scenes.Lockscreen && transitionState.toScene == Scenes.Gone -> flowOf(true) transitionState.fromScene == Scenes.Bouncer && transitionState.toScene == Scenes.Gone -> transitionState.progress.map { progress -> progress > FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } else -> isDeviceEntered } is ObservableTransitionState.Idle -> isDeviceEntered } } } else { transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition -> if (!isInTransition) { defaultSurfaceBehindVisibility } else { Loading @@ -106,6 +134,7 @@ constructor( } } } } .distinctUntilChanged() /** Loading Loading @@ -162,7 +191,7 @@ constructor( */ val lockscreenVisibility: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { deviceEntryInteractor.get().isDeviceEntered.map { !it } isDeviceNotEntered } else { transitionInteractor.currentKeyguardState .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair) Loading