Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab133055 authored by 0's avatar 0
Browse files

[flexiglass] Implement stack scroll on remote input activation in Shade

When remote input is started, we pipe the target bottom bound of the remote input row after its expansion animation to flexiglass, where we diff that value with the target top bound of the IME after its expansion animation. We then artificially add the IME height to the stack content height, and scroll it by the diff so that the remote input row isn't overlapped by the IME.

Bug: 359876385
Test: manually verified stack scrolls above IME when remote input starts
Test: manually verified stack scrolls above IME when a second remote input is started while another is already active
Test: manually verified stack scrolls above IME even when there is no space below the remote input row, and then scrolls back down when IME is dismissed
Test: manually verified remote input in HUN is unaffected
Flag: com.android.systemui.scene_container
Change-Id: Ibe85ae9f6e10502fcf986b3a0056f57716a2cbb5
parent c99762b2
Loading
Loading
Loading
Loading
+0 −21
Original line number Original line Diff line number Diff line
@@ -16,15 +16,10 @@


package com.android.systemui.common.ui.compose.windowinsets
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.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -36,9 +31,6 @@ val LocalDisplayCutout = staticCompositionLocalOf { DisplayCutout() }
/** The corner radius in px of the current display. */
/** The corner radius in px of the current display. */
val LocalScreenCornerRadius = staticCompositionLocalOf { 0.dp }
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
@Composable
fun ScreenDecorProvider(
fun ScreenDecorProvider(
    displayCutout: StateFlow<DisplayCutout>,
    displayCutout: StateFlow<DisplayCutout>,
@@ -48,22 +40,9 @@ fun ScreenDecorProvider(
    val cutout by displayCutout.collectAsStateWithLifecycle()
    val cutout by displayCutout.collectAsStateWithLifecycle()
    val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
    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(
    CompositionLocalProvider(
        LocalScreenCornerRadius provides screenCornerRadiusDp,
        LocalScreenCornerRadius provides screenCornerRadiusDp,
        LocalDisplayCutout provides cutout,
        LocalDisplayCutout provides cutout,
        LocalRawScreenHeight provides screenHeight,
    ) {
    ) {
        content()
        content()
    }
    }
+8 −5
Original line number Original line Diff line number Diff line
@@ -24,9 +24,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
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.IntOffset
import androidx.compose.ui.unit.dp
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import kotlin.math.max
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.roundToInt
import kotlin.math.tanh
import kotlin.math.tanh
@@ -36,9 +38,10 @@ import kotlinx.coroutines.launch
@Composable
@Composable
fun Modifier.stackVerticalOverscroll(
fun Modifier.stackVerticalOverscroll(
    coroutineScope: CoroutineScope,
    coroutineScope: CoroutineScope,
    canScrollForward: () -> Boolean
    canScrollForward: () -> Boolean,
): Modifier {
): Modifier {
    val screenHeight = LocalRawScreenHeight.current
    val screenHeight =
        with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
    val overscrollOffset = remember { Animatable(0f) }
    val overscrollOffset = remember { Animatable(0f) }
    val stackNestedScrollConnection = remember {
    val stackNestedScrollConnection = remember {
        NotificationStackNestedScrollConnection(
        NotificationStackNestedScrollConnection(
@@ -60,10 +63,10 @@ fun Modifier.stackVerticalOverscroll(
                    overscrollOffset.animateTo(
                    overscrollOffset.animateTo(
                        targetValue = 0f,
                        targetValue = 0f,
                        initialVelocity = velocityAvailable,
                        initialVelocity = velocityAvailable,
                        animationSpec = tween()
                        animationSpec = tween(),
                    )
                    )
                }
                }
            }
            },
        )
        )
    }
    }


+112 −37
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.notifications.ui.composable


import android.util.Log
import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
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.scrollBy
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
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.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.absoluteOffset
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
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.onPlaced
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
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.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
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.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
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.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import kotlin.math.roundToInt
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch


object Notifications {
object Notifications {
@@ -171,7 +177,7 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
            setCurrent = { scrollOffset = it },
            setCurrent = { scrollOffset = it },
            min = minScrollOffset,
            min = minScrollOffset,
            max = maxScrollOffset,
            max = maxScrollOffset,
            delta
            delta,
        )
        )
    }
    }


@@ -209,8 +215,8 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
                            calculateHeadsUpPlaceholderYOffset(
                            calculateHeadsUpPlaceholderYOffset(
                                scrollOffset.roundToInt(),
                                scrollOffset.roundToInt(),
                                minScrollOffset.roundToInt(),
                                minScrollOffset.roundToInt(),
                                stackScrollView.topHeadsUpHeight
                                stackScrollView.topHeadsUpHeight,
                            )
                            ),
                    )
                    )
                }
                }
                .thenIf(isHeadsUp) {
                .thenIf(isHeadsUp) {
@@ -218,11 +224,8 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace(
                            bottomBehavior = NestedScrollBehavior.EdgeAlways
                            bottomBehavior = NestedScrollBehavior.EdgeAlways
                        )
                        )
                        .nestedScroll(nestedScrollConnection)
                        .nestedScroll(nestedScrollConnection)
                        .scrollable(
                        .scrollable(orientation = Orientation.Vertical, state = scrollableState)
                            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
 * Adds the space where notification stack should appear in the scene, with a scrim and nested
 * scrolling.
 * scrolling.
 */
 */
@OptIn(ExperimentalLayoutApi::class)
@Composable
@Composable
fun SceneScope.NotificationScrollingStack(
fun SceneScope.NotificationScrollingStack(
    shadeSession: SaveableSession,
    shadeSession: SaveableSession,
@@ -291,7 +295,7 @@ fun SceneScope.NotificationScrollingStack(
    val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
    val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
    val bottomPadding = if (shouldReserveSpaceForNavBar) navBarHeight else 0.dp
    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
     * 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() }
        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.
    // we are not scrolled to the top unless the scrim is at its maximum offset.
    LaunchedEffect(viewModel, scrimOffset) {
    LaunchedEffect(viewModel, scrimOffset) {
        snapshotFlow { scrimOffset.value >= 0f }
        snapshotFlow { scrimOffset.value >= 0f }
@@ -342,15 +354,34 @@ fun SceneScope.NotificationScrollingStack(
    LaunchedEffect(syntheticScroll, scrimOffset, scrollState) {
    LaunchedEffect(syntheticScroll, scrimOffset, scrollState) {
        snapshotFlow { syntheticScroll.value }
        snapshotFlow { syntheticScroll.value }
            .collect { delta ->
            .collect { delta ->
                val minOffset = minScrimOffset()
                scrollNotificationStack(
                if (scrimOffset.value > minOffset) {
                    scope = coroutineScope,
                    val remainingDelta = (minOffset - (scrimOffset.value - delta)).coerceAtLeast(0f)
                    delta = delta,
                    scrimOffset.snapTo((scrimOffset.value - delta).coerceAtLeast(minOffset))
                    animate = false,
                    if (remainingDelta > 0f) {
                    scrimOffset = scrimOffset,
                        scrollState.scrollBy(remainingDelta)
                    minScrimOffset = minScrimOffset,
                    scrollState = scrollState,
                )
            }
            }
                } else {
    }
                    scrollState.scrollTo(delta.roundToInt())

    // 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 &&
                        scrimOffset.value < 0 &&
                            layoutState.isTransitioning(
                            layoutState.isTransitioning(
                                from = Scenes.Shade,
                                from = Scenes.Shade,
                                to = Scenes.QuickSettings
                                to = Scenes.QuickSettings,
                            )
                            )
                    ) {
                    ) {
                        IntOffset(
                        IntOffset(
                            x = 0,
                            x = 0,
                            y = (scrimOffset.value * (1 - shadeToQsFraction)).roundToInt()
                            y = (scrimOffset.value * (1 - shadeToQsFraction)).roundToInt(),
                        )
                        )
                    } else {
                    } else {
                        IntOffset(x = 0, y = scrimOffset.value.roundToInt())
                        IntOffset(x = 0, y = scrimOffset.value.roundToInt())
@@ -458,13 +489,11 @@ fun SceneScope.NotificationScrollingStack(
                    .thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() }
                    .thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() }
                    .debugBackground(viewModel, DEBUG_BOX_COLOR)
                    .debugBackground(viewModel, DEBUG_BOX_COLOR)
        ) {
        ) {
            NotificationPlaceholder(
            Column(
                stackScrollView = stackScrollView,
                viewModel = viewModel,
                modifier =
                modifier =
                    Modifier.verticalNestedScrollToScene(
                    Modifier.verticalNestedScrollToScene(
                            topBehavior = NestedScrollBehavior.EdgeWithPreview,
                            topBehavior = NestedScrollBehavior.EdgeWithPreview,
                            isExternalOverscrollGesture = { isCurrentGestureOverscroll.value }
                            isExternalOverscrollGesture = { isCurrentGestureOverscroll.value },
                        )
                        )
                        .thenIf(shadeMode == ShadeMode.Single) {
                        .thenIf(shadeMode == ShadeMode.Single) {
                            Modifier.nestedScroll(scrimNestedScrollConnection)
                            Modifier.nestedScroll(scrimNestedScrollConnection)
@@ -473,18 +502,31 @@ fun SceneScope.NotificationScrollingStack(
                        .verticalScroll(scrollState)
                        .verticalScroll(scrollState)
                        .padding(top = topPadding)
                        .padding(top = topPadding)
                        .fillMaxWidth()
                        .fillMaxWidth()
                        .notificationStackHeight(
            ) {
                NotificationPlaceholder(
                    stackScrollView = stackScrollView,
                    viewModel = viewModel,
                    modifier =
                        Modifier.notificationStackHeight(
                                view = stackScrollView,
                                view = stackScrollView,
                                totalVerticalPadding = topPadding + bottomPadding,
                                totalVerticalPadding = topPadding + bottomPadding,
                            )
                            )
                            .onSizeChanged { size -> stackHeight.intValue = size.height },
                            .onSizeChanged { size -> stackHeight.intValue = size.height },
                )
                )
                Spacer(
                    modifier =
                        Modifier.windowInsetsBottomHeight(WindowInsets.imeAnimationTarget)
                            .onGloballyPositioned { coordinates: LayoutCoordinates ->
                                imeTop.floatValue = screenHeight - coordinates.size.height
                            }
                )
            }
        }
        }
        if (shouldIncludeHeadsUpSpace) {
        if (shouldIncludeHeadsUpSpace) {
            HeadsUpNotificationSpace(
            HeadsUpNotificationSpace(
                stackScrollView = stackScrollView,
                stackScrollView = stackScrollView,
                viewModel = viewModel,
                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(
private fun calculateCornerRadius(
    scrimCornerRadius: Dp,
    scrimCornerRadius: Dp,
    screenCornerRadius: Dp,
    screenCornerRadius: Dp,
@@ -618,7 +696,7 @@ private fun consumeDeltaWithinRange(
    setCurrent: (Float) -> Unit,
    setCurrent: (Float) -> Unit,
    min: Float,
    min: Float,
    max: Float,
    max: Float,
    delta: Float
    delta: Float,
): Float {
): Float {
    return if (delta < 0 && current > min) {
    return if (delta < 0 && current > min) {
        val remainder = (current + delta - min).coerceAtMost(0f)
        val remainder = (current + delta - min).coerceAtMost(0f)
@@ -631,10 +709,7 @@ private fun consumeDeltaWithinRange(
    } else 0f
    } else 0f
}
}


private inline fun debugLog(
private inline fun debugLog(viewModel: NotificationsPlaceholderViewModel, msg: () -> Any) {
    viewModel: NotificationsPlaceholderViewModel,
    msg: () -> Any,
) {
    if (viewModel.isDebugLoggingEnabled) {
    if (viewModel.isDebugLoggingEnabled) {
        Log.d(TAG, msg().toString())
        Log.d(TAG, msg().toString())
    }
    }
+3 −5
Original line number Original line Diff line number Diff line
@@ -61,6 +61,7 @@ import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.colorResource
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.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
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.LocalDisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -229,17 +229,16 @@ private fun SceneScope.QuickSettingsScene(
                }
                }
                .thenIf(cutoutLocation != CutoutLocation.CENTER) { Modifier.displayCutoutPadding() }
                .thenIf(cutoutLocation != CutoutLocation.CENTER) { Modifier.displayCutoutPadding() }
    ) {
    ) {
        val density = LocalDensity.current
        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
        val isCustomizerShowing by
        val isCustomizerShowing by
            viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
            viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
        val customizingAnimationDuration by
        val customizingAnimationDuration by
            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
        val screenHeight = LocalRawScreenHeight.current
        val screenHeight = with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() }


        BackHandler(enabled = isCustomizing) { viewModel.qsSceneAdapter.requestCloseCustomizer() }
        BackHandler(enabled = isCustomizing) { viewModel.qsSceneAdapter.requestCloseCustomizer() }


        val collapsedHeaderHeight =
            with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
        val lifecycleOwner = LocalLifecycleOwner.current
        val lifecycleOwner = LocalLifecycleOwner.current
        val footerActionsViewModel =
        val footerActionsViewModel =
            remember(lifecycleOwner, viewModel) {
            remember(lifecycleOwner, viewModel) {
@@ -268,7 +267,6 @@ private fun SceneScope.QuickSettingsScene(


        val navBarBottomHeight =
        val navBarBottomHeight =
            WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
            WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
        val density = LocalDensity.current
        val bottomPadding by
        val bottomPadding by
            animateDpAsState(
            animateDpAsState(
                targetValue = if (isCustomizing) 0.dp else navBarBottomHeight,
                targetValue = if (isCustomizing) 0.dp else navBarBottomHeight,
+17 −3
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import dagger.Module
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
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
 * 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 {
interface RemoteInputRepository {
    /** Whether remote input is currently active for any notification. */
    /** Whether remote input is currently active for any notification. */
    val isRemoteInputActive: Flow<Boolean>
    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
@SysUISingleton
class RemoteInputRepositoryImpl
class RemoteInputRepositoryImpl
@Inject
@Inject
constructor(
constructor(private val notificationRemoteInputManager: NotificationRemoteInputManager) :
    private val notificationRemoteInputManager: NotificationRemoteInputManager,
    RemoteInputRepository {
) : RemoteInputRepository {
    override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
    override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
        trySend(false) // initial value is false
        trySend(false) // initial value is false
        val callback =
        val callback =
@@ -52,6 +60,12 @@ constructor(
        notificationRemoteInputManager.addControllerCallback(callback)
        notificationRemoteInputManager.addControllerCallback(callback)
        awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
        awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
    }
    }

    override val remoteInputRowBottomBound = MutableStateFlow<Float?>(null)

    override fun setRemoteInputRowBottomBound(bottom: Float?) {
        remoteInputRowBottomBound.value = bottom
    }
}
}


@Module
@Module
Loading