diff options
author | 2025-02-10 13:10:06 -0800 | |
---|---|---|
committer | 2025-02-10 13:10:06 -0800 | |
commit | a015d7eeca96aae598e88d2e40284f860b1d6cf6 (patch) | |
tree | dcf12c66a84ebdf0eb1e96b7de2d0fca6b60cf18 | |
parent | 289ca076a2774e88e3e228f75a0f08ba38e63f24 (diff) | |
parent | 31f9b781897388834a7622b53d1c35c4aac73b8a (diff) |
Merge changes from topics "caitlinshk-chips-color", "caitlinshk-text-truncation" into main
* changes:
[SB][Notif] Hide text in chips if not *all* of the text fits.
[SB][Notif] Add a subtle outline to notification chips.
[SB][Notif] Update notif chips to use system theme colors.
14 files changed, 150 insertions, 216 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index dbe8f8226d43..c7b3175a636f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -31,7 +31,6 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -265,91 +264,25 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - fun chip_positiveStartTime_notPromoted_colorsAreThemed() = + fun chip_positiveStartTime_colorsAreAccentThemed() = testScope.runTest { val latest by collectLastValue(underTest.chip) repo.setOngoingCallState(inCallModel(startTimeMs = 1000, promotedContent = null)) assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo(ColorsModel.Themed) + .isEqualTo(ColorsModel.AccentThemed) } @Test - fun chip_zeroStartTime_notPromoted_colorsAreThemed() = + fun chip_zeroStartTime_colorsAreAccentThemed() = testScope.runTest { val latest by collectLastValue(underTest.chip) repo.setOngoingCallState(inCallModel(startTimeMs = 0, promotedContent = null)) assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo(ColorsModel.Themed) - } - - @Test - @DisableFlags(StatusBarNotifChips.FLAG_NAME) - fun chip_positiveStartTime_promoted_notifChipsFlagOff_colorsAreThemed() = - testScope.runTest { - val latest by collectLastValue(underTest.chip) - - repo.setOngoingCallState( - inCallModel(startTimeMs = 1000, promotedContent = PROMOTED_CONTENT_WITH_COLOR) - ) - - assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo(ColorsModel.Themed) - } - - @Test - @DisableFlags(StatusBarNotifChips.FLAG_NAME) - fun chip_zeroStartTime_promoted_notifChipsFlagOff_colorsAreThemed() = - testScope.runTest { - val latest by collectLastValue(underTest.chip) - - repo.setOngoingCallState( - inCallModel(startTimeMs = 0, promotedContent = PROMOTED_CONTENT_WITH_COLOR) - ) - - assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo(ColorsModel.Themed) - } - - @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun chip_positiveStartTime_promoted_notifChipsFlagOn_colorsAreCustom() = - testScope.runTest { - val latest by collectLastValue(underTest.chip) - - repo.setOngoingCallState( - inCallModel(startTimeMs = 1000, promotedContent = PROMOTED_CONTENT_WITH_COLOR) - ) - - assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo( - ColorsModel.Custom( - backgroundColorInt = PROMOTED_BACKGROUND_COLOR, - primaryTextColorInt = PROMOTED_PRIMARY_TEXT_COLOR, - ) - ) - } - - @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun chip_zeroStartTime_promoted_notifChipsFlagOff_colorsAreCustom() = - testScope.runTest { - val latest by collectLastValue(underTest.chip) - - repo.setOngoingCallState( - inCallModel(startTimeMs = 0, promotedContent = PROMOTED_CONTENT_WITH_COLOR) - ) - - assertThat((latest as OngoingActivityChipModel.Active).colors) - .isEqualTo( - ColorsModel.Custom( - backgroundColorInt = PROMOTED_BACKGROUND_COLOR, - primaryTextColorInt = PROMOTED_PRIMARY_TEXT_COLOR, - ) - ) + .isEqualTo(ColorsModel.AccentThemed) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index 192ad879891f..aaa9b58a45df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -186,7 +186,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) - fun chips_onePromotedNotif_colorMatches() = + fun chips_onePromotedNotif_colorIsSystemThemed() = kosmos.runTest { val latest by collectLastValue(underTest.chips) @@ -209,10 +209,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { ) assertThat(latest).hasSize(1) - val colors = latest!![0].colors - assertThat(colors).isInstanceOf(ColorsModel.Custom::class.java) - assertThat((colors as ColorsModel.Custom).backgroundColorInt).isEqualTo(56) - assertThat((colors as ColorsModel.Custom).primaryTextColorInt).isEqualTo(89) + assertThat(latest!![0].colors).isEqualTo(ColorsModel.SystemThemed) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt index d727089094f0..9ec5a42714bf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt @@ -52,24 +52,13 @@ class ChipTextTruncationHelperTest : SysuiTestCase() { } @Test - fun shouldShowText_desiredSlightlyLargerThanMax_true() { + fun shouldShowText_desiredMoreThanMax_false() { val result = underTest.shouldShowText( desiredTextWidthPx = (MAX_WIDTH * 1.1).toInt(), widthMeasureSpec = UNLIMITED_WIDTH_SPEC, ) - assertThat(result).isTrue() - } - - @Test - fun shouldShowText_desiredMoreThanTwiceMax_false() { - val result = - underTest.shouldShowText( - desiredTextWidthPx = (MAX_WIDTH * 2.2).toInt(), - widthMeasureSpec = UNLIMITED_WIDTH_SPEC, - ) - assertThat(result).isFalse() } @@ -80,8 +69,8 @@ class ChipTextTruncationHelperTest : SysuiTestCase() { View.MeasureSpec.makeMeasureSpec(MAX_WIDTH / 2, View.MeasureSpec.AT_MOST) ) - // WHEN desired is more than twice the smallerWidthSpec - val desiredWidth = (MAX_WIDTH * 1.1).toInt() + // WHEN desired is more than the smallerWidthSpec + val desiredWidth = ((MAX_WIDTH / 2) * 1.1).toInt() val result = underTest.shouldShowText( @@ -100,8 +89,8 @@ class ChipTextTruncationHelperTest : SysuiTestCase() { View.MeasureSpec.makeMeasureSpec(MAX_WIDTH * 3, View.MeasureSpec.AT_MOST) ) - // WHEN desired is more than twice the max - val desiredWidth = (MAX_WIDTH * 2.2).toInt() + // WHEN desired is more than the max + val desiredWidth = (MAX_WIDTH * 1.1).toInt() val result = underTest.shouldShowText( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt index 60030ad4e428..e3a84fd2c2eb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt @@ -54,7 +54,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { OngoingActivityChipModel.Active.Timer( key = KEY, icon = createIcon(R.drawable.ic_cake), - colors = ColorsModel.Themed, + colors = ColorsModel.AccentThemed, startTimeMs = 100L, onClickListenerLegacy = null, clickBehavior = OngoingActivityChipModel.ClickBehavior.None, @@ -68,7 +68,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { OngoingActivityChipModel.Active.IconOnly( key = KEY, icon = createIcon(R.drawable.ic_hotspot), - colors = ColorsModel.Themed, + colors = ColorsModel.AccentThemed, onClickListenerLegacy = null, clickBehavior = OngoingActivityChipModel.ClickBehavior.None, ) @@ -90,7 +90,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { OngoingActivityChipModel.Active.Timer( key = KEY, icon = createIcon(R.drawable.ic_cake), - colors = ColorsModel.Themed, + colors = ColorsModel.AccentThemed, startTimeMs = 100L, onClickListenerLegacy = null, clickBehavior = OngoingActivityChipModel.ClickBehavior.None, @@ -132,7 +132,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { OngoingActivityChipModel.Active.Timer( key = KEY, icon = createIcon(R.drawable.ic_cake), - colors = ColorsModel.Themed, + colors = ColorsModel.AccentThemed, startTimeMs = 100L, onClickListenerLegacy = null, clickBehavior = OngoingActivityChipModel.ClickBehavior.None, diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml b/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml index 6f42286d9fac..b66a88a3e523 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml @@ -43,9 +43,6 @@ ongoing_activity_chip_short_time_delta] will ever be shown at one time. --> <!-- Shows a timer, like 00:01. --> - <!-- Don't use the LimitedWidth style for the timer because the end of the timer is often - the most important value. ChipChronometer has the correct logic for when the timer is - too large for the space allowed. --> <com.android.systemui.statusbar.chips.ui.view.ChipChronometer android:id="@+id/ongoing_activity_chip_time" style="@style/StatusBar.Chip.Text" @@ -54,14 +51,14 @@ <!-- Shows generic text. --> <com.android.systemui.statusbar.chips.ui.view.ChipTextView android:id="@+id/ongoing_activity_chip_text" - style="@style/StatusBar.Chip.Text.LimitedWidth" + style="@style/StatusBar.Chip.Text" android:visibility="gone" /> <!-- Shows a time delta in short form, like "15min" or "1hr". --> <com.android.systemui.statusbar.chips.ui.view.ChipDateTimeView android:id="@+id/ongoing_activity_chip_short_time_delta" - style="@style/StatusBar.Chip.Text.LimitedWidth" + style="@style/StatusBar.Chip.Text" android:visibility="gone" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 2d3c07b93cb1..648e4c2e3ac7 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1811,6 +1811,7 @@ <dimen name="ongoing_activity_chip_text_end_padding_for_embedded_padding_icon">6dp</dimen> <dimen name="ongoing_activity_chip_text_fading_edge_length">12dp</dimen> <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen> + <dimen name="ongoing_activity_chip_outline_width">2px</dimen> <!-- Status bar user chip --> <dimen name="status_bar_user_chip_avatar_size">16dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7f2c89346423..4961a7ece69a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -93,15 +93,6 @@ <item name="android:textColor">?android:attr/colorPrimary</item> </style> - <!-- Style for a status bar chip text that has a maximum width. Since there's so little room in - the status bar chip area, don't ellipsize the text and instead just fade it out a bit at - the end. --> - <style name="StatusBar.Chip.Text.LimitedWidth"> - <item name="android:ellipsize">none</item> - <item name="android:requiresFadingEdge">horizontal</item> - <item name="android:fadingEdgeLength">@dimen/ongoing_activity_chip_text_fading_edge_length</item> - </style> - <style name="Chipbar" /> <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title"> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt index a2c0226addfa..f466278e15a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt @@ -32,9 +32,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel -import com.android.systemui.statusbar.chips.ui.model.ColorsModel.Companion.toCustomColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel @@ -86,12 +84,7 @@ constructor( OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon) } - val colors = - if (StatusBarNotifChips.isEnabled && state.promotedContent != null) { - state.promotedContent.toCustomColorsModel() - } else { - ColorsModel.Themed - } + val colors = ColorsModel.AccentThemed // This block mimics OngoingCallController#updateChip. if (state.startTimeMs <= 0L) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt index 8357df42937e..2d6102e310f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -27,7 +27,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips -import com.android.systemui.statusbar.chips.ui.model.ColorsModel.Companion.toCustomColorsModel +import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor @@ -85,8 +85,7 @@ constructor( contentDescription, ) } - val colors = this.promotedContent.toCustomColorsModel() - + val colors = ColorsModel.SystemThemed val clickListener: () -> Unit = { // The notification pipeline needs everything to run on the main thread, so keep // this event on the main thread. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt index 456cd121a540..d41353b2c176 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.chips.ui.binder import android.annotation.IdRes +import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.view.View @@ -32,6 +33,7 @@ import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer @@ -76,8 +78,10 @@ object OngoingActivityChipBinder { chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) chipShortTimeDeltaView.setTextColor(textColor) - (chipBackgroundView.background as GradientDrawable).color = - chipModel.colors.background(chipContext) + (chipBackgroundView.background as GradientDrawable).setBackgroundColors( + chipModel.colors, + chipContext, + ) } is OngoingActivityChipModel.Inactive -> { // The Chronometer should be stopped to prevent leaks -- see b/192243808 and @@ -460,5 +464,20 @@ object OngoingActivityChipBinder { chipView.minimumWidth = minimumWidth } + private fun GradientDrawable.setBackgroundColors(colors: ColorsModel, context: Context) { + this.color = colors.background(context) + val outline = colors.outline(context) + if (outline != null) { + this.setStroke( + context.resources.getDimensionPixelSize( + R.dimen.ongoing_activity_chip_outline_width + ), + outline, + ) + } else { + this.setStroke(0, /* color= */ 0) + } + } + @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt index 32de0fbfd870..8443d106dfb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt @@ -20,16 +20,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text 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.Modifier -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.graphics.graphicsLayer import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope @@ -37,6 +30,8 @@ import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.text.TextMeasurer +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp @@ -83,15 +78,14 @@ fun ChipContent(viewModel: OngoingActivityChipModel.Active, modifier: Modifier = softWrap = false, modifier = modifier - .customTextContentLayout( + .hideTextIfDoesNotFit( + text = text, + textStyle = textStyle, + textMeasurer = textMeasurer, maxTextWidth = maxTextWidth, startPadding = startPadding, endPadding = endPadding, - ) { constraintWidth -> - val intrinsicWidth = - textMeasurer.measure(text, textStyle, softWrap = false).size.width - intrinsicWidth <= constraintWidth - } + ) .neverDecreaseWidth(), ) } @@ -108,7 +102,6 @@ fun ChipContent(viewModel: OngoingActivityChipModel.Active, modifier: Modifier = } is OngoingActivityChipModel.Active.Text -> { - var hasOverflow by remember { mutableStateOf(false) } val text = viewModel.text Text( text = text, @@ -116,24 +109,14 @@ fun ChipContent(viewModel: OngoingActivityChipModel.Active, modifier: Modifier = style = textStyle, softWrap = false, modifier = - modifier - .customTextContentLayout( - maxTextWidth = maxTextWidth, - startPadding = startPadding, - endPadding = endPadding, - ) { constraintWidth -> - val intrinsicWidth = - textMeasurer.measure(text, textStyle, softWrap = false).size.width - hasOverflow = intrinsicWidth > constraintWidth - constraintWidth.toFloat() / intrinsicWidth.toFloat() > 0.5f - } - .overflowFadeOut( - hasOverflow = { hasOverflow }, - fadeLength = - dimensionResource( - id = R.dimen.ongoing_activity_chip_text_fading_edge_length - ), - ), + modifier.hideTextIfDoesNotFit( + text = text, + textStyle = textStyle, + textMeasurer = textMeasurer, + maxTextWidth = maxTextWidth, + startPadding = startPadding, + endPadding = endPadding, + ), ) } @@ -180,45 +163,67 @@ private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode { } /** - * A custom layout modifier for text that ensures its text is only visible if a provided - * [shouldShow] callback returns true. Imposes a provided [maxTextWidthPx]. Also, accounts for - * provided padding values if provided and ensures its text is placed with the provided padding - * included around it. + * A custom layout modifier for text that ensures the text is only visible if it completely fits + * within the constrained bounds. Imposes a provided [maxTextWidthPx]. Also, accounts for provided + * padding values if provided and ensures its text is placed with the provided padding included + * around it. */ -private fun Modifier.customTextContentLayout( +private fun Modifier.hideTextIfDoesNotFit( + text: String, + textStyle: TextStyle, + textMeasurer: TextMeasurer, maxTextWidth: Dp, startPadding: Dp = 0.dp, endPadding: Dp = 0.dp, - shouldShow: (constraintWidth: Int) -> Boolean, ): Modifier { return this.then( - CustomTextContentLayoutElement(maxTextWidth, startPadding, endPadding, shouldShow) + HideTextIfDoesNotFitElement( + text, + textStyle, + textMeasurer, + maxTextWidth, + startPadding, + endPadding, + ) ) } -private data class CustomTextContentLayoutElement( +private data class HideTextIfDoesNotFitElement( + val text: String, + val textStyle: TextStyle, + val textMeasurer: TextMeasurer, val maxTextWidth: Dp, val startPadding: Dp, val endPadding: Dp, - val shouldShow: (constrainedWidth: Int) -> Boolean, -) : ModifierNodeElement<CustomTextContentLayoutNode>() { - override fun create(): CustomTextContentLayoutNode { - return CustomTextContentLayoutNode(maxTextWidth, startPadding, endPadding, shouldShow) +) : ModifierNodeElement<HideTextIfDoesNotFitNode>() { + override fun create(): HideTextIfDoesNotFitNode { + return HideTextIfDoesNotFitNode( + text, + textStyle, + textMeasurer, + maxTextWidth, + startPadding, + endPadding, + ) } - override fun update(node: CustomTextContentLayoutNode) { - node.shouldShow = shouldShow + override fun update(node: HideTextIfDoesNotFitNode) { + node.text = text + node.textStyle = textStyle + node.textMeasurer = textMeasurer node.maxTextWidth = maxTextWidth node.startPadding = startPadding node.endPadding = endPadding } } -private class CustomTextContentLayoutNode( +private class HideTextIfDoesNotFitNode( + var text: String, + var textStyle: TextStyle, + var textMeasurer: TextMeasurer, var maxTextWidth: Dp, var startPadding: Dp, var endPadding: Dp, - var shouldShow: (constrainedWidth: Int) -> Boolean, ) : Modifier.Node(), LayoutModifierNode { override fun MeasureScope.measure( measurable: Measurable, @@ -230,9 +235,10 @@ private class CustomTextContentLayoutNode( .coerceAtLeast(constraints.minWidth) val placeable = measurable.measure(constraints.copy(maxWidth = maxWidth)) - val height = placeable.height - val width = placeable.width - return if (shouldShow(maxWidth)) { + val intrinsicWidth = textMeasurer.measure(text, textStyle, softWrap = false).size.width + return if (intrinsicWidth <= maxWidth) { + val height = placeable.height + val width = placeable.width layout(width + horizontalPadding.roundToPx(), height) { placeable.place(startPadding.roundToPx(), 0) } @@ -241,20 +247,3 @@ private class CustomTextContentLayoutNode( } } } - -private fun Modifier.overflowFadeOut(hasOverflow: () -> Boolean, fadeLength: Dp): Modifier { - return graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen).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) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt index 76c53861f0ab..1cdf6800fb97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.ui.compose import android.content.res.ColorStateList import android.view.ViewGroup import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -103,6 +104,13 @@ private fun ChipBody( } else { dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding } + + val outline = model.colors.outline(context) + val outlineWidth = dimensionResource(R.dimen.ongoing_activity_chip_outline_width) + + val shape = + RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)) + // 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. Box( @@ -121,12 +129,7 @@ private fun ChipBody( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, modifier = - Modifier.clip( - RoundedCornerShape( - dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius) - ) - ) - .height(dimensionResource(R.dimen.ongoing_appops_chip_height)) + Modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height)) .thenIf(isClickable) { Modifier.widthIn(min = minWidth) } .layout { measurable, constraints -> val placeable = measurable.measure(constraints) @@ -136,7 +139,14 @@ private fun ChipBody( } } } - .background(Color(model.colors.background(context).defaultColor)) + .background(Color(model.colors.background(context).defaultColor), shape = shape) + .thenIf(outline != null) { + Modifier.border( + width = outlineWidth, + color = Color(outline!!), + shape = shape, + ) + } .padding( horizontal = if (hasEmbeddedIcon) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt index 25f90f9a0065..4954cb0a1b24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt @@ -21,7 +21,6 @@ import android.content.res.ColorStateList import androidx.annotation.ColorInt import com.android.settingslib.Utils import com.android.systemui.res.R -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel /** Model representing how the chip in the status bar should be colored. */ sealed interface ColorsModel { @@ -31,13 +30,38 @@ sealed interface ColorsModel { /** The color for the text (and icon) on the chip. */ @ColorInt fun text(context: Context): Int - /** The chip should match the theme's primary color. */ - data object Themed : ColorsModel { + /** The color to use for the chip outline, or null if the chip shouldn't have an outline. */ + @ColorInt fun outline(context: Context): Int? + + /** The chip should match the theme's primary accent color. */ + // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it + // only gets updated when a different configuration change happens, like a rotation. + data object AccentThemed : ColorsModel { override fun background(context: Context): ColorStateList = Utils.getColorAttr(context, com.android.internal.R.attr.colorAccent) override fun text(context: Context) = Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + + override fun outline(context: Context) = null + } + + /** The chip should match the system theme main color. */ + // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it + // only gets updated when a different configuration change happens, like a rotation. + data object SystemThemed : ColorsModel { + override fun background(context: Context): ColorStateList = + ColorStateList.valueOf( + context.getColor(com.android.internal.R.color.materialColorSurfaceDim) + ) + + override fun text(context: Context) = + context.getColor(com.android.internal.R.color.materialColorOnSurface) + + override fun outline(context: Context) = + // Outline is required on the SystemThemed chip to guarantee the chip doesn't completely + // blend in with the background. + context.getColor(com.android.internal.R.color.materialColorOutlineVariant) } /** The chip should have the given background color and primary text color. */ @@ -46,6 +70,8 @@ sealed interface ColorsModel { ColorStateList.valueOf(backgroundColorInt) override fun text(context: Context): Int = primaryTextColorInt + + override fun outline(context: Context) = null } /** The chip should have a red background with white text. */ @@ -55,15 +81,7 @@ sealed interface ColorsModel { } override fun text(context: Context) = context.getColor(android.R.color.white) - } - companion object { - /** Converts the promoted notification colors to a [Custom] colors model. */ - fun PromotedNotificationContentModel.toCustomColorsModel(): Custom { - return Custom( - backgroundColorInt = this.colors.backgroundColor, - primaryTextColorInt = this.colors.primaryTextColor, - ) - } + override fun outline(context: Context) = null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt index 52495eb55436..c19b144b7f42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt @@ -51,9 +51,8 @@ class ChipTextTruncationHelper(private val view: View) { } /** - * Returns true if this view should show the text because there's enough room for a substantial - * amount of text, and returns false if this view should hide the text because the text is much - * too long. + * Returns true if this view should show the text because there's enough room for all the text, + * and returns false if this view should hide the text because not all of it fits. * * @param desiredTextWidthPx should be calculated by having the view measure itself with * [unlimitedWidthMeasureSpec] and then sending its `measuredWidth` to this method. (This @@ -82,9 +81,8 @@ class ChipTextTruncationHelper(private val view: View) { enforcedTextWidth = maxWidthBasedOnDimension } - // Only show the text if at least 50% of it can show. (Assume that if < 50% of the text will - // be visible, the text will be more confusing than helpful.) - return desiredTextWidthPx <= enforcedTextWidth * 2 + // Only show the text if all of it can show + return desiredTextWidthPx <= enforcedTextWidth } private fun fetchMaxWidth() = |