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

Commit ec351bf7 authored by Omar Miatello's avatar Omar Miatello Committed by Android (Google) Code Review
Browse files

Merge "Add ScrollController in PriorityNestedScrollConnection" into main

parents 5c0ed71c cf92b9b3
Loading
Loading
Loading
Loading
+35 −26
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.systemui.notifications.ui.composable

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController

/**
 * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -58,34 +60,41 @@ fun NotificationScrimNestedScrollConnection(
            offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
        },
        canStartPostFling = { false },
        canStopOnPreFling = { false },
        onStart = { offsetAvailable -> onStart(offsetAvailable) },
        onScroll = { offsetAvailable, _ ->
        onStart = { firstScroll ->
            onStart(firstScroll)
            object : ScrollController {
                override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
                    val currentHeight = scrimOffset()
                    val amountConsumed =
                if (offsetAvailable > 0) {
                        if (deltaScroll > 0) {
                            val amountLeft = maxScrimOffset - currentHeight
                    offsetAvailable.fastCoerceAtMost(amountLeft)
                            deltaScroll.fastCoerceAtMost(amountLeft)
                        } else {
                            val amountLeft = minScrimOffset() - currentHeight
                    offsetAvailable.fastCoerceAtLeast(amountLeft)
                            deltaScroll.fastCoerceAtLeast(amountLeft)
                        }
                    snapScrimOffset(currentHeight + amountConsumed)
            amountConsumed
        },
        onStop = { velocityAvailable ->
            onStop(velocityAvailable)
                    return amountConsumed
                }

                override suspend fun onStop(initialVelocity: Float): Float {
                    onStop(initialVelocity)
                    if (scrimOffset() < minScrimOffset()) {
                        animateScrimOffset(minScrimOffset())
                    }
                    // Don't consume the velocity on pre/post fling
            0f
        },
        onCancel = {
                    return 0f
                }

                override fun onCancel() {
                    onStop(0f)
                    if (scrimOffset() < minScrimOffset()) {
                        animateScrimOffset(minScrimOffset())
                    }
                }

                override fun canStopOnPreFling() = false
            }
        },
    )
}
+24 −13
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
@@ -30,6 +31,7 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.tanh
@@ -92,20 +94,29 @@ fun NotificationStackNestedScrollConnection(
            offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
        },
        canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
        canStopOnPreFling = { false },
        onStart = { offsetAvailable -> onStart(offsetAvailable) },
        onScroll = { offsetAvailable, _ ->
        onStart = { firstScroll ->
            onStart(firstScroll)
            object : ScrollController {
                override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
                    val minOffset = 0f
            val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
                    val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset())
                    if (consumed != 0f) {
                        onScroll(consumed)
                    }
            consumed
        },
        onStop = { velocityAvailable ->
            onStop(velocityAvailable)
            velocityAvailable
                    return consumed
                }

                override suspend fun onStop(initialVelocity: Float): Float {
                    onStop(initialVelocity)
                    return initialVelocity
                }

                override fun onCancel() {
                    onStop(0f)
                }

                override fun canStopOnPreFling() = false
            }
        },
        onCancel = { onStop(0f) },
    )
}
+53 −44
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.compose.animation.scene

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
@@ -27,6 +28,7 @@ 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.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
import kotlin.math.absoluteValue

internal typealias SuspendedValue<T> = suspend () -> T
@@ -66,6 +68,7 @@ internal class DraggableHandlerImpl(
    internal val orientation: Orientation,
) : DraggableHandler {
    internal val nestedScrollKey = Any()

    /** The [DraggableHandler] can only have one active [DragController] at a time. */
    private var dragController: DragControllerImpl? = null

@@ -345,6 +348,7 @@ private class DragControllerImpl(
                    distance == DistanceUnspecified ||
                        swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
                        desiredOffset

                    distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
                    else -> desiredOffset.fastCoerceIn(distance, 0f)
                }
@@ -545,6 +549,7 @@ internal class Swipes(
            upOrLeftResult == null && downOrRightResult == null -> null
            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
                upOrLeftResult

            else -> downOrRightResult
        }
    }
@@ -608,7 +613,6 @@ internal class NestedScrollHandlerImpl(
            return overscrollSpec != null
        }

        var dragController: DragController? = null
        var isIntercepting = false

        return PriorityNestedScrollConnection(
@@ -669,10 +673,12 @@ internal class NestedScrollHandlerImpl(
                            canChangeScene = isZeroOffset
                            isZeroOffset && hasNextScene(offsetAvailable)
                        }

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

                        NestedScrollBehavior.EdgeAlways -> {
                            canChangeScene = true
                            hasNextScene(offsetAvailable)
@@ -710,53 +716,56 @@ internal class NestedScrollHandlerImpl(

                canStart
            },
            // We need to maintain scroll priority even if the scene transition can no longer
            // consume the scroll gesture to allow us to return to the previous scene.
            canStopOnScroll = { _, _ -> false },
            canStopOnPreFling = { true },
            onStart = { offsetAvailable ->
            onStart = { firstScroll ->
                val pointersInfo = pointersInfo()
                scrollController(
                    dragController =
                        draggableHandler.onDragStarted(
                            pointersDown = pointersInfo.pointersDown,
                            startedPosition = pointersInfo.startedPosition,
                        overSlop = if (isIntercepting) 0f else offsetAvailable,
                            overSlop = if (isIntercepting) 0f else firstScroll,
                        ),
                    canChangeScene = canChangeScene,
                    pointersInfoOwner = pointersInfoOwner,
                )
            },
            onScroll = { offsetAvailable, _ ->
                val controller = dragController ?: error("Should be called after onStart")
        )
    }
}

private fun scrollController(
    dragController: DragController,
    canChangeScene: Boolean,
    pointersInfoOwner: PointersInfoOwner,
): ScrollController {
    return object : ScrollController {
        override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
            val pointersInfo = pointersInfoOwner.pointersInfo()
            if (pointersInfo.isMouseWheel) {
                // Do not support mouse wheel interactions
                    return@PriorityNestedScrollConnection 0f
                return 0f
            }

                // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
                // initiated in a nested child.
                controller.onDrag(delta = offsetAvailable)
            },
            onStop = { velocityAvailable ->
                val controller = dragController ?: error("Should be called after onStart")
                try {
                    controller
                        .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
            return dragController.onDrag(delta = deltaScroll)
        }

        override suspend fun onStop(initialVelocity: Float): Float {
            return dragController
                .onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
                .invoke()
                } finally {
                    // onStop might still be running when a new gesture begins.
                    // To prevent conflicts, we should only remove the drag controller if it's the
                    // same one that was active initially.
                    if (dragController == controller) {
                        dragController = null
        }

        override fun onCancel() {
            dragController.onStop(velocity = 0f, canChangeContent = canChangeScene)
        }
            },
            onCancel = {
                val controller = dragController ?: error("Should be called after onStart")
                controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
                dragController = null
            },
        )

        /**
         * We need to maintain scroll priority even if the scene transition can no longer consume
         * the scroll gesture to allow us to return to the previous scene.
         */
        override fun canCancelScroll(available: Float, consumed: Float) = false

        override fun canStopOnPreFling() = true
    }
}

+34 −18
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.compose.nestedscroll

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost

@@ -54,23 +55,38 @@ fun LargeTopAppBarNestedScrollConnection(
            offsetAvailable > 0 && height() < maxHeight()
        },
        canStartPostFling = { false },
        canStopOnPreFling = { false },
        onStart = { /* do nothing */ },
        onScroll = { offsetAvailable, _ ->
        onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
    )
}

private class LargeTopAppBarScrollController(
    val height: () -> Float,
    val maxHeight: () -> Float,
    val minHeight: () -> Float,
    val onHeightChanged: (Float) -> Unit,
) : ScrollController {
    override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
        val currentHeight = height()
        val amountConsumed =
                if (offsetAvailable > 0) {
            if (deltaScroll > 0) {
                val amountLeft = maxHeight() - currentHeight
                    offsetAvailable.fastCoerceAtMost(amountLeft)
                deltaScroll.fastCoerceAtMost(amountLeft)
            } else {
                val amountLeft = minHeight() - currentHeight
                    offsetAvailable.fastCoerceAtLeast(amountLeft)
                deltaScroll.fastCoerceAtLeast(amountLeft)
            }
        onHeightChanged(currentHeight + amountConsumed)
            amountConsumed
        },
        return amountConsumed
    }

    override suspend fun onStop(initialVelocity: Float): Float {
        // Don't consume the velocity on pre/post fling
        onStop = { 0f },
        onCancel = { /* do nothing */ },
    )
        return 0f
    }

    override fun onCancel() {
        // do nothing
    }

    override fun canStopOnPreFling() = false
}
+234 −130

File changed.

Preview size limit exceeded, changes collapsed.

Loading