summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author AndrĂ¡s Kurucz <kurucz@google.com> 2025-03-21 12:31:02 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-21 12:31:02 -0700
commit30917f7b684f67aa72cc9c43c676a0b2ac624625 (patch)
tree7d78d622c0844bd0fd8c82b3b8829d3b07d4e98b
parent8c2618bd8d5f1b2ad6ed4979ac3ae04c559a7cd9 (diff)
parent436a937d3efffd7c3237a6b745144b2fc424370f (diff)
Merge changes Ib1a98438,I3d09709a into main
* changes: [Flexiglass] Wire in CUJ_NOTIFICATION_SHADE_SCROLL_FLING [Flexiglass] Remove Modifier.stackVerticalOverscroll
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt7
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt181
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt151
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt10
7 files changed, 220 insertions, 164 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
index cb713ece12a5..5ed72c7d94a2 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
@@ -55,7 +55,12 @@ open class BaseContentOverscrollEffect(
get() = animatable.value
override val isInProgress: Boolean
- get() = overscrollDistance != 0f
+ /**
+ * We need both checks, because [overscrollDistance] can be
+ * - zero while it is already being animated, if the animation starts from 0
+ * - greater than zero without an animation, if the content is still being dragged
+ */
+ get() = overscrollDistance != 0f || animatable.isRunning
override fun applyToScroll(
delta: Offset,
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
index e7c47fb56130..8a1fa3724d15 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
@@ -16,12 +16,17 @@
package com.android.compose.gesture.effect
+import androidx.compose.foundation.OverscrollEffect
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.overscroll
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
@@ -32,11 +37,14 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
import kotlin.properties.Delegates
+import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,7 +55,13 @@ class OffsetOverscrollEffectTest {
private val BOX_TAG = "box"
- private data class LayoutInfo(val layoutSize: Dp, val touchSlop: Float, val density: Density) {
+ private data class LayoutInfo(
+ val layoutSize: Dp,
+ val touchSlop: Float,
+ val density: Density,
+ val scrollableState: ScrollableState,
+ val overscrollEffect: OverscrollEffect,
+ ) {
fun expectedOffset(currentOffset: Dp): Dp {
return with(density) {
OffsetOverscrollEffect.computeOffset(this, currentOffset.toPx()).toDp()
@@ -55,22 +69,29 @@ class OffsetOverscrollEffectTest {
}
}
- private fun setupOverscrollableBox(scrollableOrientation: Orientation): LayoutInfo {
+ private fun setupOverscrollableBox(
+ scrollableOrientation: Orientation,
+ canScroll: () -> Boolean,
+ ): LayoutInfo {
val layoutSize: Dp = 200.dp
var touchSlop: Float by Delegates.notNull()
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
lateinit var density: Density
+ lateinit var scrollableState: ScrollableState
+ lateinit var overscrollEffect: OverscrollEffect
+
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- val overscrollEffect = rememberOffsetOverscrollEffect()
+ scrollableState = rememberScrollableState { if (canScroll()) it else 0f }
+ overscrollEffect = rememberOffsetOverscrollEffect()
Box(
Modifier.overscroll(overscrollEffect)
// A scrollable that does not consume the scroll gesture.
.scrollable(
- state = rememberScrollableState { 0f },
+ state = scrollableState,
orientation = scrollableOrientation,
overscrollEffect = overscrollEffect,
)
@@ -78,12 +99,16 @@ class OffsetOverscrollEffectTest {
.testTag(BOX_TAG)
)
}
- return LayoutInfo(layoutSize, touchSlop, density)
+ return LayoutInfo(layoutSize, touchSlop, density, scrollableState, overscrollEffect)
}
@Test
fun applyVerticalOffset_duringVerticalOverscroll() {
- val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { false },
+ )
rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
@@ -99,7 +124,11 @@ class OffsetOverscrollEffectTest {
@Test
fun applyNoOffset_duringHorizontalOverscroll() {
- val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { false },
+ )
rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
@@ -113,7 +142,11 @@ class OffsetOverscrollEffectTest {
@Test
fun backToZero_afterOverscroll() {
- val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { false },
+ )
rule.onRoot().performTouchInput {
down(center)
@@ -131,7 +164,11 @@ class OffsetOverscrollEffectTest {
@Test
fun offsetOverscroll_followTheTouchPointer() {
- val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { false },
+ )
// First gesture, drag down.
rule.onRoot().performTouchInput {
@@ -165,4 +202,130 @@ class OffsetOverscrollEffectTest {
.onNodeWithTag(BOX_TAG)
.assertTopPositionInRootIsEqualTo(info.expectedOffset(-info.layoutSize))
}
+
+ @Test
+ fun isScrollInProgress_overscroll() = runTest {
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { false },
+ )
+
+ // Start a swipe gesture, and swipe down to start an overscroll.
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx() / 2))
+ }
+
+ assertThat(info.scrollableState.isScrollInProgress).isTrue()
+ assertThat(info.overscrollEffect.isInProgress).isTrue()
+
+ // Finish the swipe gesture.
+ rule.onRoot().performTouchInput { up() }
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isTrue()
+
+ // Wait until the overscroll returns to idle.
+ rule.awaitIdle()
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+ }
+
+ @Test
+ fun isScrollInProgress_scroll() = runTest {
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { true },
+ )
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Start a swipe gesture, and swipe down to scroll.
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx() / 2))
+ }
+
+ assertThat(info.scrollableState.isScrollInProgress).isTrue()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+
+ // Finish the swipe gesture.
+ rule.onRoot().performTouchInput { up() }
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isTrue()
+
+ // Wait until the overscroll returns to idle.
+ rule.awaitIdle()
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+ }
+
+ @Test
+ fun isScrollInProgress_flingToScroll() = runTest {
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ canScroll = { true },
+ )
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Swipe down and leave some velocity to start a fling.
+ rule.onRoot().performTouchInput {
+ swipeWithVelocity(
+ Offset.Zero,
+ Offset(0f, info.touchSlop + info.layoutSize.toPx() / 2),
+ endVelocity = 100f,
+ )
+ }
+
+ assertThat(info.scrollableState.isScrollInProgress).isTrue()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+
+ // Wait until the fling is finished.
+ rule.awaitIdle()
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+ }
+
+ @Test
+ fun isScrollInProgress_flingToOverscroll() = runTest {
+ // Start with a scrollable state.
+ var canScroll by mutableStateOf(true)
+ val info =
+ setupOverscrollableBox(scrollableOrientation = Orientation.Vertical) { canScroll }
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Swipe down and leave some velocity to start a fling.
+ rule.onRoot().performTouchInput {
+ swipeWithVelocity(
+ Offset.Zero,
+ Offset(0f, info.touchSlop + info.layoutSize.toPx() / 2),
+ endVelocity = 100f,
+ )
+ }
+
+ assertThat(info.scrollableState.isScrollInProgress).isTrue()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+
+ // The fling reaches the end of the scrollable region, and an overscroll starts.
+ canScroll = false
+ rule.mainClock.advanceTimeUntil { !info.scrollableState.isScrollInProgress }
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isTrue()
+
+ // Wait until the overscroll returns to idle.
+ rule.awaitIdle()
+
+ assertThat(info.scrollableState.isScrollInProgress).isFalse()
+ assertThat(info.overscrollEffect.isInProgress).isFalse()
+ }
}
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
deleted file mode 100644
index e1ee59ba0626..000000000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-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
-import androidx.compose.ui.Modifier
-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.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.Velocity
-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
-import kotlin.math.roundToInt
-import kotlin.math.tanh
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-
-@Composable
-fun Modifier.stackVerticalOverscroll(
- coroutineScope: CoroutineScope,
- canScrollForward: () -> Boolean,
-): Modifier {
- val screenHeight =
- with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
- val overscrollOffset = remember { Animatable(0f) }
- 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(
- remember {
- object : NestedScrollConnection {
- override suspend fun onPostFling(
- consumed: Velocity,
- available: Velocity,
- ): Velocity {
- return if (available.y < 0f && !canScrollForward()) {
- overscrollOffset.animateTo(
- targetValue = 0f,
- initialVelocity = available.y,
- animationSpec = tween(),
- )
- available
- } else {
- Velocity.Zero
- }
- }
- }
- }
- )
- .nestedScroll(stackNestedScrollConnection)
- .offset { IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) }
- )
-}
-
-fun NotificationStackNestedScrollConnection(
- stackOffset: () -> Float,
- canScrollForward: () -> Boolean,
- onStart: (Float) -> Unit = {},
- onScroll: (Float) -> Unit,
- onStop: (Float) -> Unit = {},
- flingBehavior: FlingBehavior,
-): PriorityNestedScrollConnection {
- return PriorityNestedScrollConnection(
- orientation = Orientation.Vertical,
- canStartPreScroll = { _, _, _ -> false },
- canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
- offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
- },
- onStart = { firstScroll ->
- onStart(firstScroll)
- object : ScrollController {
- override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
- val minOffset = 0f
- val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset())
- if (consumed != 0f) {
- onScroll(consumed)
- }
- return consumed
- }
-
- override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
- val consumedByScroll = flingToScroll(initialVelocity, flingBehavior)
- onStop(initialVelocity - consumedByScroll)
- return initialVelocity
- }
-
- override fun onCancel() {
- onStop(0f)
- }
-
- override fun canStopOnPreFling() = false
- }
- },
- )
-}
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 79b346439d5d..2f9cfb6aa211 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
@@ -78,6 +78,7 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -92,7 +93,11 @@ import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.gesture.NestedScrollableBound
+import com.android.compose.gesture.effect.OffsetOverscrollEffect
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect
import com.android.compose.modifiers.thenIf
+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
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
@@ -288,17 +293,19 @@ fun ContentScope.NotificationScrollingStack(
shadeSession: SaveableSession,
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
+ jankMonitor: InteractionJankMonitor,
maxScrimTop: () -> Float,
shouldPunchHoleBehindScrim: Boolean,
stackTopPadding: Dp,
stackBottomPadding: Dp,
+ modifier: Modifier = Modifier,
shouldFillMaxSize: Boolean = true,
shouldIncludeHeadsUpSpace: Boolean = true,
shouldShowScrim: Boolean = true,
supportNestedScrolling: Boolean,
onEmptySpaceClick: (() -> Unit)? = null,
- modifier: Modifier = Modifier,
) {
+ val composeViewRoot = LocalView.current
val coroutineScope = shadeSession.sessionCoroutineScope()
val density = LocalDensity.current
val screenCornerRadius = LocalScreenCornerRadius.current
@@ -477,6 +484,21 @@ fun ContentScope.NotificationScrollingStack(
)
}
+ val overScrollEffect: OffsetOverscrollEffect = rememberOffsetOverscrollEffect()
+ // whether the stack is moving due to a swipe or fling
+ val isScrollInProgress =
+ scrollState.isScrollInProgress || overScrollEffect.isInProgress || scrimOffset.isRunning
+
+ LaunchedEffect(isScrollInProgress) {
+ if (isScrollInProgress) {
+ jankMonitor.begin(composeViewRoot, CUJ_NOTIFICATION_SHADE_SCROLL_FLING)
+ debugLog(viewModel) { "STACK scroll begins" }
+ } else {
+ debugLog(viewModel) { "STACK scroll ends" }
+ jankMonitor.end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING)
+ }
+ }
+
Box(
modifier =
modifier
@@ -577,8 +599,7 @@ fun ContentScope.NotificationScrollingStack(
.thenIf(supportNestedScrolling) {
Modifier.nestedScroll(scrimNestedScrollConnection)
}
- .stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward }
- .verticalScroll(scrollState)
+ .verticalScroll(scrollState, overscrollEffect = overScrollEffect)
.padding(top = stackTopPadding, bottom = stackBottomPadding)
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 37ce358a2a98..6d37e0affd6a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -29,6 +29,7 @@ import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
@@ -69,6 +70,7 @@ constructor(
private val keyguardClockViewModel: KeyguardClockViewModel,
private val mediaCarouselController: MediaCarouselController,
@Named(QUICK_QS_PANEL) private val mediaHost: Lazy<MediaHost>,
+ private val jankMonitor: InteractionJankMonitor,
) : Overlay {
override val key = Overlays.NotificationsShade
@@ -146,6 +148,7 @@ constructor(
shadeSession = shadeSession,
stackScrollView = stackScrollView.get(),
viewModel = placeholderViewModel,
+ jankMonitor = jankMonitor,
maxScrimTop = { 0f },
shouldPunchHoleBehindScrim = false,
stackTopPadding = notificationStackPadding,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 0a711487ccb1..d667f68e4fdd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -75,6 +75,7 @@ import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -126,6 +127,7 @@ constructor(
private val contentViewModelFactory: QuickSettingsSceneContentViewModel.Factory,
private val mediaCarouselController: MediaCarouselController,
@Named(MediaModule.QS_PANEL) private val mediaHost: MediaHost,
+ private val jankMonitor: InteractionJankMonitor,
) : ExclusiveActivatable(), Scene {
override val key = Scenes.QuickSettings
@@ -165,6 +167,7 @@ constructor(
mediaHost = mediaHost,
modifier = modifier,
shadeSession = shadeSession,
+ jankMonitor = jankMonitor,
)
}
@@ -186,6 +189,7 @@ private fun ContentScope.QuickSettingsScene(
mediaHost: MediaHost,
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
+ jankMonitor: InteractionJankMonitor,
) {
val cutoutLocation = LocalDisplayCutout.current.location
val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
@@ -432,6 +436,7 @@ private fun ContentScope.QuickSettingsScene(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
+ jankMonitor = jankMonitor,
maxScrimTop = { minNotificationStackTop.toFloat() },
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
stackTopPadding = notificationStackPadding,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 885d34fb95c9..60e32d7ce824 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -73,6 +73,7 @@ import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
@@ -145,6 +146,7 @@ constructor(
private val mediaCarouselController: MediaCarouselController,
@Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
@Named(QS_PANEL) private val qsMediaHost: MediaHost,
+ private val jankMonitor: InteractionJankMonitor,
) : ExclusiveActivatable(), Scene {
override val key = Scenes.Shade
@@ -182,6 +184,7 @@ constructor(
mediaCarouselController = mediaCarouselController,
qqsMediaHost = qqsMediaHost,
qsMediaHost = qsMediaHost,
+ jankMonitor = jankMonitor,
modifier = modifier,
shadeSession = shadeSession,
usingCollapsedLandscapeMedia =
@@ -212,6 +215,7 @@ private fun ContentScope.ShadeScene(
mediaCarouselController: MediaCarouselController,
qqsMediaHost: MediaHost,
qsMediaHost: MediaHost,
+ jankMonitor: InteractionJankMonitor,
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
usingCollapsedLandscapeMedia: Boolean,
@@ -229,6 +233,7 @@ private fun ContentScope.ShadeScene(
modifier = modifier,
shadeSession = shadeSession,
usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
+ jankMonitor = jankMonitor,
)
is ShadeMode.Split ->
SplitShade(
@@ -240,6 +245,7 @@ private fun ContentScope.ShadeScene(
mediaHost = qsMediaHost,
modifier = modifier,
shadeSession = shadeSession,
+ jankMonitor = jankMonitor,
)
is ShadeMode.Dual -> error("Dual shade is implemented separately as an overlay.")
}
@@ -253,6 +259,7 @@ private fun ContentScope.SingleShade(
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
mediaCarouselController: MediaCarouselController,
mediaHost: MediaHost,
+ jankMonitor: InteractionJankMonitor,
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
usingCollapsedLandscapeMedia: Boolean,
@@ -379,6 +386,7 @@ private fun ContentScope.SingleShade(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
+ jankMonitor = jankMonitor,
maxScrimTop = { maxNotifScrimTop.toFloat() },
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
stackTopPadding = notificationStackPadding,
@@ -419,6 +427,7 @@ private fun ContentScope.SplitShade(
mediaHost: MediaHost,
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
+ jankMonitor: InteractionJankMonitor,
) {
val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle()
@@ -596,6 +605,7 @@ private fun ContentScope.SplitShade(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
+ jankMonitor = jankMonitor,
maxScrimTop = { 0f },
stackTopPadding = notificationStackPadding,
stackBottomPadding = notificationStackPadding,