Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +1 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ fun NotificationScrimNestedScrollConnection( maxScrimOffset: Float, contentHeight: () -> Float, minVisibleScrimHeight: () -> Float, isCurrentGestureOverscroll: () -> Boolean, onStart: (Float) -> Unit = {}, onStop: (Float) -> Unit = {}, flingBehavior: FlingBehavior, Loading @@ -60,7 +59,7 @@ 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, _, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) offsetAvailable > 0 && (scrimOffset() < maxScrimOffset) }, onStart = { firstScroll -> onStart(firstScroll) Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +46 −8 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.boundsInWindow Loading @@ -95,6 +96,9 @@ import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.gesture.effect.OffsetOverscrollEffect import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect import com.android.compose.modifiers.thenIf import com.android.compose.nestedscroll.OnStopScope import com.android.compose.nestedscroll.PriorityNestedScrollConnection import com.android.compose.nestedscroll.ScrollController import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius Loading Loading @@ -460,13 +464,7 @@ fun ContentScope.NotificationScrollingStack( } val scrimNestedScrollConnection = shadeSession.rememberSession( key = "ScrimConnection", scrimOffset, minScrimTop, viewModel.isCurrentGestureOverscroll, density, ) { shadeSession.rememberSession(key = "ScrimConnection", scrimOffset, minScrimTop, density) { val flingSpec: DecayAnimationSpec<Float> = splineBasedDecay(density) val flingBehavior = NotificationScrimFlingBehavior(flingSpec) NotificationScrimNestedScrollConnection( Loading @@ -479,11 +477,50 @@ fun ContentScope.NotificationScrollingStack( maxScrimOffset = 0f, contentHeight = { stackHeight.intValue.toFloat() }, minVisibleScrimHeight = minVisibleScrimHeight, isCurrentGestureOverscroll = { viewModel.isCurrentGestureOverscroll }, flingBehavior = flingBehavior, ) } val swipeToExpandNotificationScrollConnection = shadeSession.rememberSession( key = "SwipeToExpandNotificationScrollConnection", scrimOffset, minScrimTop, density, viewModel.isCurrentGestureExpandingNotification, ) { PriorityNestedScrollConnection( orientation = Orientation.Vertical, canStartPreScroll = { _, _, _ -> false }, canStartPostScroll = { _, _, _ -> viewModel.isCurrentGestureExpandingNotification }, onStart = { firstScroll -> object : ScrollController { override fun onScroll( deltaScroll: Float, source: NestedScrollSource, ): Float { return if (viewModel.isCurrentGestureExpandingNotification) { // consume all the amount, when this swipe is expanding a // notification deltaScroll } else { // don't consume anything, when the expansion is done 0f } } override fun onCancel() { // No-op } override fun canStopOnPreFling(): Boolean = false override suspend fun OnStopScope.onStop(initialVelocity: Float): Float = 0f } }, ) } val overScrollEffect: OffsetOverscrollEffect = rememberOffsetOverscrollEffect() // whether the stack is moving due to a swipe or fling val isScrollInProgress = Loading Loading @@ -596,6 +633,7 @@ fun ContentScope.NotificationScrollingStack( Column( modifier = Modifier.disableSwipesWhenScrolling() .nestedScroll(swipeToExpandNotificationScrollConnection) .thenIf(supportNestedScrolling) { Modifier.nestedScroll(scrimNestedScrollConnection) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt +0 −20 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { private var wasStarted = false 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 { Loading @@ -54,7 +53,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { maxScrimOffset = MAX_SCRIM_OFFSET, contentHeight = { contentHeight }, minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT }, isCurrentGestureOverscroll = { isCurrentGestureOverscroll }, onStart = { isStarted = true }, onStop = { wasStarted = true Loading Loading @@ -183,24 +181,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { assertThat(isStarted).isEqualTo(false) } @Test fun canStartPostScroll_externalOverscrollGesture_startButIgnoreScroll() = runTest { scrimOffset = MAX_SCRIM_OFFSET isCurrentGestureOverscroll = true val offsetConsumed = scrollConnection.onPostScroll( consumed = Offset.Zero, available = Offset(x = 0f, y = 1f), source = UserInput, ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) // Returning 0 offset will immediately stop the connection assertThat(wasStarted).isEqualTo(true) assertThat(isStarted).isEqualTo(false) } @Test fun canContinueScroll_inBetweenMinMaxOffset_true() = runTest { scrimOffset = (MIN_SCRIM_OFFSET + MAX_SCRIM_OFFSET) / 2f Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +9 −17 Original line number Diff line number Diff line Loading @@ -1299,9 +1299,10 @@ public class NotificationStackScrollLayout } @Override public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) { public void setCurrentGestureExpandingNotificationConsumer( @Nullable Consumer<Boolean> consumer) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer); mScrollViewFields.setCurrentGestureExpandingNotificationConsumer(consumer); } @Override Loading Loading @@ -3656,13 +3657,13 @@ public class NotificationStackScrollLayout if (action == MotionEvent.ACTION_DOWN && !isTouchInGuts) { mController.closeControlsDueToOutsideTouch(); } if (mIsBeingDragged) { if (mIsBeingDragged || mExpandingNotification) { boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL; if (mSendingTouchesToSceneFramework) { MotionEvent adjustedEvent = MotionEvent.obtain(ev); adjustedEvent.setLocation(ev.getRawX(), ev.getRawY()); mScrollViewFields.sendCurrentGestureOverscroll( getExpandedInThisMotion() && !isUpOrCancel); mScrollViewFields.sendCurrentGestureExpandingNotification( mExpandingNotification && !isUpOrCancel); mController.sendTouchToSceneFramework(adjustedEvent); adjustedEvent.recycle(); } else if (!isUpOrCancel) { Loading @@ -3673,14 +3674,15 @@ public class NotificationStackScrollLayout downEvent.setAction(MotionEvent.ACTION_DOWN); downEvent.setLocation(ev.getRawX(), ev.getRawY()); mScrollViewFields.sendCurrentGestureInGuts(isTouchInGuts); mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion()); mScrollViewFields.sendCurrentGestureExpandingNotification( mExpandingNotification); mController.sendTouchToSceneFramework(downEvent); downEvent.recycle(); } if (isUpOrCancel) { mScrollViewFields.sendCurrentGestureInGuts(false); mScrollViewFields.sendCurrentGestureOverscroll(false); mScrollViewFields.sendCurrentGestureExpandingNotification(false); setIsBeingDragged(false); } } Loading Loading @@ -5872,11 +5874,6 @@ public class NotificationStackScrollLayout return mExpandingNotification; } @VisibleForTesting void setExpandingNotification(boolean isExpanding) { mExpandingNotification = isExpanding; } boolean getDisallowScrollingInThisMotion() { return mDisallowScrollingInThisMotion; } Loading @@ -5889,11 +5886,6 @@ public class NotificationStackScrollLayout return mExpandedInThisMotion; } @VisibleForTesting void setExpandedInThisMotion(boolean expandedInThisMotion) { mExpandedInThisMotion = expandedInThisMotion; } boolean getDisallowDismissInThisMotion() { return mDisallowDismissInThisMotion; } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt +6 −5 Original line number Diff line number Diff line Loading @@ -62,9 +62,10 @@ class ScrollViewFields { /** * When a gesture is consumed internally by NSSL but needs to be handled by other elements (such * as the notif scrim) as overscroll, we can notify the placeholder through here. * as the notif scrim), we can notify the placeholder through here. */ var currentGestureOverscrollConsumer: Consumer<Boolean>? = null var currentGestureExpandingNotificationConsumer: Consumer<Boolean>? = null /** * When a gesture is on open notification guts, which means scene container should not close the * guts off of this gesture, we can notify the placeholder through here. Loading @@ -81,9 +82,9 @@ class ScrollViewFields { fun sendSyntheticScroll(syntheticScroll: Float) = syntheticScrollConsumer?.accept(syntheticScroll) /** send [isCurrentGestureOverscroll] to the [currentGestureOverscrollConsumer], if present. */ fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) = currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll) /** send [isExpanding] to the [currentGestureExpandingNotificationConsumer], if present. */ fun sendCurrentGestureExpandingNotification(isExpanding: Boolean) = currentGestureExpandingNotificationConsumer?.accept(isExpanding) /** send [isCurrentGestureInGuts] to the [currentGestureInGutsConsumer], if present. */ fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) = Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +1 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ fun NotificationScrimNestedScrollConnection( maxScrimOffset: Float, contentHeight: () -> Float, minVisibleScrimHeight: () -> Float, isCurrentGestureOverscroll: () -> Boolean, onStart: (Float) -> Unit = {}, onStop: (Float) -> Unit = {}, flingBehavior: FlingBehavior, Loading @@ -60,7 +59,7 @@ 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, _, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) offsetAvailable > 0 && (scrimOffset() < maxScrimOffset) }, onStart = { firstScroll -> onStart(firstScroll) Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +46 −8 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.boundsInWindow Loading @@ -95,6 +96,9 @@ import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.gesture.effect.OffsetOverscrollEffect import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect import com.android.compose.modifiers.thenIf import com.android.compose.nestedscroll.OnStopScope import com.android.compose.nestedscroll.PriorityNestedScrollConnection import com.android.compose.nestedscroll.ScrollController import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius Loading Loading @@ -460,13 +464,7 @@ fun ContentScope.NotificationScrollingStack( } val scrimNestedScrollConnection = shadeSession.rememberSession( key = "ScrimConnection", scrimOffset, minScrimTop, viewModel.isCurrentGestureOverscroll, density, ) { shadeSession.rememberSession(key = "ScrimConnection", scrimOffset, minScrimTop, density) { val flingSpec: DecayAnimationSpec<Float> = splineBasedDecay(density) val flingBehavior = NotificationScrimFlingBehavior(flingSpec) NotificationScrimNestedScrollConnection( Loading @@ -479,11 +477,50 @@ fun ContentScope.NotificationScrollingStack( maxScrimOffset = 0f, contentHeight = { stackHeight.intValue.toFloat() }, minVisibleScrimHeight = minVisibleScrimHeight, isCurrentGestureOverscroll = { viewModel.isCurrentGestureOverscroll }, flingBehavior = flingBehavior, ) } val swipeToExpandNotificationScrollConnection = shadeSession.rememberSession( key = "SwipeToExpandNotificationScrollConnection", scrimOffset, minScrimTop, density, viewModel.isCurrentGestureExpandingNotification, ) { PriorityNestedScrollConnection( orientation = Orientation.Vertical, canStartPreScroll = { _, _, _ -> false }, canStartPostScroll = { _, _, _ -> viewModel.isCurrentGestureExpandingNotification }, onStart = { firstScroll -> object : ScrollController { override fun onScroll( deltaScroll: Float, source: NestedScrollSource, ): Float { return if (viewModel.isCurrentGestureExpandingNotification) { // consume all the amount, when this swipe is expanding a // notification deltaScroll } else { // don't consume anything, when the expansion is done 0f } } override fun onCancel() { // No-op } override fun canStopOnPreFling(): Boolean = false override suspend fun OnStopScope.onStop(initialVelocity: Float): Float = 0f } }, ) } val overScrollEffect: OffsetOverscrollEffect = rememberOffsetOverscrollEffect() // whether the stack is moving due to a swipe or fling val isScrollInProgress = Loading Loading @@ -596,6 +633,7 @@ fun ContentScope.NotificationScrollingStack( Column( modifier = Modifier.disableSwipesWhenScrolling() .nestedScroll(swipeToExpandNotificationScrollConnection) .thenIf(supportNestedScrolling) { Modifier.nestedScroll(scrimNestedScrollConnection) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt +0 −20 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { private var wasStarted = false 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 { Loading @@ -54,7 +53,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { maxScrimOffset = MAX_SCRIM_OFFSET, contentHeight = { contentHeight }, minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT }, isCurrentGestureOverscroll = { isCurrentGestureOverscroll }, onStart = { isStarted = true }, onStop = { wasStarted = true Loading Loading @@ -183,24 +181,6 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { assertThat(isStarted).isEqualTo(false) } @Test fun canStartPostScroll_externalOverscrollGesture_startButIgnoreScroll() = runTest { scrimOffset = MAX_SCRIM_OFFSET isCurrentGestureOverscroll = true val offsetConsumed = scrollConnection.onPostScroll( consumed = Offset.Zero, available = Offset(x = 0f, y = 1f), source = UserInput, ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) // Returning 0 offset will immediately stop the connection assertThat(wasStarted).isEqualTo(true) assertThat(isStarted).isEqualTo(false) } @Test fun canContinueScroll_inBetweenMinMaxOffset_true() = runTest { scrimOffset = (MIN_SCRIM_OFFSET + MAX_SCRIM_OFFSET) / 2f Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +9 −17 Original line number Diff line number Diff line Loading @@ -1299,9 +1299,10 @@ public class NotificationStackScrollLayout } @Override public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) { public void setCurrentGestureExpandingNotificationConsumer( @Nullable Consumer<Boolean> consumer) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer); mScrollViewFields.setCurrentGestureExpandingNotificationConsumer(consumer); } @Override Loading Loading @@ -3656,13 +3657,13 @@ public class NotificationStackScrollLayout if (action == MotionEvent.ACTION_DOWN && !isTouchInGuts) { mController.closeControlsDueToOutsideTouch(); } if (mIsBeingDragged) { if (mIsBeingDragged || mExpandingNotification) { boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL; if (mSendingTouchesToSceneFramework) { MotionEvent adjustedEvent = MotionEvent.obtain(ev); adjustedEvent.setLocation(ev.getRawX(), ev.getRawY()); mScrollViewFields.sendCurrentGestureOverscroll( getExpandedInThisMotion() && !isUpOrCancel); mScrollViewFields.sendCurrentGestureExpandingNotification( mExpandingNotification && !isUpOrCancel); mController.sendTouchToSceneFramework(adjustedEvent); adjustedEvent.recycle(); } else if (!isUpOrCancel) { Loading @@ -3673,14 +3674,15 @@ public class NotificationStackScrollLayout downEvent.setAction(MotionEvent.ACTION_DOWN); downEvent.setLocation(ev.getRawX(), ev.getRawY()); mScrollViewFields.sendCurrentGestureInGuts(isTouchInGuts); mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion()); mScrollViewFields.sendCurrentGestureExpandingNotification( mExpandingNotification); mController.sendTouchToSceneFramework(downEvent); downEvent.recycle(); } if (isUpOrCancel) { mScrollViewFields.sendCurrentGestureInGuts(false); mScrollViewFields.sendCurrentGestureOverscroll(false); mScrollViewFields.sendCurrentGestureExpandingNotification(false); setIsBeingDragged(false); } } Loading Loading @@ -5872,11 +5874,6 @@ public class NotificationStackScrollLayout return mExpandingNotification; } @VisibleForTesting void setExpandingNotification(boolean isExpanding) { mExpandingNotification = isExpanding; } boolean getDisallowScrollingInThisMotion() { return mDisallowScrollingInThisMotion; } Loading @@ -5889,11 +5886,6 @@ public class NotificationStackScrollLayout return mExpandedInThisMotion; } @VisibleForTesting void setExpandedInThisMotion(boolean expandedInThisMotion) { mExpandedInThisMotion = expandedInThisMotion; } boolean getDisallowDismissInThisMotion() { return mDisallowDismissInThisMotion; } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt +6 −5 Original line number Diff line number Diff line Loading @@ -62,9 +62,10 @@ class ScrollViewFields { /** * When a gesture is consumed internally by NSSL but needs to be handled by other elements (such * as the notif scrim) as overscroll, we can notify the placeholder through here. * as the notif scrim), we can notify the placeholder through here. */ var currentGestureOverscrollConsumer: Consumer<Boolean>? = null var currentGestureExpandingNotificationConsumer: Consumer<Boolean>? = null /** * When a gesture is on open notification guts, which means scene container should not close the * guts off of this gesture, we can notify the placeholder through here. Loading @@ -81,9 +82,9 @@ class ScrollViewFields { fun sendSyntheticScroll(syntheticScroll: Float) = syntheticScrollConsumer?.accept(syntheticScroll) /** send [isCurrentGestureOverscroll] to the [currentGestureOverscrollConsumer], if present. */ fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) = currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll) /** send [isExpanding] to the [currentGestureExpandingNotificationConsumer], if present. */ fun sendCurrentGestureExpandingNotification(isExpanding: Boolean) = currentGestureExpandingNotificationConsumer?.accept(isExpanding) /** send [isCurrentGestureInGuts] to the [currentGestureInGutsConsumer], if present. */ fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) = Loading