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

Commit 7b3abf1c authored by William Xiao's avatar William Xiao Committed by Android (Google) Code Review
Browse files

Merge changes I33b84f05,I3f746a2c into main

* changes:
  Add transitions between glanceable hub and dozing
  Add helper methods to CommunalInteractor
parents e34c1d2d 67d0f996
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -556,6 +556,43 @@ class CommunalInteractorTest : SysuiTestCase() {
            assertThat(isCommunalShowing()).isEqualTo(true)
        }

    @Test
    fun isIdleOnCommunal() =
        testScope.runTest {
            val transitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank)
                )
            communalRepository.setTransitionState(transitionState)

            // isIdleOnCommunal is false when not on communal.
            val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
            runCurrent()
            assertThat(isIdleOnCommunal).isEqualTo(false)

            // Transition to communal.
            transitionState.value =
                ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
            runCurrent()

            // isIdleOnCommunal is now true since we're on communal.
            assertThat(isIdleOnCommunal).isEqualTo(true)

            // Start transition away from communal.
            transitionState.value =
                ObservableCommunalTransitionState.Transition(
                    fromScene = CommunalSceneKey.Communal,
                    toScene = CommunalSceneKey.Blank,
                    progress = flowOf(0f),
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            runCurrent()

            // isIdleOnCommunal turns false as soon as transition away starts.
            assertThat(isIdleOnCommunal).isEqualTo(false)
        }

    @Test
    fun testShowWidgetEditorStartsActivity() =
        testScope.runTest {
+12 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ import kotlinx.coroutines.flow.stateIn
class CommunalInteractor
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    @Application applicationScope: CoroutineScope,
    private val communalRepository: CommunalRepository,
    private val widgetRepository: CommunalWidgetRepository,
    private val communalPrefsRepository: CommunalPrefsRepository,
@@ -134,6 +134,17 @@ constructor(
    val isCommunalShowing: Flow<Boolean> =
        communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }

    /**
     * Flow that emits a boolean if the communal UI is fully visible and not in transition.
     *
     * This will not be true while transitioning to the hub and will turn false immediately when a
     * swipe to exit the hub starts.
     */
    val isIdleOnCommunal: Flow<Boolean> =
        communalRepository.transitionState.map {
            it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Communal
        }

    val isKeyguardVisible: Flow<Boolean> = keyguardInteractor.isKeyguardVisible

    /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
+16 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ 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
@@ -25,7 +26,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -45,6 +46,7 @@ constructor(
    @Main mainDispatcher: CoroutineDispatcher,
    private val keyguardInteractor: KeyguardInteractor,
    private val powerInteractor: PowerInteractor,
    private val communalInteractor: CommunalInteractor,
) :
    TransitionInteractor(
        fromState = KeyguardState.DOZING,
@@ -54,26 +56,33 @@ constructor(
    ) {

    override fun start() {
        listenForDozingToLockscreenOrOccluded()
        listenForDozingToLockscreenHubOrOccluded()
        listenForDozingToGone()
        listenForTransitionToCamera(scope, keyguardInteractor)
    }

    private fun listenForDozingToLockscreenOrOccluded() {
    private fun listenForDozingToLockscreenHubOrOccluded() {
        scope.launch {
            powerInteractor.isAwake
                .sample(
                    combine(
                        startedKeyguardTransitionStep,
                        keyguardInteractor.isKeyguardOccluded,
                        ::Pair
                        communalInteractor.isIdleOnCommunal,
                        ::Triple
                    ),
                    ::toTriple
                    ::toQuad
                )
                .collect { (isAwake, lastStartedTransition, occluded) ->
                .collect { (isAwake, lastStartedTransition, occluded, isIdleOnCommunal) ->
                    if (isAwake && lastStartedTransition.to == KeyguardState.DOZING) {
                        startTransitionTo(
                            if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
                            if (occluded) {
                                KeyguardState.OCCLUDED
                            } else if (isIdleOnCommunal) {
                                KeyguardState.GLANCEABLE_HUB
                            } else {
                                KeyguardState.LOCKSCREEN
                            }
                        )
                    }
                }
+22 −0
Original line number Diff line number Diff line
@@ -25,17 +25,24 @@ 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.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@SysUISingleton
class FromGlanceableHubTransitionInteractor
@Inject
constructor(
    @Background private val scope: CoroutineScope,
    private val glanceableHubTransitions: GlanceableHubTransitions,
    override val transitionRepository: KeyguardTransitionRepository,
    transitionInteractor: KeyguardTransitionInteractor,
    private val powerInteractor: PowerInteractor,
    @Main mainDispatcher: CoroutineDispatcher,
    @Background bgDispatcher: CoroutineDispatcher,
) :
@@ -50,6 +57,7 @@ constructor(
            return
        }
        listenForHubToLockscreen()
        listenForHubToDozing()
    }

    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -71,6 +79,20 @@ constructor(
        )
    }

    private fun listenForHubToDozing() {
        scope.launch {
            powerInteractor.isAsleep.sample(startedKeyguardTransitionStep, ::Pair).collect {
                (isAsleep, lastStartedStep) ->
                if (lastStartedStep.to == fromState && isAsleep) {
                    startTransitionTo(
                        toState = KeyguardState.DOZING,
                        modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
                    )
                }
            }
        }
    }

    companion object {
        const val TAG = "FromGlanceableHubTransitionInteractor"
        val DEFAULT_DURATION = 500.milliseconds
+59 −0
Original line number Diff line number Diff line
@@ -250,6 +250,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
                    transitionRepository = transitionRepository,
                    transitionInteractor = transitionInteractor,
                    powerInteractor = powerInteractor,
                    communalInteractor = communalInteractor,
                )
                .apply { start() }

@@ -279,11 +280,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {

        fromGlanceableHubTransitionInteractor =
            FromGlanceableHubTransitionInteractor(
                    scope = testScope,
                    bgDispatcher = testDispatcher,
                    mainDispatcher = testDispatcher,
                    glanceableHubTransitions = glanceableHubTransitions,
                    transitionRepository = transitionRepository,
                    transitionInteractor = transitionInteractor,
                    powerInteractor = powerInteractor,
                )
                .apply { start() }
    }
@@ -724,6 +727,38 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
            coroutineContext.cancelChildren()
        }

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

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

            // WHEN the device begins to wake
            powerInteractor.setAwakeForTest()
            runCurrent()

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

            coroutineContext.cancelChildren()
        }

    @Test
    fun goneToDozing() =
        testScope.runTest {
@@ -1577,6 +1612,30 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
            coroutineContext.cancelChildren()
        }

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

            // WHEN the device begins to sleep
            powerInteractor.setAsleepForTest()
            runCurrent()

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

            coroutineContext.cancelChildren()
        }

    private fun createKeyguardInteractor(): KeyguardInteractor {
        return KeyguardInteractorFactory.create(
                featureFlags = featureFlags,
Loading