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

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

Remove Element.lastSharedState and SceneState.lastState

Bug: 316901148
Test: PlatformComposeSceneTransitionLayoutTests
Flag: N/A
Change-Id: Ica81455bf185da811df0ff0d03b209cc5719fce6
parent 8ec20c50
Loading
Loading
Loading
Loading
+12 −55
Original line number Original line Diff line number Diff line
@@ -46,15 +46,6 @@ import kotlinx.coroutines.launch
/** An element on screen, that can be composed in one or more scenes. */
/** An element on screen, that can be composed in one or more scenes. */
@Stable
@Stable
internal class Element(val key: ElementKey) {
internal class Element(val key: ElementKey) {
    /**
     * The last state of this element, coming from any scene. Note that this state will be unstable
     * if this element is present in multiple scenes but the shared element animation is disabled,
     * given that multiple instances of the element with different states will write to this state.
     * You should prefer using [SceneState.lastState] in the current scene when it is defined.
     */
    // TODO(b/316901148): Remove this.
    val lastSharedState = State()

    /** The mapping between a scene and the state this element has in that scene, if any. */
    /** The mapping between a scene and the state this element has in that scene, if any. */
    // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions
    // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions
    // are first seen by composition then layout/drawing code. See 316901148#comment2 for details.
    // are first seen by composition then layout/drawing code. See 316901148#comment2 for details.
@@ -64,24 +55,9 @@ internal class Element(val key: ElementKey) {
        return "Element(key=$key)"
        return "Element(key=$key)"
    }
    }


    /** The state of this element, either in a specific scene or in a shared context. */
    class State {
        /** The offset of the element, relative to the SceneTransitionLayout containing it. */
        var offset = Offset.Unspecified

        /** The size of this element. */
        var size = SizeUnspecified

        /** The alpha of this element. */
        var alpha = AlphaUnspecified
    }

    /** The last and target state of this element in a given scene. */
    /** The last and target state of this element in a given scene. */
    @Stable
    @Stable
    class SceneState(val scene: SceneKey) {
    class SceneState(val scene: SceneKey) {
        // TODO(b/316901148): Remove this.
        val lastState = State()

        var targetSize by mutableStateOf(SizeUnspecified)
        var targetSize by mutableStateOf(SizeUnspecified)
        var targetOffset by mutableStateOf(Offset.Unspecified)
        var targetOffset by mutableStateOf(Offset.Unspecified)


@@ -95,7 +71,6 @@ internal class Element(val key: ElementKey) {


    companion object {
    companion object {
        val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
        val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
        val AlphaUnspecified = Float.MIN_VALUE
    }
    }
}
}


@@ -425,19 +400,13 @@ private fun IntermediateMeasureScope.measure(
            ::lerp,
            ::lerp,
        )
        )


    val placeable =
    return maybePlaceable
        maybePlaceable
        ?: measurable.measure(
        ?: measurable.measure(
            Constraints.fixed(
            Constraints.fixed(
                targetSize.width.coerceAtLeast(0),
                targetSize.width.coerceAtLeast(0),
                targetSize.height.coerceAtLeast(0),
                targetSize.height.coerceAtLeast(0),
            )
            )
        )
        )

    val size = placeable.size()
    element.lastSharedState.size = size
    sceneState.lastState.size = size
    return placeable
}
}


private fun getDrawScale(
private fun getDrawScale(
@@ -477,9 +446,12 @@ private fun IntermediateMeasureScope.place(
            sceneState.targetOffset = targetOffsetInScene
            sceneState.targetOffset = targetOffsetInScene
        }
        }


        // No need to place the element in this scene if we don't want to draw it anyways.
        if (!shouldDrawElement(layoutImpl, scene, element)) {
            return
        }

        val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
        val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
        val lastSharedState = element.lastSharedState
        val lastSceneState = sceneState.lastState
        val targetOffset =
        val targetOffset =
            computeValue(
            computeValue(
                layoutImpl,
                layoutImpl,
@@ -493,30 +465,15 @@ private fun IntermediateMeasureScope.place(
                ::lerp,
                ::lerp,
            )
            )


        lastSharedState.offset = targetOffset
        lastSceneState.offset = targetOffset

        // No need to place the element in this scene if we don't want to draw it anyways. Note that
        // it's still important to compute the target offset and update last(Shared|Scene)State,
        // otherwise they will be out of date.
        if (!shouldDrawElement(layoutImpl, scene, element)) {
            return
        }

        val offset = (targetOffset - currentOffset).round()
        val offset = (targetOffset - currentOffset).round()
        if (isElementOpaque(layoutImpl, element, scene)) {
        if (isElementOpaque(layoutImpl, element, scene)) {
            // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
            // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
            // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
            // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
            // case.
            // case.
            placeable.place(offset)
            placeable.place(offset)
            lastSharedState.alpha = 1f
            lastSceneState.alpha = 1f
        } else {
        } else {
            placeable.placeWithLayer(offset) {
            placeable.placeWithLayer(offset) {
                val alpha = elementAlpha(layoutImpl, element, scene)
                this.alpha = elementAlpha(layoutImpl, element, scene)
                this.alpha = alpha
                lastSharedState.alpha = alpha
                lastSceneState.alpha = alpha
            }
            }
        }
        }
    }
    }
+0 −92
Original line number Original line Diff line number Diff line
@@ -16,7 +16,6 @@


package com.android.compose.animation.scene
package com.android.compose.animation.scene


import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Box
@@ -35,17 +34,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.test.subjects.DpOffsetSubject
import com.android.compose.test.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
@@ -574,89 +567,4 @@ class ElementTest {
            after { assertThat(fooCompositions).isEqualTo(1) }
            after { assertThat(fooCompositions).isEqualTo(1) }
        }
        }
    }
    }

    @Test
    fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() {
        var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
        var density: Density? = null

        fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set")

        fun density() = density ?: error("density was not set")

        fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) }

        fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map")

        fun Element.lastSharedOffset() = lastSharedState.offset.toDpOffset()

        fun Element.lastOffsetIn(scene: SceneKey) =
            (sceneStates[scene] ?: error("$scene not in sceneValues map"))
                .lastState
                .offset
                .toDpOffset()

        rule.testTransition(
            from = TestScenes.SceneA,
            to = TestScenes.SceneB,
            transitionLayout = { currentScene, onChangeScene ->
                density = LocalDensity.current

                SceneTransitionLayoutForTesting(
                    state =
                        updateSceneTransitionLayoutState(
                            currentScene = currentScene,
                            onChangeScene = onChangeScene,
                            transitions =
                                transitions {
                                    from(TestScenes.SceneA, to = TestScenes.SceneB) {
                                        spec = tween(durationMillis = 4 * 16, easing = LinearEasing)
                                    }
                                }
                        ),
                    onLayoutImpl = { nullableLayoutImpl = it },
                ) {
                    scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) }
                    scene(TestScenes.SceneB) {
                        Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo))
                    }
                }
            }
        ) {
            val tolerance = DpOffsetSubject.DefaultTolerance

            before {
                val expected = DpOffset(0.dp, 0.dp)
                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
            }

            at(16) {
                val expected = DpOffset(10.dp, 20.dp)
                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
            }

            at(32) {
                val expected = DpOffset(20.dp, 40.dp)
                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
            }

            at(48) {
                val expected = DpOffset(30.dp, 60.dp)
                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
            }

            after {
                val expected = DpOffset(40.dp, 80.dp)
                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
            }
        }
    }
}
}