diff options
| author | 2024-09-12 14:34:15 +0000 | |
|---|---|---|
| committer | 2024-10-01 12:35:22 +0000 | |
| commit | 408325d2d265d67b4e4e3ae119a7f8ca03fa0f9b (patch) | |
| tree | 37997a61242acf14c1525d173215f00074a18a4b | |
| parent | 78388235f37aa7b78070cc0bcf5a35acdc2e5411 (diff) | |
animateOffset can partially consume the fling velocity
When a scene is configured to disallow overscrolling
(`overscrollDisabled()` is enabled), a fling gesture on that scene may
not use up all of the velocity. The unused velocity can then be passed
on to the ancestor of the component.
Test: atest DraggableHandlerTest
Bug: 336710600
Flag: com.android.systemui.scene_container
Change-Id: Ia1b0620fe37cfaf5b339d1e87c38ba5c22be95e8
2 files changed, 30 insertions, 4 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index 18d8328b2856..205267da151a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -28,10 +28,8 @@ import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.SuspendedValue -import kotlinx.coroutines.CancellationException import kotlin.math.absoluteValue import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.withTimeout internal fun createSwipeAnimation( layoutState: MutableSceneTransitionLayoutStateImpl, @@ -425,6 +423,9 @@ internal class SwipeAnimation<T : ContentKey>( // Immediately stop this transition if we are bouncing on a content that // does not bounce. if (!contentTransition.isWithinProgressRange(progress)) { + // We are no longer able to consume the velocity, the rest can be + // consumed by another component in the hierarchy. + velocityConsumed.complete(initialVelocity - velocity) throw SnapException() } } @@ -433,8 +434,10 @@ internal class SwipeAnimation<T : ContentKey>( } catch (_: SnapException) { /* Ignore. */ } finally { - // The animation consumed the whole available velocity - velocityConsumed.complete(initialVelocity) + if (!velocityConsumed.isCompleted) { + // The animation consumed the whole available velocity + velocityConsumed.complete(initialVelocity) + } } } 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 c0524b730ab8..2c41b35da998 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 @@ -1177,6 +1177,29 @@ class DraggableHandlerTest { } @Test + fun emptyOverscrollAbortsSettleAnimationAndExposeTheConsumedVelocity() = runGestureTest { + // Overscrolling on scene B does nothing. + layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) } + + // Swipe up to scene B at progress = 200%. + val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) + val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.99f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.99f) + + // Release the finger. + dragController.onDragStoppedAnimateNow( + velocity = -velocityThreshold, + onAnimationStart = { assertTransition(fromScene = SceneA, toScene = SceneB) }, + onAnimationEnd = { consumedVelocity -> + // Our progress value was 0.99f and it is coerced in `[0..1]` (overscrollDisabled). + // Some of the velocity will be used for animation, but not all of it. + assertThat(consumedVelocity).isLessThan(0f) + assertThat(consumedVelocity).isGreaterThan(-velocityThreshold) + }, + ) + } + + @Test fun overscroll_releaseBetween0And100Percent_up() = runGestureTest { // Make scene B overscrollable. layoutState.transitions = transitions { |