summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alejandro Nijamkin <nijamkin@google.com> 2025-03-05 13:34:53 -0800
committer Alejandro Nijamkin <nijamkin@google.com> 2025-03-05 13:34:53 -0800
commit626bcf1d28be476bb5a21a1574a73d7ba6ebf86d (patch)
treea012f27cba9b6ce5d29fb057b6096e5153b16758
parent63cc7448fa76ad7a4e3fa4d5032a53c75c047c0a (diff)
[Media] Color extraction.
The interactor implementation provides the color scheme by providing an updated MediaColorScheme each time new colors are updated. The composable hierarchy takes that new value and supplies it through it hierarchy through an instance of AnimatedColorScheme that animated to the new colors each time they need to change. Bug: 397989775 Test: verified in the compose gallery app Flag: EXEMPT code remains unused Change-Id: I940a95b5605e1f9ef066058c84294a335ad297d4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt4
5 files changed, 81 insertions, 19 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt
index 9f7a4f2a4966..e64ce73226f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.ImageBitmap
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
import com.android.systemui.media.remedia.shared.model.MediaSessionState
/** Data model representing a media session. */
@@ -34,6 +35,8 @@ interface MediaSessionModel {
val background: ImageBitmap?
+ val colorScheme: MediaColorScheme
+
val title: String
val subtitle: String
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt
new file mode 100644
index 000000000000..8dba170f0928
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.shared.model
+
+import androidx.compose.ui.graphics.Color
+
+data class MediaColorScheme(val primary: Color, val onPrimary: Color)
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index b3b953f1d0a9..9c6568057d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -20,6 +20,7 @@ package com.android.systemui.media.remedia.ui.compose
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
+import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
@@ -66,6 +67,7 @@ import androidx.compose.material3.SliderState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
@@ -105,7 +107,6 @@ import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
import com.android.compose.animation.scene.transitions
-import com.android.compose.theme.LocalAndroidColorScheme
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
@@ -114,6 +115,7 @@ import com.android.systemui.common.ui.compose.load
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
import com.android.systemui.media.remedia.shared.model.MediaSessionState
import com.android.systemui.media.remedia.ui.viewmodel.MediaCardGutsViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaCardViewModel
@@ -275,6 +277,22 @@ private fun Card(
}
}
+@Composable
+private fun rememberAnimatedColorScheme(colorScheme: MediaColorScheme): AnimatedColorScheme {
+ val animatedPrimary by animateColorAsState(targetValue = colorScheme.primary)
+ val animatedOnPrimary by animateColorAsState(targetValue = colorScheme.onPrimary)
+
+ return remember {
+ object : AnimatedColorScheme {
+ override val primary: Color
+ get() = animatedPrimary
+
+ override val onPrimary: Color
+ get() = animatedOnPrimary
+ }
+ }
+}
+
/**
* Renders the foreground of a card, including all UI content and the internal "guts".
*
@@ -297,6 +315,8 @@ private fun ContentScope.CardForeground(
val isGutsVisible = viewModel.guts.isVisible
LaunchedEffect(isGutsVisible) { gutsAlphaAnimatable.animateTo(if (isGutsVisible) 1f else 0f) }
+ val colorScheme = rememberAnimatedColorScheme(viewModel.colorScheme)
+
// Use a custom layout to measure the content even if the content is being hidden because the
// internal guts are showing. This is needed because only the content knows the size the of the
// card and the guts are set to be the same size of the content.
@@ -306,6 +326,7 @@ private fun ContentScope.CardForeground(
viewModel = viewModel,
threeRows = threeRows,
fillHeight = fillHeight,
+ colorScheme = colorScheme,
modifier =
Modifier.graphicsLayer {
compositingStrategy = CompositingStrategy.ModulateAlpha
@@ -315,6 +336,7 @@ private fun ContentScope.CardForeground(
CardGuts(
viewModel = viewModel.guts,
+ colorScheme = colorScheme,
modifier =
Modifier.graphicsLayer {
compositingStrategy = CompositingStrategy.ModulateAlpha
@@ -349,6 +371,7 @@ private fun ContentScope.CardForegroundContent(
viewModel: MediaCardViewModel,
threeRows: Boolean,
fillHeight: Boolean,
+ colorScheme: AnimatedColorScheme,
modifier: Modifier = Modifier,
) {
Column(
@@ -366,12 +389,16 @@ private fun ContentScope.CardForegroundContent(
// Icon.
Icon(
icon = viewModel.icon,
- tint = LocalAndroidColorScheme.current.primaryFixed,
+ tint = colorScheme.primary,
modifier = Modifier.size(24.dp).clip(CircleShape),
)
Spacer(modifier = Modifier.weight(1f))
viewModel.outputSwitcherChips.fastForEach { chip ->
- OutputSwitcherChip(viewModel = chip, modifier = Modifier.padding(start = 8.dp))
+ OutputSwitcherChip(
+ viewModel = chip,
+ colorScheme = colorScheme,
+ modifier = Modifier.padding(start = 8.dp),
+ )
}
}
@@ -402,8 +429,8 @@ private fun ContentScope.CardForegroundContent(
PlayPauseAction(
viewModel = checkNotNull(viewModel.playPauseAction),
buttonWidth = 48.dp,
- buttonColor = LocalAndroidColorScheme.current.primaryFixed,
- iconColor = LocalAndroidColorScheme.current.onPrimaryFixed,
+ buttonColor = colorScheme.primary,
+ iconColor = colorScheme.onPrimary,
buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
)
}
@@ -470,8 +497,8 @@ private fun ContentScope.CardForegroundContent(
PlayPauseAction(
viewModel = checkNotNull(viewModel.playPauseAction),
buttonWidth = 48.dp,
- buttonColor = LocalAndroidColorScheme.current.primaryFixed,
- iconColor = LocalAndroidColorScheme.current.onPrimaryFixed,
+ buttonColor = colorScheme.primary,
+ iconColor = colorScheme.onPrimary,
buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
)
}
@@ -787,7 +814,11 @@ private fun SeekBarTrack(
/** Renders the internal "guts" of a card. */
@Composable
-private fun CardGuts(viewModel: MediaCardGutsViewModel, modifier: Modifier = Modifier) {
+private fun CardGuts(
+ viewModel: MediaCardGutsViewModel,
+ colorScheme: AnimatedColorScheme,
+ modifier: Modifier = Modifier,
+) {
Box(
modifier =
modifier.pointerInput(Unit) { detectLongPressGesture { viewModel.onLongClick() } }
@@ -822,7 +853,7 @@ private fun CardGuts(viewModel: MediaCardGutsViewModel, modifier: Modifier = Mod
) {
Text(
text = checkNotNull(viewModel.primaryAction.text),
- color = LocalAndroidColorScheme.current.onPrimaryFixed,
+ color = colorScheme.onPrimary,
)
}
@@ -880,28 +911,22 @@ private fun ContentScope.Metadata(
@Composable
private fun OutputSwitcherChip(
viewModel: MediaOutputSwitcherChipViewModel,
+ colorScheme: AnimatedColorScheme,
modifier: Modifier = Modifier,
) {
PlatformButton(
onClick = viewModel.onClick,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = LocalAndroidColorScheme.current.primaryFixed
- ),
+ colors = ButtonDefaults.buttonColors(containerColor = colorScheme.primary),
contentPadding = PaddingValues(start = 8.dp, end = 12.dp, top = 4.dp, bottom = 4.dp),
modifier = modifier.height(24.dp),
) {
- Icon(
- icon = viewModel.icon,
- tint = LocalAndroidColorScheme.current.onPrimaryFixed,
- modifier = Modifier.size(16.dp),
- )
+ Icon(icon = viewModel.icon, tint = colorScheme.onPrimary, modifier = Modifier.size(16.dp))
viewModel.text?.let {
Spacer(Modifier.size(4.dp))
Text(
text = viewModel.text,
style = MaterialTheme.typography.bodySmall,
- color = LocalAndroidColorScheme.current.onPrimaryFixed,
+ color = colorScheme.onPrimary,
)
}
}
@@ -1030,6 +1055,12 @@ data class MediaUiBehavior(
val isFalsingProtectionNeeded: Boolean = false,
)
+@Stable
+private interface AnimatedColorScheme {
+ val primary: Color
+ val onPrimary: Color
+}
+
private object Media {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
index 4c3f273e86a5..833a04ddcb55 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.ImageBitmap
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
/** Models UI state for a media card. */
@Stable
@@ -34,6 +35,8 @@ interface MediaCardViewModel {
val background: ImageBitmap?
+ val colorScheme: MediaColorScheme
+
val title: String
val subtitle: String
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
index 27b129b18d6d..b4f3d2724e75 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.media.remedia.domain.interactor.MediaInteractor
import com.android.systemui.media.remedia.domain.model.MediaActionModel
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
import com.android.systemui.media.remedia.shared.model.MediaSessionState
import com.android.systemui.res.R
import dagger.assisted.Assisted
@@ -66,6 +67,9 @@ constructor(
override val background: ImageBitmap?
get() = session.background
+ override val colorScheme: MediaColorScheme
+ get() = session.colorScheme
+
override val title = session.title
override val subtitle = session.subtitle
override val actionButtonLayout = session.actionButtonLayout