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

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

Wait for overscroll to finish before finishing a transition

This CL ensures that a SwipeTransition waits for the associated
overscroll effect to finish its animation before the transition is
flagged as finished to the STLState (and therefore removed). This avoids
recomposing the STL right when the user release their finger, thus
avoiding potential jank in the middle of a transition.

Bug: 383524412
Test: Manual, saw that the recomposition happened at the end of the
 transition
Test: Performance runs in Forrest, see http://screen/3KjNsghkEUm9nVs
Flag: EXEMPT performance fix
Change-Id: I5ed31ed94a99210c144497b630265c41548f662c
parent 6ee3b28e
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.math.absoluteValue
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -359,13 +360,24 @@ private class DragControllerImpl(
            return swipeAnimation.animateOffset(velocity, targetContent)
        }

        val overscrollCompletable = CompletableDeferred<Unit>()
        try {
            overscrollEffect.applyToFling(
                velocity = velocity.toVelocity(),
                performFling = {
                    val velocityLeft = it.toFloat()
                swipeAnimation.animateOffset(velocityLeft, targetContent).toVelocity()
                    swipeAnimation
                        .animateOffset(
                            velocityLeft,
                            targetContent,
                            overscrollCompletable = overscrollCompletable,
                        )
                        .toVelocity()
                },
            )
        } finally {
            overscrollCompletable.complete(Unit)
        }

        return velocity
    }
+13 −2
Original line number Diff line number Diff line
@@ -271,7 +271,7 @@ internal class SwipeAnimation<T : ContentKey>(

    /** The offset animation that animates the offset once the user lifts their finger. */
    private var offsetAnimation: Animatable<Float, AnimationVector1D>? by mutableStateOf(null)
    private val offsetAnimationRunnable = CompletableDeferred<(suspend () -> Unit)?>()
    private val offsetAnimationRunnable = CompletableDeferred<suspend () -> Unit>()

    val isUserInputOngoing: Boolean
        get() = offsetAnimation == null
@@ -333,6 +333,7 @@ internal class SwipeAnimation<T : ContentKey>(
        initialVelocity: Float,
        targetContent: T,
        spec: AnimationSpec<Float>? = null,
        overscrollCompletable: CompletableDeferred<Unit>? = null,
    ): Float {
        check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }

@@ -391,7 +392,12 @@ internal class SwipeAnimation<T : ContentKey>(
        // detail).
        if (skipAnimation) {
            // Unblock the job.
            offsetAnimationRunnable.complete(null)
            offsetAnimationRunnable.complete {
                // Wait for overscroll to finish so that the transition is removed from the STLState
                // only after the overscroll is done, to avoid dropping frame right when the user
                // lifts their finger and overscroll is animated to 0.
                overscrollCompletable?.await()
            }
            return 0f
        }

@@ -450,6 +456,11 @@ internal class SwipeAnimation<T : ContentKey>(
                    // The animation consumed the whole available velocity
                    velocityConsumed.complete(initialVelocity)
                }

                // Wait for overscroll to finish so that the transition is removed from the STLState
                // only after the overscroll is done, to avoid dropping frame right when the user
                // lifts their finger and overscroll is animated to 0.
                overscrollCompletable?.await()
            }
        }