summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jordan Demeulenaere <jdemeulenaere@google.com> 2024-03-08 15:25:44 +0100
committer Jordan Demeulenaere <jdemeulenaere@google.com> 2024-03-08 15:32:02 +0100
commitc4a8ae2ab37546d2bf38afbc5c829acad345b635 (patch)
tree291efd497b9d94e0ea63e62c6dcc6d13a246f011
parent0acf75e1df2446218219b4f2822c60b9c9d1c572 (diff)
Simplify the swipe offset animation logic
This CL simplifies the logic when animating the offset of a swipe transition, after the user lifted their finger. The previous implementation was made more complicated just to avoid allocating more than one Animatable per SwipeTransition, which does not really have an impact on performance anyways. Bug: 290930950 Test: atest DraggableHandlerTest Flag: N/A Change-Id: I7b0d4bb1c6f29a3bfc02778e2fe8b03e80b5a6be
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt73
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt11
2 files changed, 42 insertions, 42 deletions
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 b94e49bb0edc..391dd5132bd8 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
@@ -20,6 +20,7 @@ package com.android.compose.animation.scene
import android.util.Log
import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.getValue
@@ -573,7 +574,7 @@ private class SwipeTransition(
// Important: If we are going to return early because distance is equal to 0, we should
// still make sure we read the offset before returning so that the calling code still
// subscribes to the offset value.
- val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
+ val offset = offsetAnimation?.animatable?.value ?: dragOffset
val distance = distance()
if (distance == DistanceUnspecified) {
@@ -588,20 +589,11 @@ private class SwipeTransition(
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
- /**
- * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture.
- */
- var isAnimatingOffset by mutableStateOf(false)
+ /** The offset animation that animates the offset once the user lifts their finger. */
+ private var offsetAnimation: OffsetAnimation? by mutableStateOf(null)
- // If we are not animating offset, it means the offset is being driven by the user's finger.
override val isUserInputOngoing: Boolean
- get() = !isAnimatingOffset
-
- /** The animatable used to animate the offset once the user lifted its finger. */
- val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold)
-
- /** Job to check that there is at most one offset animation in progress. */
- private var offsetAnimationJob: Job? = null
+ get() = offsetAnimation == null
/**
* The [TransformationSpecImpl] associated to this transition.
@@ -647,25 +639,21 @@ private class SwipeTransition(
return distance
}
- /** Ends any previous [offsetAnimationJob] and runs the new [job]. */
- private fun startOffsetAnimation(job: () -> Job) {
+ /** Ends any previous [offsetAnimation] and runs the new [animation]. */
+ private fun startOffsetAnimation(animation: () -> OffsetAnimation) {
cancelOffsetAnimation()
- offsetAnimationJob = job()
+ offsetAnimation = animation()
}
/** Cancel any ongoing offset animation. */
// TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
// the same time.
fun cancelOffsetAnimation() {
- offsetAnimationJob?.cancel()
- finishOffsetAnimation()
- }
+ val animation = offsetAnimation ?: return
+ offsetAnimation = null
- fun finishOffsetAnimation() {
- if (isAnimatingOffset) {
- isAnimatingOffset = false
- dragOffset = offsetAnimatable.value
- }
+ dragOffset = animation.animatable.value
+ animation.job.cancel()
}
fun animateOffset(
@@ -676,28 +664,29 @@ private class SwipeTransition(
onAnimationCompleted: () -> Unit,
) {
startOffsetAnimation {
- coroutineScope.launch {
- animateOffset(targetOffset, initialVelocity)
- onAnimationCompleted()
- }
- }
- }
+ val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
+ val job =
+ coroutineScope.launch {
+ animatable.animateTo(
+ targetValue = targetOffset,
+ animationSpec = swipeSpec,
+ initialVelocity = initialVelocity,
+ )
+
+ onAnimationCompleted()
+ }
- private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) {
- if (!isAnimatingOffset) {
- offsetAnimatable.snapTo(dragOffset)
+ OffsetAnimation(animatable, job)
}
- isAnimatingOffset = true
+ }
- val animationSpec = transformationSpec
- offsetAnimatable.animateTo(
- targetValue = targetOffset,
- animationSpec = swipeSpec,
- initialVelocity = initialVelocity,
- )
+ private class OffsetAnimation(
+ /** The animatable used to animate the offset. */
+ val animatable: Animatable<Float, AnimationVector1D>,
- finishOffsetAnimation()
- }
+ /** The job in which [animatable] is animated. */
+ val job: Job,
+ )
companion object {
const val DistanceUnspecified = 0f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index eb9b4280aacb..557545b10ed3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -951,4 +951,15 @@ class DraggableHandlerTest {
assertThat(transition).isNotNull()
assertThat(transition!!.progress).isEqualTo(-0.1f)
}
+
+ @Test
+ fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest {
+ // Swipe up from the middle to transition to scene B.
+ val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.1f))
+ assertTransition(fromScene = SceneA, toScene = SceneB, isUserInputOngoing = true)
+
+ dragController.onDragStopped(velocity = 0f)
+ assertTransition(isUserInputOngoing = false)
+ }
}