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

Commit a3fa000a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ic5bc0907,If8c69911 into main

* changes:
  [flexiglass] Fix surfaceBehindVisibility
  [flexiglass] Rewrite lockscreenVisibility logic
parents bf6451e4 86d55956
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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) ||
+96 −81
Original line number Diff line number Diff line
@@ -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,13 +358,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
            kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
            assertThat(currentScene).isEqualTo(Scenes.Gone)

            listOf(
                    Scenes.Shade,
                    Scenes.QuickSettings,
                    Scenes.Shade,
                    Scenes.Gone,
                )
                .forEach { scene ->
            listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Gone).forEach { scene ->
                kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
                kosmos.sceneInteractor.changeScene(scene, "")
                assertThat(currentScene).isEqualTo(scene)
@@ -386,13 +376,8 @@ 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 ->
            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}\"")
@@ -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),
            )
    }
}
+144 −94
Original line number Diff line number Diff line
@@ -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 ->
                sceneInteractor.get().transitionState.flatMapLatestConflated { state ->
                    when {
                                transitionState.fromContent == Scenes.Lockscreen &&
                                    transitionState.toContent == Scenes.Gone ->
                                    sceneInteractor
                                        .get()
                                        .isTransitionUserInputOngoing
                                        .flatMapLatestConflated { isUserInputOngoing ->
                        state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) ->
                            (state as Transition).isUserInputOngoing.flatMapLatestConflated {
                                isUserInputOngoing ->
                                if (isUserInputOngoing) {
                                    isDeviceEntered
                                } else {
                                    flowOf(true)
                                }
                            }
                                transitionState.fromContent == Scenes.Bouncer &&
                                    transitionState.toContent == Scenes.Gone ->
                                    transitionState.progress.map { progress ->
                        state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) ->
                            (state as Transition).progress.map { progress ->
                                progress >
                                    FromPrimaryBouncerTransitionInteractor
                                        .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
                            }
                                else -> isDeviceEntered
                            }
                        is ObservableTransitionState.Idle -> isDeviceEntered
                        else -> lockscreenVisibilityWithScenes.map { !it }
                    }
                }
            } else {
@@ -219,16 +210,62 @@ constructor(
        }

    /**
     * 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
     * only inform WM once we're done with the keyguard and we're fully GONE. Don't use this if you
     * want to know if the AOD/clock/notifs/etc. are visible.
     * Scenes that are part of the keyguard and are shown when the device is locked or when the
     * keyguard still needs to be dismissed.
     */
    val lockscreenVisibility: Flow<Boolean> =
        if (SceneContainerFlag.isEnabled) {
    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 {
                                else ->
                                    throw IllegalStateException("Unknown scene: ${it.fromContent}")
                            }
                        }
                    }
                },
                wakeToGoneInteractor.canWakeDirectlyToGone,
                ::Pair,
            )
            .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) ->
                lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone
            }

    private val lockscreenVisibilityLegacy =
        combine(
                transitionInteractor.currentKeyguardState,
                wakeToGoneInteractor.canWakeDirectlyToGone,
@@ -288,8 +325,21 @@ constructor(
                    currentState != KeyguardState.GONE
                }
            }
                .distinctUntilChanged()

    /**
     * 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
     * only inform WM once we're done with the keyguard and we're fully GONE. Don't use this if you
     * want to know if the AOD/clock/notifs/etc. are visible.
     */
    val lockscreenVisibility: Flow<Boolean> =
        if (SceneContainerFlag.isEnabled) {
                lockscreenVisibilityWithScenes
            } else {
                lockscreenVisibilityLegacy
            }
            .distinctUntilChanged()

    /**
     * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
+0 −1
Original line number Diff line number Diff line
@@ -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) {