From 8ab4cbb0d04700f9f8762cdf1f9e4483f6f98ca1 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Fri, 25 Sep 2026 02:49:15 +0000 Subject: [PATCH] tint the library background and fab from the current artwork --- .../com/subtracks/ui/library/LibraryScreen.kt | 18 +++++++-- .../com/subtracks/ui/theme/ArtworkTheme.kt | 6 +++ .../com/subtracks/ui/AppScreenshotTest.kt | 39 +++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt index 4ec9b715..14081dbc 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt @@ -97,6 +97,11 @@ import com.subtracks.data.prefs.ListQuery import com.subtracks.data.prefs.PlaylistSort import com.subtracks.data.prefs.SongSort import com.subtracks.data.prefs.StarredFilter +import com.subtracks.playback.PlaybackController +import com.subtracks.ui.theme.ArtworkColors +import com.subtracks.ui.theme.librarySurfaceColor +import com.subtracks.ui.theme.rememberArtworkColors +import org.koin.compose.koinInject import org.koin.compose.viewmodel.koinViewModel import kotlin.math.abs import kotlin.math.roundToInt @@ -167,6 +172,7 @@ fun LibraryRoute( onOpenSettings: () -> Unit, bottomInset: Dp, viewModel: LibraryViewModel = koinViewModel(), + playbackController: PlaybackController = koinInject(), ) { var selectedTab by rememberSaveable { mutableStateOf(LibraryTab.Albums) } var previousTab by rememberSaveable { mutableStateOf(LibraryTab.Albums) } @@ -187,11 +193,14 @@ fun LibraryRoute( val term by viewModel.search(tab.listTab()).collectAsStateWithLifecycle() "${query.sort}|${query.descending}|${query.starred}|$term" } + val playback by playbackController.state.collectAsStateWithLifecycle() + val artwork = rememberArtworkColors(playbackController.coverArt(playback.item, thumbnail = true)) LibraryScreen( selectedTab = selectedTab, onTabSelected = { selectedTab = it }, syncing = syncing, bottomInset = bottomInset, + artwork = artwork, albums = viewModel.albums.collectAsLazyPagingItems(), artists = viewModel.artists.collectAsLazyPagingItems(), songs = viewModel.songs.collectAsLazyPagingItems(), @@ -257,6 +266,7 @@ fun LibraryScreen( onClearFilters: () -> Unit = {}, search: String = "", onSearchChange: (String) -> Unit = {}, + artwork: ArtworkColors? = null, modifier: Modifier = Modifier, ) { val pagerState = @@ -302,7 +312,7 @@ fun LibraryScreen( modifier = Modifier .fillMaxSize() - .background(MaterialTheme.colorScheme.background) + .background(artwork?.let(::librarySurfaceColor) ?: MaterialTheme.colorScheme.background) .nestedScroll(scrollBehavior.nestedScrollConnection), ) { Column( @@ -419,8 +429,8 @@ fun LibraryScreen( if (!searchActive) { FloatingActionButton( onClick = { showOptions = true }, - containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, - contentColor = MaterialTheme.colorScheme.onBackground, + containerColor = artwork?.scheme?.primary ?: MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = artwork?.scheme?.onPrimary ?: MaterialTheme.colorScheme.onBackground, modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp), ) { Box { @@ -433,7 +443,7 @@ fun LibraryScreen( .offset(x = 4.dp, y = (-4).dp) .size(8.dp) .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary), + .background(artwork?.scheme?.onPrimary ?: MaterialTheme.colorScheme.primary), ) } } 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 487f1133..e2c22c7a 100644 --- a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt +++ b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt @@ -33,6 +33,7 @@ private const val DARKEN_START_SCREENS = 0.5f private const val DARKEN_END_SCREENS = 1.5f private const val DARKEN_KNEE = 0.2f private const val PLAYER_SURFACE_SATURATION_FACTOR = 1.6f +private const val LIBRARY_SURFACE_LIGHTNESS = 0.10f @Immutable data class ArtworkColors( @@ -211,6 +212,11 @@ fun playerSurfaceColor(artwork: ArtworkColors): Color { return Color.hsl(hue, (saturation * PLAYER_SURFACE_SATURATION_FACTOR).coerceIn(0f, 1f), lightness) } +fun librarySurfaceColor(artwork: ArtworkColors): Color { + val (hue, saturation, _) = artwork.scheme.surfaceContainerHigh.toHsl() + return Color.hsl(hue, saturation, LIBRARY_SURFACE_LIGHTNESS) +} + fun heroBarColor( artwork: ArtworkColors?, scrollPx: Float, diff --git a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt index 3c3f9fa6..775c65b9 100644 --- a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt @@ -117,6 +117,44 @@ class AppScreenshotTest { composeRule.onRoot().captureRoboImage("src/test/screenshots/library_playlists.png") } + @Test + fun libraryWithMiniPlayer() { + composeRule.setContent { + SubtracksTheme { + val artwork = artworkColorsFromSeed(Color.rgb(120, 80, 200)) + 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.png") + } + @Test fun albumDetail() { composeRule.setContent { @@ -396,6 +434,7 @@ class AppScreenshotTest { onSongClick = {}, onSync = {}, onOpenSettings = {}, + artwork = artworkColorsFromSeed(Color.rgb(120, 80, 200)), ) } }