mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
polish the themed screens: comment line, brighter mini player, art preloading
This commit is contained in:
@@ -384,6 +384,13 @@ class PlaybackController(
|
||||
thumbnail: Boolean = false,
|
||||
): CoverArtRef? = sourceRepository.coverArt(item?.coverArtId, thumbnail)
|
||||
|
||||
suspend fun upcomingCoverArt(thumbnail: Boolean = false): CoverArtRef? {
|
||||
val snap = snapshot ?: return null
|
||||
val position = currentPosition() ?: return null
|
||||
val next = queueRepository.itemAt(snap, position + 1) ?: return null
|
||||
return sourceRepository.coverArt(next.toQueueItem().coverArtId, thumbnail)
|
||||
}
|
||||
|
||||
private fun play(
|
||||
entries: List<QueueEntry>,
|
||||
startPosition: Long,
|
||||
|
||||
@@ -213,6 +213,7 @@ fun HeroHeader(
|
||||
topInset: Dp,
|
||||
controlsModifier: Modifier = Modifier,
|
||||
thumbnailRef: CoverArtRef? = null,
|
||||
comment: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
@@ -252,6 +253,17 @@ fun HeroHeader(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
if (!comment.isNullOrBlank()) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = comment,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(20.dp))
|
||||
Row(
|
||||
modifier = controlsModifier,
|
||||
|
||||
@@ -160,7 +160,7 @@ fun ArtistDetailScreen(
|
||||
ref = art,
|
||||
name = artist?.name.orEmpty(),
|
||||
thumbnailRef = artThumbnail,
|
||||
showPlaceholder = false,
|
||||
showPlaceholder = art == null,
|
||||
modifier = Modifier.fillMaxWidth().height(ART_HEIGHT),
|
||||
)
|
||||
Text(
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.subtracks.ui.components.CoverArt
|
||||
import com.subtracks.ui.components.EmptyState
|
||||
import com.subtracks.ui.components.LoadingState
|
||||
import com.subtracks.ui.components.rememberViewportFill
|
||||
import com.subtracks.ui.theme.PrefetchArtworkSeeds
|
||||
|
||||
@Composable
|
||||
fun ArtistsContent(
|
||||
@@ -57,6 +58,7 @@ fun ArtistsContent(
|
||||
items(count = items.itemCount, key = items.itemKey { it.id }) { index ->
|
||||
val artist = items[index]
|
||||
if (artist != null) {
|
||||
PrefetchArtworkSeeds(coverArt(artist.coverArt, true))
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
|
||||
@@ -65,7 +65,8 @@ fun PlaylistDetailScreen(
|
||||
art = coverArt(playlist?.coverArt, false),
|
||||
thumbnailRef = coverArt(playlist?.coverArt, true),
|
||||
name = playlist?.name.orEmpty(),
|
||||
subtitle = playlistSubtitle(playlist),
|
||||
subtitle = playlistSummary(playlist),
|
||||
comment = playlist?.comment,
|
||||
hasSongs = songs.itemCount > 0,
|
||||
onPlay = { onSongClick(0) },
|
||||
onShuffle = {},
|
||||
@@ -93,12 +94,11 @@ fun PlaylistDetailScreen(
|
||||
)
|
||||
}
|
||||
|
||||
private fun playlistSubtitle(playlist: Playlist?): String {
|
||||
private fun playlistSummary(playlist: Playlist?): String {
|
||||
playlist ?: return ""
|
||||
return listOfNotNull(
|
||||
"${playlist.songCount} ${if (playlist.songCount == 1L) "song" else "songs"}",
|
||||
formatDuration(playlist.duration),
|
||||
playlist.comment?.takeIf { it.isNotBlank() },
|
||||
).joinToString(" $DOT ")
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ fun MiniPlayer(
|
||||
val item = state.item ?: return
|
||||
ArtworkTheme(artwork) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
color = artwork?.accents?.firstOrNull() ?: MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
modifier =
|
||||
modifier.fillMaxWidth().clickable(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.subtracks.ui.playback
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -34,6 +35,7 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -45,11 +47,15 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.SingletonImageLoader
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import com.subtracks.data.model.CoverArtRef
|
||||
import com.subtracks.playback.PlaybackController
|
||||
import com.subtracks.playback.PlaybackState
|
||||
import com.subtracks.ui.components.CoverArt
|
||||
import com.subtracks.ui.theme.ArtworkColors
|
||||
import com.subtracks.ui.theme.ArtworkSeedCache
|
||||
import com.subtracks.ui.theme.ArtworkTheme
|
||||
import com.subtracks.ui.theme.HeroGradient
|
||||
import com.subtracks.ui.theme.rememberArtworkColors
|
||||
@@ -63,11 +69,22 @@ fun NowPlayingRoute(
|
||||
controller: PlaybackController = koinInject(),
|
||||
) {
|
||||
val state by controller.state.collectAsStateWithLifecycle()
|
||||
val context = LocalPlatformContext.current
|
||||
val art = controller.coverArt(state.item)
|
||||
val thumbnail = controller.coverArt(state.item, thumbnail = true)
|
||||
LaunchedEffect(state.item?.id, state.hasNext) {
|
||||
if (!state.hasNext) return@LaunchedEffect
|
||||
controller.upcomingCoverArt(thumbnail = true)?.let {
|
||||
ArtworkSeedCache.prefetch(context, it)
|
||||
prefetchImage(context, it)
|
||||
}
|
||||
controller.upcomingCoverArt()?.let { prefetchImage(context, it) }
|
||||
}
|
||||
NowPlayingScreen(
|
||||
state = state,
|
||||
coverArt = art,
|
||||
artwork = rememberArtworkColors(art),
|
||||
thumbnailRef = thumbnail,
|
||||
artwork = rememberArtworkColors(thumbnail ?: art),
|
||||
onBack = onBack,
|
||||
onQueue = onQueue,
|
||||
onPlayPause = controller::togglePlayPause,
|
||||
@@ -78,6 +95,20 @@ fun NowPlayingRoute(
|
||||
)
|
||||
}
|
||||
|
||||
private fun prefetchImage(
|
||||
context: Context,
|
||||
ref: CoverArtRef,
|
||||
) {
|
||||
SingletonImageLoader.get(context).enqueue(
|
||||
ImageRequest
|
||||
.Builder(context)
|
||||
.data(ref.url)
|
||||
.memoryCacheKey(ref.cacheKey)
|
||||
.diskCacheKey(ref.cacheKey)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NowPlayingScreen(
|
||||
@@ -90,6 +121,7 @@ fun NowPlayingScreen(
|
||||
onNext: () -> Unit,
|
||||
onPrevious: () -> Unit,
|
||||
onSeek: (Long) -> Unit,
|
||||
thumbnailRef: CoverArtRef? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val playButtonSize = 96.dp
|
||||
@@ -133,6 +165,7 @@ fun NowPlayingScreen(
|
||||
CoverArt(
|
||||
ref = coverArt,
|
||||
name = state.item?.title.orEmpty(),
|
||||
thumbnailRef = thumbnailRef,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
|
||||
Reference in New Issue
Block a user