From f0b04418efebff13dcb2757ee39714f2ba95fae7 Mon Sep 17 00:00:00 2001 From: omarmt Date: Fri, 27 Sep 2024 15:13:59 +0000 Subject: Add support for fromSource in nested scroll STL's nested scroll connection now uses the information available in the DraggableHandler to compute the current action (Swipe.Resolved). Test: atest DraggableHandlerTest Bug: 366387626 Flag: com.android.systemui.scene_container Change-Id: Ib6b53679de0a3a1aa8bf4fe096f3bb04f3292c8f --- .../compose/animation/scene/DraggableHandler.kt | 49 ++++++++----------- .../animation/scene/MultiPointerDraggable.kt | 3 +- .../animation/scene/DraggableHandlerTest.kt | 56 ++++++++++++++++++++-- 3 files changed, 74 insertions(+), 34 deletions(-) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 367faed7b7f7..84fea557ffc4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -188,7 +188,7 @@ internal class DraggableHandlerImpl( return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation) } - private fun resolveSwipeSource(startedPosition: Offset?): SwipeSource.Resolved? { + internal fun resolveSwipeSource(startedPosition: Offset?): SwipeSource.Resolved? { if (startedPosition == null) return null return layoutImpl.swipeSourceDetector.source( layoutSize = layoutImpl.lastSize, @@ -198,7 +198,7 @@ internal class DraggableHandlerImpl( ) } - private fun resolveSwipe( + internal fun resolveSwipe( pointersDown: Int, fromSource: SwipeSource.Resolved?, isUpOrLeft: Boolean, @@ -558,6 +558,14 @@ internal class NestedScrollHandlerImpl( val connection: PriorityNestedScrollConnection = nestedScrollConnection() + private fun PointersInfo.resolveSwipe(isUpOrLeft: Boolean): Swipe.Resolved { + return draggableHandler.resolveSwipe( + pointersDown = pointersDown, + fromSource = draggableHandler.resolveSwipeSource(startedPosition), + isUpOrLeft = isUpOrLeft, + ) + } + private fun nestedScrollConnection(): PriorityNestedScrollConnection { // If we performed a long gesture before entering priority mode, we would have to avoid // moving on to the next scene. @@ -574,36 +582,19 @@ internal class NestedScrollHandlerImpl( val transitionState = layoutState.transitionState val scene = transitionState.currentScene val fromScene = layoutImpl.scene(scene) - val nextScene = + val resolvedSwipe = when { - amount < 0f -> { - val actionUpOrLeft = - Swipe.Resolved( - direction = - when (orientation) { - Orientation.Horizontal -> SwipeDirection.Resolved.Left - Orientation.Vertical -> SwipeDirection.Resolved.Up - }, - pointerCount = pointersInfo().pointersDown, - fromSource = null, - ) - fromScene.userActions[actionUpOrLeft] - } - amount > 0f -> { - val actionDownOrRight = - Swipe.Resolved( - direction = - when (orientation) { - Orientation.Horizontal -> SwipeDirection.Resolved.Right - Orientation.Vertical -> SwipeDirection.Resolved.Down - }, - pointerCount = pointersInfo().pointersDown, - fromSource = null, - ) - fromScene.userActions[actionDownOrRight] - } + amount < 0f -> pointersInfo().resolveSwipe(isUpOrLeft = true) + amount > 0f -> pointersInfo().resolveSwipe(isUpOrLeft = false) else -> null } + val nextScene = + resolvedSwipe?.let { + fromScene.userActions[it] + ?: if (it.fromSource != null) { + fromScene.userActions[it.copy(fromSource = null)] + } else null + } if (nextScene != null) return true if (transitionState !is TransitionState.Idle) return false 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 0bb1d928c2b4..ab11be8b671f 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 @@ -213,8 +213,9 @@ internal class MultiPointerDraggableNode( internal fun pointersInfo(): PointersInfo { return PointersInfo( + // This may be null, i.e. when the user uses TalkBack startedPosition = startedPosition, - // Note: We could have 0 pointers during fling or for other reasons. + // We could have 0 pointers during fling or for other reasons. pointersDown = pointersDown.coerceAtLeast(1), ) } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index b64b8be90ad2..6972dbe49019 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -129,6 +129,10 @@ class DraggableHandlerTest { val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical) val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) + var pointerInfoOwner: () -> PointersInfo = { + PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) + } + fun nestedScrollConnection( nestedScrollBehavior: NestedScrollBehavior, isExternalOverscrollGesture: Boolean = false, @@ -139,9 +143,7 @@ class DraggableHandlerTest { topOrLeftBehavior = nestedScrollBehavior, bottomOrRightBehavior = nestedScrollBehavior, isExternalOverscrollGesture = { isExternalOverscrollGesture }, - pointersInfoOwner = { - PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) - }, + pointersInfoOwner = { pointerInfoOwner() }, ) .connection @@ -155,11 +157,18 @@ class DraggableHandlerTest { fun downOffset(fractionOfScreen: Float) = if (fractionOfScreen < 0f) { - error("upOffset() is required, not implemented yet") + error("use upOffset()") } else { Offset(x = 0f, y = down(fractionOfScreen)) } + fun upOffset(fractionOfScreen: Float) = + if (fractionOfScreen < 0f) { + error("use downOffset()") + } else { + Offset(x = 0f, y = up(fractionOfScreen)) + } + val transitionState: TransitionState get() = layoutState.transitionState @@ -1070,6 +1079,45 @@ class DraggableHandlerTest { assertThat(transition!!.progress).isEqualTo(-0.1f) } + @Test + fun nestedScrollUseFromSourceInfo() = runGestureTest { + // Start at scene C. + navigateToSceneC() + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Drag from the **top** of the screen + pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) } + assertIdle(currentScene = SceneC) + + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + // userAction: Swipe.Up to SceneB + toScene = SceneB, + progress = 0.1f, + ) + + // Reset to SceneC + nestedScroll.preFling(Velocity.Zero) + advanceUntilIdle() + + // Drag from the **bottom** of the screen + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1) + } + assertIdle(currentScene = SceneC) + + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + // userAction: Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA + toScene = SceneA, + progress = 0.1f, + ) + } + @Test fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest { // Swipe up from the middle to transition to scene B. -- cgit v1.2.3-59-g8ed1b