Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +35 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } }, ) } packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt +24 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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) }, ) } packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +53 −44 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) } Loading Loading @@ -545,6 +549,7 @@ internal class Swipes( upOrLeftResult == null && downOrRightResult == null -> null (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> upOrLeftResult else -> downOrRightResult } } Loading Loading @@ -608,7 +613,6 @@ internal class NestedScrollHandlerImpl( return overscrollSpec != null } var dragController: DragController? = null var isIntercepting = false return PriorityNestedScrollConnection( Loading Loading @@ -669,10 +673,12 @@ internal class NestedScrollHandlerImpl( canChangeScene = isZeroOffset isZeroOffset && hasNextScene(offsetAvailable) } NestedScrollBehavior.EdgeWithPreview -> { canChangeScene = isZeroOffset hasNextScene(offsetAvailable) } NestedScrollBehavior.EdgeAlways -> { canChangeScene = true hasNextScene(offsetAvailable) Loading Loading @@ -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 } } Loading packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt +34 −18 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +234 −130 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +35 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } }, ) }
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt +24 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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) }, ) }
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +53 −44 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) } Loading Loading @@ -545,6 +549,7 @@ internal class Swipes( upOrLeftResult == null && downOrRightResult == null -> null (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> upOrLeftResult else -> downOrRightResult } } Loading Loading @@ -608,7 +613,6 @@ internal class NestedScrollHandlerImpl( return overscrollSpec != null } var dragController: DragController? = null var isIntercepting = false return PriorityNestedScrollConnection( Loading Loading @@ -669,10 +673,12 @@ internal class NestedScrollHandlerImpl( canChangeScene = isZeroOffset isZeroOffset && hasNextScene(offsetAvailable) } NestedScrollBehavior.EdgeWithPreview -> { canChangeScene = isZeroOffset hasNextScene(offsetAvailable) } NestedScrollBehavior.EdgeAlways -> { canChangeScene = true hasNextScene(offsetAvailable) Loading Loading @@ -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 } } Loading
packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt +34 −18 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 }
packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +234 −130 File changed.Preview size limit exceeded, changes collapsed. Show changes