Skip to content

Commit 3142889

Browse files
Ghosutojoeyhuab
authored andcommitted
SystemUI: Add color modes for music chip bg [1/2]
Signed-off-by: Ghosuto <clash.raja10@gmail.com> Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
1 parent 33a18ed commit 3142889

3 files changed

Lines changed: 170 additions & 6 deletions

File tree

core/java/android/provider/Settings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7762,6 +7762,11 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean
77627762
*/
77637763
public static final String ONGOING_COMPACT_MODE = "ongoing_compact_mode";
77647764

7765+
/**
7766+
* @hide
7767+
*/
7768+
public static final String ONGOING_CHIP_COLOR_MODE = "ongoing_chip_color_mode";
7769+
77657770
/**
77667771
* Keys we no longer back up under the current schema, but want to continue to
77677772
* process when restoring historical backup datasets.

packages/SystemUI/src/com/android/systemui/statusbar/OnGoingActionProgressController.kt

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import android.content.Context
1313
import android.content.Intent
1414
import android.database.ContentObserver
1515
import android.graphics.Bitmap
16+
import android.graphics.Color
1617
import android.media.MediaMetadata
1718
import android.net.Uri
1819
import android.os.Handler
@@ -28,7 +29,9 @@ import android.graphics.drawable.Drawable
2829
import androidx.compose.runtime.Immutable
2930
import androidx.compose.ui.graphics.ImageBitmap
3031
import androidx.compose.ui.graphics.asImageBitmap
32+
import androidx.core.graphics.ColorUtils
3133
import androidx.core.graphics.drawable.toBitmap
34+
import androidx.palette.graphics.Palette
3235
import com.android.systemui.res.R
3336
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
3437
import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener
@@ -76,6 +79,7 @@ class OnGoingActionProgressController(
7679
private var headsUpPinned = false
7780
private var isEnabled = false
7881
private var isCompactModeEnabled = false
82+
private var chipColorMode = CHIP_COLOR_MODE_DEFAULT
7983

8084
private var currentProgress = 0
8185
private var currentProgressMax = 0
@@ -85,6 +89,10 @@ class OnGoingActionProgressController(
8589
private var currentArtistName: String? = null
8690
private var currentAlbumArt: Bitmap? = null
8791

92+
private var currentChipBgColor: Int? = null
93+
private var lastColorExtractedIcon: Drawable? = null
94+
private var lastColorExtractedAlbumArt: Bitmap? = null
95+
8896
private var lastObservedTitle: String? = null
8997

9098
private var isMenuVisible = false
@@ -119,7 +127,8 @@ class OnGoingActionProgressController(
119127
if (uri == null) return
120128
if (uri == Settings.System.getUriFor(ONGOING_ACTION_CHIP_ENABLED) ||
121129
uri == Settings.System.getUriFor(ONGOING_MEDIA_PROGRESS) ||
122-
uri == Settings.System.getUriFor(ONGOING_COMPACT_MODE_ENABLED)) {
130+
uri == Settings.System.getUriFor(Settings.System.ONGOING_COMPACT_MODE) ||
131+
uri == Settings.System.getUriFor(Settings.System.ONGOING_CHIP_COLOR_MODE)) {
123132
updateSettings()
124133
}
125134
}
@@ -143,6 +152,12 @@ class OnGoingActionProgressController(
143152
this,
144153
UserHandle.USER_ALL
145154
)
155+
contentResolver.registerContentObserver(
156+
Settings.System.getUriFor(Settings.System.ONGOING_CHIP_COLOR_MODE),
157+
false,
158+
this,
159+
UserHandle.USER_ALL
160+
)
146161
updateSettings()
147162
}
148163

@@ -195,6 +210,7 @@ class OnGoingActionProgressController(
195210
private fun onTrackChanged() {
196211
needsFullUiUpdate = true
197212
currentAlbumArt = null
213+
invalidateChipBgColor()
198214
scheduleAlbumArtRetry()
199215
}
200216

@@ -210,6 +226,7 @@ class OnGoingActionProgressController(
210226

211227
if (art != null) {
212228
currentAlbumArt = art
229+
if (chipColorMode == CHIP_COLOR_MODE_ALBUM_ART) invalidateChipBgColor()
213230
requestUiUpdate()
214231
return@launch
215232
}
@@ -280,6 +297,98 @@ class OnGoingActionProgressController(
280297
}
281298
}
282299

300+
private fun invalidateChipBgColor() {
301+
currentChipBgColor = null
302+
lastColorExtractedIcon = null
303+
lastColorExtractedAlbumArt = null
304+
}
305+
306+
private suspend fun extractDominantColorFromBitmap(bitmap: Bitmap): Int? =
307+
withContext(bgDispatcher) {
308+
try {
309+
if (bitmap.isRecycled || bitmap.width <= 0 || bitmap.height <= 0) return@withContext null
310+
311+
val palette = Palette.from(bitmap).generate()
312+
313+
val candidates = listOfNotNull(
314+
palette.vibrantSwatch,
315+
palette.mutedSwatch,
316+
palette.dominantSwatch,
317+
palette.darkVibrantSwatch,
318+
palette.darkMutedSwatch,
319+
)
320+
321+
candidates
322+
.map { it.rgb }
323+
.firstOrNull { color ->
324+
val alpha = Color.alpha(color)
325+
val luminance = ColorUtils.calculateLuminance(color)
326+
alpha > 200 && luminance > 0.05 && luminance < 0.95
327+
}
328+
} catch (e: Exception) {
329+
Log.w(TAG, "Failed to extract dominant color from bitmap", e)
330+
null
331+
}
332+
}
333+
334+
private fun extractAndApplyChipBgColorFromIcon(icon: Drawable) {
335+
if (icon === lastColorExtractedIcon) return
336+
337+
mainScope.launch {
338+
val size = (48f * context.resources.displayMetrics.density).toInt().coerceAtLeast(1)
339+
val bitmap = try {
340+
withContext(bgDispatcher) {
341+
icon.toBitmap(width = size, height = size, config = Bitmap.Config.ARGB_8888)
342+
}
343+
} catch (e: Exception) {
344+
Log.w(TAG, "Failed to rasterize icon for chip color extraction", e)
345+
null
346+
} ?: return@launch
347+
348+
lastColorExtractedIcon = icon
349+
currentChipBgColor = extractDominantColorFromBitmap(bitmap)
350+
updateProgressState()
351+
}
352+
}
353+
354+
private fun extractAndApplyChipBgColorFromAlbumArt(albumArt: Bitmap, iconFallback: Drawable?) {
355+
if (albumArt === lastColorExtractedAlbumArt) return
356+
357+
mainScope.launch {
358+
val color = extractDominantColorFromBitmap(albumArt)
359+
360+
if (color != null) {
361+
lastColorExtractedAlbumArt = albumArt
362+
currentChipBgColor = color
363+
updateProgressState()
364+
return@launch
365+
}
366+
367+
Log.d(TAG, "Album art color extraction failed, falling back to icon color")
368+
if (iconFallback == null) {
369+
lastColorExtractedAlbumArt = albumArt
370+
currentChipBgColor = null
371+
updateProgressState()
372+
return@launch
373+
}
374+
375+
val size = (48f * context.resources.displayMetrics.density).toInt().coerceAtLeast(1)
376+
val iconBitmap = try {
377+
withContext(bgDispatcher) {
378+
iconFallback.toBitmap(width = size, height = size,
379+
config = Bitmap.Config.ARGB_8888)
380+
}
381+
} catch (e: Exception) {
382+
Log.w(TAG, "Failed to rasterize icon fallback for chip color extraction", e)
383+
null
384+
}
385+
386+
lastColorExtractedAlbumArt = albumArt
387+
currentChipBgColor = iconBitmap?.let { extractDominantColorFromBitmap(it) }
388+
updateProgressState()
389+
}
390+
}
391+
283392
private fun updateProgressState() {
284393
var isVisible = !isForceHidden && !headsUpPinned && !isSystemChipVisible
285394
val hasMediaSession = isMediaSessionActiveForChip()
@@ -302,6 +411,7 @@ class OnGoingActionProgressController(
302411
isMediaPlaying = false,
303412
trackTitle = null,
304413
artistName = null,
414+
chipBgColor = null,
305415
)
306416
)
307417
return
@@ -338,6 +448,25 @@ class OnGoingActionProgressController(
338448
val trackTitle = if (hasMediaSession) currentTrackTitle else null
339449
val artistName = if (hasMediaSession) currentArtistName else null
340450

451+
if (hasMediaSession) {
452+
when (chipColorMode) {
453+
CHIP_COLOR_MODE_ICON -> {
454+
currentIcon?.let { extractAndApplyChipBgColorFromIcon(it) }
455+
}
456+
CHIP_COLOR_MODE_ALBUM_ART -> {
457+
val art = currentAlbumArt
458+
if (art != null) {
459+
extractAndApplyChipBgColorFromAlbumArt(art, currentIcon)
460+
} else {
461+
currentIcon?.let { extractAndApplyChipBgColorFromIcon(it) }
462+
}
463+
}
464+
}
465+
}
466+
467+
val resolvedChipBgColor = if (chipColorMode != CHIP_COLOR_MODE_DEFAULT) currentChipBgColor
468+
else null
469+
341470
publish(
342471
ProgressState(
343472
isVisible = true,
@@ -351,6 +480,7 @@ class OnGoingActionProgressController(
351480
isMediaPlaying = isMediaPlaying,
352481
trackTitle = trackTitle,
353482
artistName = artistName,
483+
chipBgColor = resolvedChipBgColor,
354484
)
355485
)
356486
}
@@ -822,6 +952,7 @@ class OnGoingActionProgressController(
822952
val wasEnabled = isEnabled
823953
val wasShowingMedia = showMediaProgress
824954
val wasCompactMode = isCompactModeEnabled
955+
val wasChipColorMode = chipColorMode
825956

826957
isEnabled = Settings.System.getIntForUser(
827958
contentResolver,
@@ -844,11 +975,23 @@ class OnGoingActionProgressController(
844975
UserHandle.USER_CURRENT
845976
) == 1
846977

978+
chipColorMode = Settings.System.getIntForUser(
979+
contentResolver,
980+
Settings.System.ONGOING_CHIP_COLOR_MODE,
981+
CHIP_COLOR_MODE_DEFAULT,
982+
UserHandle.USER_CURRENT
983+
)
984+
847985
if (wasEnabled != isEnabled || wasShowingMedia != showMediaProgress || wasCompactMode != isCompactModeEnabled) {
848986
needsFullUiUpdate = true
849987
isExpanded = false
850988
}
851989

990+
if (wasChipColorMode != chipColorMode) {
991+
invalidateChipBgColor()
992+
needsFullUiUpdate = true
993+
}
994+
852995
requestUiUpdate()
853996
}
854997

@@ -897,6 +1040,10 @@ class OnGoingActionProgressController(
8971040
private const val ALBUM_ART_RETRY_INTERVAL_MS = 300L
8981041
private const val POSITION_RESET_THRESHOLD_MS = 1_500L
8991042

1043+
const val CHIP_COLOR_MODE_DEFAULT = 0
1044+
const val CHIP_COLOR_MODE_ICON = 1
1045+
const val CHIP_COLOR_MODE_ALBUM_ART = 2
1046+
9001047
private val HAPTIC_CLICK =
9011048
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
9021049
private val HAPTIC_DOUBLE =
@@ -919,4 +1066,5 @@ data class ProgressState(
9191066
val isMediaPlaying: Boolean = false,
9201067
val trackTitle: String? = null,
9211068
val artistName: String? = null,
1069+
val chipBgColor: Int? = null,
9221070
)

packages/SystemUI/src/com/android/systemui/statusbar/OngoingActionProgressCompose.kt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import androidx.compose.ui.unit.sp
8383
import androidx.compose.ui.viewinterop.AndroidView
8484
import androidx.compose.ui.window.Popup
8585
import androidx.compose.ui.window.PopupProperties
86-
import com.android.internal.graphics.ColorUtils
86+
import androidx.core.graphics.ColorUtils
8787
import com.android.systemui.media.controls.ui.binder.SeekBarObserver
8888
import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
8989
import com.android.systemui.res.R
@@ -103,6 +103,8 @@ private const val TAG = "OngoingActionProgressCompose"
103103
private const val EXPAND_DURATION_MS = 350
104104
private const val COLLAPSE_DURATION_MS = 250
105105

106+
private const val CHIP_TEXT_LUMINANCE_THRESHOLD = 0.6
107+
106108
/**
107109
* Composable that displays an ongoing action progress indicator in the status bar.
108110
* Shows app icon and progress bar for notifications with progress information.
@@ -437,12 +439,12 @@ private fun SeekBarCompose(
437439
if (layer != null) {
438440
val bg = layer.findDrawableByLayerId(android.R.id.background)
439441
bg?.mutate()?.setTint(
440-
ColorUtils.setAlphaComponent(android.graphics.Color.WHITE, 90)
442+
com.android.internal.graphics.ColorUtils.setAlphaComponent(android.graphics.Color.WHITE, 90)
441443
)
442444

443445
val secondary = layer.findDrawableByLayerId(android.R.id.secondaryProgress)
444446
secondary?.mutate()?.setTint(
445-
ColorUtils.setAlphaComponent(android.graphics.Color.WHITE, 60)
447+
com.android.internal.graphics.ColorUtils.setAlphaComponent(android.graphics.Color.WHITE, 60)
446448
)
447449

448450
// Replace ONLY the progress layer with SquigglyProgress.
@@ -545,8 +547,16 @@ private fun MusicChip(
545547
chipShape: RoundedCornerShape,
546548
gestureModifier: Modifier,
547549
) {
548-
val bg = colorResource(android.R.color.system_accent1_500)
549-
val text = colorResource(android.R.color.system_accent1_100)
550+
val bg = if (state.chipBgColor != null)
551+
Color(state.chipBgColor)
552+
else
553+
colorResource(android.R.color.system_accent1_500)
554+
555+
val text = if (state.chipBgColor != null &&
556+
ColorUtils.calculateLuminance(state.chipBgColor) >= CHIP_TEXT_LUMINANCE_THRESHOLD)
557+
Color.Black
558+
else
559+
colorResource(android.R.color.system_accent1_100)
550560

551561
Row(
552562
modifier = Modifier
@@ -673,6 +683,7 @@ class OnGoingActionProgressComposeController(
673683
isMediaPlaying = state.isMediaPlaying,
674684
trackTitle = state.trackTitle,
675685
artistName = state.artistName,
686+
chipBgColor = state.chipBgColor,
676687
)
677688
}
678689
}

0 commit comments

Comments
 (0)