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

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

Don't share elements when the shared animation is disabled

This CL ensures that we share elements between overlays and the current
scene during a Transition.ReplaceOverlay only if the shared element
transition of this element has not been disabled

Bug: 353679003
Test: atest OverlayTest
Flag: com.android.systemui.scene_container
Change-Id: I575769cfe424f9a95f2d8464795bf23b583c4cd0
parent c11cd39e
Loading
Loading
Loading
Loading
+23 −8
Original line number Diff line number Diff line
@@ -828,15 +828,21 @@ private fun shouldPlaceElement(

    // Don't place the element in this content if this content is not part of the current element
    // transition.
    if (content != transition.fromContent && content != transition.toContent) {
    val isReplacingOverlay = transition is TransitionState.Transition.ReplaceOverlay
    if (
        content != transition.fromContent &&
            content != transition.toContent &&
            (!isReplacingOverlay || content != transition.currentScene)
    ) {
        return false
    }

    // Place the element if it is not shared.
    if (
        transition.fromContent !in element.stateByContent ||
            transition.toContent !in element.stateByContent
    ) {
    var copies = 0
    if (transition.fromContent in element.stateByContent) copies++
    if (transition.toContent in element.stateByContent) copies++
    if (isReplacingOverlay && transition.currentScene in element.stateByContent) copies++
    if (copies <= 1) {
        return true
    }

@@ -1269,9 +1275,10 @@ private inline fun <T> computeValue(

    // If we are replacing an overlay and the element is both in a single overlay and in the current
    // scene, interpolate the state of the element using the current scene as the other scene.
    var currentSceneState: Element.State? = null
    if (!isSharedElement && transition is TransitionState.Transition.ReplaceOverlay) {
        val currentSceneState = element.stateByContent[transition.currentScene]
        if (currentSceneState != null) {
        currentSceneState = element.stateByContent[transition.currentScene]
        if (currentSceneState != null && isSharedElementEnabled(element.key, transition)) {
            return interpolateSharedElement(
                transition = transition,
                contentValue = contentValue,
@@ -1290,6 +1297,8 @@ private inline fun <T> computeValue(
            when {
                isSharedElement && currentContent == fromContent -> fromState
                isSharedElement -> toState
                currentSceneState != null && currentContent == transition.currentScene ->
                    currentSceneState
                else -> fromState ?: toState
            }
        )
@@ -1409,7 +1418,13 @@ private inline fun <T> computeValue(
    val rangeProgress = transformation.range?.progress(progress) ?: progress

    // Interpolate between the value at rest and the value before entering/after leaving.
    val isEntering = content == toContent
    val isEntering =
        when {
            content == toContent -> true
            content == fromContent -> false
            content == transition.currentScene -> toState == null
            else -> content == toContent
        }
    return if (isEntering) {
        lerp(targetValue, idleValue, rangeProgress)
    } else {
+79 −0
Original line number Diff line number Diff line
@@ -653,6 +653,85 @@ class OverlayTest {
        }
    }

    @Test
    fun replaceAnimation_elementInCurrentSceneAndOneOverlay_sharedElementDisabled() {
        rule.testReplaceOverlayTransition(
            currentSceneContent = {
                Box(Modifier.size(width = 180.dp, height = 120.dp)) {
                    Foo(width = 60.dp, height = 40.dp)
                }
            },
            fromContent = {},
            fromAlignment = Alignment.TopStart,
            toContent = { Foo(width = 100.dp, height = 80.dp) },
            transition = {
                // 4 frames of animation
                spec = tween(4 * 16, easing = LinearEasing)

                // Scale Foo to/from size 0 in each content instead of sharing it.
                sharedElement(TestElements.Foo, enabled = false)
                scaleSize(TestElements.Foo, width = 0f, height = 0f)
            },
        ) {
            before {
                rule
                    .onNode(isElement(TestElements.Foo, content = SceneA))
                    .assertSizeIsEqualTo(60.dp, 40.dp)
                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
                rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
            }

            at(16) {
                rule
                    .onNode(isElement(TestElements.Foo, content = SceneA))
                    .assertSizeIsEqualTo(45.dp, 30.dp)
                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
                rule
                    .onNode(isElement(TestElements.Foo, content = OverlayB))
                    .assertSizeIsEqualTo(25.dp, 20.dp)
                    .assertPositionInRootIsEqualTo(((180 - 25) / 2f).dp, ((120 - 20) / 2f).dp)
            }

            at(32) {
                rule
                    .onNode(isElement(TestElements.Foo, content = SceneA))
                    .assertSizeIsEqualTo(30.dp, 20.dp)
                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
                rule
                    .onNode(isElement(TestElements.Foo, content = OverlayB))
                    .assertSizeIsEqualTo(50.dp, 40.dp)
                    .assertPositionInRootIsEqualTo(((180 - 50) / 2f).dp, ((120 - 40) / 2f).dp)
            }

            at(48) {
                rule
                    .onNode(isElement(TestElements.Foo, content = SceneA))
                    .assertSizeIsEqualTo(15.dp, 10.dp)
                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
                rule
                    .onNode(isElement(TestElements.Foo, content = OverlayB))
                    .assertSizeIsEqualTo(75.dp, 60.dp)
                    .assertPositionInRootIsEqualTo(((180 - 75) / 2f).dp, ((120 - 60) / 2f).dp)
            }

            after {
                rule
                    .onNode(isElement(TestElements.Foo, content = SceneA))
                    .assertExists()
                    .assertIsNotDisplayed()
                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
                rule
                    .onNode(isElement(TestElements.Foo, content = OverlayB))
                    .assertSizeIsEqualTo(100.dp, 80.dp)
                    .assertPositionInRootIsEqualTo(40.dp, 20.dp)
            }
        }
    }

    @Test
    fun overscrollingOverlay_movableElementNotInOverlay() {
        val state =