summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author omarmt <omarmt@google.com> 2023-10-16 22:22:37 +0000
committer omarmt <omarmt@google.com> 2023-10-24 09:46:10 +0000
commitb51f7231b3ac7750366fa992d76e5ce2a69fad6b (patch)
treed996db0a05a269ea3f6979be6bb115370e2b28dd
parente73b0cb09e3ed525cd5be74924fd56338ff6ca50 (diff)
SceneGestureHandler simplification: remove overscroll method
In order to simplify the reasoning about the states of the transition between scenes, it is possible to remove the overscroll animation, since it is conceptually equivalent to calling the stop method without permission to change scene. Note about the test: we changed the overscroll implementation and the overscroll test (scrollStartedInScene_doOverscrollAnimation) is still working. Test: atest SceneGestureHandlerTest Bug: 291025415 Change-Id: Id0ef8ab9d7855452a3d6c8f3581064d80f32c14a
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt151
1 files changed, 74 insertions, 77 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 0ea3295a7144..c776ae98dcb7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -196,6 +196,8 @@ class SceneGestureHandler(
}
internal fun onDrag(delta: Float) {
+ if (delta == 0f) return
+
swipeTransition.dragOffset += delta
// First check transition.fromScene should be changed for the case where the user quickly
@@ -293,48 +295,77 @@ class SceneGestureHandler(
return
}
- // We were not animating.
- if (swipeTransition._fromScene == swipeTransition._toScene) {
- transitionState = TransitionState.Idle(swipeTransition._fromScene.key)
- return
+ fun animateTo(targetScene: Scene, targetOffset: Float) {
+ // If the effective current scene changed, it should be reflected right now in the
+ // current scene state, even before the settle animation is ongoing. That way all the
+ // swipeables and back handlers will be refreshed and the user can for instance quickly
+ // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then
+ // immediately go back B => A.
+ if (targetScene != swipeTransition._currentScene) {
+ swipeTransition._currentScene = targetScene
+ layoutImpl.onChangeScene(targetScene.key)
+ }
+
+ animateOffset(
+ initialVelocity = velocity,
+ targetOffset = targetOffset,
+ targetScene = targetScene.key
+ )
}
- // Compute the destination scene (and therefore offset) to settle in.
- val targetOffset: Float
- val targetScene: Scene
- val offset = swipeTransition.dragOffset
- val distance = swipeTransition.distance
- if (
- canChangeScene &&
+ val fromScene = swipeTransition._fromScene
+ if (canChangeScene) {
+ // If we are halfway between two scenes, we check what the target will be based on the
+ // velocity and offset of the transition, then we launch the animation.
+
+ val toScene = swipeTransition._toScene
+ if (fromScene == toScene) {
+ // We were not animating.
+ transitionState = TransitionState.Idle(fromScene.key)
+ return
+ }
+
+ // Compute the destination scene (and therefore offset) to settle in.
+ val offset = swipeTransition.dragOffset
+ val distance = swipeTransition.distance
+ if (
shouldCommitSwipe(
offset,
distance,
velocity,
- wasCommitted = swipeTransition._currentScene == swipeTransition._toScene,
+ wasCommitted = swipeTransition._currentScene == toScene,
)
- ) {
- targetOffset = distance
- targetScene = swipeTransition._toScene
+ ) {
+ // Animate to the next scene
+ animateTo(targetScene = toScene, targetOffset = distance)
+ } else {
+ // Animate to the initial scene
+ animateTo(targetScene = fromScene, targetOffset = 0f)
+ }
} else {
- targetOffset = 0f
- targetScene = swipeTransition._fromScene
- }
-
- // If the effective current scene changed, it should be reflected right now in the current
- // scene state, even before the settle animation is ongoing. That way all the swipeables and
- // back handlers will be refreshed and the user can for instance quickly swipe vertically
- // from A => B then horizontally from B => C, or swipe from A => B then immediately go back
- // B => A.
- if (targetScene != swipeTransition._currentScene) {
- swipeTransition._currentScene = targetScene
- layoutImpl.onChangeScene(targetScene.key)
+ // We are doing an overscroll animation between scenes. In this case, we can also start
+ // from the idle position.
+
+ val startFromIdlePosition = swipeTransition.dragOffset == 0f
+
+ if (startFromIdlePosition) {
+ // If there is a next scene, we start the overscroll animation.
+ val target = fromScene.findTargetSceneAndDistance(velocity)
+ val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key
+ if (isValidTarget) {
+ swipeTransition._toScene = layoutImpl.scene(target.sceneKey)
+ swipeTransition._distance = target.distance
+
+ animateTo(targetScene = fromScene, targetOffset = 0f)
+ } else {
+ // We will not animate
+ transitionState = TransitionState.Idle(fromScene.key)
+ }
+ } else {
+ // We were between two scenes: animate to the initial scene.
+ animateTo(targetScene = fromScene, targetOffset = 0f)
+ }
}
-
- animateOffset(
- initialVelocity = velocity,
- targetOffset = targetOffset,
- targetScene = targetScene.key
- )
}
/**
@@ -407,47 +438,6 @@ class SceneGestureHandler(
}
}
- internal fun animateOverscroll(velocity: Velocity): Velocity {
- val velocityAmount =
- when (orientation) {
- Orientation.Vertical -> velocity.y
- Orientation.Horizontal -> velocity.x
- }
-
- if (velocityAmount == 0f) {
- // There is no remaining velocity
- return Velocity.Zero
- }
-
- val fromScene = currentScene
- val target = fromScene.findTargetSceneAndDistance(velocityAmount)
- val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key
-
- if (!isValidTarget || isDrivingTransition) {
- // We have not found a valid target or we are already in a transition
- return Velocity.Zero
- }
-
- swipeTransition._currentScene = fromScene
- swipeTransition._fromScene = fromScene
- swipeTransition._toScene = layoutImpl.scene(target.sceneKey)
- swipeTransition._distance = target.distance
- swipeTransition.absoluteDistance = target.distance.absoluteValue
- swipeTransition.stopOffsetAnimation()
- swipeTransition.dragOffset = 0f
-
- transitionState = swipeTransition
-
- animateOffset(
- initialVelocity = velocityAmount,
- targetOffset = 0f,
- targetScene = fromScene.key
- )
-
- // The animateOffset animation consumes any remaining velocity.
- return velocity
- }
-
private class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
var _currentScene by mutableStateOf(initialScene)
override val currentScene: SceneKey
@@ -623,9 +613,16 @@ class SceneNestedScrollHandler(
velocityAvailable
},
onPostFling = { velocityAvailable ->
- // If there is any velocity left, we can try running an overscroll animation between
- // scenes.
- gestureHandler.animateOverscroll(velocity = velocityAvailable)
+ val velocityAmount = velocityAvailable.toAmount()
+ if (velocityAmount != 0f) {
+ // If there is any velocity left, we can try running an overscroll animation
+ // between scenes.
+ gestureHandler.onDragStarted()
+ gestureHandler.onDragStopped(velocity = velocityAmount, canChangeScene = false)
+ }
+
+ // We consumed any remaining velocity.
+ velocityAvailable
},
)
}