feat: M3 weighted shuffle v1 — real /api/radio with scoring #21
@@ -27,15 +27,21 @@ private const val GRADIENT_TWEEN_MS = 600
|
||||
* androidx.palette on a background thread, and animates the result
|
||||
* via [animateColorAsState] so track changes tween smoothly.
|
||||
*
|
||||
* Returns [Color.Transparent] while the bitmap is loading or on
|
||||
* any failure — callers should layer a fallback (e.g.
|
||||
* `MaterialTheme.colorScheme.surface`) underneath the gradient
|
||||
* tint so the screen never sits on a flat black.
|
||||
* The held color is NOT reset when [coverUrl] changes — it stays on
|
||||
* the previous track's dominant until the new palette resolves, so the
|
||||
* gradient tweens old→new directly instead of dipping toward the
|
||||
* fallback mid-swap. Mirrors `now_playing_screen.dart`'s preload-then-
|
||||
* swap ("keep the previous dominant"); the cover image swaps smoothly
|
||||
* via `CoverPrefetcher`, which warms the next track's bytes into Coil.
|
||||
*
|
||||
* Starts at [Color.Transparent] (cold mount) and resets to it only
|
||||
* when a track has no cover — callers layer a base color underneath so
|
||||
* the screen never sits on flat black.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDominantColor(coverUrl: String?): Color {
|
||||
val context = LocalContext.current
|
||||
var extracted by remember(coverUrl) { mutableStateOf(Color.Transparent) }
|
||||
var extracted by remember { mutableStateOf(Color.Transparent) }
|
||||
|
||||
LaunchedEffect(coverUrl) {
|
||||
if (coverUrl.isNullOrEmpty()) {
|
||||
@@ -47,6 +53,7 @@ fun rememberDominantColor(coverUrl: String?): Color {
|
||||
.allowHardware(false)
|
||||
.build()
|
||||
val result = SingletonImageLoader.get(context).execute(request)
|
||||
// Keep the previous dominant on load failure rather than dropping.
|
||||
if (result !is SuccessResult) return@LaunchedEffect
|
||||
val bitmap = result.image.toBitmap()
|
||||
val palette = withContext(Dispatchers.Default) {
|
||||
|
||||
Reference in New Issue
Block a user