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

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

Merge changes I44ae8865,I3328b2d2 into main

* changes:
  Fade keyguard when entering glanceable hub
  Add keyguard transitions between lockscreen and glanceable hub
parents d0d31e13 2608720c
Loading
Loading
Loading
Loading
+131 −2
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -39,6 +40,9 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -69,9 +73,9 @@ class CommunalInteractorTest : SysuiTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        testScope = TestScope()
        testScope = TestScope(StandardTestDispatcher())

        val withDeps = CommunalInteractorFactory.create()
        val withDeps = CommunalInteractorFactory.create(testScope)

        tutorialRepository = withDeps.tutorialRepository
        communalRepository = withDeps.communalRepository
@@ -378,6 +382,131 @@ class CommunalInteractorTest : SysuiTestCase() {
            assertThat(desiredScene()).isEqualTo(targetScene)
        }

    @Test
    fun transitionProgress_onTargetScene_fullProgress() =
        testScope.runTest {
            val targetScene = CommunalSceneKey.Blank
            val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
            val transitionProgress by collectLastValue(transitionProgressFlow)

            val transitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(targetScene)
                )
            underTest.setTransitionState(transitionState)

            // We're on the target scene.
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
        }

    @Test
    fun transitionProgress_notOnTargetScene_noProgress() =
        testScope.runTest {
            val targetScene = CommunalSceneKey.Blank
            val currentScene = CommunalSceneKey.Communal
            val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
            val transitionProgress by collectLastValue(transitionProgressFlow)

            val transitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(currentScene)
                )
            underTest.setTransitionState(transitionState)

            // Transition progress is still idle, but we're not on the target scene.
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))
        }

    @Test
    fun transitionProgress_transitioningToTrackedScene() =
        testScope.runTest {
            val currentScene = CommunalSceneKey.Communal
            val targetScene = CommunalSceneKey.Blank
            val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
            val transitionProgress by collectLastValue(transitionProgressFlow)

            var transitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(currentScene)
                )
            underTest.setTransitionState(transitionState)

            // Progress starts at 0.
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))

            val progress = MutableStateFlow(0f)
            transitionState =
                MutableStateFlow(
                    ObservableCommunalTransitionState.Transition(
                        fromScene = currentScene,
                        toScene = targetScene,
                        progress = progress,
                        isInitiatedByUserInput = false,
                        isUserInputOngoing = flowOf(false),
                    )
                )
            underTest.setTransitionState(transitionState)

            // Partially transition.
            progress.value = .4f
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(.4f))

            // Transition is at full progress.
            progress.value = 1f
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(1f))

            // Transition finishes.
            transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene))
            underTest.setTransitionState(transitionState)
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
        }

    @Test
    fun transitionProgress_transitioningAwayFromTrackedScene() =
        testScope.runTest {
            val currentScene = CommunalSceneKey.Blank
            val targetScene = CommunalSceneKey.Communal
            val transitionProgressFlow = underTest.transitionProgressToScene(currentScene)
            val transitionProgress by collectLastValue(transitionProgressFlow)

            var transitionState =
                MutableStateFlow<ObservableCommunalTransitionState>(
                    ObservableCommunalTransitionState.Idle(currentScene)
                )
            underTest.setTransitionState(transitionState)

            // Progress starts at 0.
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))

            val progress = MutableStateFlow(0f)
            transitionState =
                MutableStateFlow(
                    ObservableCommunalTransitionState.Transition(
                        fromScene = currentScene,
                        toScene = targetScene,
                        progress = progress,
                        isInitiatedByUserInput = false,
                        isUserInputOngoing = flowOf(false),
                    )
                )
            underTest.setTransitionState(transitionState)

            // Partially transition.
            progress.value = .4f

            // This is a transition we don't care about the progress of.
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition)

            // Transition is at full progress.
            progress.value = 1f
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition)

            // Transition finishes.
            transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene))
            underTest.setTransitionState(transitionState)
            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
        }

    @Test
    fun isCommunalShowing() =
        testScope.runTest {
+34 −0
Original line number Diff line number Diff line
@@ -203,4 +203,38 @@ class KeyguardRootViewModelTest : SysuiTestCase() {

            assertThat(isVisible?.isAnimating).isEqualTo(false)
        }

    @Test
    fun alpha_glanceableHubOpen_isZero() =
        testScope.runTest {
            val alpha by collectLastValue(underTest.alpha)

            keyguardTransitionRepository.sendTransitionSteps(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.GLANCEABLE_HUB,
                testScope,
            )

            assertThat(alpha).isEqualTo(0f)
        }

    @Test
    fun alpha_glanceableHubClosed_isOne() =
        testScope.runTest {
            val alpha by collectLastValue(underTest.alpha)

            // Transition to the glanceable hub and back.
            keyguardTransitionRepository.sendTransitionSteps(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.GLANCEABLE_HUB,
                testScope,
            )
            keyguardTransitionRepository.sendTransitionSteps(
                from = KeyguardState.GLANCEABLE_HUB,
                to = KeyguardState.LOCKSCREEN,
                testScope,
            )

            assertThat(alpha).isEqualTo(1.0f)
        }
}
+39 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

@@ -77,6 +79,29 @@ constructor(
        communalRepository.setTransitionState(transitionState)
    }

    /** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
    fun transitionProgressToScene(targetScene: CommunalSceneKey) =
        transitionState
            .flatMapLatest { state ->
                when (state) {
                    is ObservableCommunalTransitionState.Idle ->
                        flowOf(CommunalTransitionProgress.Idle(state.scene))
                    is ObservableCommunalTransitionState.Transition ->
                        if (state.toScene == targetScene) {
                            state.progress.map {
                                CommunalTransitionProgress.Transition(
                                    // Clamp the progress values between 0 and 1 as actual progress
                                    // values can be higher than 0 or lower than 1 due to a fling.
                                    progress = it.coerceIn(0.0f, 1.0f)
                                )
                            }
                        } else {
                            flowOf(CommunalTransitionProgress.OtherTransition)
                        }
                }
            }
            .distinctUntilChanged()

    /**
     * Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
     * [CommunalSceneKey.Communal].
@@ -232,3 +257,17 @@ constructor(
        }
    }
}

/** Simplified transition progress data class for tracking a single transition between scenes. */
sealed class CommunalTransitionProgress {
    /** No transition/animation is currently running. */
    data class Idle(val scene: CommunalSceneKey) : CommunalTransitionProgress()

    /** There is a transition animating to the expected scene. */
    data class Transition(
        val progress: Float,
    ) : CommunalTransitionProgress()

    /** There is a transition animating to a scene other than the expected scene. */
    data object OtherTransition : CommunalTransitionProgress()
}
+15 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.Flags
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -32,6 +33,7 @@ import kotlinx.coroutines.CoroutineDispatcher
class FromGlanceableHubTransitionInteractor
@Inject
constructor(
    private val glanceableHubTransitions: GlanceableHubTransitions,
    override val transitionRepository: KeyguardTransitionRepository,
    transitionInteractor: KeyguardTransitionInteractor,
    @Main mainDispatcher: CoroutineDispatcher,
@@ -47,6 +49,7 @@ constructor(
        if (!Flags.communalHub()) {
            return
        }
        listenForHubToLockscreen()
    }

    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -56,6 +59,18 @@ constructor(
        }
    }

    /**
     * Listens for the glanceable hub transition to lock screen and directly drives the keyguard
     * transition.
     */
    private fun listenForHubToLockscreen() {
        glanceableHubTransitions.listenForLockscreenAndHubTransition(
            transitionName = "listenForHubToLockscreen",
            transitionOwnerName = TAG,
            toScene = CommunalSceneKey.Blank
        )
    }

    companion object {
        const val TAG = "FromGlanceableHubTransitionInteractor"
        val DEFAULT_DURATION = 500.milliseconds
+20 −0
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.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -62,6 +63,7 @@ constructor(
    private val flags: FeatureFlags,
    private val shadeRepository: ShadeRepository,
    private val powerInteractor: PowerInteractor,
    private val glanceableHubTransitions: GlanceableHubTransitions,
    inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
) :
    TransitionInteractor(
@@ -81,6 +83,7 @@ constructor(
        listenForLockscreenToPrimaryBouncerDragging()
        listenForLockscreenToAlternateBouncer()
        listenForLockscreenTransitionToCamera()
        listenForLockscreenToGlanceableHub()
    }

    /**
@@ -381,6 +384,22 @@ constructor(
        }
    }

    /**
     * Listens for transition from glanceable hub back to lock screen and directly drives the
     * keyguard transition.
     */
    private fun listenForLockscreenToGlanceableHub() {
        if (!com.android.systemui.Flags.communalHub()) {
            return
        }

        glanceableHubTransitions.listenForLockscreenAndHubTransition(
            transitionName = "listenForLockscreenToGlanceableHub",
            transitionOwnerName = TAG,
            toScene = CommunalSceneKey.Communal
        )
    }

    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
        return ValueAnimator().apply {
            interpolator = Interpolators.LINEAR
@@ -406,5 +425,6 @@ constructor(
        val TO_AOD_DURATION = 500.milliseconds
        val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
        val TO_GONE_DURATION = DEFAULT_DURATION
        val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
    }
}
Loading