diff options
| author | 2023-10-16 22:22:37 +0000 | |
|---|---|---|
| committer | 2023-10-24 09:46:10 +0000 | |
| commit | b51f7231b3ac7750366fa992d76e5ce2a69fad6b (patch) | |
| tree | d996db0a05a269ea3f6979be6bb115370e2b28dd | |
| parent | e73b0cb09e3ed525cd5be74924fd56338ff6ca50 (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.kt | 151 |
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 }, ) } |