Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.shade.ui.composable.isFullWidthShade import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import dagger.Lazy Loading Loading @@ -115,6 +116,8 @@ constructor( } OverlayShadeHeader( viewModel = headerViewModel, notificationsHighlight = ShadeHeader.ChipHighlight.Strong, quickSettingsHighlight = ShadeHeader.ChipHighlight.Weak, showClock = !isFullWidth, modifier = Modifier.element(NotificationsShade.Elements.StatusBar), ) Loading packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +3 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.QuickSettingsOverlayHeader import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.shade.ui.composable.isFullWidthShade import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape Loading Loading @@ -152,6 +153,8 @@ constructor( header = { OverlayShadeHeader( viewModel = quickSettingsContainerViewModel.shadeHeaderViewModel, notificationsHighlight = ShadeHeader.ChipHighlight.Weak, quickSettingsHighlight = ShadeHeader.ChipHighlight.Strong, showClock = true, modifier = Modifier.element(QuickSettingsShade.Elements.StatusBar), ) Loading packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +46 −21 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember Loading Loading @@ -99,7 +100,6 @@ import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddi import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingVertical import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer Loading Loading @@ -138,13 +138,37 @@ object ShadeHeader { val ChipPaddingVertical = 4.dp val StatusBarHeight: Dp @Composable get() = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() @Composable get() = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() } object TestTags { const val Root = "shade_header_root" } /** Represents the background highlighting of a header icons chip. */ sealed interface ChipHighlight { val backgroundColor: Color @Composable @ReadOnlyComposable get val foregroundColor: Color @Composable @ReadOnlyComposable get data object Weak : ChipHighlight { override val backgroundColor: Color @Composable get() = MaterialTheme.colorScheme.surface.copy(alpha = 0.1f) override val foregroundColor: Color @Composable get() = MaterialTheme.colorScheme.onSurface } data object Strong : ChipHighlight { override val backgroundColor: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer override val foregroundColor: Color @Composable get() = MaterialTheme.colorScheme.onPrimaryContainer } } } /** The status bar that appears above the Shade scene on small screens */ Loading Loading @@ -309,6 +333,8 @@ fun ContentScope.ExpandedShadeHeader( @Composable fun ContentScope.OverlayShadeHeader( viewModel: ShadeHeaderViewModel, notificationsHighlight: ShadeHeader.ChipHighlight, quickSettingsHighlight: ShadeHeader.ChipHighlight, showClock: Boolean, modifier: Modifier = Modifier, ) { Loading @@ -326,7 +352,6 @@ fun ContentScope.OverlayShadeHeader( horizontalArrangement = Arrangement.spacedBy(5.dp), modifier = Modifier.padding(horizontal = horizontalPadding), ) { val chipHighlight = viewModel.notificationsChipHighlight if (showClock) { Clock( onClick = viewModel::onClockClicked, Loading @@ -335,7 +360,7 @@ fun ContentScope.OverlayShadeHeader( } NotificationsChip( onClick = viewModel::onNotificationIconChipClicked, backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme), backgroundColor = notificationsHighlight.backgroundColor, modifier = Modifier.bouncy( isEnabled = viewModel.animateNotificationsChipBounce, Loading @@ -350,7 +375,7 @@ fun ContentScope.OverlayShadeHeader( VariableDayDate( longerDateText = viewModel.longerDateText, shorterDateText = viewModel.shorterDateText, textColor = chipHighlight.foregroundColor(MaterialTheme.colorScheme), textColor = notificationsHighlight.foregroundColor, ) } } Loading @@ -361,9 +386,8 @@ fun ContentScope.OverlayShadeHeader( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = horizontalPadding), ) { val chipHighlight = viewModel.quickSettingsChipHighlight SystemIconChip( backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme), backgroundColor = quickSettingsHighlight.backgroundColor, onClick = viewModel::onSystemIconChipClicked, modifier = Modifier.bouncy( Loading @@ -380,16 +404,18 @@ fun ContentScope.OverlayShadeHeader( with(LocalDensity.current) { (if (NewStatusBarIcons.isEnabled) 3.sp else 6.sp).toDp() } val isHighlighted = quickSettingsHighlight is ShadeHeader.ChipHighlight.Strong StatusIcons( viewModel = viewModel, useExpandedFormat = false, modifier = Modifier.padding(end = paddingEnd).weight(1f, fill = false), isHighlighted = isHighlighted, ) BatteryIcon( createBatteryMeterViewController = viewModel.createBatteryMeterViewController, useExpandedFormat = false, chipHighlight = chipHighlight, isHighlighted = isHighlighted, ) } if (isPrivacyChipVisible) { Loading Loading @@ -523,7 +549,7 @@ private fun BatteryIcon( createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController, useExpandedFormat: Boolean, modifier: Modifier = Modifier, chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None, isHighlighted: Boolean = false, ) { val localContext = LocalContext.current val themedContext = Loading Loading @@ -557,11 +583,11 @@ private fun BatteryIcon( if (useExpandedFormat) BatteryMeterView.MODE_ESTIMATE else BatteryMeterView.MODE_ON ) // TODO(b/397223606): Get the actual spec for this. if (chipHighlight is HeaderChipHighlight.Strong) { batteryIcon.updateColors(primaryColor, inverseColor, inverseColor) } else if (chipHighlight is HeaderChipHighlight.Weak) { batteryIcon.updateColors(primaryColor, inverseColor, primaryColor) } batteryIcon.updateColors( primaryColor, inverseColor, if (isHighlighted) inverseColor else primaryColor, ) }, modifier = modifier, ) Loading Loading @@ -642,6 +668,7 @@ private fun ContentScope.StatusIcons( viewModel: ShadeHeaderViewModel, useExpandedFormat: Boolean, modifier: Modifier = Modifier, isHighlighted: Boolean = false, ) { val localContext = LocalContext.current val themedContext = Loading Loading @@ -672,11 +699,9 @@ private fun ContentScope.StatusIcons( viewModel.createTintedIconManager(iconContainer, StatusBarLocation.QS) } val chipHighlight = viewModel.quickSettingsChipHighlight // TODO(408001821): Use composable system status icons here instead. AndroidView( factory = { context -> factory = { iconManager.setTint(primaryColor, inverseColor) viewModel.statusBarIconController.addIconGroup(iconManager) Loading Loading @@ -712,9 +737,9 @@ private fun ContentScope.StatusIcons( } // TODO(b/397223606): Get the actual spec for this. if (chipHighlight is HeaderChipHighlight.Strong) { if (isHighlighted) { iconManager.setTint(inverseColor, primaryColor) } else if (chipHighlight is HeaderChipHighlight.Weak) { } else { iconManager.setTint(primaryColor, inverseColor) } }, Loading @@ -726,7 +751,7 @@ private fun ContentScope.StatusIcons( private fun NotificationsChip( onClick: () -> Unit, modifier: Modifier = Modifier, backgroundColor: Color = Color.Unspecified, backgroundColor: Color, content: @Composable BoxScope.() -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +0 −88 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn Loading @@ -25,11 +24,8 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.enableSingleShade import com.android.systemui.shade.domain.interactor.enableSplitShade import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor import com.android.systemui.testKosmos Loading Loading @@ -248,90 +244,6 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) } @Test fun highlightChips_notifsOpenInSingleShade_bothNone() = testScope.runTest { kosmos.enableSingleShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.Shade) assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_notifsOpenInSplitShade_bothNone() = testScope.runTest { kosmos.enableSplitShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.Shade) assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_quickSettingsOpenInSingleShade_bothNone() = testScope.runTest { kosmos.enableSingleShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.QuickSettings) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_notifsOpenInDualShade_notifsStrongQuickSettingsWeak() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.NotificationsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone, overlay = Overlays.NotificationsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) } @Test fun highlightChips_quickSettingsOpenInDualShade_notifsWeakQuickSettingsStrong() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.QuickSettingsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone, overlay = Overlays.QuickSettingsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) } @Test fun highlightChips_noOverlaysInDualShade_bothNone() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } companion object { private val SUB_1 = SubscriptionModel( Loading packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +3 −62 Original line number Diff line number Diff line Loading @@ -24,9 +24,7 @@ import android.icu.text.DateFormat import android.icu.text.DisplayContext import android.provider.Settings import android.view.ViewGroup import androidx.compose.material3.ColorScheme import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.IntRect import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.battery.BatteryMeterViewController Loading Loading @@ -98,34 +96,6 @@ constructor( (ViewGroup, StatusBarLocation) -> BatteryMeterViewController = batteryMeterViewControllerFactory::create val notificationsChipHighlight: HeaderChipHighlight by hydrator.hydratedStateOf( traceName = "notificationsChipHighlight", initialValue = HeaderChipHighlight.None, source = sceneInteractor.currentOverlays.map { overlays -> when { Overlays.NotificationsShade in overlays -> HeaderChipHighlight.Strong Overlays.QuickSettingsShade in overlays -> HeaderChipHighlight.Weak else -> HeaderChipHighlight.None } }, ) val quickSettingsChipHighlight: HeaderChipHighlight by hydrator.hydratedStateOf( traceName = "quickSettingsChipHighlight", initialValue = HeaderChipHighlight.None, source = sceneInteractor.currentOverlays.map { overlays -> when { Overlays.QuickSettingsShade in overlays -> HeaderChipHighlight.Strong Overlays.NotificationsShade in overlays -> HeaderChipHighlight.Weak else -> HeaderChipHighlight.None } }, ) /** True if there is exactly one mobile connection. */ val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier Loading Loading @@ -264,39 +234,10 @@ constructor( dualShadeEducationInteractor.onDualShadeEducationElementBoundsChange(element, bounds) } /** Represents the background highlight of a header icons chip. */ sealed interface HeaderChipHighlight { fun backgroundColor(colorScheme: ColorScheme): Color fun foregroundColor(colorScheme: ColorScheme): Color data object None : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = Color.Unspecified override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary } data object Weak : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.surface.copy(alpha = 0.1f) override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSurface } data object Strong : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.primaryContainer override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onPrimaryContainer } } private fun getFormatFromPattern(pattern: String?): DateFormat { val format = DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()) format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE) return format return DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()).apply { setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE) } } @AssistedFactory Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.shade.ui.composable.isFullWidthShade import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import dagger.Lazy Loading Loading @@ -115,6 +116,8 @@ constructor( } OverlayShadeHeader( viewModel = headerViewModel, notificationsHighlight = ShadeHeader.ChipHighlight.Strong, quickSettingsHighlight = ShadeHeader.ChipHighlight.Weak, showClock = !isFullWidth, modifier = Modifier.element(NotificationsShade.Elements.StatusBar), ) Loading
packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +3 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.QuickSettingsOverlayHeader import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.shade.ui.composable.isFullWidthShade import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape Loading Loading @@ -152,6 +153,8 @@ constructor( header = { OverlayShadeHeader( viewModel = quickSettingsContainerViewModel.shadeHeaderViewModel, notificationsHighlight = ShadeHeader.ChipHighlight.Weak, quickSettingsHighlight = ShadeHeader.ChipHighlight.Strong, showClock = true, modifier = Modifier.element(QuickSettingsShade.Elements.StatusBar), ) Loading
packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +46 −21 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember Loading Loading @@ -99,7 +100,6 @@ import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddi import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingVertical import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer Loading Loading @@ -138,13 +138,37 @@ object ShadeHeader { val ChipPaddingVertical = 4.dp val StatusBarHeight: Dp @Composable get() = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() @Composable get() = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() } object TestTags { const val Root = "shade_header_root" } /** Represents the background highlighting of a header icons chip. */ sealed interface ChipHighlight { val backgroundColor: Color @Composable @ReadOnlyComposable get val foregroundColor: Color @Composable @ReadOnlyComposable get data object Weak : ChipHighlight { override val backgroundColor: Color @Composable get() = MaterialTheme.colorScheme.surface.copy(alpha = 0.1f) override val foregroundColor: Color @Composable get() = MaterialTheme.colorScheme.onSurface } data object Strong : ChipHighlight { override val backgroundColor: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer override val foregroundColor: Color @Composable get() = MaterialTheme.colorScheme.onPrimaryContainer } } } /** The status bar that appears above the Shade scene on small screens */ Loading Loading @@ -309,6 +333,8 @@ fun ContentScope.ExpandedShadeHeader( @Composable fun ContentScope.OverlayShadeHeader( viewModel: ShadeHeaderViewModel, notificationsHighlight: ShadeHeader.ChipHighlight, quickSettingsHighlight: ShadeHeader.ChipHighlight, showClock: Boolean, modifier: Modifier = Modifier, ) { Loading @@ -326,7 +352,6 @@ fun ContentScope.OverlayShadeHeader( horizontalArrangement = Arrangement.spacedBy(5.dp), modifier = Modifier.padding(horizontal = horizontalPadding), ) { val chipHighlight = viewModel.notificationsChipHighlight if (showClock) { Clock( onClick = viewModel::onClockClicked, Loading @@ -335,7 +360,7 @@ fun ContentScope.OverlayShadeHeader( } NotificationsChip( onClick = viewModel::onNotificationIconChipClicked, backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme), backgroundColor = notificationsHighlight.backgroundColor, modifier = Modifier.bouncy( isEnabled = viewModel.animateNotificationsChipBounce, Loading @@ -350,7 +375,7 @@ fun ContentScope.OverlayShadeHeader( VariableDayDate( longerDateText = viewModel.longerDateText, shorterDateText = viewModel.shorterDateText, textColor = chipHighlight.foregroundColor(MaterialTheme.colorScheme), textColor = notificationsHighlight.foregroundColor, ) } } Loading @@ -361,9 +386,8 @@ fun ContentScope.OverlayShadeHeader( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = horizontalPadding), ) { val chipHighlight = viewModel.quickSettingsChipHighlight SystemIconChip( backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme), backgroundColor = quickSettingsHighlight.backgroundColor, onClick = viewModel::onSystemIconChipClicked, modifier = Modifier.bouncy( Loading @@ -380,16 +404,18 @@ fun ContentScope.OverlayShadeHeader( with(LocalDensity.current) { (if (NewStatusBarIcons.isEnabled) 3.sp else 6.sp).toDp() } val isHighlighted = quickSettingsHighlight is ShadeHeader.ChipHighlight.Strong StatusIcons( viewModel = viewModel, useExpandedFormat = false, modifier = Modifier.padding(end = paddingEnd).weight(1f, fill = false), isHighlighted = isHighlighted, ) BatteryIcon( createBatteryMeterViewController = viewModel.createBatteryMeterViewController, useExpandedFormat = false, chipHighlight = chipHighlight, isHighlighted = isHighlighted, ) } if (isPrivacyChipVisible) { Loading Loading @@ -523,7 +549,7 @@ private fun BatteryIcon( createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController, useExpandedFormat: Boolean, modifier: Modifier = Modifier, chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None, isHighlighted: Boolean = false, ) { val localContext = LocalContext.current val themedContext = Loading Loading @@ -557,11 +583,11 @@ private fun BatteryIcon( if (useExpandedFormat) BatteryMeterView.MODE_ESTIMATE else BatteryMeterView.MODE_ON ) // TODO(b/397223606): Get the actual spec for this. if (chipHighlight is HeaderChipHighlight.Strong) { batteryIcon.updateColors(primaryColor, inverseColor, inverseColor) } else if (chipHighlight is HeaderChipHighlight.Weak) { batteryIcon.updateColors(primaryColor, inverseColor, primaryColor) } batteryIcon.updateColors( primaryColor, inverseColor, if (isHighlighted) inverseColor else primaryColor, ) }, modifier = modifier, ) Loading Loading @@ -642,6 +668,7 @@ private fun ContentScope.StatusIcons( viewModel: ShadeHeaderViewModel, useExpandedFormat: Boolean, modifier: Modifier = Modifier, isHighlighted: Boolean = false, ) { val localContext = LocalContext.current val themedContext = Loading Loading @@ -672,11 +699,9 @@ private fun ContentScope.StatusIcons( viewModel.createTintedIconManager(iconContainer, StatusBarLocation.QS) } val chipHighlight = viewModel.quickSettingsChipHighlight // TODO(408001821): Use composable system status icons here instead. AndroidView( factory = { context -> factory = { iconManager.setTint(primaryColor, inverseColor) viewModel.statusBarIconController.addIconGroup(iconManager) Loading Loading @@ -712,9 +737,9 @@ private fun ContentScope.StatusIcons( } // TODO(b/397223606): Get the actual spec for this. if (chipHighlight is HeaderChipHighlight.Strong) { if (isHighlighted) { iconManager.setTint(inverseColor, primaryColor) } else if (chipHighlight is HeaderChipHighlight.Weak) { } else { iconManager.setTint(primaryColor, inverseColor) } }, Loading @@ -726,7 +751,7 @@ private fun ContentScope.StatusIcons( private fun NotificationsChip( onClick: () -> Unit, modifier: Modifier = Modifier, backgroundColor: Color = Color.Unspecified, backgroundColor: Color, content: @Composable BoxScope.() -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +0 −88 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn Loading @@ -25,11 +24,8 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.enableSingleShade import com.android.systemui.shade.domain.interactor.enableSplitShade import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor import com.android.systemui.testKosmos Loading Loading @@ -248,90 +244,6 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) } @Test fun highlightChips_notifsOpenInSingleShade_bothNone() = testScope.runTest { kosmos.enableSingleShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.Shade) assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_notifsOpenInSplitShade_bothNone() = testScope.runTest { kosmos.enableSplitShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.Shade) assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_quickSettingsOpenInSingleShade_bothNone() = testScope.runTest { kosmos.enableSingleShade() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setScene(Scenes.QuickSettings) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(currentOverlays).isEmpty() assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } @Test fun highlightChips_notifsOpenInDualShade_notifsStrongQuickSettingsWeak() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.NotificationsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone, overlay = Overlays.NotificationsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) } @Test fun highlightChips_quickSettingsOpenInDualShade_notifsWeakQuickSettingsStrong() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.QuickSettingsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone, overlay = Overlays.QuickSettingsShade) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.Weak) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.Strong) } @Test fun highlightChips_noOverlaysInDualShade_bothNone() = testScope.runTest { // Test the lockscreen scenario. setupDualShadeState(scene = Scenes.Lockscreen) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) // Test the unlocked scenario. setupDualShadeState(scene = Scenes.Gone) assertThat(underTest.notificationsChipHighlight).isEqualTo(HeaderChipHighlight.None) assertThat(underTest.quickSettingsChipHighlight).isEqualTo(HeaderChipHighlight.None) } companion object { private val SUB_1 = SubscriptionModel( Loading
packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +3 −62 Original line number Diff line number Diff line Loading @@ -24,9 +24,7 @@ import android.icu.text.DateFormat import android.icu.text.DisplayContext import android.provider.Settings import android.view.ViewGroup import androidx.compose.material3.ColorScheme import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.IntRect import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.battery.BatteryMeterViewController Loading Loading @@ -98,34 +96,6 @@ constructor( (ViewGroup, StatusBarLocation) -> BatteryMeterViewController = batteryMeterViewControllerFactory::create val notificationsChipHighlight: HeaderChipHighlight by hydrator.hydratedStateOf( traceName = "notificationsChipHighlight", initialValue = HeaderChipHighlight.None, source = sceneInteractor.currentOverlays.map { overlays -> when { Overlays.NotificationsShade in overlays -> HeaderChipHighlight.Strong Overlays.QuickSettingsShade in overlays -> HeaderChipHighlight.Weak else -> HeaderChipHighlight.None } }, ) val quickSettingsChipHighlight: HeaderChipHighlight by hydrator.hydratedStateOf( traceName = "quickSettingsChipHighlight", initialValue = HeaderChipHighlight.None, source = sceneInteractor.currentOverlays.map { overlays -> when { Overlays.QuickSettingsShade in overlays -> HeaderChipHighlight.Strong Overlays.NotificationsShade in overlays -> HeaderChipHighlight.Weak else -> HeaderChipHighlight.None } }, ) /** True if there is exactly one mobile connection. */ val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier Loading Loading @@ -264,39 +234,10 @@ constructor( dualShadeEducationInteractor.onDualShadeEducationElementBoundsChange(element, bounds) } /** Represents the background highlight of a header icons chip. */ sealed interface HeaderChipHighlight { fun backgroundColor(colorScheme: ColorScheme): Color fun foregroundColor(colorScheme: ColorScheme): Color data object None : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = Color.Unspecified override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary } data object Weak : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.surface.copy(alpha = 0.1f) override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSurface } data object Strong : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.primaryContainer override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onPrimaryContainer } } private fun getFormatFromPattern(pattern: String?): DateFormat { val format = DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()) format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE) return format return DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()).apply { setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE) } } @AssistedFactory Loading