summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt149
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt2
18 files changed, 241 insertions, 77 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
index 296fc27ac0ff..dcf32b2bcda4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
@@ -16,15 +16,10 @@
package com.android.systemui.common.ui.compose.windowinsets
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.systemBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -36,9 +31,6 @@ val LocalDisplayCutout = staticCompositionLocalOf { DisplayCutout() }
/** The corner radius in px of the current display. */
val LocalScreenCornerRadius = staticCompositionLocalOf { 0.dp }
-/** The screen height in px without accounting for any screen insets (cutouts, status/nav bars) */
-val LocalRawScreenHeight = staticCompositionLocalOf { 0f }
-
@Composable
fun ScreenDecorProvider(
displayCutout: StateFlow<DisplayCutout>,
@@ -48,22 +40,9 @@ fun ScreenDecorProvider(
val cutout by displayCutout.collectAsStateWithLifecycle()
val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
- val density = LocalDensity.current
- val navBarHeight =
- with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
- val statusBarHeight = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
- val displayCutoutHeight = WindowInsets.displayCutout.asPaddingValues().calculateTopPadding()
- val screenHeight =
- with(density) {
- (LocalConfiguration.current.screenHeightDp.dp +
- maxOf(statusBarHeight, displayCutoutHeight))
- .toPx()
- } + navBarHeight
-
CompositionLocalProvider(
LocalScreenCornerRadius provides screenCornerRadiusDp,
LocalDisplayCutout provides cutout,
- LocalRawScreenHeight provides screenHeight,
) {
content()
}
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 897a8613263f..a2ae8bbf66e4 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
@@ -24,9 +24,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
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.dp
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
-import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.tanh
@@ -36,9 +38,10 @@ import kotlinx.coroutines.launch
@Composable
fun Modifier.stackVerticalOverscroll(
coroutineScope: CoroutineScope,
- canScrollForward: () -> Boolean
+ canScrollForward: () -> Boolean,
): Modifier {
- val screenHeight = LocalRawScreenHeight.current
+ val screenHeight =
+ with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
val overscrollOffset = remember { Animatable(0f) }
val stackNestedScrollConnection = remember {
NotificationStackNestedScrollConnection(
@@ -60,10 +63,10 @@ fun Modifier.stackVerticalOverscroll(
overscrollOffset.animateTo(
targetValue = 0f,
initialVelocity = velocityAvailable,
- animationSpec = tween()
+ animationSpec = tween(),
)
}
- }
+ },
)
}
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 91ecfc18a76e..1b99a9644575 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
@@ -19,6 +19,7 @@ package com.android.systemui.notifications.ui.composable
import android.util.Log
import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
@@ -29,6 +30,8 @@ import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.absoluteOffset
@@ -36,9 +39,11 @@ import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
@@ -68,6 +73,7 @@ import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onPlaced
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.res.dimensionResource
import androidx.compose.ui.unit.Dp
@@ -81,7 +87,6 @@ import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
-import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
@@ -96,6 +101,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati
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.roundToInt
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
object Notifications {
@@ -171,7 +177,7 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
setCurrent = { scrollOffset = it },
min = minScrollOffset,
max = maxScrollOffset,
- delta
+ delta,
)
}
@@ -209,8 +215,8 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
calculateHeadsUpPlaceholderYOffset(
scrollOffset.roundToInt(),
minScrollOffset.roundToInt(),
- stackScrollView.topHeadsUpHeight
- )
+ stackScrollView.topHeadsUpHeight,
+ ),
)
}
.thenIf(isHeadsUp) {
@@ -218,11 +224,8 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
bottomBehavior = NestedScrollBehavior.EdgeAlways
)
.nestedScroll(nestedScrollConnection)
- .scrollable(
- orientation = Orientation.Vertical,
- state = scrollableState,
- )
- }
+ .scrollable(orientation = Orientation.Vertical, state = scrollableState)
+ },
)
}
@@ -259,6 +262,7 @@ fun SceneScope.ConstrainedNotificationStack(
* Adds the space where notification stack should appear in the scene, with a scrim and nested
* scrolling.
*/
+@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SceneScope.NotificationScrollingStack(
shadeSession: SaveableSession,
@@ -291,7 +295,7 @@ fun SceneScope.NotificationScrollingStack(
val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
val bottomPadding = if (shouldReserveSpaceForNavBar) navBarHeight else 0.dp
- val screenHeight = LocalRawScreenHeight.current
+ val screenHeight = with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
/**
* The height in px of the contents of notification stack. Depending on the number of
@@ -325,6 +329,14 @@ fun SceneScope.NotificationScrollingStack(
screenHeight - maxScrimTop() - with(density) { navBarHeight.toPx() }
}
+ val isRemoteInputActive by viewModel.isRemoteInputActive.collectAsStateWithLifecycle(false)
+
+ // The bottom Y bound of the currently focused remote input notification.
+ val remoteInputRowBottom by viewModel.remoteInputRowBottomBound.collectAsStateWithLifecycle(0f)
+
+ // The top y bound of the IME.
+ val imeTop = remember { mutableFloatStateOf(0f) }
+
// we are not scrolled to the top unless the scrim is at its maximum offset.
LaunchedEffect(viewModel, scrimOffset) {
snapshotFlow { scrimOffset.value >= 0f }
@@ -342,15 +354,34 @@ fun SceneScope.NotificationScrollingStack(
LaunchedEffect(syntheticScroll, scrimOffset, scrollState) {
snapshotFlow { syntheticScroll.value }
.collect { delta ->
- val minOffset = minScrimOffset()
- if (scrimOffset.value > minOffset) {
- val remainingDelta = (minOffset - (scrimOffset.value - delta)).coerceAtLeast(0f)
- scrimOffset.snapTo((scrimOffset.value - delta).coerceAtLeast(minOffset))
- if (remainingDelta > 0f) {
- scrollState.scrollBy(remainingDelta)
- }
- } else {
- scrollState.scrollTo(delta.roundToInt())
+ scrollNotificationStack(
+ scope = coroutineScope,
+ delta = delta,
+ animate = false,
+ scrimOffset = scrimOffset,
+ minScrimOffset = minScrimOffset,
+ scrollState = scrollState,
+ )
+ }
+ }
+
+ // if remote input state changes, compare the row and IME's overlap and offset the scrim and
+ // placeholder accordingly.
+ LaunchedEffect(isRemoteInputActive, remoteInputRowBottom, imeTop) {
+ imeTop.floatValue = 0f
+ snapshotFlow { imeTop.floatValue }
+ .collect { imeTopValue ->
+ // only scroll the stack if ime value has been populated (ime placeholder has been
+ // composed at least once), and our remote input row overlaps with the ime bounds.
+ if (isRemoteInputActive && imeTopValue > 0f && remoteInputRowBottom > imeTopValue) {
+ scrollNotificationStack(
+ scope = coroutineScope,
+ delta = remoteInputRowBottom - imeTopValue,
+ animate = true,
+ scrimOffset = scrimOffset,
+ minScrimOffset = minScrimOffset,
+ scrollState = scrollState,
+ )
}
}
}
@@ -394,12 +425,12 @@ fun SceneScope.NotificationScrollingStack(
scrimOffset.value < 0 &&
layoutState.isTransitioning(
from = Scenes.Shade,
- to = Scenes.QuickSettings
+ to = Scenes.QuickSettings,
)
) {
IntOffset(
x = 0,
- y = (scrimOffset.value * (1 - shadeToQsFraction)).roundToInt()
+ y = (scrimOffset.value * (1 - shadeToQsFraction)).roundToInt(),
)
} else {
IntOffset(x = 0, y = scrimOffset.value.roundToInt())
@@ -458,13 +489,11 @@ fun SceneScope.NotificationScrollingStack(
.thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() }
.debugBackground(viewModel, DEBUG_BOX_COLOR)
) {
- NotificationPlaceholder(
- stackScrollView = stackScrollView,
- viewModel = viewModel,
+ Column(
modifier =
Modifier.verticalNestedScrollToScene(
topBehavior = NestedScrollBehavior.EdgeWithPreview,
- isExternalOverscrollGesture = { isCurrentGestureOverscroll.value }
+ isExternalOverscrollGesture = { isCurrentGestureOverscroll.value },
)
.thenIf(shadeMode == ShadeMode.Single) {
Modifier.nestedScroll(scrimNestedScrollConnection)
@@ -473,18 +502,31 @@ fun SceneScope.NotificationScrollingStack(
.verticalScroll(scrollState)
.padding(top = topPadding)
.fillMaxWidth()
- .notificationStackHeight(
- view = stackScrollView,
- totalVerticalPadding = topPadding + bottomPadding,
- )
- .onSizeChanged { size -> stackHeight.intValue = size.height },
- )
+ ) {
+ NotificationPlaceholder(
+ stackScrollView = stackScrollView,
+ viewModel = viewModel,
+ modifier =
+ Modifier.notificationStackHeight(
+ view = stackScrollView,
+ totalVerticalPadding = topPadding + bottomPadding,
+ )
+ .onSizeChanged { size -> stackHeight.intValue = size.height },
+ )
+ Spacer(
+ modifier =
+ Modifier.windowInsetsBottomHeight(WindowInsets.imeAnimationTarget)
+ .onGloballyPositioned { coordinates: LayoutCoordinates ->
+ imeTop.floatValue = screenHeight - coordinates.size.height
+ }
+ )
+ }
}
if (shouldIncludeHeadsUpSpace) {
HeadsUpNotificationSpace(
stackScrollView = stackScrollView,
viewModel = viewModel,
- modifier = Modifier.padding(top = topPadding)
+ modifier = Modifier.padding(top = topPadding),
)
}
}
@@ -572,6 +614,42 @@ private fun SceneScope.NotificationPlaceholder(
)
}
+private suspend fun scrollNotificationStack(
+ scope: CoroutineScope,
+ delta: Float,
+ animate: Boolean,
+ scrimOffset: Animatable<Float, AnimationVector1D>,
+ minScrimOffset: () -> Float,
+ scrollState: ScrollState,
+) {
+ val minOffset = minScrimOffset()
+ if (scrimOffset.value > minOffset) {
+ val remainingDelta =
+ (minOffset - (scrimOffset.value - delta)).coerceAtLeast(0f).roundToInt()
+ if (remainingDelta > 0) {
+ if (animate) {
+ // launch a new coroutine for the remainder animation so that it doesn't suspend the
+ // scrim animation, allowing both to play simultaneously.
+ scope.launch { scrollState.animateScrollTo(remainingDelta) }
+ } else {
+ scrollState.scrollTo(remainingDelta)
+ }
+ }
+ val newScrimOffset = (scrimOffset.value - delta).coerceAtLeast(minOffset)
+ if (animate) {
+ scrimOffset.animateTo(newScrimOffset)
+ } else {
+ scrimOffset.snapTo(newScrimOffset)
+ }
+ } else {
+ if (animate) {
+ scrollState.animateScrollBy(delta)
+ } else {
+ scrollState.scrollBy(delta)
+ }
+ }
+}
+
private fun calculateCornerRadius(
scrimCornerRadius: Dp,
screenCornerRadius: Dp,
@@ -618,7 +696,7 @@ private fun consumeDeltaWithinRange(
setCurrent: (Float) -> Unit,
min: Float,
max: Float,
- delta: Float
+ delta: Float,
): Float {
return if (delta < 0 && current > min) {
val remainder = (current + delta - min).coerceAtMost(0f)
@@ -631,10 +709,7 @@ private fun consumeDeltaWithinRange(
} else 0f
}
-private inline fun debugLog(
- viewModel: NotificationsPlaceholderViewModel,
- msg: () -> Any,
-) {
+private inline fun debugLog(viewModel: NotificationsPlaceholderViewModel, msg: () -> Any) {
if (viewModel.isDebugLoggingEnabled) {
Log.d(TAG, msg().toString())
}
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 fa92bef34f38..0c1c16522567 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
@@ -61,6 +61,7 @@ import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.colorResource
@@ -79,7 +80,6 @@ import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
-import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -229,17 +229,16 @@ private fun SceneScope.QuickSettingsScene(
}
.thenIf(cutoutLocation != CutoutLocation.CENTER) { Modifier.displayCutoutPadding() }
) {
+ val density = LocalDensity.current
val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
val isCustomizerShowing by
viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
val customizingAnimationDuration by
viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
- val screenHeight = LocalRawScreenHeight.current
+ val screenHeight = with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
BackHandler(enabled = isCustomizing) { viewModel.qsSceneAdapter.requestCloseCustomizer() }
- val collapsedHeaderHeight =
- with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
val lifecycleOwner = LocalLifecycleOwner.current
val footerActionsViewModel =
remember(lifecycleOwner, viewModel) {
@@ -268,7 +267,6 @@ private fun SceneScope.QuickSettingsScene(
val navBarBottomHeight =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
- val density = LocalDensity.current
val bottomPadding by
animateDpAsState(
targetValue = if (isCustomizing) 0.dp else navBarBottomHeight,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
index c0302bc348b6..9af4b8c18c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
@@ -25,6 +25,7 @@ import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
/**
* Repository used for tracking the state of notification remote input (e.g. when the user presses
@@ -33,14 +34,21 @@ import kotlinx.coroutines.flow.Flow
interface RemoteInputRepository {
/** Whether remote input is currently active for any notification. */
val isRemoteInputActive: Flow<Boolean>
+
+ /**
+ * The bottom bound of the currently focused remote input notification row, or null if there
+ * isn't one.
+ */
+ val remoteInputRowBottomBound: Flow<Float?>
+
+ fun setRemoteInputRowBottomBound(bottom: Float?)
}
@SysUISingleton
class RemoteInputRepositoryImpl
@Inject
-constructor(
- private val notificationRemoteInputManager: NotificationRemoteInputManager,
-) : RemoteInputRepository {
+constructor(private val notificationRemoteInputManager: NotificationRemoteInputManager) :
+ RemoteInputRepository {
override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
trySend(false) // initial value is false
val callback =
@@ -52,6 +60,12 @@ constructor(
notificationRemoteInputManager.addControllerCallback(callback)
awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
}
+
+ override val remoteInputRowBottomBound = MutableStateFlow<Float?>(null)
+
+ override fun setRemoteInputRowBottomBound(bottom: Float?) {
+ remoteInputRowBottomBound.value = bottom
+ }
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
index 68f727b046c0..b83b0cc8d2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
@@ -20,13 +20,24 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.data.repository.RemoteInputRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.mapNotNull
/**
* Interactor used for business logic pertaining to the notification remote input (e.g. when the
* user presses "reply" on a notification and the keyboard opens).
*/
@SysUISingleton
-class RemoteInputInteractor @Inject constructor(remoteInputRepository: RemoteInputRepository) {
+class RemoteInputInteractor
+@Inject
+constructor(private val remoteInputRepository: RemoteInputRepository) {
/** Is remote input currently active for a notification? */
val isRemoteInputActive: Flow<Boolean> = remoteInputRepository.isRemoteInputActive
+
+ /** The bottom bound of the currently focused remote input notification row. */
+ val remoteInputRowBottomBound: Flow<Float> =
+ remoteInputRepository.remoteInputRowBottomBound.mapNotNull { it }
+
+ fun setRemoteInputRowBottomBound(bottom: Float?) {
+ remoteInputRepository.setRemoteInputRowBottomBound(bottom)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index cb3e26b9f8ea..5003a6af5c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -21,6 +21,7 @@ import static android.service.notification.NotificationListenerService.REASON_CA
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.policy.RemoteInputView.FOCUS_ANIMATION_MIN_SCALE;
import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.animation.Animator;
@@ -83,6 +84,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -118,6 +120,7 @@ import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.Compile;
@@ -830,6 +833,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mPrivateLayout.setRemoteInputController(r);
}
+ /**
+ * Return the cumulative y-value that the actions container expands via its scale animator when
+ * remote input is activated.
+ */
+ public float getRemoteInputActionsContainerExpandedOffset() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ RemoteInputView expandedRemoteInput = mPrivateLayout.getExpandedRemoteInput();
+ if (expandedRemoteInput == null) return 0f;
+ View actionsContainerLayout = expandedRemoteInput.getActionsContainerLayout();
+ if (actionsContainerLayout == null) return 0f;
+
+ return actionsContainerLayout.getHeight() * (1 - FOCUS_ANIMATION_MIN_SCALE) * 0.5f;
+ }
+
public void addChildNotification(ExpandableNotificationRow row) {
addChildNotification(row, -1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7543f3b48e48..e7c67f93eb78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -99,6 +99,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -120,7 +121,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.notification.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -740,6 +740,15 @@ public class NotificationStackScrollLayout
updateFooter();
}
+ void sendRemoteInputRowBottomBound(Float bottom) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ if (bottom != null) {
+ bottom += getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin);
+ }
+ mScrollViewFields.sendRemoteInputRowBottomBound(bottom);
+ }
+
/** Setter for filtered notifs, to be removed with the FooterViewRefactor flag. */
public void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
FooterViewRefactor.assertInLegacyMode();
@@ -1274,6 +1283,11 @@ public class NotificationStackScrollLayout
}
@Override
+ public void setRemoteInputRowBottomBoundConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setRemoteInputRowBottomBoundConsumer(consumer);
+ }
+
+ @Override
public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
mScrollViewFields.setHeadsUpHeightConsumer(consumer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e5f63c1cb480..dad6894a43ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -98,6 +98,9 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.HeadsUpNotificationViewControllerEmptyImpl;
+import com.android.systemui.statusbar.notification.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -129,9 +132,6 @@ import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.notification.HeadsUpNotificationViewControllerEmptyImpl;
-import com.android.systemui.statusbar.notification.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -1605,6 +1605,9 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
boolean remoteInputActive) {
+ if (SceneContainerFlag.isEnabled()) {
+ sendRemoteInputRowBottomBound(entry, remoteInputActive);
+ }
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
if (!FooterViewRefactor.isEnabled()) {
@@ -1620,6 +1623,15 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.requestDisallowLongPress();
mView.requestDisallowDismiss();
}
+
+ private void sendRemoteInputRowBottomBound(NotificationEntry entry,
+ boolean remoteInputActive) {
+ ExpandableNotificationRow row = entry.getRow();
+ float top = row.getTranslationY();
+ int height = row.getActualHeight();
+ float bottom = top + height + row.getRemoteInputActionsContainerExpandedOffset();
+ mView.sendRemoteInputRowBottomBound(remoteInputActive ? bottom : null);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index aa3953987c10..c08ed6120832 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -57,6 +57,13 @@ class ScrollViewFields {
* guts off of this gesture, we can notify the placeholder through here.
*/
var currentGestureInGutsConsumer: Consumer<Boolean>? = null
+
+ /**
+ * When a notification begins remote input, its bottom Y bound is sent to the placeholder
+ * through here in order to adjust to accommodate the IME.
+ */
+ var remoteInputRowBottomBoundConsumer: Consumer<Float?>? = null
+
/**
* Any time the heads up height is recalculated, it should be updated here to be used by the
* placeholder
@@ -75,6 +82,10 @@ class ScrollViewFields {
fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) =
currentGestureInGutsConsumer?.accept(isCurrentGestureInGuts)
+ /** send [bottomY] to the [remoteInputRowBottomBoundConsumer], if present. */
+ fun sendRemoteInputRowBottomBound(bottomY: Float?) =
+ remoteInputRowBottomBoundConsumer?.accept(bottomY)
+
/** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 235b4da3f029..41c02934efa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -74,6 +74,9 @@ interface NotificationScrollView {
/** Set a consumer for current gesture in guts events */
fun setCurrentGestureInGutsConsumer(consumer: Consumer<Boolean>?)
+ /** Set a consumer for current remote input notification row bottom bound events */
+ fun setRemoteInputRowBottomBoundConsumer(consumer: Consumer<Float?>?)
+
/** Set a consumer for heads up height changed events */
fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 6d5553fec6b4..2e37dead8787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -108,10 +108,14 @@ constructor(
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
view.setCurrentGestureInGutsConsumer(viewModel.currentGestureInGutsConsumer)
+ view.setRemoteInputRowBottomBoundConsumer(
+ viewModel.remoteInputRowBottomBoundConsumer
+ )
DisposableHandle {
view.setSyntheticScrollConsumer(null)
view.setCurrentGestureOverscrollConsumer(null)
view.setCurrentGestureInGutsConsumer(null)
+ view.setRemoteInputRowBottomBoundConsumer(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 8d7007b2fba4..5b2e02d446cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
@@ -56,6 +57,7 @@ constructor(
dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
+ private val remoteInputInteractor: RemoteInputInteractor,
private val sceneInteractor: SceneInteractor,
// TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
// while the flag is off, creating this object too early results in a crash
@@ -240,6 +242,10 @@ constructor(
val currentGestureInGutsConsumer: (Boolean) -> Unit =
stackAppearanceInteractor::setCurrentGestureInGuts
+ /** Receives the bottom bound of the currently focused remote input notification row. */
+ val remoteInputRowBottomBoundConsumer: (Float?) -> Unit =
+ remoteInputInteractor::setRemoteInputRowBottomBound
+
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
combine(sceneInteractor.currentScene, sceneInteractor.currentOverlays) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 69c1bf3b61b7..c8e83581e831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -49,6 +50,7 @@ constructor(
private val sceneInteractor: SceneInteractor,
private val shadeInteractor: ShadeInteractor,
private val headsUpNotificationInteractor: HeadsUpNotificationInteractor,
+ remoteInputInteractor: RemoteInputInteractor,
featureFlags: FeatureFlagsClassic,
dumpManager: DumpManager,
) :
@@ -132,6 +134,12 @@ constructor(
val isCurrentGestureOverscroll: Flow<Boolean> =
interactor.isCurrentGestureOverscroll.dumpWhileCollecting("isCurrentGestureOverScroll")
+ /** Whether remote input is currently active for any notification. */
+ val isRemoteInputActive = remoteInputInteractor.isRemoteInputActive
+
+ /** The bottom bound of the currently focused remote input notification row. */
+ val remoteInputRowBottomBound = remoteInputInteractor.remoteInputRowBottomBound
+
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
interactor.setScrolledToTop(scrolledToTop)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 31776cf5ad1b..16d5f8d30593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -106,7 +106,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private static final long FOCUS_ANIMATION_CROSSFADE_DURATION = 50;
private static final long FOCUS_ANIMATION_FADE_IN_DELAY = 33;
private static final long FOCUS_ANIMATION_FADE_IN_DURATION = 83;
- private static final float FOCUS_ANIMATION_MIN_SCALE = 0.5f;
+ public static final float FOCUS_ANIMATION_MIN_SCALE = 0.5f;
private static final long DEFOCUS_ANIMATION_FADE_OUT_DELAY = 120;
private static final long DEFOCUS_ANIMATION_CROSSFADE_DELAY = 180;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
index c416ea1c1b39..91602c23ac17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
@@ -16,8 +16,13 @@
package com.android.systemui.statusbar.data.repository
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
class FakeRemoteInputRepository : RemoteInputRepository {
override val isRemoteInputActive = MutableStateFlow(false)
+ override val remoteInputRowBottomBound: Flow<Float?> = flowOf(null)
+
+ override fun setRemoteInputRowBottomBound(bottom: Float?) {}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
index 6370a5d9c80b..7244d465ed7e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
val Kosmos.notificationScrollViewModel by Fixture {
@@ -29,6 +30,7 @@ val Kosmos.notificationScrollViewModel by Fixture {
dumpManager = dumpManager,
stackAppearanceInteractor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
+ remoteInputInteractor = remoteInputInteractor,
sceneInteractor = sceneInteractor,
keyguardInteractor = { keyguardInteractor },
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 8bfc390ecfa3..e5cf0a90ebbd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
@@ -31,6 +32,7 @@ val Kosmos.notificationsPlaceholderViewModel by Fixture {
sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
headsUpNotificationInteractor = headsUpNotificationInteractor,
+ remoteInputInteractor = remoteInputInteractor,
featureFlags = featureFlagsClassic,
dumpManager = dumpManager,
)