diff --git a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt index e2c22c7a..29a62130 100644 --- a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt +++ b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt @@ -53,6 +53,7 @@ fun artworkColorsFromSeeds( ): ArtworkColors { val (hue, saturation, _) = Color(primarySeed or 0xFF000000.toInt()).toHsl() val s = saturation.coerceAtMost(0.85f) + val darkHue = harmonizeDarkHue(hue) fun tone( h: Float, @@ -61,7 +62,7 @@ fun artworkColorsFromSeeds( ) = Color.hsl(h % 360f, sat.coerceIn(0f, 1f), l.coerceIn(0f, 1f)) val primary = tone(hue, s, 0.62f).withMinLuminance(PRIMARY_MIN_LUMINANCE) - val background = tone(hue, (s * 0.35f).coerceAtMost(0.20f), 0.06f) + val background = tone(darkHue, (s * 0.35f).coerceAtMost(0.20f), 0.06f) val onBackground = tone(hue, (s * 0.10f).coerceAtMost(0.08f), 0.95f) val secondary = @@ -75,14 +76,14 @@ fun artworkColorsFromSeeds( } ?: tone(hue, s, 0.40f) val gradientSat = if (s > 0.05f) s.coerceAtLeast(MIN_GRADIENT_SATURATION) else s - val gradientHigh = tone(hue, (gradientSat * 0.85f).coerceAtMost(0.70f), 0.26f) - val gradientLow = tone(hue, gradientSat * 0.55f, 0.12f) + val gradientHigh = tone(darkHue, (gradientSat * 0.85f).coerceAtMost(0.70f), 0.26f) + val gradientLow = tone(darkHue, gradientSat * 0.55f, 0.12f) val accents = listOf( - tone(hue, gradientSat, 0.72f).withMaxLuminance(ACCENT_MAX_LUMINANCE), + tone(darkHue, gradientSat, 0.72f).withMaxLuminance(ACCENT_MAX_LUMINANCE), secondary.withMaxLuminance(ACCENT_MAX_LUMINANCE), ) - val darkPrimary = tone(hue, gradientSat * 0.75f, 0.10f) + val darkPrimary = tone(darkHue, gradientSat * 0.75f, 0.10f) val backdrop = maxOf(gradientHigh.luminance(), gradientLow.luminance(), accents.maxOf { it.luminance() }) val onSurfaceVariant = @@ -108,15 +109,15 @@ fun artworkColorsFromSeeds( surface = background, onSurface = onBackground, surfaceTint = primary, - surfaceVariant = tone(hue, s * 0.25f, 0.17f), + surfaceVariant = tone(darkHue, s * 0.25f, 0.17f), onSurfaceVariant = onSurfaceVariant, - surfaceContainerLowest = tone(hue, s * 0.25f, 0.04f), - surfaceContainerLow = tone(hue, s * 0.25f, 0.08f), - surfaceContainer = tone(hue, s * 0.25f, 0.10f), - surfaceContainerHigh = tone(hue, s * 0.22f, 0.14f), - surfaceContainerHighest = tone(hue, s * 0.20f, 0.18f), + surfaceContainerLowest = tone(darkHue, s * 0.25f, 0.04f), + surfaceContainerLow = tone(darkHue, s * 0.25f, 0.08f), + surfaceContainer = tone(darkHue, s * 0.25f, 0.10f), + surfaceContainerHigh = tone(darkHue, s * 0.22f, 0.14f), + surfaceContainerHighest = tone(darkHue, s * 0.20f, 0.18f), outline = tone(hue, s * 0.15f, 0.55f), - outlineVariant = tone(hue, s * 0.18f, 0.28f), + outlineVariant = tone(darkHue, s * 0.18f, 0.28f), ) return ArtworkColors( @@ -174,6 +175,11 @@ private fun blendHue( return (from + delta * fraction + 360f) % 360f } +private fun harmonizeDarkHue(hue: Float): Float { + if (hue !in 0f..120f) return hue + return 358f - hue * (40f / 120f) +} + @Composable fun rememberArtworkColors( ref: CoverArtRef?, diff --git a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt index 775c65b9..b2c14c92 100644 --- a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt @@ -155,6 +155,44 @@ class AppScreenshotTest { composeRule.onRoot().captureRoboImage("src/test/screenshots/library_with_mini_player.png") } + @Test + fun libraryWithMiniPlayerWarm() { + composeRule.setContent { + SubtracksTheme { + val artwork = artworkColorsFromSeed(Color.rgb(222, 150, 30)) + Column(Modifier.fillMaxSize()) { + LibraryScreen( + selectedTab = LibraryTab.Albums, + onTabSelected = {}, + albums = remember { flowOf(PagingData.from(Fixtures.albums)) }.collectAsLazyPagingItems(), + artists = remember { flowOf(PagingData.from(Fixtures.artists)) }.collectAsLazyPagingItems(), + songs = remember { flowOf(PagingData.from(Fixtures.songItems)) }.collectAsLazyPagingItems(), + playlists = remember { flowOf(PagingData.from(Fixtures.playlists)) }.collectAsLazyPagingItems(), + coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } }, + onAlbumClick = {}, + onArtistClick = {}, + onPlaylistClick = {}, + onSongClick = {}, + onSync = {}, + onOpenSettings = {}, + artwork = artwork, + modifier = Modifier.weight(1f), + ) + MiniPlayer( + state = Fixtures.playbackState(), + coverArt = CoverArtRef("art-al-kid-a", "test:art-al-kid-a"), + artwork = artwork, + onExpand = {}, + onPlayPause = {}, + onNext = {}, + ) + } + } + } + awaitTag(ALBUM_COVER_TAG) + composeRule.onRoot().captureRoboImage("src/test/screenshots/library_with_mini_player_warm.png") + } + @Test fun albumDetail() { composeRule.setContent { diff --git a/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt b/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt index 1ebb1d18..75e18c95 100644 --- a/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt @@ -59,6 +59,19 @@ class ArtworkColorsTest { } } + @Test + fun warmHuesKeepDarkSurfacesOutOfBrown() { + val warmSeeds = listOf(0xFFE6C822.toInt(), 0xFFE68A22.toInt(), 0xFFD53A3A.toInt()) + for (seed in warmSeeds) { + val colors = artworkColorsFromSeed(seed) + assertTrue("gradientHigh for $seed", colors.gradientHigh.blue >= colors.gradientHigh.green) + assertTrue( + "surfaceContainerHigh for $seed", + colors.scheme.surfaceContainerHigh.blue >= colors.scheme.surfaceContainerHigh.green, + ) + } + } + private fun contrast( a: Color, b: Color,