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

Commit 042ca77a authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Do not consider elements with alpha=0f when interrupting

Bug: 290930950
Test: atest ElementTest
Flag: com.android.systemui.scene_container
Change-Id: Id5ef455cea3b7dc1ab8c5a3f41e35e69c71f87a1
parent a280ccf8
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -306,7 +306,6 @@ internal class ElementNode(
        return layout(placeable.width, placeable.height) { place(transition, placeable) }
    }

    @OptIn(ExperimentalComposeUiApi::class)
    private fun Placeable.PlacementScope.place(
        transition: TransitionState.Transition?,
        placeable: Placeable,
@@ -561,10 +560,20 @@ private fun reconcileStates(
}

private fun Element.SceneState.selfUpdateValuesBeforeInterruption() {
    offsetBeforeInterruption = lastOffset
    sizeBeforeInterruption = lastSize

    if (lastAlpha > 0f) {
        offsetBeforeInterruption = lastOffset
        scaleBeforeInterruption = lastScale
        alphaBeforeInterruption = lastAlpha
    } else {
        // Consider the element as not placed in this scene if it was fully transparent.
        // TODO(b/290930950): Look into using derived state inside place() instead to not even place
        // the element at all when alpha == 0f.
        offsetBeforeInterruption = Offset.Unspecified
        scaleBeforeInterruption = Scale.Unspecified
        alphaBeforeInterruption = Element.AlphaUnspecified
    }
}

private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState) {
+83 −5
Original line number Diff line number Diff line
@@ -851,7 +851,8 @@ class ElementTest {
            rule.runOnUiThread {
                MutableSceneTransitionLayoutState(
                    initialScene = SceneA,
                    transitions = transitions {
                    transitions =
                        transitions {
                            from(SceneA, to = SceneB) {
                                translate(TestElements.Foo, y = translateY)
                            }
@@ -2010,4 +2011,81 @@ class ElementTest {
            )
            .isEqualTo(Element.SizeUnspecified)
    }

    @Test
    fun transparentElementIsNotImpactingInterruption() = runTest {
        val state =
            rule.runOnIdle {
                MutableSceneTransitionLayoutStateImpl(
                    SceneA,
                    transitions {
                        from(SceneA, to = SceneB) {
                            // In A => B, Foo is not shared and first fades out from A then fades in
                            // B.
                            sharedElement(TestElements.Foo, enabled = false)
                            fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) }
                            fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) }
                        }

                        from(SceneB, to = SceneA) {
                            // In B => A, Foo is shared.
                            sharedElement(TestElements.Foo, enabled = true)
                        }
                    }
                )
            }

        @Composable
        fun SceneScope.Foo(modifier: Modifier = Modifier) {
            Box(modifier.element(TestElements.Foo).size(10.dp))
        }

        rule.setContent {
            SceneTransitionLayout(state) {
                scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }

                // Define A after B so that Foo is placed in A during A <=> B.
                scene(SceneA) { Foo() }
            }
        }

        // Start A => B at 70%.
        rule.runOnUiThread {
            state.startTransition(
                transition(
                    from = SceneA,
                    to = SceneB,
                    progress = { 0.7f },
                    onFinish = neverFinish(),
                )
            )
        }

        rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(0.dp, 0.dp)
        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)

        // Start B => A at 50% with interruptionProgress = 100%. Foo is placed in A and should still
        // be at (40dp, 60dp) given that it was fully transparent in A before the interruption.
        var interruptionProgress by mutableStateOf(1f)
        rule.runOnUiThread {
            state.startTransition(
                transition(
                    from = SceneB,
                    to = SceneA,
                    progress = { 0.5f },
                    interruptionProgress = { interruptionProgress },
                    onFinish = neverFinish(),
                )
            )
        }

        rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()

        // Set the interruption progress to 0%. Foo should be at (20dp, 30dp) given that B => is at
        // 50%.
        interruptionProgress = 0f
        rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
    }
}