summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-12-10 21:45:52 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-10 21:45:52 +0000
commiteb11444200d02d1039d12bf9c7682c2554777599 (patch)
tree12b2f0ae4be66bb29632382385f17ba8400795b6
parent799131ca8af087a2a8fa52730e82024d7eff0729 (diff)
parent545ff2868a7cc10e671887c560ad2a316c90ce74 (diff)
Merge "Update responsive grid layout + alpha animation" into main
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt57
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt52
2 files changed, 98 insertions, 11 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index a55b6d720dd6..15edd0b05bfc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -151,6 +151,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
@@ -947,12 +948,28 @@ private fun BoxScope.CommunalHubLazyGrid(
}
}
} else {
+ val itemAlpha =
+ if (communalResponsiveGrid()) {
+ val percentVisible by
+ remember(gridState, index) {
+ derivedStateOf { calculatePercentVisible(gridState, index) }
+ }
+ animateFloatAsState(percentVisible)
+ } else {
+ null
+ }
+
CommunalContent(
model = item,
viewModel = viewModel,
size = size,
selected = false,
- modifier = Modifier.requiredSize(dpSize).animateItem(),
+ modifier =
+ Modifier.requiredSize(dpSize).animateItem().thenIf(
+ communalResponsiveGrid()
+ ) {
+ Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+ },
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
@@ -1856,6 +1873,44 @@ private fun CommunalContentModel.getSpanOrMax(maxSpan: Int?) =
size.span
}
+private fun IntRect.percentOverlap(other: IntRect): Float {
+ val intersection = intersect(other)
+ if (intersection.width < 0 || intersection.height < 0) {
+ return 0f
+ }
+ val overlapArea = intersection.width * intersection.height
+ val area = width * height
+ return overlapArea.toFloat() / area.toFloat()
+}
+
+private fun calculatePercentVisible(state: LazyGridState, index: Int): Float {
+ val viewportSize = state.layoutInfo.viewportSize
+ val visibleRect =
+ IntRect(
+ offset =
+ IntOffset(
+ state.layoutInfo.viewportStartOffset + state.layoutInfo.beforeContentPadding,
+ 0,
+ ),
+ size =
+ IntSize(
+ width =
+ viewportSize.width -
+ state.layoutInfo.beforeContentPadding -
+ state.layoutInfo.afterContentPadding,
+ height = viewportSize.height,
+ ),
+ )
+
+ val itemInfo = state.layoutInfo.visibleItemsInfo.find { it.index == index }
+ return if (itemInfo != null) {
+ val boundingBox = IntRect(itemInfo.offset, itemInfo.size)
+ boundingBox.percentOverlap(visibleRect)
+ } else {
+ 0f
+ }
+}
+
private object Colors {
val DisabledColorFilter by lazy { disabledColorMatrix() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index 21b34748a364..c7930549abe8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -36,7 +36,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.toComposeRect
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.unit.Dp
@@ -45,6 +47,7 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.coerceAtMost
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
+import androidx.window.layout.WindowMetricsCalculator
/**
* Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain
@@ -71,7 +74,7 @@ fun ResponsiveLazyHorizontalGrid(
"$minHorizontalArrangement and $minVerticalArrangement, respectively."
}
BoxWithConstraints(modifier) {
- val gridSize = rememberGridSize(maxWidth = maxWidth, maxHeight = maxHeight)
+ val gridSize = rememberGridSize()
val layoutDirection = LocalLayoutDirection.current
val density = LocalDensity.current
@@ -128,25 +131,43 @@ fun ResponsiveLazyHorizontalGrid(
val extraWidth = maxWidth - usedWidth
val extraHeight = maxHeight - usedHeight
- val finalStartPadding = minStartPadding + extraWidth / 2
+ // If there is a single column or single row, distribute extra space evenly across the grid.
+ // Otherwise, distribute it along the content padding to center the content.
+ val distributeHorizontalSpaceAlongGutters = gridSize.height == 1 || gridSize.width == 1
+ val evenlyDistributedWidth =
+ if (distributeHorizontalSpaceAlongGutters) {
+ extraWidth / (gridSize.width + 1)
+ } else {
+ extraWidth / 2
+ }
+
+ val finalStartPadding = minStartPadding + evenlyDistributedWidth
+ val finalEndPadding = minEndPadding + evenlyDistributedWidth
val finalTopPadding = minTopPadding + extraHeight / 2
val finalContentPadding =
PaddingValues(
start = finalStartPadding,
- end = minEndPadding + extraWidth / 2,
+ end = finalEndPadding,
top = finalTopPadding,
bottom = minBottomPadding + extraHeight / 2,
)
with(density) { setContentOffset(Offset(finalStartPadding.toPx(), finalTopPadding.toPx())) }
+ val horizontalArrangement =
+ if (distributeHorizontalSpaceAlongGutters) {
+ minHorizontalArrangement + evenlyDistributedWidth
+ } else {
+ minHorizontalArrangement
+ }
+
LazyHorizontalGrid(
rows = GridCells.Fixed(gridSize.height),
modifier = Modifier.fillMaxSize(),
state = state,
contentPadding = finalContentPadding,
- horizontalArrangement = Arrangement.spacedBy(minHorizontalArrangement),
+ horizontalArrangement = Arrangement.spacedBy(horizontalArrangement),
verticalArrangement = Arrangement.spacedBy(minVerticalArrangement),
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
@@ -210,27 +231,38 @@ data class SizeInfo(
}
@Composable
-private fun rememberGridSize(maxWidth: Dp, maxHeight: Dp): IntSize {
+private fun rememberGridSize(): IntSize {
val configuration = LocalConfiguration.current
val orientation = configuration.orientation
+ val screenSize = calculateWindowSize()
- return remember(orientation, maxWidth, maxHeight) {
+ return remember(orientation, screenSize) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
IntSize(
- width = calculateNumCellsWidth(maxWidth),
- height = calculateNumCellsHeight(maxHeight),
+ width = calculateNumCellsWidth(screenSize.width),
+ height = calculateNumCellsHeight(screenSize.height),
)
} else {
// In landscape we invert the rows/columns to ensure we match the same area as portrait.
// This keeps the number of elements in the grid consistent when changing orientation.
IntSize(
- width = calculateNumCellsHeight(maxWidth),
- height = calculateNumCellsWidth(maxHeight),
+ width = calculateNumCellsHeight(screenSize.width),
+ height = calculateNumCellsWidth(screenSize.height),
)
}
}
}
+@Composable
+fun calculateWindowSize(): DpSize {
+ // Observe view configuration changes and recalculate the size class on each change.
+ LocalConfiguration.current
+ val density = LocalDensity.current
+ val context = LocalContext.current
+ val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+ return with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
+}
+
private fun calculateNumCellsWidth(width: Dp) =
// See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
when {