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

Commit edc181e0 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Always reset interruption deltas

This CL ensures that we always reset the interruption deltas when an
interruption happens. This is usually not necessary, but in some
specific timings (e.g. if a transition is finished right after an
interruption occured) not cleaning might lead the unexpected
interruption deltas being animated.

Bug: 290930950
Test: ElementTest
Flag: com.android.systemui.scene_container
Change-Id: I3e27690a10270ee71d54677f77bca13f11002bc0
parent d2581dbb
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -513,22 +513,23 @@ private fun prepareInterruption(
    // Remove the interruption values to all scenes but the scene(s) where the element will be
    // placed, to make sure that interruption deltas are computed only right after this interruption
    // is prepared.
    fun maybeCleanPlacementValuesBeforeInterruption(sceneState: Element.SceneState) {
    fun cleanInterruptionValues(sceneState: Element.SceneState) {
        sceneState.sizeInterruptionDelta = IntSize.Zero
        sceneState.offsetInterruptionDelta = Offset.Zero
        sceneState.alphaInterruptionDelta = 0f
        sceneState.scaleInterruptionDelta = Scale.Zero

        if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) {
            sceneState.offsetBeforeInterruption = Offset.Unspecified
            sceneState.alphaBeforeInterruption = Element.AlphaUnspecified
            sceneState.scaleBeforeInterruption = Scale.Unspecified

            sceneState.offsetInterruptionDelta = Offset.Zero
            sceneState.alphaInterruptionDelta = 0f
            sceneState.scaleInterruptionDelta = Scale.Zero
        }
    }

    previousFromState?.let { maybeCleanPlacementValuesBeforeInterruption(it) }
    previousToState?.let { maybeCleanPlacementValuesBeforeInterruption(it) }
    fromState?.let { maybeCleanPlacementValuesBeforeInterruption(it) }
    toState?.let { maybeCleanPlacementValuesBeforeInterruption(it) }
    previousFromState?.let { cleanInterruptionValues(it) }
    previousToState?.let { cleanInterruptionValues(it) }
    fromState?.let { cleanInterruptionValues(it) }
    toState?.let { cleanInterruptionValues(it) }
}

/**
+62 −0
Original line number Diff line number Diff line
@@ -1838,4 +1838,66 @@ class ElementTest {
        rule.onNode(hasTestTag("fooParentInSceneA")).assertSizeIsEqualTo(40.dp, 60.dp)
        rule.onNode(hasTestTag("fooParentInSceneC")).assertSizeIsEqualTo(40.dp, 60.dp)
    }

    @Test
    fun interruptionDeltasAreProperlyCleaned() = runTest {
        val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }

        @Composable
        fun SceneScope.Foo(offset: Dp) {
            Box(Modifier.fillMaxSize()) {
                Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp))
            }
        }

        rule.setContent {
            SceneTransitionLayout(state, Modifier.size(200.dp)) {
                scene(SceneA) { Foo(offset = 0.dp) }
                scene(SceneB) { Foo(offset = 20.dp) }
                scene(SceneC) { Foo(offset = 40.dp) }
            }
        }

        // Start A => B at 50%.
        val aToB =
            transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
        rule.runOnUiThread { state.startTransition(aToB) }
        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp)

        // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the
        // position of Foo is unchanged and converges to (20dp, 20dp).
        var interruptionProgress by mutableStateOf(1f)
        val bToC =
            transition(
                from = SceneB,
                to = SceneC,
                progress = { 0f },
                interruptionProgress = { interruptionProgress },
                onFinish = neverFinish(),
            )
        rule.runOnUiThread { state.startTransition(bToC) }
        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp)

        // Finish the interruption and leave the transition progress at 0f. We should be at the same
        // state as in B.
        interruptionProgress = 0f
        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(20.dp, 20.dp)

        // Finish both transitions but directly start a new one B => A with interruption progress
        // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
        // correctly cleaned.
        rule.runOnUiThread {
            state.finishTransition(aToB, idleScene = SceneB)
            state.finishTransition(bToC, idleScene = SceneB)
            state.startTransition(
                transition(
                    from = SceneB,
                    to = SceneA,
                    progress = { 0f },
                    interruptionProgress = { 1f },
                )
            )
        }
        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 20.dp)
    }
}