diff options
| author | 2025-02-21 06:53:38 -0800 | |
|---|---|---|
| committer | 2025-02-21 06:53:38 -0800 | |
| commit | c1794771c418f0ac540b08f7d2a5054aa739763c (patch) | |
| tree | f310badb7e5c079a2fce2e5de2f66fee691bc400 | |
| parent | 94fba95e67330d585ce04fc7622bce313fcf0b21 (diff) | |
| parent | 8df269a3da6750de78f4dab649f5e960dd621a38 (diff) | |
Merge "[ROSP] Update StatusBarPopupChip to align with UX specs" into main
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt | 140 |
1 files changed, 112 insertions, 28 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt index eb85d2f32f29..c77decda3622 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt @@ -16,33 +16,49 @@ package com.android.systemui.statusbar.featurepods.popups.ui.compose -import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.hoverable +import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.layout.layout +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.modifiers.thenIf import com.android.systemui.common.ui.compose.Icon +import com.android.systemui.res.R import com.android.systemui.statusbar.featurepods.popups.shared.model.HoverBehavior import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel @@ -54,31 +70,48 @@ import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipM @Composable fun StatusBarPopupChip(viewModel: PopupChipModel.Shown, modifier: Modifier = Modifier) { val hasHoverBehavior = viewModel.hoverBehavior !is HoverBehavior.None - val hoverInteractionSource = remember { MutableInteractionSource() } - val isHovered by hoverInteractionSource.collectIsHoveredAsState() + val interactionSource = remember { MutableInteractionSource() } + val hoveredState by interactionSource.collectIsHoveredAsState() + val isHovered = hasHoverBehavior && hoveredState val isPopupShown = viewModel.isPopupShown - + val indication = if (hoveredState) null else LocalIndication.current + val chipShape = + RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)) val chipBackgroundColor = if (isPopupShown) { - MaterialTheme.colorScheme.primaryContainer + MaterialTheme.colorScheme.primary } else { - MaterialTheme.colorScheme.surfaceContainerHighest + MaterialTheme.colorScheme.surfaceDim } - Surface( - shape = RoundedCornerShape(16.dp), + + // Use a Box with `fillMaxHeight` to create a larger click surface for the chip. The visible + // height of the chip is determined by the height of the background of the Row below. The + // `indication` for Clicks is applied in the Row below as well. + Box( + contentAlignment = Alignment.Center, modifier = - modifier - .widthIn(max = 120.dp) - .padding(vertical = 4.dp) - .animateContentSize() - .thenIf(hasHoverBehavior) { Modifier.hoverable(hoverInteractionSource) } - .thenIf(!isPopupShown) { Modifier.clickable { viewModel.showPopup() } }, - color = chipBackgroundColor, + modifier.minimumInteractiveComponentSize().thenIf(!isPopupShown) { + Modifier.clickable( + onClick = { viewModel.showPopup() }, + indication = null, + interactionSource = interactionSource, + ) + }, ) { Row( - modifier = Modifier.padding(start = 4.dp, end = 8.dp), - verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = + Modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height)) + .clip(chipShape) + .background(chipBackgroundColor) + .border( + width = dimensionResource(id = R.dimen.ongoing_activity_chip_outline_width), + color = MaterialTheme.colorScheme.outlineVariant, + shape = chipShape, + ) + .indication(interactionSource, indication) + .padding(start = 4.dp, end = 8.dp), ) { val iconColor = if (isHovered) chipBackgroundColor else contentColorFor(chipBackgroundColor) @@ -92,9 +125,11 @@ fun StatusBarPopupChip(viewModel: PopupChipModel.Shown, modifier: Modifier = Mod else -> viewModel.icon }, modifier = - Modifier.thenIf(isHovered) { - Modifier.padding(3.dp) - .background(color = iconBackgroundColor, shape = CircleShape) + Modifier.height(20.dp) + .width(20.dp) + .thenIf(isHovered) { + Modifier.background(color = iconBackgroundColor, shape = CircleShape) + .padding(2.dp) } .thenIf(hoverBehavior is HoverBehavior.Button) { Modifier.clickable( @@ -102,18 +137,67 @@ fun StatusBarPopupChip(viewModel: PopupChipModel.Shown, modifier: Modifier = Mod onClick = (hoverBehavior as HoverBehavior.Button).onIconPressed, indication = ripple(), interactionSource = iconInteractionSource, + enabled = isHovered, ) - } - .padding(3.dp), + }, tint = iconColor, ) + val text = viewModel.chipText + val textStyle = MaterialTheme.typography.labelLarge + val textMeasurer = rememberTextMeasurer() + var textOverflow by remember { mutableStateOf(false) } + Text( - text = viewModel.chipText, - style = MaterialTheme.typography.labelLarge, + text = text, + style = textStyle, softWrap = false, - overflow = TextOverflow.Ellipsis, + modifier = + Modifier.widthIn( + max = + dimensionResource(id = R.dimen.ongoing_activity_chip_max_text_width) + ) + .layout { measurables, constraints -> + val placeable = measurables.measure(constraints) + val intrinsicWidth = + textMeasurer.measure(text, textStyle, softWrap = false).size.width + textOverflow = intrinsicWidth > constraints.maxWidth + + layout(placeable.width, placeable.height) { + if (textOverflow) { + placeable.placeWithLayer(0, 0) { + compositingStrategy = CompositingStrategy.Offscreen + } + } else { + placeable.place(0, 0) + } + } + } + .overflowFadeOut( + hasOverflow = { textOverflow }, + fadeLength = + dimensionResource( + id = R.dimen.ongoing_activity_chip_text_fading_edge_length + ), + ), + ) + } + } +} + +private fun Modifier.overflowFadeOut(hasOverflow: () -> Boolean, fadeLength: Dp): Modifier { + return drawWithCache { + val width = size.width + val start = (width - fadeLength.toPx()).coerceAtLeast(0f) + val gradient = + Brush.horizontalGradient( + colors = listOf(Color.Black, Color.Transparent), + startX = start, + endX = width, ) + onDrawWithContent { + drawContent() + if (hasOverflow()) drawRect(brush = gradient, blendMode = BlendMode.DstIn) } } } |