diff options
| author | 2024-02-06 15:18:25 +0000 | |
|---|---|---|
| committer | 2024-02-08 10:39:40 +0000 | |
| commit | 1d967cdccd9d08df38d2ab8a387c75b860e6bc89 (patch) | |
| tree | dffc6fe6576aa31f89ca53238bdbf7b57649dfe8 | |
| parent | 2c47d801c40824184bacb5b9c010b0473ac726bf (diff) | |
Make SwipeTransition an implementation detail of SceneGestureHandler
In this refactor the class has been extracted and made private.
Test: atest SceneGestureHandlerTest
Bug: 317063114
Flag: NA
Change-Id: Ia6a610eeee36d1b2d0e72846250f14808c8022eb
2 files changed, 164 insertions, 133 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index b3d2bc994c08..90dfa108ab24 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -46,7 +46,7 @@ internal class SceneGestureHandler( val draggable: DraggableHandler = SceneDraggableHandler(this) private var _swipeTransition: SwipeTransition? = null - internal var swipeTransition: SwipeTransition + private var swipeTransition: SwipeTransition get() = _swipeTransition ?: error("SwipeTransition needs to be initialized") set(value) { _swipeTransition = value @@ -149,7 +149,17 @@ internal class SceneGestureHandler( val result = findUserActionResult(fromScene, directionOffset = overSlop, updateSwipesResults = true) ?: return - updateTransition(SwipeTransition(fromScene, result), force = true) + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + upOrLeftResult = upOrLeftResult, + downOrRightResult = downOrRightResult, + layoutImpl = layoutImpl, + orientation = orientation + ) + + updateTransition(newSwipeTransition, force = true) } private fun updateSwipes(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) { @@ -242,11 +252,18 @@ internal class SceneGestureHandler( result.toScene != swipeTransition.toScene || result.transitionKey != swipeTransition.key ) { - updateTransition( - SwipeTransition(fromScene, result).apply { - this.dragOffset = swipeTransition.dragOffset - } - ) + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + upOrLeftResult = upOrLeftResult, + downOrRightResult = downOrRightResult, + layoutImpl = layoutImpl, + orientation = orientation + ) + .apply { dragOffset = swipeTransition.dragOffset } + + updateTransition(newSwipeTransition) } } @@ -354,18 +371,6 @@ internal class SceneGestureHandler( } } - private fun computeAbsoluteDistance( - fromScene: Scene, - result: UserActionResult, - ): Float { - return if (result == upOrLeftResult) { - -fromScene.getAbsoluteDistance(result.distance) - } else { - check(result == downOrRightResult) - fromScene.getAbsoluteDistance(result.distance) - } - } - internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) { // The state was changed since the drag started; don't do anything. if (!isDrivingTransition) { @@ -438,11 +443,18 @@ internal class SceneGestureHandler( return } - updateTransition( - SwipeTransition(fromScene, result).apply { - _currentScene = swipeTransition._currentScene - } - ) + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + upOrLeftResult = upOrLeftResult, + downOrRightResult = downOrRightResult, + layoutImpl = layoutImpl, + orientation = orientation + ) + .apply { _currentScene = swipeTransition._currentScene } + + updateTransition(newSwipeTransition) animateTo(targetScene = fromScene, targetOffset = 0f) } else { // We were between two scenes: animate to the initial scene. @@ -486,136 +498,153 @@ internal class SceneGestureHandler( } } - private fun SwipeTransition(fromScene: Scene, result: UserActionResult): SwipeTransition { - return SwipeTransition( - result.transitionKey, - fromScene, - layoutImpl.scene(result.toScene), - computeAbsoluteDistance(fromScene, result), - ) + companion object { + private const val TAG = "SceneGestureHandler" } +} - internal class SwipeTransition( - val key: TransitionKey?, - val _fromScene: Scene, - val _toScene: Scene, - /** - * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is - * above or to the left of [toScene]. - */ - val distance: Float, - ) : TransitionState.Transition(_fromScene.key, _toScene.key) { - var _currentScene by mutableStateOf(_fromScene) - override val currentScene: SceneKey - get() = _currentScene.key - - override val progress: Float - get() { - val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset - return offset / distance - } - - override val isInitiatedByUserInput = true - - /** The current offset caused by the drag gesture. */ - var dragOffset by mutableFloatStateOf(0f) +private fun SwipeTransition( + fromScene: Scene, + result: UserActionResult, + upOrLeftResult: UserActionResult?, + downOrRightResult: UserActionResult?, + layoutImpl: SceneTransitionLayoutImpl, + orientation: Orientation, +): SwipeTransition { + val userActionDistance = result.distance ?: DefaultSwipeDistance + val absoluteDistance = + with(userActionDistance) { + layoutImpl.density.absoluteDistance(fromScene.targetSize, orientation) + } - /** - * Whether the offset is animated (the user lifted their finger) or if it is driven by - * gesture. - */ - var isAnimatingOffset by mutableStateOf(false) + return SwipeTransition( + key = result.transitionKey, + _fromScene = fromScene, + _toScene = layoutImpl.scene(result.toScene), + distance = + when (result) { + upOrLeftResult -> -absoluteDistance + downOrRightResult -> absoluteDistance + else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)") + }, + ) +} - // If we are not animating offset, it means the offset is being driven by the user's finger. - override val isUserInputOngoing: Boolean - get() = !isAnimatingOffset +private class SwipeTransition( + val key: TransitionKey?, + val _fromScene: Scene, + val _toScene: Scene, + /** + * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above + * or to the left of [toScene] + */ + val distance: Float, +) : TransitionState.Transition(_fromScene.key, _toScene.key) { + var _currentScene by mutableStateOf(_fromScene) + override val currentScene: SceneKey + get() = _currentScene.key + + override val progress: Float + get() { + val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset + return offset / distance + } - /** The animatable used to animate the offset once the user lifted its finger. */ - val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) + override val isInitiatedByUserInput = true - /** Job to check that there is at most one offset animation in progress. */ - private var offsetAnimationJob: Job? = null + /** The current offset caused by the drag gesture. */ + var dragOffset by mutableFloatStateOf(0f) - /** The spec to use when animating this transition to either [fromScene] or [toScene]. */ - lateinit var swipeSpec: SpringSpec<Float> + /** + * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture. + */ + var isAnimatingOffset by mutableStateOf(false) - /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - private fun startOffsetAnimation(job: () -> Job) { - cancelOffsetAnimation() - offsetAnimationJob = job() - } + // If we are not animating offset, it means the offset is being driven by the user's finger. + override val isUserInputOngoing: Boolean + get() = !isAnimatingOffset - /** 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() - } + /** The animatable used to animate the offset once the user lifted its finger. */ + val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) - fun finishOffsetAnimation() { - if (isAnimatingOffset) { - isAnimatingOffset = false - dragOffset = offsetAnimatable.value - } - } + /** Job to check that there is at most one offset animation in progress. */ + private var offsetAnimationJob: Job? = null - fun animateOffset( - // TODO(b/317063114) The CoroutineScope should be removed. - coroutineScope: CoroutineScope, - initialVelocity: Float, - targetOffset: Float, - onAnimationCompleted: () -> Unit, - ) { - startOffsetAnimation { - coroutineScope.launch { - animateOffset(targetOffset, initialVelocity) - onAnimationCompleted() - } - } - } + /** The spec to use when animating this transition to either [fromScene] or [toScene]. */ + lateinit var swipeSpec: SpringSpec<Float> - private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { - if (!isAnimatingOffset) { - offsetAnimatable.snapTo(dragOffset) - } - isAnimatingOffset = true + /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ + private fun startOffsetAnimation(job: () -> Job) { + cancelOffsetAnimation() + offsetAnimationJob = job() + } - offsetAnimatable.animateTo( - targetValue = targetOffset, - animationSpec = swipeSpec, - initialVelocity = initialVelocity, - ) + /** 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() + } - finishOffsetAnimation() + fun finishOffsetAnimation() { + if (isAnimatingOffset) { + isAnimatingOffset = false + dragOffset = offsetAnimatable.value } } - companion object { - private const val TAG = "SceneGestureHandler" + fun animateOffset( + // TODO(b/317063114) The CoroutineScope should be removed. + coroutineScope: CoroutineScope, + initialVelocity: Float, + targetOffset: Float, + onAnimationCompleted: () -> Unit, + ) { + startOffsetAnimation { + coroutineScope.launch { + animateOffset(targetOffset, initialVelocity) + onAnimationCompleted() + } + } } - private object DefaultSwipeDistance : UserActionDistance { - override fun Density.absoluteDistance( - fromSceneSize: IntSize, - orientation: Orientation, - ): Float { - return when (orientation) { - Orientation.Horizontal -> fromSceneSize.width - Orientation.Vertical -> fromSceneSize.height - }.toFloat() + private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { + if (!isAnimatingOffset) { + offsetAnimatable.snapTo(dragOffset) } + isAnimatingOffset = true + + offsetAnimatable.animateTo( + targetValue = targetOffset, + animationSpec = swipeSpec, + initialVelocity = initialVelocity, + ) + + finishOffsetAnimation() } +} - /** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */ - private class Swipes( - val upOrLeft: Swipe?, - val downOrRight: Swipe?, - val upOrLeftNoSource: Swipe?, - val downOrRightNoSource: Swipe?, - ) +private object DefaultSwipeDistance : UserActionDistance { + override fun Density.absoluteDistance( + fromSceneSize: IntSize, + orientation: Orientation, + ): Float { + return when (orientation) { + Orientation.Horizontal -> fromSceneSize.width + Orientation.Vertical -> fromSceneSize.height + }.toFloat() + } } +/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */ +private class Swipes( + val upOrLeft: Swipe?, + val downOrRight: Swipe?, + val upOrLeftNoSource: Swipe?, + val downOrRightNoSource: Swipe?, +) + private class SceneDraggableHandler( private val gestureHandler: SceneGestureHandler, ) : DraggableHandler { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index dacbdb484d0c..c91d29880ffb 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -127,6 +127,9 @@ class SceneGestureHandlerTest { val progress: Float get() = (transitionState as Transition).progress + val isUserInputOngoing: Boolean + get() = (transitionState as Transition).isUserInputOngoing + fun advanceUntilIdle() { testScope.testScheduler.advanceUntilIdle() } @@ -538,12 +541,11 @@ class SceneGestureHandlerTest { onDragStopped(velocity = velocityThreshold) assertTransition(currentScene = SceneC) - assertThat(sceneGestureHandler.isDrivingTransition).isTrue() - assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isTrue() + assertThat(isUserInputOngoing).isFalse() // Start a new gesture while the offset is animating onDragStartedImmediately() - assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse() + assertThat(isUserInputOngoing).isTrue() } @Test |