Add deferTransitionProgress flag to STL (1/2)

This CL adds a new deferTransitionProgress flag to STL to reduce
perceivable jank by waiting for the first expensive composition frame of
a transition to be done before changing the progress of the transition.
This works with gesture-based transitions and one-off transitions.

See b/400688335#comment2 for details.

Bug: 400688335
Test: atest NestedDraggableTest
Flag: EXEMPT new behavior disabled by default
Change-Id: I839529e2bc1d792ae8a37d5ddf3e7c61a12fe3cb
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 2ea9c48..362748e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -112,6 +112,16 @@
 
     interface Controller {
         /**
+         * Whether this controller is ready to drag. [onDrag] will be called only if this returns
+         * `true`, and any drag event will be ignored until then.
+         *
+         * This can for instance be used to wait for the content we are dragging to to be composed
+         * before actually dragging, reducing perceivable jank at the beginning of a drag.
+         */
+        val isReadyToDrag: Boolean
+            get() = true
+
+        /**
          * Whether drags that were started from nested scrolls should be automatically
          * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
          * [onDrag].
@@ -274,6 +284,9 @@
     /** The pointers currently down, in order of which they were done and mapping to their type. */
     private val pointersDown = linkedMapOf<PointerId, PointerType>()
 
+    /** Whether the next drag event should be ignored. */
+    private var ignoreNextDrag = false
+
     init {
         delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
     }
@@ -426,6 +439,7 @@
         velocityTracker: VelocityTracker,
     ) {
         velocityTracker.addPointerInputChange(change)
+        if (shouldIgnoreDrag(controller)) return
 
         scrollWithOverscroll(delta.toOffset()) { deltaFromOverscroll ->
             scrollWithNestedScroll(deltaFromOverscroll) { deltaFromNestedScroll ->
@@ -434,6 +448,23 @@
         }
     }
 
+    private fun shouldIgnoreDrag(controller: NestedDraggable.Controller): Boolean {
+        return when {
+            !controller.isReadyToDrag -> {
+                // The controller is not ready yet, so we are waiting for an expensive frame to be
+                // composed. We should ignore this drag and the next one, given that the first delta
+                // after an expensive frame will be large.
+                ignoreNextDrag = true
+                true
+            }
+            ignoreNextDrag -> {
+                ignoreNextDrag = false
+                true
+            }
+            else -> false
+        }
+    }
+
     private fun onDragStopped(controller: NestedDraggable.Controller, velocity: Velocity) {
         // We launch in the scope of the dispatcher so that the fling is not cancelled if this node
         // is removed right after onDragStopped() is called.
@@ -617,6 +648,8 @@
     }
 
     private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
+        if (shouldIgnoreDrag(controller.controller)) return offset
+
         return scrollWithOverscroll(offset) { delta ->
             val available = delta.toFloat()
             val consumed = controller.controller.onDrag(available)
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index b247993..b316173 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -972,6 +972,36 @@
     }
 
     @Test
+    fun isReadyToDrag() {
+        var isReadyToDrag by mutableStateOf(false)
+        val draggable = TestDraggable(isReadyToDrag = { isReadyToDrag })
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 10f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        rule.onRoot().performTouchInput { moveBy(20f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        // Flag as ready to drag. We still ignore the next drag after that.
+        isReadyToDrag = true
+        rule.onRoot().performTouchInput { moveBy(30f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        // Now we drag.
+        rule.onRoot().performTouchInput { moveBy(40f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(40f)
+    }
+
+    @Test
     fun consumeNestedPreScroll() {
         var consumeNestedPreScroll by mutableStateOf(false)
         val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
@@ -1060,6 +1090,7 @@
             },
         private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
         private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+        private val isReadyToDrag: () -> Boolean = { true },
         private val autoStopNestedDrags: Boolean = false,
     ) : NestedDraggable {
         var shouldStartDrag = true
@@ -1092,6 +1123,9 @@
             return object : NestedDraggable.Controller {
                 override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
 
+                override val isReadyToDrag: Boolean
+                    get() = isReadyToDrag()
+
                 override fun onDrag(delta: Float): Float {
                     onDragCalled = true
                     onDragDelta += delta
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
index b04d89d..0b0df06c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.runtime.withFrameNanos
 import com.android.compose.animation.scene.content.state.TransitionState
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -47,6 +48,11 @@
                 oneOffAnimation.animatable = it
             }
 
+        if (layoutState.deferTransitionProgress) {
+            // Defer the animation by one frame so that the transition progress is changed only when
+            // the expensive first composition frame is done.
+            withFrameNanos {}
+        }
         animatable.animateTo(targetProgress, animationSpec, initialVelocity)
     }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 024ca22..36eafa4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -290,6 +290,15 @@
     val isDrivingTransition: Boolean
         get() = layoutState.transitionState == swipeAnimation.contentTransition
 
+    override val isReadyToDrag: Boolean
+        get() {
+            return !layoutState.deferTransitionProgress ||
+                with(draggableHandler.layoutImpl.elementStateScope) {
+                    swipeAnimation.fromContent.targetSize() != null &&
+                        swipeAnimation.toContent.targetSize() != null
+                }
+        }
+
     init {
         check(!isDrivingTransition) { "Multiple controllers with the same SwipeTransition" }
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 4da83c3..a8b676d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -462,7 +462,9 @@
                 // swipes.
                 .swipeToScene(horizontalDraggableHandler)
                 .swipeToScene(verticalDraggableHandler)
-                .then(LayoutElement(layoutImpl = this))
+                .then(
+                    LayoutElement(layoutImpl = this, transitionState = this.state.transitionState)
+                )
         ) {
             LookaheadScope {
                 if (_lookaheadScope == null) {
@@ -623,23 +625,28 @@
     @VisibleForTesting internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
 }
 
-private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
-    ModifierNodeElement<LayoutNode>() {
-    override fun create(): LayoutNode = LayoutNode(layoutImpl)
+private data class LayoutElement(
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val transitionState: TransitionState,
+) : ModifierNodeElement<LayoutNode>() {
+    override fun create(): LayoutNode = LayoutNode(layoutImpl, transitionState)
 
     override fun update(node: LayoutNode) {
         node.layoutImpl = layoutImpl
+        node.transitionState = transitionState
     }
 }
 
-private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
-    Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
+private class LayoutNode(
+    var layoutImpl: SceneTransitionLayoutImpl,
+    var transitionState: TransitionState,
+) : Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
     override fun onRemeasured(size: IntSize) {
         layoutImpl.lastSize = size
     }
 
     override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
-        return layoutImpl.state.isTransitioning()
+        return transitionState is TransitionState.Transition.ChangeScene
     }
 
     @ExperimentalComposeUiApi
@@ -652,8 +659,7 @@
 
         val width: Int
         val height: Int
-        val transition =
-            layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeScene
+        val transition = transitionState as? TransitionState.Transition.ChangeScene
         if (transition == null) {
             width = placeable.width
             height = placeable.height
@@ -662,6 +668,9 @@
             val fromSize = layoutImpl.scene(transition.fromScene).targetSize
             val toSize = layoutImpl.scene(transition.toScene).targetSize
 
+            check(fromSize != Element.SizeUnspecified) { "fromSize is unspecified " }
+            check(toSize != Element.SizeUnspecified) { "toSize is unspecified" }
+
             // Optimization: make sure we don't read state.progress if fromSize ==
             // toSize to avoid running this code every frame when the layout size does
             // not change.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 56e8c45..4e28dd5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -234,6 +234,10 @@
  *   `from` overlay by `to` overlay.
  * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
  *   [SceneTransitionLayoutState]s.
+ * @param deferTransitionProgress whether we should wait for the first composition to be done before
+ *   changing the progress of a transition. This can help reduce perceivable jank at the start of a
+ *   transition in case the first composition of a content takes a lot of time and we are going to
+ *   miss that first frame.
  */
 fun MutableSceneTransitionLayoutState(
     initialScene: SceneKey,
@@ -246,6 +250,9 @@
     canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
     onTransitionStart: (TransitionState.Transition) -> Unit = {},
     onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    deferTransitionProgress: Boolean = false,
 ): MutableSceneTransitionLayoutState {
     return MutableSceneTransitionLayoutStateImpl(
         initialScene,
@@ -258,6 +265,7 @@
         canReplaceOverlay,
         onTransitionStart,
         onTransitionEnd,
+        deferTransitionProgress,
     )
 }
 
@@ -272,6 +280,9 @@
     canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
     onTransitionStart: (TransitionState.Transition) -> Unit = {},
     onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    deferTransitionProgress: Boolean = false,
 ): MutableSceneTransitionLayoutState {
     val motionScheme = MaterialTheme.motionScheme
     val layoutState = remember {
@@ -286,6 +297,7 @@
             canReplaceOverlay = canReplaceOverlay,
             onTransitionStart = onTransitionStart,
             onTransitionEnd = onTransitionEnd,
+            deferTransitionProgress = deferTransitionProgress,
         )
     }
 
@@ -298,6 +310,7 @@
         layoutState.canReplaceOverlay = canReplaceOverlay
         layoutState.onTransitionStart = onTransitionStart
         layoutState.onTransitionEnd = onTransitionEnd
+        layoutState.deferTransitionProgress = deferTransitionProgress
     }
     return layoutState
 }
@@ -317,6 +330,8 @@
     },
     internal var onTransitionStart: (TransitionState.Transition) -> Unit = {},
     internal var onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    internal var deferTransitionProgress: Boolean = false,
 ) : MutableSceneTransitionLayoutState {
     private val creationThread: Thread = Thread.currentThread()
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 2d2a8154..5d4232d8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -38,7 +38,7 @@
     }
 
     override fun ContentKey.targetSize(): IntSize? {
-        return layoutImpl.content(this).targetSize.takeIf { it != IntSize.Zero }
+        return layoutImpl.content(this).targetSize.takeIf { it != Element.SizeUnspecified }
     }
 }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 90bf92a..7492f37 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -93,9 +93,10 @@
     val containerState = ContainerState()
 
     // Important: All fields in this class should be backed by State given that contents are updated
-    // directly during composition, outside of a SideEffect.
+    // directly during composition, outside of a SideEffect, or are observed during composition,
+    // layout or drawing.
     var content by mutableStateOf(content)
-    var targetSize by mutableStateOf(IntSize.Zero)
+    var targetSize by mutableStateOf(Element.SizeUnspecified)
     var userActions by mutableStateOf(actions)
     var zIndex by mutableFloatStateOf(zIndex)
 
@@ -212,9 +213,17 @@
         return if (isElevationPossible) delegate(ContainerNode(content.containerState)) else null
     }
 
+    override fun onDetach() {
+        this.content.targetSize = Element.SizeUnspecified
+    }
+
     fun update(content: Content, isElevationPossible: Boolean, isInvisible: Boolean) {
-        if (content != this.content || isElevationPossible != this.isElevationPossible) {
+        if (content != this.content) {
+            this.content.targetSize = Element.SizeUnspecified
             this.content = content
+        }
+
+        if (content != this.content || isElevationPossible != this.isElevationPossible) {
             this.isElevationPossible = isElevationPossible
 
             containerDelegate?.let { undelegate(it) }
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
index 5dbb013..770f859 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -76,7 +76,7 @@
         },
         {
           "x": 5.2,
-          "y": 5.6
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -326,11 +326,11 @@
         },
         {
           "width": 150,
-          "height": 293.2
+          "height": 292.8
         },
         {
           "width": 150,
-          "height": 293.2
+          "height": 292.8
         },
         {
           "width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
index 1543d18..6b7de56 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -81,8 +81,8 @@
           "y": 5.2
         },
         {
-          "x": 5.6,
-          "y": 6.8
+          "x": 5.2,
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -344,7 +344,7 @@
         },
         {
           "width": 150,
-          "height": 294
+          "height": 292.4
         },
         {
           "width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
index 115483cf..015df8f 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -84,8 +84,8 @@
           "y": 5.2
         },
         {
-          "x": 5.6,
-          "y": 6.4
+          "x": 5.2,
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -259,7 +259,7 @@
         },
         {
           "width": 150,
-          "height": 290
+          "height": 288.8
         },
         {
           "width": 150,
@@ -279,34 +279,34 @@
         },
         {
           "width": 150,
-          "height": 223.6
+          "height": 224
         },
         {
           "width": 150,
-          "height": 182.8
+          "height": 183.2
         },
         {
           "width": 150,
-          "height": 141.2
+          "height": 141.6
         },
         {
           "width": 150,
-          "height": 104
+          "height": 104.4
         },
         {
-          "width": 147.6,
-          "height": 74
+          "width": 148,
+          "height": 74.4
         },
         {
           "width": 139.6,
-          "height": 50.8
+          "height": 51.2
         },
         {
           "width": 133.6,
-          "height": 32
+          "height": 32.4
         },
         {
-          "width": 129.2,
+          "width": 129.6,
           "height": 15.6
         },
         {
@@ -409,19 +409,19 @@
         1,
         1,
         1,
-        0.99479187,
-        0.8575029,
-        0.65572864,
-        0.4691311,
-        0.3215357,
-        0.21380007,
-        0.13896108,
-        0.0887118,
-        0.05580789,
-        0.03467691,
-        0.021318138,
-        0.0129826665,
-        0.007839739,
+        0.99532944,
+        0.8594856,
+        0.65783304,
+        0.47089338,
+        0.32286334,
+        0.21474117,
+        0.139602,
+        0.089136004,
+        0.056082606,
+        0.03485179,
+        0.02142787,
+        0.013050735,
+        0.007881463,
         0,
         0,
         0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
index f44d4cd..5ac0621 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -72,7 +72,7 @@
         },
         {
           "x": 5.2,
-          "y": 5.6
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -306,11 +306,11 @@
         },
         {
           "width": 150,
-          "height": 293.2
+          "height": 292.8
         },
         {
           "width": 150,
-          "height": 293.2
+          "height": 292.8
         },
         {
           "width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
index 9b68c71..1cae67b 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -79,8 +79,8 @@
           "y": 5.2
         },
         {
-          "x": 5.6,
-          "y": 6.8
+          "x": 5.2,
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -334,7 +334,7 @@
         },
         {
           "width": 150,
-          "height": 294
+          "height": 292.4
         },
         {
           "width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
index 86805bd..ca87bc2 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -83,8 +83,8 @@
           "y": 5.2
         },
         {
-          "x": 5.6,
-          "y": 6.4
+          "x": 5.2,
+          "y": 5.2
         },
         {
           "x": 5.2,
@@ -254,7 +254,7 @@
         },
         {
           "width": 150,
-          "height": 290
+          "height": 288.8
         },
         {
           "width": 150,
@@ -274,27 +274,27 @@
         },
         {
           "width": 150,
-          "height": 223.6
+          "height": 224
         },
         {
           "width": 150,
-          "height": 182.8
+          "height": 183.2
         },
         {
           "width": 150,
-          "height": 141.2
+          "height": 141.6
         },
         {
           "width": 150,
-          "height": 104
+          "height": 104.4
         },
         {
           "width": 150,
-          "height": 72
+          "height": 72.4
         },
         {
           "width": 150,
-          "height": 46
+          "height": 46.4
         },
         {
           "width": 150,
@@ -400,19 +400,19 @@
         1,
         1,
         1,
-        0.99479187,
-        0.8575029,
-        0.65572864,
-        0.4691311,
-        0.3215357,
-        0.21380007,
-        0.13896108,
-        0.0887118,
-        0.05580789,
-        0.03467691,
-        0.021318138,
-        0.0129826665,
-        0.007839739,
+        0.99532944,
+        0.8594856,
+        0.65783304,
+        0.47089338,
+        0.32286334,
+        0.21474117,
+        0.139602,
+        0.089136004,
+        0.056082606,
+        0.03485179,
+        0.02142787,
+        0.013050735,
+        0.007881463,
         0,
         0,
         0,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
index e4a87ba..7d9a32b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
@@ -58,17 +58,24 @@
                 assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
             }
 
+            at(16) {
+                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.75f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.75f, 0.75f))
+            }
+
             at(32) {
                 val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
                 assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
                 assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
             }
 
-            at(64) {
+            at(48) {
                 val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
-                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
-                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.25f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.25f, 0.25f))
             }
+
             after { onElement(TestElements.Foo).assertDoesNotExist() }
         }
     }