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

Commit 4156a92e authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

Merge changes from topic "resizable-item-frame" into main

* changes:
  Update widget spacing for medium screens
  Animate grid paddings change for edit mode transition
  Update glanceable hub layout specs on compact screens
  Update specs for the resizable widget frame
parents 7b304647 cb11bd45
Loading
Loading
Loading
Loading
+1 −8
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.systemui.communal.ui.compose

import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
@@ -40,7 +39,6 @@ import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
@@ -175,14 +173,9 @@ constructor(
                        } else {
                            constraints.maxHeight - lockIconBounds.top
                        }
                    // Bias the widgets up by a small offset for visual balance in landscape
                    // orientation
                    val verticalOffset =
                        (if (orientation == Configuration.ORIENTATION_LANDSCAPE) (-3).dp else 0.dp)
                            .roundToPx()
                    // Use even top and bottom margin for grid to be centered in maxHeight (window)
                    communalGridMaxHeight = constraints.maxHeight - communalGridVerticalMargin * 2
                    communalGridPositionY = communalGridVerticalMargin + verticalOffset
                    communalGridPositionY = communalGridVerticalMargin
                } else {
                    communalGridMaxHeight = lockIconBounds?.top ?: constraints.maxHeight
                    communalGridPositionY = 0
+104 −18
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.sp
@@ -257,8 +258,15 @@ fun CommunalHub(
        viewModel.isCommunalContentVisible.collectAsStateWithLifecycle(
            initialValue = hubEditModeTransition() || !viewModel.isEditMode
        )
    val shouldShowEditModeLayout by
        viewModel.shouldShowEditModeLayout.collectAsStateWithLifecycle(
            initialValue = viewModel.isEditMode
        )
    val minContentPadding =
        if (hubEditModeTransition())
            gridContentPadding(shouldShowEditModeLayout, Dimensions.ToolbarHeight)
        else gridContentPadding(viewModel.isEditMode, toolbarSize)

    val minContentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
    ObserveScrollEffect(gridState, viewModel)

    val context = LocalContext.current
@@ -458,7 +466,7 @@ fun CommunalHub(

        if (onOpenWidgetPicker != null && onEditDone != null) {
            AnimatedVisibility(
                visible = viewModel.isEditMode && isCommunalContentVisible,
                visible = shouldShowEditModeLayout && isCommunalContentVisible,
                enter =
                    fadeIn(animationSpec = tween(durationMillis = 250, easing = LinearEasing)) +
                        slideInVertically(
@@ -842,11 +850,13 @@ private fun HorizontalGridWrapper(
        val flingBehavior =
            rememberSnapFlingBehavior(lazyGridState = gridState, snapPosition = SnapPosition.Start)
        ResponsiveLazyHorizontalGrid(
            cellAspectRatio = 1.5f,
            // Use flexible aspect ratio on compact screens to maximize the real estate
            cellAspectRatio = if (hubEditModeTransition() && isCompactWindow()) 0f else 1.5f,
            modifier = modifier,
            state = gridState,
            flingBehavior = flingBehavior,
            minContentPadding = minContentPadding,
            animateContentPadding = hubEditModeTransition(),
            minHorizontalArrangement = minHorizontalArrangement,
            minVerticalArrangement = minVerticalArrangement,
            setContentOffset = setContentOffset,
@@ -906,7 +916,10 @@ private fun BoxScope.CommunalHubLazyGrid(
    var gridItemSize: SizeInfo? by remember { mutableStateOf(null) }
    var list = communalContent
    var dragDropState: GridDragDropState? = null
    var arrangementSpacing = Dimensions.ItemSpacing
    val arrangementSpacing =
        if (communalResponsiveGrid() && isCompactWindow()) Dimensions.ItemSpacingCompact
        else if (communalResponsiveGrid() && isMediumWindow()) hubDimensions.ItemSpacingMedium
        else Dimensions.ItemSpacing
    val windowSize = WindowSizeUtils.getWindowSizeCategory(LocalContext.current)
    if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
        list = contentListState.list
@@ -942,9 +955,6 @@ private fun BoxScope.CommunalHubLazyGrid(
        Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {}
    } else if (communalResponsiveGrid()) {
        gridModifier = gridModifier.fillMaxSize()
        if (isCompactWindow()) {
            arrangementSpacing = Dimensions.ItemSpacingCompact
        }
    } else {
        gridModifier = gridModifier.height(hubDimensions.GridHeight)
    }
@@ -1197,7 +1207,9 @@ private fun Toolbar(
    val toolbarPadding = toolbarPadding()
    Box(
        modifier =
            Modifier.fillMaxWidth().padding(toolbarPadding).onSizeChanged { setToolbarSize(it) }
            Modifier.fillMaxWidth().padding(toolbarPadding).thenIf(!hubEditModeTransition()) {
                Modifier.onSizeChanged { setToolbarSize(it) }
            }
    ) {
        val addWidgetText = stringResource(R.string.hub_mode_add_widget_button_text)

@@ -1230,7 +1242,8 @@ private fun Toolbar(
                colors = filledButtonColors(),
                contentPadding = Dimensions.ButtonPadding,
                modifier =
                    Modifier.graphicsLayer { alpha = removeButtonAlpha }
                    Modifier.toolbarHeight()
                        .graphicsLayer { alpha = removeButtonAlpha }
                        .onGloballyPositioned {
                            // It's possible for this callback to fire after remove has been
                            // disabled. Check enabled state before setting.
@@ -1298,6 +1311,7 @@ private fun ToolbarButton(
            onClick = onClick,
            colors = filledButtonColors(),
            contentPadding = Dimensions.ButtonPadding,
            modifier = Modifier.toolbarHeight(),
        ) {
            Row(
                horizontalArrangement =
@@ -1320,6 +1334,7 @@ private fun ToolbarButton(
            colors = ButtonDefaults.outlinedButtonColors(contentColor = colors.primary),
            border = BorderStroke(width = 2.0.dp, color = colors.primary),
            contentPadding = Dimensions.ButtonPadding,
            modifier = Modifier.toolbarHeight(),
        ) {
            Row(
                horizontalArrangement =
@@ -1387,6 +1402,9 @@ private fun CommunalContent(
@Composable
fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
    val brush = SolidColor(MaterialTheme.colorScheme.primary)
    val cornerRadius =
        if (hubEditModeTransition()) dimensionResource(system_app_widget_background_radius)
        else 37.adjustedDp
    Box(
        modifier =
            // drawBehind lets us draw outside the bounds of the widgets so that we don't need to
@@ -1400,7 +1418,7 @@ fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
                    topLeft = Offset(-padding, -padding),
                    size =
                        Size(width = size.width + padding * 2, height = size.height + padding * 2),
                    cornerRadius = CornerRadius(37.adjustedDp.toPx()),
                    cornerRadius = CornerRadius(cornerRadius.toPx()),
                    style = Stroke(width = 3.adjustedDp.toPx()),
                )
            }
@@ -1943,8 +1961,41 @@ private fun nonScalableTextSize(sizeInDp: Dp) = with(LocalDensity.current) { siz
 * outside the grid over the toolbar, without part of it getting clipped by the container.
 */
@Composable
private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
    if (!isEditMode || toolbarSize == null) {
private fun gridContentPadding(isEditMode: Boolean, toolbarHeight: Dp): PaddingValues {
    if (communalResponsiveGrid() && hubEditModeTransition()) {
        val itemSpacing =
            if (isCompactWindow()) Dimensions.ItemSpacingCompact
            else if (isMediumWindow()) hubDimensions.ItemSpacingMedium else Dimensions.ItemSpacing
        val editModeTopPadding =
            toolbarPadding().calculateTopPadding() + toolbarHeight + itemSpacing
        // For compact windows, allow the bottom spacing to be minimum so that all items shift
        // down. For medium and large windows, use top padding for both vertical directions to
        // ensure items are centered vertically.
        val editModeBottomPadding = if (isCompactWindow()) itemSpacing else editModeTopPadding

        val finalTopPadding: Dp
        val finalBottomPadding: Dp
        if (isEditMode) {
            finalTopPadding = editModeTopPadding
            finalBottomPadding = editModeBottomPadding
        } else {
            // When in non edit mode, distribute the paddings needed for edit mode evenly on top
            // and bottom. This allows the paddings to shift, keeping the widget size consistent
            // between the two modes.
            finalTopPadding = (editModeTopPadding + editModeBottomPadding) / 2
            finalBottomPadding = finalTopPadding
        }

        return PaddingValues(
            start = itemSpacing,
            top = finalTopPadding,
            end = itemSpacing,
            bottom = finalBottomPadding,
        )
    }

    val isToolbarAbsent = !isEditMode || toolbarHeight == 0.dp
    if (isToolbarAbsent) {
        return if (communalResponsiveGrid()) {
            if (isCompactWindow()) {
                responsiveGridPaddingsWithInsets(Dimensions.ItemSpacingCompact)
@@ -1963,7 +2014,6 @@ private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): Padd
    val density = LocalDensity.current
    val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
    val screenHeight = with(density) { windowMetrics.bounds.height().toDp() }
    val toolbarHeight = with(density) { toolbarSize.height.toDp() }
    return if (communalResponsiveGrid()) {
        // In edit mode, grid spans full screen so min top padding of the grid should include space
        // taken by toolbar.
@@ -1999,6 +2049,14 @@ private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): Padd
    }
}

@Composable
private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
    return gridContentPadding(
        isEditMode,
        toolbarHeight = with(LocalDensity.current) { toolbarSize?.height?.toDp() } ?: 0.dp,
    )
}

/** Compact size in landscape or portrait */
@Composable
fun isCompactWindow(): Boolean {
@@ -2009,6 +2067,20 @@ fun isCompactWindow(): Boolean {
    }
}

/** Medium size in landscape or portrait */
@Composable
private fun isMediumWindow(): Boolean {
    val windowSizeClass = LocalWindowSizeClass.current
    return remember(windowSizeClass) {
        windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium ||
            windowSizeClass.heightSizeClass == WindowHeightSizeClass.Medium
    }
}

private fun Modifier.toolbarHeight(): Modifier {
    return this.thenIf(hubEditModeTransition()) { Modifier.height(Dimensions.ToolbarHeight) }
}

private fun CommunalContentSize.FixedSize.dp(): Dp {
    return when (this) {
        CommunalContentSize.FixedSize.FULL -> Dimensions.CardHeightFull
@@ -2075,6 +2147,8 @@ class Dimensions(val context: Context, val config: Configuration) {
    /** Responsive grid toolbar bottom padding. */
    val toolbarBottomPadding: Dp
        get() {
            if (hubEditModeTransition()) return 0.dp

            val windowSizeCategory = WindowSizeUtils.getWindowSizeCategory(context)
            return if (windowSizeCategory == WindowSizeUtils.WindowSizeCategory.MOBILE_LANDSCAPE) {
                6.adjustedDp
@@ -2098,12 +2172,21 @@ class Dimensions(val context: Context, val config: Configuration) {
            }
        }

    val ItemSpacingMedium: Dp
        get() {
            return if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                ItemSpacingCompact
            } else {
                ItemSpacing
            }
        }

    companion object {
        val CardHeightFull
            get() = 530.adjustedDp

        val ItemSpacingCompact
            get() = 12.adjustedDp
            get() = if (hubEditModeTransition()) 16.adjustedDp else 12.adjustedDp

        val ItemSpacing
            get() = if (communalResponsiveGrid()) 32.adjustedDp else 50.adjustedDp
@@ -2118,11 +2201,14 @@ class Dimensions(val context: Context, val config: Configuration) {
            get() = 360.adjustedDp

        val WidgetOutlinePadding
            get() = 8.adjustedDp
            get() = if (hubEditModeTransition()) 0.dp else 8.adjustedDp

        val Spacing
            get() = ItemSpacing / 2

        val ToolbarHeight
            get() = 40.dp

        // The sizing/padding of the toolbar in glanceable hub edit mode
        val ToolbarPaddingTop
            get() = if (communalResponsiveGrid()) 12.adjustedDp else 27.adjustedDp
@@ -2130,15 +2216,15 @@ class Dimensions(val context: Context, val config: Configuration) {
        val ToolbarPaddingHorizontal
            get() = ItemSpacing

        val ToolbarButtonPaddingHorizontal
        private val ToolbarButtonPaddingHorizontal
            get() = 24.adjustedDp

        val ToolbarButtonPaddingVertical
        private val ToolbarButtonPaddingVertical
            get() = 16.adjustedDp

        val ButtonPadding =
            PaddingValues(
                vertical = ToolbarButtonPaddingVertical,
                vertical = if (hubEditModeTransition()) 0.dp else ToolbarButtonPaddingVertical,
                horizontal = ToolbarButtonPaddingHorizontal,
            )
        val IconSize = 40.adjustedDp
+16 −5
Original line number Diff line number Diff line
@@ -48,11 +48,14 @@ import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastIsFinite
import androidx.compose.ui.zIndex
import com.android.compose.modifiers.thenIf
import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.Flags.hubEditModeTransition
import com.android.systemui.communal.ui.viewmodel.DragHandle
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
@@ -120,6 +123,7 @@ private fun UpdateGridLayoutInfo(
private fun BoxScope.DragHandle(
    handle: DragHandle,
    dragState: AnchoredDraggableState<Int>,
    radius: Dp,
    outlinePadding: Dp,
    brush: Brush,
    alpha: () -> Float,
@@ -141,7 +145,7 @@ private fun BoxScope.DragHandle(
            if (dragState.anchors.size > 1) {
                drawCircle(
                    brush = brush,
                    radius = outlinePadding.toPx(),
                    radius = radius.toPx(),
                    center = Offset(size.width / 2, size.height / 2),
                    alpha = alpha(),
                )
@@ -183,10 +187,13 @@ fun ResizableItemFrame(
    verticalArrangement: Arrangement.Vertical,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    outlinePadding: Dp = 8.dp,
    dragHandleRadius: Dp = 8.dp,
    outlinePadding: Dp = if (hubEditModeTransition()) 0.dp else 8.dp,
    outlineColor: Color = MaterialTheme.colorScheme.primary,
    cornerRadius: Dp = 37.dp,
    strokeWidth: Dp = 3.dp,
    cornerRadius: Dp =
        if (hubEditModeTransition()) dimensionResource(system_app_widget_background_radius)
        else 37.dp,
    strokeWidth: Dp = if (hubEditModeTransition()) 4.dp else 3.dp,
    minHeightPx: Int = 0,
    maxHeightPx: Int = Int.MAX_VALUE,
    resizeMultiple: Int = 1,
@@ -197,7 +204,9 @@ fun ResizableItemFrame(
) {
    val brush = SolidColor(outlineColor)
    val onResizeUpdated by rememberUpdatedState(onResize)
    val dragHandleHeight = verticalArrangement.spacing - outlinePadding * 2
    val dragHandleHeight =
        if (hubEditModeTransition()) dragHandleRadius * 2
        else verticalArrangement.spacing - outlinePadding * 2
    val isDragging by
        remember(viewModel) {
            derivedStateOf {
@@ -217,6 +226,7 @@ fun ResizableItemFrame(
            DragHandle(
                handle = DragHandle.TOP,
                dragState = viewModel.topDragState,
                radius = dragHandleRadius,
                outlinePadding = outlinePadding,
                brush = brush,
                alpha = alpha,
@@ -226,6 +236,7 @@ fun ResizableItemFrame(
            DragHandle(
                handle = DragHandle.BOTTOM,
                dragState = viewModel.bottomDragState,
                radius = dragHandleRadius,
                outlinePadding = outlinePadding,
                brush = brush,
                alpha = alpha,
+34 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.compose

import android.content.res.Configuration
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.OverscrollEffect
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
@@ -33,6 +34,7 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberOverscrollEffect
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -55,6 +57,9 @@ import com.android.systemui.communal.util.WindowSizeUtils.COMPACT_WIDTH
 * Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain
 * the specified aspect ratio, but is otherwise resizeable in order to best fill the available
 * space.
 *
 * @param cellAspectRatio The aspect ratio (width / height) for each cell. Use 0f for flexible
 *   aspect ratio, allowing cells to fill all available space.
 */
@Composable
fun ResponsiveLazyHorizontalGrid(
@@ -63,6 +68,7 @@ fun ResponsiveLazyHorizontalGrid(
    state: LazyGridState = rememberLazyGridState(),
    setContentOffset: (offset: Offset) -> Unit = {},
    minContentPadding: PaddingValues = PaddingValues(0.dp),
    animateContentPadding: Boolean = false,
    minHorizontalArrangement: Dp = 0.dp,
    minVerticalArrangement: Dp = 0.dp,
    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
@@ -70,7 +76,6 @@ fun ResponsiveLazyHorizontalGrid(
    overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(),
    content: LazyGridScope.(sizeInfo: SizeInfo) -> Unit,
) {
    check(cellAspectRatio > 0f) { "Aspect ratio must be greater than 0, but was $cellAspectRatio" }
    check(minHorizontalArrangement.value >= 0f && minVerticalArrangement.value >= 0f) {
        "Horizontal and vertical arrangements must be non-negative, but were " +
            "$minHorizontalArrangement and $minVerticalArrangement, respectively."
@@ -146,14 +151,31 @@ fun ResponsiveLazyHorizontalGrid(
        val finalStartPadding = minStartPadding + evenlyDistributedWidth
        val finalEndPadding = minEndPadding + evenlyDistributedWidth
        val finalTopPadding = minTopPadding + extraHeight / 2
        val finalBottomPadding = minBottomPadding + extraHeight / 2

        val finalContentPadding: PaddingValues
        if (animateContentPadding) {
            val finalStartPaddingAnimated by animateDpAsState(finalStartPadding)
            val finalTopPaddingAnimated by animateDpAsState(finalTopPadding)
            val finalEndPaddingAnimated by animateDpAsState(finalEndPadding)
            val finalBottomPaddingAnimated by animateDpAsState(finalBottomPadding)

        val finalContentPadding =
            finalContentPadding =
                PaddingValues(
                    start = finalStartPaddingAnimated,
                    top = finalTopPaddingAnimated,
                    end = finalEndPaddingAnimated,
                    bottom = finalBottomPaddingAnimated,
                )
        } else {
            finalContentPadding =
                PaddingValues(
                    start = finalStartPadding,
                end = finalEndPadding,
                    top = finalTopPadding,
                bottom = minBottomPadding + extraHeight / 2,
                    end = finalEndPadding,
                    bottom = finalBottomPadding,
                )
        }

        with(density) { setContentOffset(Offset(finalStartPadding.toPx(), finalTopPadding.toPx())) }

@@ -195,7 +217,10 @@ private fun calculateUsedSpace(cellSize: Dp, numCells: Int, padding: Dp, cellSpa
    cellSize * numCells + padding + (numCells - 1) * cellSpacing

private fun calculateClosestSize(maxWidth: Dp, maxHeight: Dp, aspectRatio: Float): DpSize {
    return if (maxWidth / maxHeight > aspectRatio) {
    return if (aspectRatio <= 0f) {
        // Flexible aspect ratio. Allow cell to fill max width and height.
        DpSize(maxWidth, maxHeight)
    } else if (maxWidth / maxHeight > aspectRatio) {
        // Target is too wide, shrink width
        DpSize(maxHeight * aspectRatio, maxHeight)
    } else {
+6 −0
Original line number Diff line number Diff line
@@ -106,6 +106,12 @@ abstract class BaseCommunalViewModel(
     */
    private var currentScrollIndex = 0

    /**
     * Whether to show edit mode layout, like pushing the widgets down to make space for the toolbar
     * on top.
     */
    abstract val shouldShowEditModeLayout: Flow<Boolean>

    fun signalUserInteraction() {
        communalInteractor.signalUserInteraction()
    }
Loading