summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ahmed Mehfooz <amehfooz@google.com> 2025-02-21 06:53:38 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-21 06:53:38 -0800
commitc1794771c418f0ac540b08f7d2a5054aa739763c (patch)
treef310badb7e5c079a2fce2e5de2f66fee691bc400
parent94fba95e67330d585ce04fc7622bce313fcf0b21 (diff)
parent8df269a3da6750de78f4dab649f5e960dd621a38 (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.kt140
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)
}
}
}