diff options
10 files changed, 132 insertions, 218 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 e4c60e166fd5..8b9e9274b448 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 @@ -18,8 +18,6 @@ package com.android.systemui.notifications.ui.composable import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.util.fastCoerceAtLeast -import androidx.compose.ui.util.fastCoerceAtMost import com.android.compose.nestedscroll.PriorityNestedScrollConnection /** @@ -46,7 +44,7 @@ fun NotificationScrimNestedScrollConnection( orientation = Orientation.Vertical, // scrolling up and inner content is taller than the scrim, so scrim needs to // expand; content can scroll once scrim is at the minScrimOffset. - canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart -> offsetAvailable < 0 && offsetBeforeStart == 0f && contentHeight() > minVisibleScrimHeight() && @@ -54,38 +52,36 @@ fun NotificationScrimNestedScrollConnection( }, // scrolling down and content is done scrolling to top. After that, the scrim // needs to collapse; collapse the scrim until it is at the maxScrimOffset. - canStartPostScroll = { offsetAvailable, _, _ -> + canStartPostScroll = { offsetAvailable, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) }, canStartPostFling = { false }, - canStopOnPreFling = { false }, + canContinueScroll = { + val currentHeight = scrimOffset() + minScrimOffset() < currentHeight && currentHeight < maxScrimOffset + }, + canScrollOnFling = true, onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable, _ -> + onScroll = { offsetAvailable -> val currentHeight = scrimOffset() val amountConsumed = if (offsetAvailable > 0) { val amountLeft = maxScrimOffset - currentHeight - offsetAvailable.fastCoerceAtMost(amountLeft) + offsetAvailable.coerceAtMost(amountLeft) } else { val amountLeft = minScrimOffset() - currentHeight - offsetAvailable.fastCoerceAtLeast(amountLeft) + offsetAvailable.coerceAtLeast(amountLeft) } snapScrimOffset(currentHeight + amountConsumed) amountConsumed }, + // Don't consume the velocity on pre/post fling onStop = { velocityAvailable -> onStop(velocityAvailable) if (scrimOffset() < minScrimOffset()) { animateScrimOffset(minScrimOffset()) } - // Don't consume the velocity on pre/post fling - 0f - }, - onCancel = { - onStop(0f) - if (scrimOffset() < minScrimOffset()) { - animateScrimOffset(minScrimOffset()) - } + { 0f } }, ) } 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 edb05ebd77d1..a706585deebc 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 @@ -28,7 +28,6 @@ import androidx.compose.ui.platform.LocalConfiguration 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.PriorityNestedScrollConnection import kotlin.math.max import kotlin.math.roundToInt @@ -87,25 +86,21 @@ fun NotificationStackNestedScrollConnection( ): PriorityNestedScrollConnection { return PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _, _ -> false }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> + canStartPreScroll = { _, _ -> false }, + canStartPostScroll = { offsetAvailable, offsetBeforeStart -> offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward() }, canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() }, - canStopOnPreFling = { false }, + canContinueScroll = { stackOffset() > 0f }, + canScrollOnFling = true, onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable, _ -> - val minOffset = 0f - val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset()) - if (consumed != 0f) { - onScroll(consumed) - } - consumed + onScroll = { offsetAvailable -> + onScroll(offsetAvailable) + offsetAvailable }, onStop = { velocityAvailable -> onStop(velocityAvailable) - velocityAvailable + suspend { velocityAvailable } }, - onCancel = { onStop(0f) }, ) } 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 9f99c372e2a8..e5ef79b37b88 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,10 +27,9 @@ 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.SuspendedValue import kotlin.math.absoluteValue -internal typealias SuspendedValue<T> = suspend () -> T - internal interface DraggableHandler { /** * Start a drag in the given [startedPosition], with the given [overSlop] and number of @@ -613,7 +612,7 @@ internal class NestedScrollHandlerImpl( return PriorityNestedScrollConnection( orientation = orientation, - canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart -> canChangeScene = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f @@ -645,7 +644,7 @@ internal class NestedScrollHandlerImpl( isIntercepting = true true }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> + canStartPostScroll = { offsetAvailable, offsetBeforeStart -> val behavior: NestedScrollBehavior = when { offsetAvailable > 0f -> topOrLeftBehavior @@ -710,7 +709,8 @@ internal class NestedScrollHandlerImpl( canStart }, - canStopOnPreFling = { true }, + canContinueScroll = { true }, + canScrollOnFling = false, onStart = { offsetAvailable -> val pointersInfo = pointersInfo() dragController = @@ -720,7 +720,7 @@ internal class NestedScrollHandlerImpl( overSlop = if (isIntercepting) 0f else offsetAvailable, ) }, - onScroll = { offsetAvailable, _ -> + onScroll = { offsetAvailable -> val controller = dragController ?: error("Should be called after onStart") val pointersInfo = pointersInfoOwner.pointersInfo() @@ -735,18 +735,10 @@ internal class NestedScrollHandlerImpl( }, onStop = { velocityAvailable -> val controller = dragController ?: error("Should be called after onStart") - try { - controller - .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) - .invoke() - } finally { - dragController = null - } - }, - onCancel = { - val controller = dragController ?: error("Should be called after onStart") - controller.onStop(velocity = 0f, canChangeContent = canChangeScene) - dragController = null + + controller + .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) + .also { dragController = null } }, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index f0043e1e89b0..205267da151a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.unit.IntSize 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.SuspendedValue import kotlin.math.absoluteValue import kotlinx.coroutines.CompletableDeferred 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 ecf64b771d1f..4ae323517b26 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 @@ -18,8 +18,6 @@ package com.android.compose.nestedscroll import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.util.fastCoerceAtLeast -import androidx.compose.ui.util.fastCoerceAtMost /** * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the @@ -45,32 +43,35 @@ fun LargeTopAppBarNestedScrollConnection( orientation = Orientation.Vertical, // When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will // expand. Then, you can then scroll down the content. - canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart -> offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight() }, // When swiping down, the content will scroll up until it reaches the top. Then, the // LargeTopAppBar will expand until it reaches its [maxHeight]. - canStartPostScroll = { offsetAvailable, _, _ -> + canStartPostScroll = { offsetAvailable, _ -> offsetAvailable > 0 && height() < maxHeight() }, canStartPostFling = { false }, - canStopOnPreFling = { false }, + canContinueScroll = { + val currentHeight = height() + minHeight() < currentHeight && currentHeight < maxHeight() + }, + canScrollOnFling = true, onStart = { /* do nothing */ }, - onScroll = { offsetAvailable, _ -> + onScroll = { offsetAvailable -> val currentHeight = height() val amountConsumed = if (offsetAvailable > 0) { val amountLeft = maxHeight() - currentHeight - offsetAvailable.fastCoerceAtMost(amountLeft) + offsetAvailable.coerceAtMost(amountLeft) } else { val amountLeft = minHeight() - currentHeight - offsetAvailable.fastCoerceAtLeast(amountLeft) + offsetAvailable.coerceAtLeast(amountLeft) } onHeightChanged(currentHeight + amountConsumed) amountConsumed }, // Don't consume the velocity on pre/post fling - onStop = { 0f }, - onCancel = { /* do nothing */ }, + onStop = { { 0f } }, ) } 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 636c55799ff2..a3641e6635e7 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,59 +16,37 @@ package com.android.compose.nestedscroll -import androidx.compose.animation.core.AnimationState -import androidx.compose.animation.core.DecayAnimationSpec -import androidx.compose.animation.core.animateDecay import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.Velocity import com.android.compose.ui.util.SpaceVectorConverter -import kotlin.math.abs import kotlin.math.sign -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope + +internal typealias SuspendedValue<T> = suspend () -> T /** - * A [NestedScrollConnection] that intercepts scroll events in priority mode. + * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and + * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling. + * If it does, it will scroll before its children, until [canContinueScroll] allows it. * - * Priority mode allows this connection to take control over scroll events within a nested scroll - * hierarchy. When in priority mode, this connection consumes scroll events before its children, - * enabling custom scrolling behaviors like sticky headers. + * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop] + * after [onStart]. * - * @param orientation The orientation of the scroll. - * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll - * events in pre-scroll mode. - * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll - * events in post-scroll mode. - * @param canStartPostFling lambda that returns true if the connection can start consuming scroll - * events in post-fling mode. - * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll - * events in pre-fling (i.e. as soon as the user lifts their fingers). - * @param onStart lambda that is called when the connection starts consuming scroll events. - * @param onScroll lambda that is called when the connection consumes a scroll event and returns the - * consumed amount. - * @param onStop lambda that is called when the connection stops consuming scroll events and returns - * the consumed velocity. - * @param onCancel lambda that is called when the connection is cancelled. * @sample LargeTopAppBarNestedScrollConnection * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection */ class PriorityNestedScrollConnection( orientation: Orientation, - private val canStartPreScroll: - (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, - private val canStartPostScroll: - (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, + private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, + private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, private val canStartPostFling: (velocityAvailable: Float) -> Boolean, - private val canStopOnPreFling: () -> Boolean, + private val canContinueScroll: (source: NestedScrollSource) -> Boolean, + private val canScrollOnFling: Boolean, private val onStart: (offsetAvailable: Float) -> Unit, - private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float, - private val onStop: suspend (velocityAvailable: Float) -> Float, - private val onCancel: () -> Unit, + private val onScroll: (offsetAvailable: Float) -> Float, + private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>, ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) { /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */ @@ -76,9 +54,6 @@ class PriorityNestedScrollConnection( private var offsetScrolledBeforePriorityMode = 0f - /** This job allows us to interrupt the onStop animation */ - private var onStopJob: Deferred<Float> = CompletableDeferred(0f) - override fun onPostScroll( consumed: Offset, available: Offset, @@ -89,48 +64,62 @@ class PriorityNestedScrollConnection( // the beginning or from the last fling gesture. val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat - if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) { + if ( + isPriorityMode || + (source == NestedScrollSource.SideEffect && !canScrollOnFling) || + !canStartPostScroll(availableFloat, offsetBeforeStart) + ) { // The priority mode cannot start so we won't consume the available offset. return Offset.Zero } - return start(availableFloat, source).toOffset() + return onPriorityStart(availableFloat).toOffset() } override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { if (!isPriorityMode) { - val availableFloat = available.toFloat() - if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) { - return start(availableFloat, source).toOffset() + if (source == NestedScrollSource.UserInput || canScrollOnFling) { + val availableFloat = available.toFloat() + if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) { + return onPriorityStart(availableFloat).toOffset() + } + // We want to track the amount of offset consumed before entering priority mode + offsetScrolledBeforePriorityMode += availableFloat } + + return Offset.Zero + } + + val availableFloat = available.toFloat() + if (!canContinueScroll(source)) { + // Step 3a: We have lost priority and we no longer need to intercept scroll events. + onPriorityStop(velocity = 0f) + + // We've just reset offsetScrolledBeforePriorityMode to 0f // We want to track the amount of offset consumed before entering priority mode offsetScrolledBeforePriorityMode += availableFloat + return Offset.Zero } - return scroll(available.toFloat(), source).toOffset() + // Step 2: We have the priority and can consume the scroll events. + return onScroll(availableFloat).toOffset() } override suspend fun onPreFling(available: Velocity): Velocity { - if (!isPriorityMode) { - resetOffsetTracker() + if (isPriorityMode && canScrollOnFling) { + // We don't want to consume the velocity, we prefer to continue receiving scroll events. return Velocity.Zero } - - if (canStopOnPreFling()) { - // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the - // velocity of the fling gesture. - return stop(velocityAvailable = available.toFloat()).toVelocity() - } - - // We don't want to consume the velocity, we prefer to continue receiving scroll events. - return Velocity.Zero + // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed + // of the fling gesture. + return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity() } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { val availableFloat = available.toFloat() if (isPriorityMode) { - return stop(velocityAvailable = availableFloat).toVelocity() + return onPriorityStop(velocity = availableFloat).invoke().toVelocity() } if (!canStartPostFling(availableFloat)) { @@ -142,14 +131,10 @@ class PriorityNestedScrollConnection( // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the // overscroll behavior on the Scene level. val smallOffset = availableFloat.sign - start( - availableOffset = smallOffset, - source = NestedScrollSource.SideEffect, - skipScroll = true, - ) + onPriorityStart(availableOffset = smallOffset) // This is the last event of a scroll gesture. - return stop(availableFloat).toVelocity() + return onPriorityStop(availableFloat).invoke().toVelocity() } /** @@ -158,76 +143,36 @@ class PriorityNestedScrollConnection( * TODO(b/303224944) This method should be removed. */ fun reset() { - if (isPriorityMode) { - // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart. - cancel() - } else { - resetOffsetTracker() - } - } - - private fun shouldStop(consumed: Float): Boolean { - return consumed == 0f + // Step 3c: To ensure that an onStop is always called for every onStart. + onPriorityStop(velocity = 0f) } - private fun start( - availableOffset: Float, - source: NestedScrollSource, - skipScroll: Boolean = false, - ): Float { - check(!isPriorityMode) { - "This should never happen, start() was called when isPriorityMode" + private fun onPriorityStart(availableOffset: Float): Float { + if (isPriorityMode) { + error("This should never happen, onPriorityStart() was called when isPriorityMode") } // Step 1: It's our turn! We start capturing scroll events when one of our children has an // available offset following a scroll event. isPriorityMode = true - onStopJob.cancel() - // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is // lifted (step 3b), or this object has been destroyed (step 3c). onStart(availableOffset) - return if (skipScroll) 0f else scroll(availableOffset, source) + return onScroll(availableOffset) } - private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float { - // Step 2: We have the priority and can consume the scroll events. - val consumedByScroll = onScroll(offsetAvailable, source) - - if (shouldStop(consumedByScroll)) { - // Step 3a: We have lost priority and we no longer need to intercept scroll events. - cancel() - - // We've just reset offsetScrolledBeforePriorityMode to 0f - // We want to track the amount of offset consumed before entering priority mode - offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll - } - - return consumedByScroll - } - - /** Reset the tracking of consumed offsets before entering in priority mode. */ - private fun resetOffsetTracker() { + private fun onPriorityStop(velocity: Float): SuspendedValue<Float> { + // We can restart tracking the consumed offsets from scratch. offsetScrolledBeforePriorityMode = 0f - } - private suspend fun stop(velocityAvailable: Float): Float { - check(isPriorityMode) { "This should never happen, stop() was called before start()" } - isPriorityMode = false - resetOffsetTracker() - - return coroutineScope { - onStopJob = async { onStop(velocityAvailable) } - onStopJob.await() + if (!isPriorityMode) { + return { 0f } } - } - private fun cancel() { - check(isPriorityMode) { "This should never happen, cancel() was called before start()" } isPriorityMode = false - resetOffsetTracker() - onCancel() + + return onStop(velocity) } } 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 339445e3483a..fd0214823eaf 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 @@ -39,6 +39,7 @@ import com.android.compose.animation.scene.TestScenes.SceneC import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.Transition import com.android.compose.animation.scene.subjects.assertThat +import com.android.compose.nestedscroll.SuspendedValue import com.android.compose.test.MonotonicClockTestScope import com.android.compose.test.runMonotonicClockTest import com.android.compose.test.transition diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt index 3df608717414..c8f6e6d99933 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Velocity import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.modifiers.thenIf +import com.android.compose.nestedscroll.SuspendedValue import com.google.common.truth.Truth.assertThat import kotlin.properties.Delegates import kotlinx.coroutines.coroutineScope 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 1a3b86b936df..badc43bd3e0f 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 @@ -34,31 +34,30 @@ class PriorityNestedScrollConnectionTest { private var canStartPreScroll = false private var canStartPostScroll = false private var canStartPostFling = false - private var canStopOnPreFling = true + private var canContinueScroll = false private var isStarted = false private var lastScroll: Float? = null - private var consumeScroll = true + private var returnOnScroll = 0f private var lastStop: Float? = null - private var isCancelled: Boolean = false - private var consumeStop = true + private var returnOnStop = 0f private val scrollConnection = PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _, _ -> canStartPreScroll }, - canStartPostScroll = { _, _, _ -> canStartPostScroll }, + canStartPreScroll = { _, _ -> canStartPreScroll }, + canStartPostScroll = { _, _ -> canStartPostScroll }, canStartPostFling = { canStartPostFling }, - canStopOnPreFling = { canStopOnPreFling }, + canContinueScroll = { canContinueScroll }, + canScrollOnFling = false, onStart = { isStarted = true }, - onScroll = { offsetAvailable, _ -> - lastScroll = offsetAvailable - if (consumeScroll) offsetAvailable else 0f + onScroll = { + lastScroll = it + returnOnScroll }, onStop = { lastStop = it - if (consumeStop) it else 0f + { returnOnStop } }, - onCancel = { isCancelled = true }, ) @Test @@ -86,7 +85,7 @@ class PriorityNestedScrollConnectionTest { canStartPostScroll = true scrollConnection.onPostScroll( consumed = Offset.Zero, - available = Offset(1f, 1f), + available = Offset.Zero, source = UserInput, ) } @@ -137,55 +136,45 @@ class PriorityNestedScrollConnectionTest { @Test fun step2_onPriorityMode_shouldContinueIfAllowed() { startPriorityModePostScroll() + canContinueScroll = true - val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) + scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput) assertThat(lastScroll).isEqualTo(1f) - assertThat(scroll1.y).isEqualTo(1f) - consumeScroll = false - val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput) - assertThat(lastScroll).isEqualTo(2f) - assertThat(scroll2.y).isEqualTo(0f) + canContinueScroll = false + scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput) + assertThat(lastScroll).isNotEqualTo(2f) + assertThat(lastScroll).isEqualTo(1f) } @Test - fun step3a_onPriorityMode_shouldCancelIfCannotContinue() { + fun step3a_onPriorityMode_shouldStopIfCannotContinue() { startPriorityModePostScroll() - consumeScroll = false + canContinueScroll = false - scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) + scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput) - assertThat(isCancelled).isTrue() + assertThat(lastStop).isNotNull() } @Test fun step3b_onPriorityMode_shouldStopOnFling() = runTest { startPriorityModePostScroll() + canContinueScroll = true scrollConnection.onPreFling(available = Velocity.Zero) - assertThat(lastStop).isEqualTo(0f) - } - - @Test - fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest { - startPriorityModePostScroll() - canStopOnPreFling = false - - scrollConnection.onPreFling(available = Velocity.Zero) - assertThat(lastStop).isNull() - - scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) - assertThat(lastStop).isEqualTo(0f) + assertThat(lastStop).isNotNull() } @Test - fun step3c_onPriorityMode_shouldCancelOnReset() { + fun step3c_onPriorityMode_shouldStopOnReset() { startPriorityModePostScroll() + canContinueScroll = true scrollConnection.reset() - assertThat(isCancelled).isTrue() + assertThat(lastStop).isNotNull() } @Test 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..1f1680e0c273 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 @@ -31,7 +31,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { private var isStarted = false - private var wasStarted = false private var scrimOffset = 0f private var contentHeight = 0f private var isCurrentGestureOverscroll = false @@ -47,10 +46,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT }, isCurrentGestureOverscroll = { isCurrentGestureOverscroll }, onStart = { isStarted = true }, - onStop = { - wasStarted = true - isStarted = false - }, + onStop = { isStarted = false }, ) @Test @@ -169,7 +165,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) - assertThat(wasStarted).isEqualTo(false) assertThat(isStarted).isEqualTo(false) } @@ -186,9 +181,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) - // Returning 0 offset will immediately stop the connection - assertThat(wasStarted).isEqualTo(true) - assertThat(isStarted).isEqualTo(false) + assertThat(isStarted).isEqualTo(true) } @Test |