Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +59 −4 Original line number Diff line number Diff line Loading @@ -50,9 +50,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue Loading @@ -60,6 +62,7 @@ import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer Loading Loading @@ -95,13 +98,17 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import kotlin.math.max import kotlin.math.roundToInt import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch Loading Loading @@ -315,6 +322,12 @@ fun SceneScope.NotificationScrollingStack( */ val stackHeight = remember { mutableIntStateOf(0) } /** * Space available for the notification stack on the screen. These bounds don't scroll off the * screen, and respect the scrim paddings, scrim clipping. */ val stackBoundsOnScreen = remember { mutableStateOf(Rect.Zero) } val scrimRounding = viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding()) Loading Loading @@ -348,12 +361,19 @@ fun SceneScope.NotificationScrollingStack( // The top y bound of the IME. val imeTop = remember { mutableFloatStateOf(0f) } val shadeScrollState by remember { derivedStateOf { ShadeScrollState( // we are not scrolled to the top unless the scrim is at its maximum offset. LaunchedEffect(viewModel, scrimOffset) { snapshotFlow { scrimOffset.value >= 0f } .collect { isScrolledToTop -> viewModel.setScrolledToTop(isScrolledToTop) } isScrolledToTop = scrimOffset.value >= 0f, scrollPosition = scrollState.value, maxScrollPosition = scrollState.maxValue, ) } } LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) } // if contentHeight drops below minimum visible scrim height while scrim is // expanded, reset scrim offset. LaunchedEffect(stackHeight, scrimOffset) { Loading Loading @@ -395,6 +415,38 @@ fun SceneScope.NotificationScrollingStack( } } // TalkBack sends a scroll event, when it wants to navigate to an item that is not displayed in // the current viewport. LaunchedEffect(viewModel) { viewModel.setAccessibilityScrollEventConsumer { event -> // scroll up, or down by the height of the visible portion of the notification stack val direction = when (event) { AccessibilityScrollEvent.SCROLL_UP -> -1 AccessibilityScrollEvent.SCROLL_DOWN -> 1 } val viewPortHeight = stackBoundsOnScreen.value.height val scrollStep = max(0f, viewPortHeight - stackScrollView.stackBottomInset) val scrollPosition = scrollState.value.toFloat() val scrollRange = scrollState.maxValue.toFloat() val targetScroll = (scrollPosition + direction * scrollStep).coerceIn(0f, scrollRange) coroutineScope.launch { scrollNotificationStack( delta = targetScroll - scrollPosition, animate = false, scrimOffset = scrimOffset, minScrimOffset = minScrimOffset, scrollState = scrollState, ) } } try { awaitCancellation() } finally { viewModel.setAccessibilityScrollEventConsumer(null) } } val scrimNestedScrollConnection = shadeSession.rememberSession( scrimOffset, Loading Loading @@ -520,6 +572,9 @@ fun SceneScope.NotificationScrollingStack( .verticalScroll(scrollState) .padding(top = topPadding) .fillMaxWidth() .onGloballyPositioned { coordinates -> stackBoundsOnScreen.value = coordinates.boundsInWindow() } ) { NotificationPlaceholder( stackScrollView = stackScrollView, Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +58 −21 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_A import static com.android.systemui.Flags.notificationOverExpansionClippingFix; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_DOWN; import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_UP; import static com.android.systemui.util.DumpUtilsKt.println; import static com.android.systemui.util.DumpUtilsKt.visibilityString; Loading Loading @@ -118,8 +120,10 @@ import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCyc import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState; import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; Loading Loading @@ -609,7 +613,7 @@ public class NotificationStackScrollLayout @Override public boolean isScrolledToTop() { if (SceneContainerFlag.isEnabled()) { return mScrollViewFields.isScrolledToTop(); return mScrollViewFields.getScrollState().isScrolledToTop(); } else { return mOwnScrollY == 0; } Loading Loading @@ -1247,9 +1251,25 @@ public class NotificationStackScrollLayout } @Override public void setScrolledToTop(boolean scrolledToTop) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; mScrollViewFields.setScrolledToTop(scrolledToTop); public void setScrollState(@NonNull ShadeScrollState scrollState) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { return; } boolean forwardScrollable = scrollState.getScrollPosition() < scrollState.getMaxScrollPosition(); boolean backwardScrollable = scrollState.getScrollPosition() > 0; mScrollable = forwardScrollable || backwardScrollable; mForwardScrollable = forwardScrollable; mBackwardScrollable = backwardScrollable; boolean scrollPositionChanged = mScrollViewFields.getScrollState().getScrollPosition() != scrollState.getScrollPosition(); mScrollViewFields.setScrollState(scrollState); if (scrollPositionChanged) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); } } @Override Loading Loading @@ -1294,6 +1314,12 @@ public class NotificationStackScrollLayout mScrollViewFields.setSyntheticScrollConsumer(consumer); } @Override public void setAccessibilityScrollEventConsumer( @Nullable Consumer<AccessibilityScrollEvent> consumer) { mScrollViewFields.setAccessibilityScrollEventConsumer(consumer); } @Override public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; Loading Loading @@ -2645,6 +2671,11 @@ public class NotificationStackScrollLayout return mHeadsUpInset; } @Override public int getStackBottomInset() { return mPaddingBetweenElements + mShelf.getIntrinsicHeight(); } /** * Calculate the gap height between two different views * Loading Loading @@ -4243,17 +4274,27 @@ public class NotificationStackScrollLayout */ @Override public boolean performAccessibilityActionInternal(int action, Bundle arguments) { // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return super.performAccessibilityActionInternal(action, arguments); } if (super.performAccessibilityActionInternal(action, arguments)) { return true; } if (!isEnabled()) { return false; } if (SceneContainerFlag.isEnabled()) { switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: case android.R.id.accessibilityActionScrollDown: mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_DOWN); return true; case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: case android.R.id.accessibilityActionScrollUp: mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_UP); return true; } return false; } int direction = -1; switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: Loading Loading @@ -5029,25 +5070,21 @@ public class NotificationStackScrollLayout @Override public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { super.onInitializeAccessibilityEventInternal(event); // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return; } event.setScrollable(mScrollable); event.setMaxScrollX(mScrollX); if (SceneContainerFlag.isEnabled()) { event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition()); event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition()); } else { event.setScrollY(mOwnScrollY); event.setMaxScrollY(getScrollRange()); } } @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return; } if (mScrollable) { info.setScrollable(true); if (mBackwardScrollable) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt +18 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.stack import android.util.IndentingPrintWriter import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.util.printSection import com.android.systemui.util.println import java.util.function.Consumer Loading @@ -32,8 +34,9 @@ import java.util.function.Consumer class ScrollViewFields { /** Used to produce the clipping path */ var scrimClippingShape: ShadeScrimShape? = null /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */ var isScrolledToTop: Boolean = true /** Scroll state of the notification shade. */ var scrollState: ShadeScrollState = ShadeScrollState() /** * Height in view pixels at which the Notification Stack would like to be laid out, including Loading @@ -47,6 +50,13 @@ class ScrollViewFields { * placeholder */ var syntheticScrollConsumer: Consumer<Float>? = null /** * When the NSSL navigates through the notifications with TalkBack, it can send scroll events * here, to be able to browse through the whole list of notifications in the shade. */ var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null /** * 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. Loading Loading @@ -86,10 +96,15 @@ class ScrollViewFields { fun sendRemoteInputRowBottomBound(bottomY: Float?) = remoteInputRowBottomBoundConsumer?.accept(bottomY) /** send an [AccessibilityScrollEvent] to the [accessibilityScrollEventConsumer] if present */ fun sendAccessibilityScrollEvent(event: AccessibilityScrollEvent) { accessibilityScrollEventConsumer?.accept(event) } fun dump(pw: IndentingPrintWriter) { pw.printSection("StackViewStates") { pw.println("scrimClippingShape", scrimClippingShape) pw.println("isScrolledToTop", isScrolledToTop) pw.println("scrollState", scrollState) } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt +8 −5 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.notification.stack.data.repository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -44,9 +47,9 @@ class NotificationPlaceholderRepository @Inject constructor() { /** height made available to the notifications in the size-constrained mode of lock screen. */ val constrainedAvailableSpace = MutableStateFlow(0) /** * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any * further. */ val scrolledToTop = MutableStateFlow(true) /** Scroll state of the notification shade. */ val shadeScrollState = MutableStateFlow(ShadeScrollState()) /** A consumer of [AccessibilityScrollEvent]s. */ var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null } packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt +19 −8 Original line number Diff line number Diff line Loading @@ -23,8 +23,11 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository import com.android.systemui.statusbar.notification.stack.data.repository.NotificationViewHeightRepository import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow Loading Loading @@ -78,11 +81,9 @@ constructor( val constrainedAvailableSpace: StateFlow<Int> = placeholderRepository.constrainedAvailableSpace.asStateFlow() /** * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any * further. */ val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow() /** Scroll state of the notification shade. */ val shadeScrollState: StateFlow<ShadeScrollState> = placeholderRepository.shadeScrollState.asStateFlow() /** * The amount in px that the notification stack should scroll due to internal expansion. This Loading Loading @@ -123,9 +124,9 @@ constructor( placeholderRepository.shadeScrimBounds.value = bounds } /** Sets whether the notification stack is scrolled to the top. */ fun setScrolledToTop(scrolledToTop: Boolean) { placeholderRepository.scrolledToTop.value = scrolledToTop /** Updates the current scroll state of the notification shade. */ fun setScrollState(shadeScrollState: ShadeScrollState) { placeholderRepository.shadeScrollState.value = shadeScrollState } /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */ Loading @@ -133,6 +134,16 @@ constructor( viewHeightRepository.syntheticScroll.value = delta } /** Sends an [AccessibilityScrollEvent] to scroll the stack up or down. */ fun sendAccessibilityScrollEvent(accessibilityScrollEvent: AccessibilityScrollEvent) { placeholderRepository.accessibilityScrollEventConsumer?.accept(accessibilityScrollEvent) } /** Set a consumer for the [AccessibilityScrollEvent]s to be handled by the placeholder. */ fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) { placeholderRepository.accessibilityScrollEventConsumer = consumer } /** Sets whether the current touch gesture is overscroll. */ fun setCurrentGestureOverscroll(isOverscroll: Boolean) { viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +59 −4 Original line number Diff line number Diff line Loading @@ -50,9 +50,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue Loading @@ -60,6 +62,7 @@ import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer Loading Loading @@ -95,13 +98,17 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import kotlin.math.max import kotlin.math.roundToInt import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch Loading Loading @@ -315,6 +322,12 @@ fun SceneScope.NotificationScrollingStack( */ val stackHeight = remember { mutableIntStateOf(0) } /** * Space available for the notification stack on the screen. These bounds don't scroll off the * screen, and respect the scrim paddings, scrim clipping. */ val stackBoundsOnScreen = remember { mutableStateOf(Rect.Zero) } val scrimRounding = viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding()) Loading Loading @@ -348,12 +361,19 @@ fun SceneScope.NotificationScrollingStack( // The top y bound of the IME. val imeTop = remember { mutableFloatStateOf(0f) } val shadeScrollState by remember { derivedStateOf { ShadeScrollState( // we are not scrolled to the top unless the scrim is at its maximum offset. LaunchedEffect(viewModel, scrimOffset) { snapshotFlow { scrimOffset.value >= 0f } .collect { isScrolledToTop -> viewModel.setScrolledToTop(isScrolledToTop) } isScrolledToTop = scrimOffset.value >= 0f, scrollPosition = scrollState.value, maxScrollPosition = scrollState.maxValue, ) } } LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) } // if contentHeight drops below minimum visible scrim height while scrim is // expanded, reset scrim offset. LaunchedEffect(stackHeight, scrimOffset) { Loading Loading @@ -395,6 +415,38 @@ fun SceneScope.NotificationScrollingStack( } } // TalkBack sends a scroll event, when it wants to navigate to an item that is not displayed in // the current viewport. LaunchedEffect(viewModel) { viewModel.setAccessibilityScrollEventConsumer { event -> // scroll up, or down by the height of the visible portion of the notification stack val direction = when (event) { AccessibilityScrollEvent.SCROLL_UP -> -1 AccessibilityScrollEvent.SCROLL_DOWN -> 1 } val viewPortHeight = stackBoundsOnScreen.value.height val scrollStep = max(0f, viewPortHeight - stackScrollView.stackBottomInset) val scrollPosition = scrollState.value.toFloat() val scrollRange = scrollState.maxValue.toFloat() val targetScroll = (scrollPosition + direction * scrollStep).coerceIn(0f, scrollRange) coroutineScope.launch { scrollNotificationStack( delta = targetScroll - scrollPosition, animate = false, scrimOffset = scrimOffset, minScrimOffset = minScrimOffset, scrollState = scrollState, ) } } try { awaitCancellation() } finally { viewModel.setAccessibilityScrollEventConsumer(null) } } val scrimNestedScrollConnection = shadeSession.rememberSession( scrimOffset, Loading Loading @@ -520,6 +572,9 @@ fun SceneScope.NotificationScrollingStack( .verticalScroll(scrollState) .padding(top = topPadding) .fillMaxWidth() .onGloballyPositioned { coordinates -> stackBoundsOnScreen.value = coordinates.boundsInWindow() } ) { NotificationPlaceholder( stackScrollView = stackScrollView, Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +58 −21 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_A import static com.android.systemui.Flags.notificationOverExpansionClippingFix; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_DOWN; import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_UP; import static com.android.systemui.util.DumpUtilsKt.println; import static com.android.systemui.util.DumpUtilsKt.visibilityString; Loading Loading @@ -118,8 +120,10 @@ import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCyc import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState; import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; Loading Loading @@ -609,7 +613,7 @@ public class NotificationStackScrollLayout @Override public boolean isScrolledToTop() { if (SceneContainerFlag.isEnabled()) { return mScrollViewFields.isScrolledToTop(); return mScrollViewFields.getScrollState().isScrolledToTop(); } else { return mOwnScrollY == 0; } Loading Loading @@ -1247,9 +1251,25 @@ public class NotificationStackScrollLayout } @Override public void setScrolledToTop(boolean scrolledToTop) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; mScrollViewFields.setScrolledToTop(scrolledToTop); public void setScrollState(@NonNull ShadeScrollState scrollState) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { return; } boolean forwardScrollable = scrollState.getScrollPosition() < scrollState.getMaxScrollPosition(); boolean backwardScrollable = scrollState.getScrollPosition() > 0; mScrollable = forwardScrollable || backwardScrollable; mForwardScrollable = forwardScrollable; mBackwardScrollable = backwardScrollable; boolean scrollPositionChanged = mScrollViewFields.getScrollState().getScrollPosition() != scrollState.getScrollPosition(); mScrollViewFields.setScrollState(scrollState); if (scrollPositionChanged) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); } } @Override Loading Loading @@ -1294,6 +1314,12 @@ public class NotificationStackScrollLayout mScrollViewFields.setSyntheticScrollConsumer(consumer); } @Override public void setAccessibilityScrollEventConsumer( @Nullable Consumer<AccessibilityScrollEvent> consumer) { mScrollViewFields.setAccessibilityScrollEventConsumer(consumer); } @Override public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; Loading Loading @@ -2645,6 +2671,11 @@ public class NotificationStackScrollLayout return mHeadsUpInset; } @Override public int getStackBottomInset() { return mPaddingBetweenElements + mShelf.getIntrinsicHeight(); } /** * Calculate the gap height between two different views * Loading Loading @@ -4243,17 +4274,27 @@ public class NotificationStackScrollLayout */ @Override public boolean performAccessibilityActionInternal(int action, Bundle arguments) { // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return super.performAccessibilityActionInternal(action, arguments); } if (super.performAccessibilityActionInternal(action, arguments)) { return true; } if (!isEnabled()) { return false; } if (SceneContainerFlag.isEnabled()) { switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: case android.R.id.accessibilityActionScrollDown: mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_DOWN); return true; case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: case android.R.id.accessibilityActionScrollUp: mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_UP); return true; } return false; } int direction = -1; switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: Loading Loading @@ -5029,25 +5070,21 @@ public class NotificationStackScrollLayout @Override public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { super.onInitializeAccessibilityEventInternal(event); // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return; } event.setScrollable(mScrollable); event.setMaxScrollX(mScrollX); if (SceneContainerFlag.isEnabled()) { event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition()); event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition()); } else { event.setScrollY(mOwnScrollY); event.setMaxScrollY(getScrollRange()); } } @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. if (SceneContainerFlag.isEnabled()) { return; } if (mScrollable) { info.setScrollable(true); if (mBackwardScrollable) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt +18 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.stack import android.util.IndentingPrintWriter import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.util.printSection import com.android.systemui.util.println import java.util.function.Consumer Loading @@ -32,8 +34,9 @@ import java.util.function.Consumer class ScrollViewFields { /** Used to produce the clipping path */ var scrimClippingShape: ShadeScrimShape? = null /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */ var isScrolledToTop: Boolean = true /** Scroll state of the notification shade. */ var scrollState: ShadeScrollState = ShadeScrollState() /** * Height in view pixels at which the Notification Stack would like to be laid out, including Loading @@ -47,6 +50,13 @@ class ScrollViewFields { * placeholder */ var syntheticScrollConsumer: Consumer<Float>? = null /** * When the NSSL navigates through the notifications with TalkBack, it can send scroll events * here, to be able to browse through the whole list of notifications in the shade. */ var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null /** * 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. Loading Loading @@ -86,10 +96,15 @@ class ScrollViewFields { fun sendRemoteInputRowBottomBound(bottomY: Float?) = remoteInputRowBottomBoundConsumer?.accept(bottomY) /** send an [AccessibilityScrollEvent] to the [accessibilityScrollEventConsumer] if present */ fun sendAccessibilityScrollEvent(event: AccessibilityScrollEvent) { accessibilityScrollEventConsumer?.accept(event) } fun dump(pw: IndentingPrintWriter) { pw.printSection("StackViewStates") { pw.println("scrimClippingShape", scrimClippingShape) pw.println("isScrolledToTop", isScrolledToTop) pw.println("scrollState", scrollState) } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt +8 −5 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.notification.stack.data.repository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -44,9 +47,9 @@ class NotificationPlaceholderRepository @Inject constructor() { /** height made available to the notifications in the size-constrained mode of lock screen. */ val constrainedAvailableSpace = MutableStateFlow(0) /** * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any * further. */ val scrolledToTop = MutableStateFlow(true) /** Scroll state of the notification shade. */ val shadeScrollState = MutableStateFlow(ShadeScrollState()) /** A consumer of [AccessibilityScrollEvent]s. */ var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null }
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt +19 −8 Original line number Diff line number Diff line Loading @@ -23,8 +23,11 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository import com.android.systemui.statusbar.notification.stack.data.repository.NotificationViewHeightRepository import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow Loading Loading @@ -78,11 +81,9 @@ constructor( val constrainedAvailableSpace: StateFlow<Int> = placeholderRepository.constrainedAvailableSpace.asStateFlow() /** * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any * further. */ val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow() /** Scroll state of the notification shade. */ val shadeScrollState: StateFlow<ShadeScrollState> = placeholderRepository.shadeScrollState.asStateFlow() /** * The amount in px that the notification stack should scroll due to internal expansion. This Loading Loading @@ -123,9 +124,9 @@ constructor( placeholderRepository.shadeScrimBounds.value = bounds } /** Sets whether the notification stack is scrolled to the top. */ fun setScrolledToTop(scrolledToTop: Boolean) { placeholderRepository.scrolledToTop.value = scrolledToTop /** Updates the current scroll state of the notification shade. */ fun setScrollState(shadeScrollState: ShadeScrollState) { placeholderRepository.shadeScrollState.value = shadeScrollState } /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */ Loading @@ -133,6 +134,16 @@ constructor( viewHeightRepository.syntheticScroll.value = delta } /** Sends an [AccessibilityScrollEvent] to scroll the stack up or down. */ fun sendAccessibilityScrollEvent(accessibilityScrollEvent: AccessibilityScrollEvent) { placeholderRepository.accessibilityScrollEventConsumer?.accept(accessibilityScrollEvent) } /** Set a consumer for the [AccessibilityScrollEvent]s to be handled by the placeholder. */ fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) { placeholderRepository.accessibilityScrollEventConsumer = consumer } /** Sets whether the current touch gesture is overscroll. */ fun setCurrentGestureOverscroll(isOverscroll: Boolean) { viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll Loading