Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5014643c authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] surfaceBehindVisibility" into main

parents c273474d 726dc874
Loading
Loading
Loading
Loading
+173 −0
Original line number Diff line number Diff line
@@ -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
@@ -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() =
+2 −1
Original line number Diff line number Diff line
@@ -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").
@@ -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
    }
}
+45 −16
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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 {
@@ -106,6 +134,7 @@ constructor(
                        }
                    }
                }
            }
            .distinctUntilChanged()

    /**
@@ -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)