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

Commit 993a0b42 authored by William Xiao's avatar William Xiao
Browse files

Add transitions between glanceable hub and occluded state

Glanceable hub may be occluded when launching a showWhenLocked activity
from a widget on the hub or when dreaming starts/stops. This change
only ensures KeyguardState is accurate, it doesn't include any of the
animation work that may be needed.

Bug: 319719842
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT
Test: atest KeyguardTransitionScenariosTest
Change-Id: I4ddf547aa1517fbc7ec5f7f993d5da0a814d5b1c
parent ac9f7236
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -40,13 +40,13 @@ class FromGlanceableHubTransitionInteractor
@Inject
constructor(
    @Background private val scope: CoroutineScope,
    @Main mainDispatcher: CoroutineDispatcher,
    @Background bgDispatcher: CoroutineDispatcher,
    private val glanceableHubTransitions: GlanceableHubTransitions,
    private val keyguardInteractor: KeyguardInteractor,
    override val transitionRepository: KeyguardTransitionRepository,
    transitionInteractor: KeyguardTransitionInteractor,
    private val powerInteractor: PowerInteractor,
    @Main mainDispatcher: CoroutineDispatcher,
    @Background bgDispatcher: CoroutineDispatcher,
) :
    TransitionInteractor(
        fromState = KeyguardState.GLANCEABLE_HUB,
@@ -62,6 +62,7 @@ constructor(
        listenForHubToDozing()
        listenForHubToPrimaryBouncer()
        listenForHubToAlternateBouncer()
        listenForHubToOccluded()
        listenForHubToGone()
    }

@@ -130,6 +131,17 @@ constructor(
        }
    }

    private fun listenForHubToOccluded() {
        scope.launch {
            keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
                (isOccluded, keyguardState) ->
                if (isOccluded && keyguardState == fromState) {
                    startTransitionTo(KeyguardState.OCCLUDED)
                }
            }
        }
    }

    private fun listenForHubToGone() {
        scope.launch {
            keyguardInteractor.isKeyguardGoingAway
+16 −10
Original line number Diff line number Diff line
@@ -18,12 +18,14 @@ package com.android.systemui.keyguard.domain.interactor

import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -44,6 +46,7 @@ constructor(
    @Main mainDispatcher: CoroutineDispatcher,
    private val keyguardInteractor: KeyguardInteractor,
    private val powerInteractor: PowerInteractor,
    private val communalInteractor: CommunalInteractor,
) :
    TransitionInteractor(
        fromState = KeyguardState.OCCLUDED,
@@ -53,7 +56,7 @@ constructor(
    ) {

    override fun start() {
        listenForOccludedToLockscreen()
        listenForOccludedToLockscreenOrHub()
        listenForOccludedToDreaming()
        listenForOccludedToAodOrDozing()
        listenForOccludedToGone()
@@ -86,18 +89,15 @@ constructor(
        }
    }

    private fun listenForOccludedToLockscreen() {
    private fun listenForOccludedToLockscreenOrHub() {
        scope.launch {
            keyguardInteractor.isKeyguardOccluded
                .sample(
                    combine(
                    keyguardInteractor.isKeyguardShowing,
                    startedKeyguardTransitionStep,
                        ::Pair
                    ),
                    ::toTriple
                    communalInteractor.isIdleOnCommunal,
                )
                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
                .collect { (isOccluded, isShowing, lastStartedKeyguardState, isIdleOnCommunal) ->
                    // Occlusion signals come from the framework, and should interrupt any
                    // existing transition
                    if (
@@ -105,7 +105,13 @@ constructor(
                            isShowing &&
                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
                    ) {
                        startTransitionTo(KeyguardState.LOCKSCREEN)
                        val to =
                            if (isIdleOnCommunal) {
                                KeyguardState.GLANCEABLE_HUB
                            } else {
                                KeyguardState.LOCKSCREEN
                            }
                        startTransitionTo(to)
                    }
                }
        }
+71 −0
Original line number Diff line number Diff line
@@ -270,6 +270,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
                    transitionRepository = transitionRepository,
                    transitionInteractor = transitionInteractor,
                    powerInteractor = powerInteractor,
                    communalInteractor = communalInteractor,
                )
                .apply { start() }

@@ -1300,6 +1301,43 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
            coroutineContext.cancelChildren()
        }

    @Test
    fun occludedToGlanceableHub() =
        testScope.runTest {
            // GIVEN a device on lockscreen
            keyguardRepository.setKeyguardShowing(true)
            runCurrent()

            // GIVEN the device is idle on the glanceable hub
            val idleTransitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
                )
            communalInteractor.setTransitionState(idleTransitionState)
            runCurrent()

            // GIVEN a prior transition has run to OCCLUDED
            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
            keyguardRepository.setKeyguardOccluded(true)
            runCurrent()

            // WHEN occlusion ends
            keyguardRepository.setKeyguardOccluded(false)
            runCurrent()

            val info =
                withArgCaptor<TransitionInfo> {
                    verify(transitionRepository).startTransition(capture())
                }
            // THEN a transition to GLANCEABLE_HUB should occur
            assertThat(info.ownerName).isEqualTo(FromOccludedTransitionInteractor::class.simpleName)
            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
            assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
            assertThat(info.animator).isNotNull()

            coroutineContext.cancelChildren()
        }

    @Test
    fun occludedToAlternateBouncer() =
        testScope.runTest {
@@ -1796,6 +1834,39 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
            coroutineContext.cancelChildren()
        }

    @Test
    fun glanceableHubToOccluded() =
        testScope.runTest {
            // GIVEN a prior transition has run to GLANCEABLE_HUB
            runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.GLANCEABLE_HUB)
            runCurrent()

            // GIVEN the device is idle on the glanceable hub
            val idleTransitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
                )
            communalInteractor.setTransitionState(idleTransitionState)
            runCurrent()

            // WHEN the keyguard is occluded
            keyguardRepository.setKeyguardOccluded(true)
            runCurrent()

            val info =
                withArgCaptor<TransitionInfo> {
                    verify(transitionRepository).startTransition(capture())
                }
            // THEN a transition to OCCLUDED should occur
            assertThat(info.ownerName)
                .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
            assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
            assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
            assertThat(info.animator).isNotNull()

            coroutineContext.cancelChildren()
        }

    @Test
    fun glanceableHubToGone() =
        testScope.runTest {