summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-11-01 17:49:15 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-01 17:49:15 +0000
commit9ad90b8d8032ed973aaa8364d93cde9d56ea9a06 (patch)
tree150c4e889dbf0942198e87fe2924ed0ccfaa9fdd
parent5e4f136bd493f35002c2228e88cb33d5fe78cade (diff)
parent957f7c9970b95b9758122df61db2add43caf1808 (diff)
Merge "PriorityNestedScrollConnection: Add flingToScroll [1/2]" into main
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt64
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt18
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt35
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt10
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt10
9 files changed, 153 insertions, 40 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index 5cb45e5bd914..94c18cdbef5a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -16,11 +16,13 @@
package com.android.systemui.notifications.ui.composable
+import androidx.compose.foundation.gestures.FlingBehavior
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.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
@@ -43,6 +45,7 @@ fun NotificationScrimNestedScrollConnection(
isCurrentGestureOverscroll: () -> Boolean,
onStart: (Float) -> Unit = {},
onStop: (Float) -> Unit = {},
+ flingBehavior: FlingBehavior,
): PriorityNestedScrollConnection {
return PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
@@ -77,8 +80,9 @@ fun NotificationScrimNestedScrollConnection(
return amountConsumed
}
- override suspend fun onStop(initialVelocity: Float): Float {
- onStop(initialVelocity)
+ override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+ val consumedByScroll = flingToScroll(initialVelocity, flingBehavior)
+ onStop(initialVelocity - consumedByScroll)
if (scrimOffset() < minScrimOffset()) {
animateScrimOffset(minScrimOffset())
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index e1b74a968caa..d8abfd7a4b94 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -18,7 +18,9 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -30,6 +32,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceAtLeast
+import com.android.compose.nestedscroll.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
import kotlin.math.max
@@ -46,32 +49,35 @@ fun Modifier.stackVerticalOverscroll(
val screenHeight =
with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
val overscrollOffset = remember { Animatable(0f) }
- val stackNestedScrollConnection = remember {
- NotificationStackNestedScrollConnection(
- stackOffset = { overscrollOffset.value },
- canScrollForward = canScrollForward,
- onScroll = { offsetAvailable ->
- coroutineScope.launch {
- val maxProgress = screenHeight * 0.2f
- val tilt = 3f
- var offset =
- overscrollOffset.value +
- maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
- offset = max(offset, -1f * maxProgress)
- overscrollOffset.snapTo(offset)
- }
- },
- onStop = { velocityAvailable ->
- coroutineScope.launch {
- overscrollOffset.animateTo(
- targetValue = 0f,
- initialVelocity = velocityAvailable,
- animationSpec = tween(),
- )
- }
- },
- )
- }
+ val flingBehavior = ScrollableDefaults.flingBehavior()
+ val stackNestedScrollConnection =
+ remember(flingBehavior) {
+ NotificationStackNestedScrollConnection(
+ stackOffset = { overscrollOffset.value },
+ canScrollForward = canScrollForward,
+ onScroll = { offsetAvailable ->
+ coroutineScope.launch {
+ val maxProgress = screenHeight * 0.2f
+ val tilt = 3f
+ var offset =
+ overscrollOffset.value +
+ maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
+ offset = max(offset, -1f * maxProgress)
+ overscrollOffset.snapTo(offset)
+ }
+ },
+ onStop = { velocityAvailable ->
+ coroutineScope.launch {
+ overscrollOffset.animateTo(
+ targetValue = 0f,
+ initialVelocity = velocityAvailable,
+ animationSpec = tween(),
+ )
+ }
+ },
+ flingBehavior = flingBehavior,
+ )
+ }
return this.then(
Modifier.nestedScroll(stackNestedScrollConnection).offset {
@@ -86,6 +92,7 @@ fun NotificationStackNestedScrollConnection(
onStart: (Float) -> Unit = {},
onScroll: (Float) -> Unit,
onStop: (Float) -> Unit = {},
+ flingBehavior: FlingBehavior,
): PriorityNestedScrollConnection {
return PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
@@ -106,8 +113,9 @@ fun NotificationStackNestedScrollConnection(
return consumed
}
- override suspend fun onStop(initialVelocity: Float): Float {
- onStop(initialVelocity)
+ override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+ val consumedByScroll = flingToScroll(initialVelocity, flingBehavior)
+ onStop(initialVelocity - consumedByScroll)
return initialVelocity
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 4fa1984661da..ae273d8e2ad9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -25,6 +25,7 @@ import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollBy
@@ -451,12 +452,14 @@ fun SceneScope.NotificationScrollingStack(
}
}
+ val flingBehavior = ScrollableDefaults.flingBehavior()
val scrimNestedScrollConnection =
shadeSession.rememberSession(
scrimOffset,
maxScrimTop,
minScrimTop,
isCurrentGestureOverscroll,
+ flingBehavior,
) {
NotificationScrimNestedScrollConnection(
scrimOffset = { scrimOffset.value },
@@ -469,6 +472,7 @@ fun SceneScope.NotificationScrollingStack(
contentHeight = { stackHeight.intValue.toFloat() },
minVisibleScrimHeight = minVisibleScrimHeight,
isCurrentGestureOverscroll = { isCurrentGestureOverscroll.value },
+ flingBehavior = flingBehavior,
)
}
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 7c7202a5c7f2..7872ffad6cec 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
@@ -27,6 +27,7 @@ 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
import com.android.compose.nestedscroll.ScrollController
import kotlin.math.absoluteValue
@@ -749,7 +750,7 @@ private fun scrollController(
return dragController.onDrag(delta = deltaScroll)
}
- override suspend fun onStop(initialVelocity: Float): Float {
+ override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
return dragController
.onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
.invoke()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index 255da31719f3..a5be4dc195bc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -16,6 +16,7 @@
package com.android.compose.nestedscroll
+import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -41,6 +42,7 @@ fun LargeTopAppBarNestedScrollConnection(
onHeightChanged: (Float) -> Unit,
minHeight: () -> Float,
maxHeight: () -> Float,
+ flingBehavior: FlingBehavior,
): PriorityNestedScrollConnection {
return PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
@@ -55,7 +57,15 @@ fun LargeTopAppBarNestedScrollConnection(
offsetAvailable > 0 && height() < maxHeight()
},
canStartPostFling = { false },
- onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
+ onStart = {
+ LargeTopAppBarScrollController(
+ height = height,
+ maxHeight = maxHeight,
+ minHeight = minHeight,
+ onHeightChanged = onHeightChanged,
+ flingBehavior = flingBehavior,
+ )
+ },
)
}
@@ -64,6 +74,7 @@ private class LargeTopAppBarScrollController(
val maxHeight: () -> Float,
val minHeight: () -> Float,
val onHeightChanged: (Float) -> Unit,
+ val flingBehavior: FlingBehavior,
) : ScrollController {
override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
val currentHeight = height()
@@ -79,9 +90,8 @@ private class LargeTopAppBarScrollController(
return amountConsumed
}
- override suspend fun onStop(initialVelocity: Float): Float {
- // Don't consume the velocity on pre/post fling
- return 0f
+ override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+ return flingToScroll(initialVelocity, flingBehavior)
}
override fun onCancel() {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index ca44a5c21cab..e924ebfd2a8d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,7 +16,9 @@
package com.android.compose.nestedscroll
+import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -83,7 +85,16 @@ interface ScrollController {
* @param initialVelocity The initial velocity of the scroll when stopping.
* @return The consumed [initialVelocity] when the animation completes.
*/
- suspend fun onStop(initialVelocity: Float): Float
+ suspend fun OnStopScope.onStop(initialVelocity: Float): Float
+}
+
+interface OnStopScope {
+ /**
+ * Emits scroll events by using the [initialVelocity] and the [FlingBehavior].
+ *
+ * @return consumed velocity
+ */
+ suspend fun flingToScroll(initialVelocity: Float, flingBehavior: FlingBehavior): Float
}
/**
@@ -307,7 +318,11 @@ class PriorityNestedScrollConnection(
val controller = requireController(isStopping = false)
return coroutineScope {
try {
- async { controller.onStop(velocity) }
+ async {
+ with(controller) {
+ OnStopScopeImpl(controller = controller).onStop(velocity)
+ }
+ }
// Allows others to interrupt the job.
.also { stoppingJob = it }
// Note: this can be cancelled by [interruptStopping]
@@ -336,3 +351,19 @@ class PriorityNestedScrollConnection(
offsetScrolledBeforePriorityMode = 0f
}
}
+
+private class OnStopScopeImpl(private val controller: ScrollController) : OnStopScope {
+ override suspend fun flingToScroll(
+ initialVelocity: Float,
+ flingBehavior: FlingBehavior,
+ ): Float {
+ return with(flingBehavior) {
+ object : ScrollScope {
+ override fun scrollBy(pixels: Float): Float {
+ return controller.onScroll(pixels, NestedScrollSource.SideEffect)
+ }
+ }
+ .performFling(initialVelocity)
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index a406e13904f5..e27f9b52153d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -16,6 +16,8 @@
package com.android.compose.nestedscroll
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -29,6 +31,13 @@ class LargeTopAppBarNestedScrollConnectionTest(testCase: TestCase) {
val scrollSource = testCase.scrollSource
private var height = 0f
+ private val customFlingBehavior =
+ object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ scrollBy(initialVelocity)
+ return initialVelocity / 2f
+ }
+ }
private fun buildScrollConnection(heightRange: ClosedFloatingPointRange<Float>) =
LargeTopAppBarNestedScrollConnection(
@@ -36,6 +45,7 @@ class LargeTopAppBarNestedScrollConnectionTest(testCase: TestCase) {
onHeightChanged = { height = it },
minHeight = { heightRange.start },
maxHeight = { heightRange.endInclusive },
+ flingBehavior = customFlingBehavior,
)
private fun NestedScrollConnection.scroll(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 0364cdc4166e..54428404bd0c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -18,12 +18,15 @@
package com.android.compose.nestedscroll
+import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@@ -41,7 +44,16 @@ class PriorityNestedScrollConnectionTest {
private var consumeScroll = true
private var lastStop: Float? = null
private var isCancelled: Boolean = false
- private var consumeStop = true
+ private var onStopConsumeFlingToScroll = false
+ private var onStopConsumeAll = true
+
+ private val customFlingBehavior =
+ object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ scrollBy(initialVelocity)
+ return initialVelocity / 2f
+ }
+ }
private val scrollConnection =
PriorityNestedScrollConnection(
@@ -57,9 +69,16 @@ class PriorityNestedScrollConnectionTest {
return if (consumeScroll) deltaScroll else 0f
}
- override suspend fun onStop(initialVelocity: Float): Float {
+ override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
lastStop = initialVelocity
- return if (consumeStop) initialVelocity else 0f
+ var velocityConsumed = 0f
+ if (onStopConsumeFlingToScroll) {
+ velocityConsumed = flingToScroll(initialVelocity, customFlingBehavior)
+ }
+ if (onStopConsumeAll) {
+ velocityConsumed = initialVelocity
+ }
+ return velocityConsumed
}
override fun onCancel() {
@@ -178,6 +197,22 @@ class PriorityNestedScrollConnectionTest {
}
@Test
+ fun onStopScrollUsingFlingToScroll() = runMonotonicClockTest {
+ startPriorityModePostScroll()
+ onStopConsumeFlingToScroll = true
+ onStopConsumeAll = false
+ lastScroll = Float.NaN
+
+ val consumed = scrollConnection.onPreFling(available = Velocity(2f, 2f))
+
+ assertThat(lastStop).isEqualTo(2f)
+ // flingToScroll should try to scroll the content, customFlingBehavior uses the velocity.
+ assertThat(lastScroll).isEqualTo(2f)
+ // customFlingBehavior returns half of the vertical velocity.
+ assertThat(consumed).isEqualTo(Velocity(0f, 1f))
+ }
+
+ @Test
fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest {
startPriorityModePostScroll()
canStopOnPreFling = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
index 75479ad35725..60b95ad632e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,6 +37,13 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
private var scrimOffset = 0f
private var contentHeight = 0f
private var isCurrentGestureOverscroll = false
+ private val customFlingBehavior =
+ object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ scrollBy(initialVelocity)
+ return initialVelocity / 2f
+ }
+ }
private val scrollConnection =
NotificationScrimNestedScrollConnection(
@@ -51,6 +60,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
wasStarted = true
isStarted = false
},
+ flingBehavior = customFlingBehavior,
)
@Test