Loading packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +34 −4 Original line number Diff line number Diff line Loading @@ -16,10 +16,12 @@ package com.android.systemui.shade.ui.composable import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize Loading @@ -39,10 +41,13 @@ import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexContentPicker Loading Loading @@ -77,7 +82,8 @@ fun ContentScope.OverlayShade( Scrim(showBackgroundColor = enableTransparency, onClicked = onScrimClicked) Box( modifier = Modifier.fillMaxSize().panelContainerPadding(isFullWidth), modifier = Modifier.fillMaxSize().panelContainerPadding(isFullWidth, alignmentOnWideScreens), contentAlignment = if (isFullWidth) Alignment.TopCenter else alignmentOnWideScreens, ) { Panel( Loading Loading @@ -156,7 +162,7 @@ private fun Modifier.panelWidth(isFullWidthPanel: Boolean): Modifier { return if (isFullWidthPanel) { fillMaxWidth() } else { width(dimensionResource(id = R.dimen.shade_panel_width)) width(dimensionResource(R.dimen.shade_panel_width)) } } Loading @@ -167,14 +173,38 @@ internal fun isFullWidthShade(): Boolean { } @Composable private fun Modifier.panelContainerPadding(isFullWidthPanel: Boolean): Modifier { @ReadOnlyComposable @SuppressLint("ConfigurationScreenWidthHeight") private fun getHalfScreenWidth(): Dp { return LocalConfiguration.current.screenWidthDp.dp / 2 } @Composable private fun Modifier.panelContainerPadding( isFullWidthPanel: Boolean, alignment: Alignment, ): Modifier { if (isFullWidthPanel) { return this } // On wide screens, the shade panel width is limited to half the screen width. val halfScreenWidth = getHalfScreenWidth() val horizontalPaddingDp = dimensionResource(R.dimen.shade_panel_margin_horizontal) val (startPadding, endPadding) = when (alignment) { Alignment.TopStart -> horizontalPaddingDp to halfScreenWidth Alignment.TopEnd -> halfScreenWidth to horizontalPaddingDp else -> horizontalPaddingDp to horizontalPaddingDp } val paddings = PaddingValues(start = startPadding, end = endPadding) val layoutDirection = LocalLayoutDirection.current return windowInsetsPadding( WindowInsets.safeContent.union( WindowInsets(left = horizontalPaddingDp, right = horizontalPaddingDp) WindowInsets( left = paddings.calculateLeftPadding(layoutDirection), right = paddings.calculateRightPadding(layoutDirection), ) ) ) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +2 −2 Original line number Diff line number Diff line Loading @@ -240,8 +240,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S val dimens by collectLastValue(underTest.configurationBasedDimensions) val horizontalPosition = checkNotNull(dimens).horizontalPosition assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition) assertThat(horizontalPosition.width).isEqualTo(200) assertIs<HorizontalPosition.EdgeToMiddle>(horizontalPosition) assertThat(horizontalPosition.maxWidth).isEqualTo(200) } @Test Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt +15 −10 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import androidx.constraintlayout.widget.ConstraintSet.VERTICAL import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition.EdgeToMiddle import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition.MiddleToEdge /** * Container for the stack scroller, so that the bounds can be externally specified, such as from Loading Loading @@ -66,7 +68,10 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : constraintSet.clone(baseConstraintSet) val startConstraintId = if (horizontalPosition is HorizontalPosition.MiddleToEdge) { if (horizontalPosition is MiddleToEdge) R.id.nssl_guideline else PARENT_ID val endConstraintId = if (SceneContainerFlag.isEnabled && horizontalPosition is EdgeToMiddle) { R.id.nssl_guideline } else { PARENT_ID Loading @@ -76,9 +81,14 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : constraintSet.apply { if (SceneContainerFlag.isEnabled) { when (horizontalPosition) { is HorizontalPosition.FloatAtStart -> constrainWidth(nsslId, horizontalPosition.width) is HorizontalPosition.MiddleToEdge -> is EdgeToMiddle -> { setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio) constrainMaxWidth(nsslId, horizontalPosition.maxWidth) // Ensure START alignment in case the maxWidth is smaller than half the // parent width. constraintSet.setHorizontalBias(nsslId, 0f) } is MiddleToEdge -> setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio) else -> Unit } Loading @@ -89,12 +99,7 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : // animations. setAlpha(nsslId, nsslAlpha) connect(nsslId, START, startConstraintId, START, marginStart) if ( !SceneContainerFlag.isEnabled || horizontalPosition !is HorizontalPosition.FloatAtStart ) { connect(nsslId, END, PARENT_ID, END, marginEnd) } connect(nsslId, END, endConstraintId, END, marginEnd) connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom) connect(nsslId, TOP, PARENT_ID, TOP, marginTop) } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +11 −9 Original line number Diff line number Diff line Loading @@ -273,8 +273,10 @@ constructor( Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f) Dual -> if (isShadeLayoutWide) { HorizontalPosition.FloatAtStart( width = getDimensionPixelSize(R.dimen.shade_panel_width) HorizontalPosition.EdgeToMiddle( ratio = 0.5f, maxWidth = getDimensionPixelSize(R.dimen.shade_panel_width), ) } else { HorizontalPosition.EdgeToEdge Loading @@ -284,7 +286,7 @@ constructor( ConfigurationBasedDimensions( horizontalPosition = horizontalPosition, marginStart = if (shadeMode is Split) 0 else marginHorizontal, marginEnd = marginHorizontal, marginEnd = if (shadeMode is Dual) 0 else marginHorizontal, marginBottom = getDimensionPixelSize(R.dimen.notification_panel_margin_bottom), // y position of the NSSL in the window needs to be 0 under scene Loading Loading @@ -971,14 +973,14 @@ constructor( /** The container is using the full width of the screen (minus any margins). */ data object EdgeToEdge : HorizontalPosition /** The container is laid out from the given [ratio] of the screen width to the end edge. */ data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition /** * The container has a fixed [width] and is aligned to the start of the screen. In this * layout, the end edge of the container is floating, i.e. unconstrained. * The container is laid out from the start edge to the given [ratio] of the screen width, * or to [maxWidth], whichever dimension is smaller. */ data class FloatAtStart(val width: Int) : HorizontalPosition data class EdgeToMiddle(val ratio: Float = 0.5f, val maxWidth: Int) : HorizontalPosition /** The container is laid out from the given [ratio] of the screen width to the end edge. */ data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition } /** Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +34 −4 Original line number Diff line number Diff line Loading @@ -16,10 +16,12 @@ package com.android.systemui.shade.ui.composable import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize Loading @@ -39,10 +41,13 @@ import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexContentPicker Loading Loading @@ -77,7 +82,8 @@ fun ContentScope.OverlayShade( Scrim(showBackgroundColor = enableTransparency, onClicked = onScrimClicked) Box( modifier = Modifier.fillMaxSize().panelContainerPadding(isFullWidth), modifier = Modifier.fillMaxSize().panelContainerPadding(isFullWidth, alignmentOnWideScreens), contentAlignment = if (isFullWidth) Alignment.TopCenter else alignmentOnWideScreens, ) { Panel( Loading Loading @@ -156,7 +162,7 @@ private fun Modifier.panelWidth(isFullWidthPanel: Boolean): Modifier { return if (isFullWidthPanel) { fillMaxWidth() } else { width(dimensionResource(id = R.dimen.shade_panel_width)) width(dimensionResource(R.dimen.shade_panel_width)) } } Loading @@ -167,14 +173,38 @@ internal fun isFullWidthShade(): Boolean { } @Composable private fun Modifier.panelContainerPadding(isFullWidthPanel: Boolean): Modifier { @ReadOnlyComposable @SuppressLint("ConfigurationScreenWidthHeight") private fun getHalfScreenWidth(): Dp { return LocalConfiguration.current.screenWidthDp.dp / 2 } @Composable private fun Modifier.panelContainerPadding( isFullWidthPanel: Boolean, alignment: Alignment, ): Modifier { if (isFullWidthPanel) { return this } // On wide screens, the shade panel width is limited to half the screen width. val halfScreenWidth = getHalfScreenWidth() val horizontalPaddingDp = dimensionResource(R.dimen.shade_panel_margin_horizontal) val (startPadding, endPadding) = when (alignment) { Alignment.TopStart -> horizontalPaddingDp to halfScreenWidth Alignment.TopEnd -> halfScreenWidth to horizontalPaddingDp else -> horizontalPaddingDp to horizontalPaddingDp } val paddings = PaddingValues(start = startPadding, end = endPadding) val layoutDirection = LocalLayoutDirection.current return windowInsetsPadding( WindowInsets.safeContent.union( WindowInsets(left = horizontalPaddingDp, right = horizontalPaddingDp) WindowInsets( left = paddings.calculateLeftPadding(layoutDirection), right = paddings.calculateRightPadding(layoutDirection), ) ) ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +2 −2 Original line number Diff line number Diff line Loading @@ -240,8 +240,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S val dimens by collectLastValue(underTest.configurationBasedDimensions) val horizontalPosition = checkNotNull(dimens).horizontalPosition assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition) assertThat(horizontalPosition.width).isEqualTo(200) assertIs<HorizontalPosition.EdgeToMiddle>(horizontalPosition) assertThat(horizontalPosition.maxWidth).isEqualTo(200) } @Test Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt +15 −10 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import androidx.constraintlayout.widget.ConstraintSet.VERTICAL import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition.EdgeToMiddle import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition.MiddleToEdge /** * Container for the stack scroller, so that the bounds can be externally specified, such as from Loading Loading @@ -66,7 +68,10 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : constraintSet.clone(baseConstraintSet) val startConstraintId = if (horizontalPosition is HorizontalPosition.MiddleToEdge) { if (horizontalPosition is MiddleToEdge) R.id.nssl_guideline else PARENT_ID val endConstraintId = if (SceneContainerFlag.isEnabled && horizontalPosition is EdgeToMiddle) { R.id.nssl_guideline } else { PARENT_ID Loading @@ -76,9 +81,14 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : constraintSet.apply { if (SceneContainerFlag.isEnabled) { when (horizontalPosition) { is HorizontalPosition.FloatAtStart -> constrainWidth(nsslId, horizontalPosition.width) is HorizontalPosition.MiddleToEdge -> is EdgeToMiddle -> { setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio) constrainMaxWidth(nsslId, horizontalPosition.maxWidth) // Ensure START alignment in case the maxWidth is smaller than half the // parent width. constraintSet.setHorizontalBias(nsslId, 0f) } is MiddleToEdge -> setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio) else -> Unit } Loading @@ -89,12 +99,7 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) : // animations. setAlpha(nsslId, nsslAlpha) connect(nsslId, START, startConstraintId, START, marginStart) if ( !SceneContainerFlag.isEnabled || horizontalPosition !is HorizontalPosition.FloatAtStart ) { connect(nsslId, END, PARENT_ID, END, marginEnd) } connect(nsslId, END, endConstraintId, END, marginEnd) connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom) connect(nsslId, TOP, PARENT_ID, TOP, marginTop) } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +11 −9 Original line number Diff line number Diff line Loading @@ -273,8 +273,10 @@ constructor( Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f) Dual -> if (isShadeLayoutWide) { HorizontalPosition.FloatAtStart( width = getDimensionPixelSize(R.dimen.shade_panel_width) HorizontalPosition.EdgeToMiddle( ratio = 0.5f, maxWidth = getDimensionPixelSize(R.dimen.shade_panel_width), ) } else { HorizontalPosition.EdgeToEdge Loading @@ -284,7 +286,7 @@ constructor( ConfigurationBasedDimensions( horizontalPosition = horizontalPosition, marginStart = if (shadeMode is Split) 0 else marginHorizontal, marginEnd = marginHorizontal, marginEnd = if (shadeMode is Dual) 0 else marginHorizontal, marginBottom = getDimensionPixelSize(R.dimen.notification_panel_margin_bottom), // y position of the NSSL in the window needs to be 0 under scene Loading Loading @@ -971,14 +973,14 @@ constructor( /** The container is using the full width of the screen (minus any margins). */ data object EdgeToEdge : HorizontalPosition /** The container is laid out from the given [ratio] of the screen width to the end edge. */ data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition /** * The container has a fixed [width] and is aligned to the start of the screen. In this * layout, the end edge of the container is floating, i.e. unconstrained. * The container is laid out from the start edge to the given [ratio] of the screen width, * or to [maxWidth], whichever dimension is smaller. */ data class FloatAtStart(val width: Int) : HorizontalPosition data class EdgeToMiddle(val ratio: Float = 0.5f, val maxWidth: Int) : HorizontalPosition /** The container is laid out from the given [ratio] of the screen width to the end edge. */ data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition } /** Loading