mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
apply the prefetched theme on open and soften the hero darkening
This commit is contained in:
@@ -68,11 +68,14 @@ private object Routes {
|
|||||||
const val LIBRARY = "library"
|
const val LIBRARY = "library"
|
||||||
const val SETTINGS = "settings"
|
const val SETTINGS = "settings"
|
||||||
const val ADD_SERVER = "add-server"
|
const val ADD_SERVER = "add-server"
|
||||||
const val ALBUM_DETAIL = "album/{albumId}"
|
const val ALBUM_DETAIL = "album/{albumId}?coverArt={coverArt}"
|
||||||
const val ARTIST_DETAIL = "artist/{artistId}"
|
const val ARTIST_DETAIL = "artist/{artistId}"
|
||||||
const val PLAYLIST_DETAIL = "playlist/{playlistId}"
|
const val PLAYLIST_DETAIL = "playlist/{playlistId}"
|
||||||
|
|
||||||
fun album(id: String) = "album/${Uri.encode(id)}"
|
fun album(
|
||||||
|
id: String,
|
||||||
|
coverArt: String? = null,
|
||||||
|
) = "album/${Uri.encode(id)}?coverArt=${Uri.encode(coverArt.orEmpty())}"
|
||||||
|
|
||||||
fun artist(id: String) = "artist/${Uri.encode(id)}"
|
fun artist(id: String) = "artist/${Uri.encode(id)}"
|
||||||
|
|
||||||
@@ -135,7 +138,7 @@ private fun MainNavigation() {
|
|||||||
) {
|
) {
|
||||||
composable(Routes.LIBRARY) {
|
composable(Routes.LIBRARY) {
|
||||||
LibraryRoute(
|
LibraryRoute(
|
||||||
onAlbumClick = { album -> navController.navigate(Routes.album(album.id)) },
|
onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) },
|
||||||
onArtistClick = { artist -> navController.navigate(Routes.artist(artist.id)) },
|
onArtistClick = { artist -> navController.navigate(Routes.artist(artist.id)) },
|
||||||
onPlaylistClick = { playlist -> navController.navigate(Routes.playlist(playlist.id)) },
|
onPlaylistClick = { playlist -> navController.navigate(Routes.playlist(playlist.id)) },
|
||||||
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
|
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
|
||||||
@@ -150,10 +153,22 @@ private fun MainNavigation() {
|
|||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
route = Routes.ALBUM_DETAIL,
|
route = Routes.ALBUM_DETAIL,
|
||||||
arguments = listOf(navArgument("albumId") { type = NavType.StringType }),
|
arguments =
|
||||||
|
listOf(
|
||||||
|
navArgument("albumId") { type = NavType.StringType },
|
||||||
|
navArgument("coverArt") {
|
||||||
|
type = NavType.StringType
|
||||||
|
defaultValue = ""
|
||||||
|
},
|
||||||
|
),
|
||||||
) { entry ->
|
) { entry ->
|
||||||
AlbumDetailRoute(
|
AlbumDetailRoute(
|
||||||
albumId = entry.arguments?.getString("albumId").orEmpty(),
|
albumId = entry.arguments?.getString("albumId").orEmpty(),
|
||||||
|
coverArtId =
|
||||||
|
entry.arguments
|
||||||
|
?.getString("coverArt")
|
||||||
|
.orEmpty()
|
||||||
|
.ifEmpty { null },
|
||||||
onBack = { navController.popBackStack() },
|
onBack = { navController.popBackStack() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -164,7 +179,7 @@ private fun MainNavigation() {
|
|||||||
ArtistDetailRoute(
|
ArtistDetailRoute(
|
||||||
artistId = entry.arguments?.getString("artistId").orEmpty(),
|
artistId = entry.arguments?.getString("artistId").orEmpty(),
|
||||||
onBack = { navController.popBackStack() },
|
onBack = { navController.popBackStack() },
|
||||||
onAlbumClick = { album -> navController.navigate(Routes.album(album.id)) },
|
onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ private const val THEME_TRANSITION_MS = 100
|
|||||||
@Composable
|
@Composable
|
||||||
fun AlbumDetailRoute(
|
fun AlbumDetailRoute(
|
||||||
albumId: String,
|
albumId: String,
|
||||||
|
coverArtId: String?,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
viewModel: AlbumDetailViewModel = koinViewModel(key = albumId) { parametersOf(albumId) },
|
viewModel: AlbumDetailViewModel = koinViewModel(key = albumId) { parametersOf(albumId) },
|
||||||
playbackController: PlaybackController = koinInject(),
|
playbackController: PlaybackController = koinInject(),
|
||||||
@@ -97,11 +98,12 @@ fun AlbumDetailRoute(
|
|||||||
val songs by viewModel.songs.collectAsStateWithLifecycle(initialValue = emptyList())
|
val songs by viewModel.songs.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
val playback by playbackController.state.collectAsStateWithLifecycle()
|
val playback by playbackController.state.collectAsStateWithLifecycle()
|
||||||
val context = playback.context
|
val context = playback.context
|
||||||
|
val shortcutArt = remember(coverArtId) { coverArtId?.let { viewModel.coverArt(it, true) } }
|
||||||
AlbumDetailScreen(
|
AlbumDetailScreen(
|
||||||
album = album,
|
album = album,
|
||||||
songs = songs,
|
songs = songs,
|
||||||
coverArt = viewModel::coverArt,
|
coverArt = viewModel::coverArt,
|
||||||
artwork = rememberArtworkColors(viewModel.coverArt(album?.coverArt, true), THEME_TRANSITION_MS),
|
artwork = rememberArtworkColors(shortcutArt ?: viewModel.coverArt(album?.coverArt, true), THEME_TRANSITION_MS),
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
onSongClick = viewModel::play,
|
onSongClick = viewModel::play,
|
||||||
playingSongId = playback.item?.id.takeIf { context?.kind == QueueKind.Album && context.refId == albumId },
|
playingSongId = playback.item?.id.takeIf { context?.kind == QueueKind.Album && context.refId == albumId },
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ object ArtworkSeedCache {
|
|||||||
this.store = store
|
this.store = store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cached(cacheKey: String): Pair<Int, Int?>? = cache.get(cacheKey)
|
||||||
|
|
||||||
suspend fun seeds(
|
suspend fun seeds(
|
||||||
context: Context,
|
context: Context,
|
||||||
ref: CoverArtRef,
|
ref: CoverArtRef,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ private const val BLOB_COUNT = 4
|
|||||||
private const val ACCENT_MAX_LUMINANCE = 0.18f
|
private const val ACCENT_MAX_LUMINANCE = 0.18f
|
||||||
private const val PRIMARY_MIN_LUMINANCE = 0.30f
|
private const val PRIMARY_MIN_LUMINANCE = 0.30f
|
||||||
private const val MIN_GRADIENT_SATURATION = 0.30f
|
private const val MIN_GRADIENT_SATURATION = 0.30f
|
||||||
private const val HERO_DARKEN_MAX = 1.0f
|
private const val HERO_DARKEN_MAX = 0.88f
|
||||||
private const val DARKEN_START_SCREENS = 0.5f
|
private const val DARKEN_START_SCREENS = 0.5f
|
||||||
private const val DARKEN_END_SCREENS = 1.5f
|
private const val DARKEN_END_SCREENS = 1.5f
|
||||||
private const val DARKEN_KNEE = 0.2f
|
private const val DARKEN_KNEE = 0.2f
|
||||||
@@ -166,8 +166,10 @@ fun rememberArtworkColors(
|
|||||||
ref: CoverArtRef?,
|
ref: CoverArtRef?,
|
||||||
durationMillis: Int = ARTWORK_THEME_TRANSITION_MS,
|
durationMillis: Int = ARTWORK_THEME_TRANSITION_MS,
|
||||||
): ArtworkColors {
|
): ArtworkColors {
|
||||||
|
val cached = remember(ref?.cacheKey) { ref?.cacheKey?.let(ArtworkSeedCache::cached) }
|
||||||
val seeds by rememberArtworkSeed(ref)
|
val seeds by rememberArtworkSeed(ref)
|
||||||
val target = remember(seeds) { seeds?.let { (primary, secondary) -> artworkColorsFromSeeds(primary, secondary) } }
|
val effective = seeds ?: cached
|
||||||
|
val target = remember(effective) { effective?.let { (primary, secondary) -> artworkColorsFromSeeds(primary, secondary) } }
|
||||||
return rememberAnimatedArtworkColors(target, durationMillis)
|
return rememberAnimatedArtworkColors(target, durationMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user