Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f6fd445 authored by omarmt's avatar omarmt
Browse files

STL NestedScrollHandler uses the current Content

The scroll gesture is now being intercepted by SceneTransitionLayout if
the NestedScrollHandler finds that the current Content allows for it.

Test: atest DraggableHandlerTest
Bug: 377503459
Flag: com.android.systemui.scene_container
Change-Id: I2e97d365b23775a4ecad5d81327460299606488f
parent 70ab84d6
Loading
Loading
Loading
Loading
+6 −35
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
@@ -543,19 +542,6 @@ internal class NestedScrollHandlerImpl(

    val connection: PriorityNestedScrollConnection = nestedScrollConnection()

    private fun resolveSwipe(
        isUpOrLeft: Boolean,
        pointersDown: PointersInfo.PointersDown?,
    ): Swipe.Resolved {
        return resolveSwipe(
            orientation = draggableHandler.orientation,
            isUpOrLeft = isUpOrLeft,
            pointersDown = pointersDown,
            fromSource =
                pointersDown?.let { draggableHandler.resolveSwipeSource(it.startedPosition) },
        )
    }

    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.
@@ -563,23 +549,8 @@ internal class NestedScrollHandlerImpl(

        var lastPointersDown: PointersInfo.PointersDown? = null

        fun hasNextScene(amount: Float): Boolean {
            val transitionState = layoutState.transitionState
            val scene = transitionState.currentScene
            val fromScene = layoutImpl.scene(scene)
            val resolvedSwipe =
                when {
                    amount < 0f -> resolveSwipe(isUpOrLeft = true, lastPointersDown)
                    amount > 0f -> resolveSwipe(isUpOrLeft = false, lastPointersDown)
                    else -> null
                }
            val nextScene = resolvedSwipe?.let { fromScene.findActionResultBestMatch(it) }
            if (nextScene != null) return true

            if (transitionState !is TransitionState.Idle) return false

            val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation)
            return overscrollSpec != null
        fun shouldEnableSwipes(): Boolean {
            return layoutImpl.contentForUserActions().shouldEnableSwipes(orientation)
        }

        var isIntercepting = false
@@ -650,17 +621,17 @@ internal class NestedScrollHandlerImpl(
                    when (behavior) {
                        NestedScrollBehavior.EdgeNoPreview -> {
                            canChangeScene = isZeroOffset
                            isZeroOffset && hasNextScene(offsetAvailable)
                            isZeroOffset && shouldEnableSwipes()
                        }

                        NestedScrollBehavior.EdgeWithPreview -> {
                            canChangeScene = isZeroOffset
                            hasNextScene(offsetAvailable)
                            shouldEnableSwipes()
                        }

                        NestedScrollBehavior.EdgeAlways -> {
                            canChangeScene = true
                            hasNextScene(offsetAvailable)
                            shouldEnableSwipes()
                        }
                    }

@@ -693,7 +664,7 @@ internal class NestedScrollHandlerImpl(
                    }
                lastPointersDown = pointersDown

                val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
                val canStart = behavior.canStartOnPostFling && shouldEnableSwipes()
                if (canStart) {
                    isIntercepting = false
                }
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ private fun DraggableHandlerImpl.contentForSwipes(): Content {
}

/** Whether swipe should be enabled in the given [orientation]. */
private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
    if (userActions.isEmpty()) {
        return false
    }
+29 −0
Original line number Diff line number Diff line
@@ -1676,4 +1676,33 @@ class DraggableHandlerTest {
        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
    }

    @Test
    fun replaceOverlayNestedScroll() = runGestureTest {
        layoutState.showOverlay(OverlayA, animationScope = testScope)
        advanceUntilIdle()

        // Initial state.
        assertThat(layoutState.transitionState).isIdle()
        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)

        // Swipe down to replace overlay A by overlay B.

        val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
        nestedScroll.scroll(downOffset(0.1f))
        val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition()
        assertThat(transition).hasCurrentScene(SceneA)
        assertThat(transition).hasFromOverlay(OverlayA)
        assertThat(transition).hasToOverlay(OverlayB)
        assertThat(transition).hasCurrentOverlays(OverlayA)
        assertThat(transition).hasProgress(0.1f)

        nestedScroll.preFling(Velocity(0f, velocityThreshold))
        advanceUntilIdle()
        // Commit the gesture. The overlays are instantly swapped in the set of current overlays.
        assertThat(layoutState.transitionState).isIdle()
        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
    }
}