Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +21 −8 Original line number Diff line number Diff line Loading @@ -329,10 +329,9 @@ private fun elementTransition( if (transition == null && previousTransition != null) { // The transition was just finished. element.sceneStates.values.forEach { sceneState -> sceneState.offsetInterruptionDelta = Offset.Zero sceneState.scaleInterruptionDelta = Scale.Zero sceneState.alphaInterruptionDelta = 0f element.sceneStates.values.forEach { it.clearValuesBeforeInterruption() it.clearInterruptionDeltas() } } Loading Loading @@ -375,12 +374,22 @@ private fun prepareInterruption(element: Element) { sceneState.scaleBeforeInterruption = lastScale sceneState.alphaBeforeInterruption = lastAlpha sceneState.offsetInterruptionDelta = Offset.Zero sceneState.scaleInterruptionDelta = Scale.Zero sceneState.alphaInterruptionDelta = 0f sceneState.clearInterruptionDeltas() } } private fun Element.SceneState.clearInterruptionDeltas() { offsetInterruptionDelta = Offset.Zero scaleInterruptionDelta = Scale.Zero alphaInterruptionDelta = 0f } private fun Element.SceneState.clearValuesBeforeInterruption() { offsetBeforeInterruption = Offset.Unspecified scaleBeforeInterruption = Scale.Unspecified alphaBeforeInterruption = Element.AlphaUnspecified } /** * Compute what [value] should be if we take the * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into Loading Loading @@ -744,7 +753,11 @@ private fun ApproachMeasureScope.place( // No need to place the element in this scene if we don't want to draw it anyways. if (!shouldPlaceElement(layoutImpl, scene, element, transition)) { sceneState.lastOffset = Offset.Unspecified sceneState.offsetBeforeInterruption = Offset.Unspecified sceneState.lastScale = Scale.Unspecified sceneState.lastAlpha = Element.AlphaUnspecified sceneState.clearValuesBeforeInterruption() sceneState.clearInterruptionDeltas() return } Loading packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +33 −10 Original line number Diff line number Diff line Loading @@ -1049,24 +1049,30 @@ class ElementTest { Box(modifier.element(TestElements.Foo).size(fooSize)) } lateinit var layoutImpl: SceneTransitionLayoutImpl rule.setContent { SceneTransitionLayout(state, Modifier.size(layoutSize)) { SceneTransitionLayoutForTesting( state, Modifier.size(layoutSize), onLayoutImpl = { layoutImpl = it }, ) { // In scene A, Foo is aligned at the TopStart. scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) } } // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming // from B. We put it before (below) scene B so that we can check that interruptions // values and deltas are properly cleared once all transitions are done. scene(SceneC) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) } } // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming // from A. scene(SceneB) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) } } // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming // from B. scene(SceneC) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) } } } } Loading Loading @@ -1115,7 +1121,7 @@ class ElementTest { // Interruption progress is at 100% and bToC is at 0%, so Foo should be at the same offset // as right before the interruption. rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y) // Move the transition forward at 30% and set the interruption progress to 50%. Loading @@ -1130,7 +1136,7 @@ class ElementTest { ) rule.waitForIdle() rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo( offsetInBToCWithInterruption.x, offsetInBToCWithInterruption.y, Loading @@ -1140,7 +1146,24 @@ class ElementTest { bToCProgress = 1f interruptionProgress = 0f rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo(offsetInC.x, offsetInC.y) // Manually finish the transition. state.finishTransition(aToB, SceneB) state.finishTransition(bToC, SceneC) rule.waitForIdle() assertThat(state.currentTransition).isNull() // The interruption values should be unspecified and deltas should be set to zero. val foo = layoutImpl.elements.getValue(TestElements.Foo) assertThat(foo.sceneStates.keys).containsExactly(SceneC) val stateInC = foo.sceneStates.getValue(SceneC) assertThat(stateInC.offsetBeforeInterruption).isEqualTo(Offset.Unspecified) assertThat(stateInC.scaleBeforeInterruption).isEqualTo(Scale.Unspecified) assertThat(stateInC.alphaBeforeInterruption).isEqualTo(Element.AlphaUnspecified) assertThat(stateInC.offsetInterruptionDelta).isEqualTo(Offset.Zero) assertThat(stateInC.scaleInterruptionDelta).isEqualTo(Scale.Zero) assertThat(stateInC.alphaInterruptionDelta).isEqualTo(0f) } } Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +21 −8 Original line number Diff line number Diff line Loading @@ -329,10 +329,9 @@ private fun elementTransition( if (transition == null && previousTransition != null) { // The transition was just finished. element.sceneStates.values.forEach { sceneState -> sceneState.offsetInterruptionDelta = Offset.Zero sceneState.scaleInterruptionDelta = Scale.Zero sceneState.alphaInterruptionDelta = 0f element.sceneStates.values.forEach { it.clearValuesBeforeInterruption() it.clearInterruptionDeltas() } } Loading Loading @@ -375,12 +374,22 @@ private fun prepareInterruption(element: Element) { sceneState.scaleBeforeInterruption = lastScale sceneState.alphaBeforeInterruption = lastAlpha sceneState.offsetInterruptionDelta = Offset.Zero sceneState.scaleInterruptionDelta = Scale.Zero sceneState.alphaInterruptionDelta = 0f sceneState.clearInterruptionDeltas() } } private fun Element.SceneState.clearInterruptionDeltas() { offsetInterruptionDelta = Offset.Zero scaleInterruptionDelta = Scale.Zero alphaInterruptionDelta = 0f } private fun Element.SceneState.clearValuesBeforeInterruption() { offsetBeforeInterruption = Offset.Unspecified scaleBeforeInterruption = Scale.Unspecified alphaBeforeInterruption = Element.AlphaUnspecified } /** * Compute what [value] should be if we take the * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into Loading Loading @@ -744,7 +753,11 @@ private fun ApproachMeasureScope.place( // No need to place the element in this scene if we don't want to draw it anyways. if (!shouldPlaceElement(layoutImpl, scene, element, transition)) { sceneState.lastOffset = Offset.Unspecified sceneState.offsetBeforeInterruption = Offset.Unspecified sceneState.lastScale = Scale.Unspecified sceneState.lastAlpha = Element.AlphaUnspecified sceneState.clearValuesBeforeInterruption() sceneState.clearInterruptionDeltas() return } Loading
packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +33 −10 Original line number Diff line number Diff line Loading @@ -1049,24 +1049,30 @@ class ElementTest { Box(modifier.element(TestElements.Foo).size(fooSize)) } lateinit var layoutImpl: SceneTransitionLayoutImpl rule.setContent { SceneTransitionLayout(state, Modifier.size(layoutSize)) { SceneTransitionLayoutForTesting( state, Modifier.size(layoutSize), onLayoutImpl = { layoutImpl = it }, ) { // In scene A, Foo is aligned at the TopStart. scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) } } // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming // from B. We put it before (below) scene B so that we can check that interruptions // values and deltas are properly cleared once all transitions are done. scene(SceneC) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) } } // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming // from A. scene(SceneB) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) } } // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming // from B. scene(SceneC) { Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) } } } } Loading Loading @@ -1115,7 +1121,7 @@ class ElementTest { // Interruption progress is at 100% and bToC is at 0%, so Foo should be at the same offset // as right before the interruption. rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y) // Move the transition forward at 30% and set the interruption progress to 50%. Loading @@ -1130,7 +1136,7 @@ class ElementTest { ) rule.waitForIdle() rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo( offsetInBToCWithInterruption.x, offsetInBToCWithInterruption.y, Loading @@ -1140,7 +1146,24 @@ class ElementTest { bToCProgress = 1f interruptionProgress = 0f rule .onNode(isElement(TestElements.Foo, SceneC)) .onNode(isElement(TestElements.Foo, SceneB)) .assertPositionInRootIsEqualTo(offsetInC.x, offsetInC.y) // Manually finish the transition. state.finishTransition(aToB, SceneB) state.finishTransition(bToC, SceneC) rule.waitForIdle() assertThat(state.currentTransition).isNull() // The interruption values should be unspecified and deltas should be set to zero. val foo = layoutImpl.elements.getValue(TestElements.Foo) assertThat(foo.sceneStates.keys).containsExactly(SceneC) val stateInC = foo.sceneStates.getValue(SceneC) assertThat(stateInC.offsetBeforeInterruption).isEqualTo(Offset.Unspecified) assertThat(stateInC.scaleBeforeInterruption).isEqualTo(Scale.Unspecified) assertThat(stateInC.alphaBeforeInterruption).isEqualTo(Element.AlphaUnspecified) assertThat(stateInC.offsetInterruptionDelta).isEqualTo(Offset.Zero) assertThat(stateInC.scaleInterruptionDelta).isEqualTo(Scale.Zero) assertThat(stateInC.alphaInterruptionDelta).isEqualTo(0f) } }