Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +19 −2 Original line number Diff line number Diff line Loading @@ -157,6 +157,8 @@ class SceneGestureHandler( */ private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } internal var gestureWithPriority: Any? = null internal fun onDragStarted() { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. Loading Loading @@ -525,17 +527,23 @@ private class SceneDraggableHandler( private val gestureHandler: SceneGestureHandler, ) : DraggableHandler { override suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset) { gestureHandler.gestureWithPriority = this gestureHandler.onDragStarted() } override fun onDelta(pixels: Float) { if (gestureHandler.gestureWithPriority == this) { gestureHandler.onDrag(delta = pixels) } } override suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float) { if (gestureHandler.gestureWithPriority == this) { gestureHandler.gestureWithPriority = null gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) } } } @VisibleForTesting class SceneNestedScrollHandler( Loading Loading @@ -615,10 +623,15 @@ class SceneNestedScrollHandler( }, canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key }, onStart = { gestureHandler.gestureWithPriority = this priorityScene = nextScene gestureHandler.onDragStarted() }, onScroll = { offsetAvailable -> if (gestureHandler.gestureWithPriority != this) { return@PriorityNestedScrollConnection Offset.Zero } val amount = offsetAvailable.toAmount() // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is Loading @@ -628,6 +641,10 @@ class SceneNestedScrollHandler( amount.toOffset() }, onStop = { velocityAvailable -> if (gestureHandler.gestureWithPriority != this) { return@PriorityNestedScrollConnection Velocity.Zero } priorityScene = null gestureHandler.onDragStopped( Loading packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -312,4 +312,52 @@ class SceneGestureHandlerTest { advanceUntilIdle() assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeDraggableStart_drag_shouldBeIgnored() = runGestureTest { draggable.onDelta(deltaInPixels10) assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest { draggable.onDragStopped(coroutineScope, velocityThreshold) assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest { nestedScroll.onPreFling(Velocity(0f, velocityThreshold)) assertScene(currentScene = SceneA, isIdle = true) } @Test fun startNestedScrollWhileDragging() = runGestureTest { draggable.onDragStarted(coroutineScope, Offset.Zero) assertScene(currentScene = SceneA, isIdle = false) val transition = transitionState as Transition draggable.onDelta(deltaInPixels10) assertThat(transition.progress).isEqualTo(0.1f) // now we can intercept the scroll events nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.2f) // this should be ignored, we are scrolling now! draggable.onDragStopped(coroutineScope, velocityThreshold) assertScene(currentScene = SceneA, isIdle = false) nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.3f) nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.4f) nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) assertScene(currentScene = SceneC, isIdle = false) // wait for the stop animation advanceUntilIdle() assertScene(currentScene = SceneC, isIdle = true) } } Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +19 −2 Original line number Diff line number Diff line Loading @@ -157,6 +157,8 @@ class SceneGestureHandler( */ private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } internal var gestureWithPriority: Any? = null internal fun onDragStarted() { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. Loading Loading @@ -525,17 +527,23 @@ private class SceneDraggableHandler( private val gestureHandler: SceneGestureHandler, ) : DraggableHandler { override suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset) { gestureHandler.gestureWithPriority = this gestureHandler.onDragStarted() } override fun onDelta(pixels: Float) { if (gestureHandler.gestureWithPriority == this) { gestureHandler.onDrag(delta = pixels) } } override suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float) { if (gestureHandler.gestureWithPriority == this) { gestureHandler.gestureWithPriority = null gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) } } } @VisibleForTesting class SceneNestedScrollHandler( Loading Loading @@ -615,10 +623,15 @@ class SceneNestedScrollHandler( }, canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key }, onStart = { gestureHandler.gestureWithPriority = this priorityScene = nextScene gestureHandler.onDragStarted() }, onScroll = { offsetAvailable -> if (gestureHandler.gestureWithPriority != this) { return@PriorityNestedScrollConnection Offset.Zero } val amount = offsetAvailable.toAmount() // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is Loading @@ -628,6 +641,10 @@ class SceneNestedScrollHandler( amount.toOffset() }, onStop = { velocityAvailable -> if (gestureHandler.gestureWithPriority != this) { return@PriorityNestedScrollConnection Velocity.Zero } priorityScene = null gestureHandler.onDragStopped( Loading
packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -312,4 +312,52 @@ class SceneGestureHandlerTest { advanceUntilIdle() assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeDraggableStart_drag_shouldBeIgnored() = runGestureTest { draggable.onDelta(deltaInPixels10) assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest { draggable.onDragStopped(coroutineScope, velocityThreshold) assertScene(currentScene = SceneA, isIdle = true) } @Test fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest { nestedScroll.onPreFling(Velocity(0f, velocityThreshold)) assertScene(currentScene = SceneA, isIdle = true) } @Test fun startNestedScrollWhileDragging() = runGestureTest { draggable.onDragStarted(coroutineScope, Offset.Zero) assertScene(currentScene = SceneA, isIdle = false) val transition = transitionState as Transition draggable.onDelta(deltaInPixels10) assertThat(transition.progress).isEqualTo(0.1f) // now we can intercept the scroll events nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.2f) // this should be ignored, we are scrolling now! draggable.onDragStopped(coroutineScope, velocityThreshold) assertScene(currentScene = SceneA, isIdle = false) nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.3f) nestedScrollEvents(available = offsetY10) assertThat(transition.progress).isEqualTo(0.4f) nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) assertScene(currentScene = SceneC, isIdle = false) // wait for the stop animation advanceUntilIdle() assertScene(currentScene = SceneC, isIdle = true) } }