diff options
2 files changed, 65 insertions, 6 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index 8552aaf0ebff..436e5bf6debd 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity import androidx.compose.ui.util.fastForEach +import kotlin.math.sign /** * Make an element draggable in the given [orientation]. @@ -223,12 +224,31 @@ private suspend fun PointerInputScope.detectDragGestures( // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once // it is public. - when (orientation) { - Orientation.Horizontal -> - awaitHorizontalTouchSlopOrCancellation(down.id, onSlopReached) - Orientation.Vertical -> - awaitVerticalTouchSlopOrCancellation(down.id, onSlopReached) + val drag = + when (orientation) { + Orientation.Horizontal -> + awaitHorizontalTouchSlopOrCancellation(down.id, onSlopReached) + Orientation.Vertical -> + awaitVerticalTouchSlopOrCancellation(down.id, onSlopReached) + } + + // Make sure that overSlop is not 0f. This can happen when the user drags by exactly + // the touch slop. However, the overSlop we pass to onDragStarted() is used to + // compute the direction we are dragging in, so overSlop should never be 0f unless + // we intercept an ongoing swipe transition (i.e. startDragImmediately() returned + // true). + if (drag != null && overSlop == 0f) { + val deltaOffset = drag.position - initialDown.position + val delta = + when (orientation) { + Orientation.Horizontal -> deltaOffset.y + Orientation.Vertical -> deltaOffset.y + } + check(delta != 0f) + overSlop = delta.sign } + + drag } if (drag != null) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 940335853221..560f3b7c7d53 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -74,6 +74,7 @@ class SwipeToSceneTest { mapOf( Swipe.Left to TestScenes.SceneB, Swipe.Down to TestScenes.SceneC, + Swipe.Up to TestScenes.SceneB, ), ) { Box(Modifier.fillMaxSize()) @@ -357,7 +358,7 @@ class SwipeToSceneTest { // detected as a drag event. var touchSlop = 0f - val layoutState = MutableSceneTransitionLayoutState(TestScenes.SceneA) + val layoutState = layoutState() val verticalSwipeDistance = 50.dp assertThat(verticalSwipeDistance).isNotEqualTo(LayoutHeight) @@ -392,4 +393,42 @@ class SwipeToSceneTest { assertThat(transition).isNotNull() assertThat(transition!!.progress).isEqualTo(0.5f) } + + @Test + fun swipeByTouchSlop() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState) + } + + // Swipe down by exactly touchSlop, so that the drag overSlop is 0f. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + + // We should still correctly compute that we are swiping down to scene C. + var transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition?.toScene).isEqualTo(TestScenes.SceneC) + + // Release the finger, animating back to scene A. + rule.onRoot().performTouchInput { up() } + rule.waitForIdle() + assertThat(layoutState.currentTransition).isNull() + assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneA) + + // Swipe up by exactly touchSlop, so that the drag overSlop is 0f. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, -touchSlop), delayMillis = 1_000) + } + + // We should still correctly compute that we are swiping up to scene B. + transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition?.toScene).isEqualTo(TestScenes.SceneB) + } } |