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

Commit 90f4c094 authored by Lucas Silva's avatar Lucas Silva
Browse files

Reset keyguard translation when transition is cancelled

When transitioning to/from the glanceable hub, we translate the keyguard
elements offscreen to the left. If the transition gets cancelled, we
should move the elements back to their original places.

This is reproducible with the following steps:
1) Swipe open the hub from the lockscreen
2) Swipe back slowly and pause halfway through the transition
3) Run "adb shell cmd dreams start-dreaming" to start dreams, while
   paused halfway through the transition.
4) The HUB->LOCKSCREEN transition is cancelled in favor of the HUB->DREAM transition
5) Notifications and clock are stuck with a weird x-translation

Fixes: 329096635
Test: atest SystemUiRoboTest:GlanceableHubToLockscreenTransitionViewModelTest
Test: atest KeyguardTransitionRepositoryTest
Flag: ACONFIG com.android.systemui.communal_hub TEAMFOOD
Change-Id: I09f50bd36c3191d9cb84a61fbb178e570bddcfe3
parent 8ff0d0dc
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -99,6 +99,31 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
            values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
        }

    @Test
    fun lockscreenTranslationX_resetsAfterCancellation() =
        testScope.runTest {
            configurationRepository.setDimensionPixelSize(
                R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
                100
            )
            val values by collectValues(underTest.keyguardTranslationX)
            assertThat(values).isEmpty()

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0.3f),
                    step(0.5f),
                    step(0.9f, TransitionState.CANCELED)
                ),
                testScope,
            )

            assertThat(values).hasSize(4)
            values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
            assertThat(values.last().value).isEqualTo(0f)
        }

    private fun step(
        value: Float,
        state: TransitionState = TransitionState.RUNNING
+5 −0
Original line number Diff line number Diff line
@@ -159,6 +159,11 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
        lastAnimator?.cancel()
        lastAnimator = info.animator

        // Cancel any existing manual transitions
        updateTransitionId?.let { uuid ->
            updateTransition(uuid, lastStep.value, TransitionState.CANCELED)
        }

        info.animator?.let { animator ->
            // An animator was provided, so use it to run the transition
            animator.setFloatValues(startingValue, 1f)
+5 −1
Original line number Diff line number Diff line
@@ -72,7 +72,11 @@ constructor(
                    duration = TO_LOCKSCREEN_DURATION,
                    onStep = { value -> -translatePx + value * translatePx },
                    interpolator = EMPHASIZED,
                    onCancel = { -translatePx.toFloat() },
                    // Move notifications back to their original position since they can be
                    // accessed from the shade, and also keyguard elements in case the animation
                    // is cancelled.
                    onFinish = { 0f },
                    onCancel = { 0f },
                    name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardTranslationX"
                )
            }
+2 −1
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ constructor(
                    duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
                    onStep = { value -> value * translatePx },
                    // Move notifications back to their original position since they can be
                    // accessed from the shade.
                    // accessed from the shade, and also keyguard elements in case the animation
                    // is cancelled.
                    onFinish = { 0f },
                    onCancel = { 0f },
                    interpolator = EMPHASIZED,
+99 −0
Original line number Diff line number Diff line
@@ -27,7 +27,9 @@ import com.android.app.animation.Interpolators
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -37,6 +39,7 @@ import com.google.common.truth.Truth.assertThat
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.UUID
import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -223,6 +226,101 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
            job.cancel()
        }

    @Test
    fun startingSecondManualTransitionWillCancelPreviousManualTransition() =
        TestScope().runTest {
            // Drop initial steps from OFF which are sent in the constructor
            val steps = mutableListOf<TransitionStep>()
            val job =
                underTest.transitions
                    .dropWhile { step -> step.from == OFF }
                    .onEach { steps.add(it) }
                    .launchIn(this)

            val firstUuid =
                underTest.startTransition(
                    TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
                )
            runCurrent()

            checkNotNull(firstUuid)
            underTest.updateTransition(firstUuid, 0.5f, TransitionState.RUNNING)
            runCurrent()

            val secondUuid =
                underTest.startTransition(
                    TransitionInfo(OWNER_NAME, AOD, DREAMING, animator = null)
                )
            runCurrent()

            checkNotNull(secondUuid)
            underTest.updateTransition(secondUuid, 0.7f, TransitionState.RUNNING)
            // Trying to transition the old uuid should be ignored.
            underTest.updateTransition(firstUuid, 0.6f, TransitionState.RUNNING)
            runCurrent()

            assertThat(steps)
                .containsExactly(
                    TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME),
                    TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME),
                    TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.CANCELED, OWNER_NAME),
                    TransitionStep(AOD, DREAMING, 0.5f, TransitionState.STARTED, OWNER_NAME),
                    TransitionStep(AOD, DREAMING, 0.7f, TransitionState.RUNNING, OWNER_NAME),
                )
                .inOrder()

            job.cancel()
        }

    @Test
    fun startingSecondTransitionWillCancelPreviousManualTransition() =
        TestScope().runTest {
            // Drop initial steps from OFF which are sent in the constructor
            val steps = mutableListOf<TransitionStep>()
            val job =
                underTest.transitions
                    .dropWhile { step -> step.from == OFF }
                    .onEach { steps.add(it) }
                    .launchIn(this)

            val uuid =
                underTest.startTransition(
                    TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
                )
            runCurrent()

            checkNotNull(uuid)
            underTest.updateTransition(uuid, 0.5f, TransitionState.RUNNING)
            runCurrent()

            // Start new transition to dreaming, should cancel previous one.
            runner.startTransition(
                this,
                TransitionInfo(
                    OWNER_NAME,
                    AOD,
                    DREAMING,
                    getAnimator(),
                    TransitionModeOnCanceled.RESET,
                ),
            )
            runCurrent()

            // Trying to transition the old uuid should be ignored.
            underTest.updateTransition(uuid, 0.6f, TransitionState.RUNNING)
            runCurrent()

            assertThat(steps.take(3))
                .containsExactly(
                    TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME),
                    TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME),
                    TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.CANCELED, OWNER_NAME),
                )
                .inOrder()

            job.cancel()
        }

    @Test
    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() {
        underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
@@ -336,6 +434,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {

    private class WtfHandler : TerribleFailureHandler {
        var failed = false

        override fun onTerribleFailure(tag: String, what: TerribleFailure, system: Boolean) {
            failed = true
        }