make the now playing slider seekable and show full artwork

This commit is contained in:
austinried
2026-10-04 00:29:54 +00:00
parent d7434b80cc
commit 92ef7bfcfb
5 changed files with 74 additions and 14 deletions
@@ -376,7 +376,10 @@ class PlaybackController(
fun previous() = skip(-1)
fun seekTo(positionMs: Long) {
player?.seekTo(positionMs)
val player = player ?: return
player.ensurePrepared()
player.seekTo(positionMs)
player.play()
}
fun coverArt(
@@ -1,8 +1,10 @@
package com.subtracks.ui.components
import android.content.Context
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
@@ -19,7 +21,9 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.compose.AsyncImagePainter
import coil3.compose.LocalPlatformContext
import coil3.compose.rememberAsyncImagePainter
import coil3.request.ImageRequest
import coil3.request.crossfade
import com.subtracks.data.model.CoverArtRef
@@ -31,11 +35,21 @@ fun CoverArt(
modifier: Modifier = Modifier,
thumbnailRef: CoverArtRef? = null,
showPlaceholder: Boolean = true,
square: Boolean = true,
) {
val context = LocalPlatformContext.current
var failed by remember(ref, thumbnailRef) { mutableStateOf(false) }
var thumbnailLoaded by remember(ref, thumbnailRef) { mutableStateOf(false) }
Box(modifier = modifier.background(MaterialTheme.colorScheme.surfaceVariant)) {
val model = remember(ref, thumbnailRef) { ref?.let { imageRequest(context, it, crossfade = thumbnailRef != null) } }
val painter = rememberAsyncImagePainter(model)
val failed = painter.state is AsyncImagePainter.State.Error
val intrinsic = painter.intrinsicSize
val ratio =
if (!square && intrinsic.width > 0f && intrinsic.height > 0f && intrinsic.width.isFinite() && intrinsic.height.isFinite()) {
intrinsic.width / intrinsic.height
} else {
1f
}
Box(modifier = modifier.aspectRatio(ratio).background(MaterialTheme.colorScheme.surfaceVariant)) {
if (showPlaceholder || (failed && !thumbnailLoaded)) {
Text(
text = name.trim().take(1).uppercase(),
@@ -48,17 +62,16 @@ fun CoverArt(
AsyncImage(
model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) },
contentDescription = null,
contentScale = ContentScale.Crop,
contentScale = if (square) ContentScale.Crop else ContentScale.Fit,
onSuccess = { thumbnailLoaded = true },
modifier = Modifier.fillMaxSize(),
)
}
if (ref != null) {
AsyncImage(
model = remember(ref, thumbnailRef) { imageRequest(context, ref, crossfade = thumbnailRef != null) },
Image(
painter = painter,
contentDescription = null,
contentScale = ContentScale.Crop,
onError = { failed = true },
contentScale = if (square) ContentScale.Crop else ContentScale.Fit,
modifier = Modifier.fillMaxSize(),
)
}
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -234,10 +233,10 @@ fun HeroHeader(
ref = art,
name = name,
thumbnailRef = thumbnailRef,
square = false,
modifier =
Modifier
.fillMaxWidth(0.86f)
.aspectRatio(1f)
.shadow(elevation = 3.dp, shape = RoundedCornerShape(2.dp), clip = false)
.clip(RoundedCornerShape(2.dp)),
)
@@ -30,6 +30,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
@@ -46,6 +47,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -125,6 +127,14 @@ fun NowPlayingScreen(
) {
val playButtonSize = 96.dp
val playCircleDiameter = playButtonSize * 20f / 24f
val titleStyle =
MaterialTheme.typography.headlineSmall.copy(
platformStyle = PlatformTextStyle(includeFontPadding = false),
)
val subtitleStyle =
MaterialTheme.typography.bodyMedium.copy(
platformStyle = PlatformTextStyle(includeFontPadding = false),
)
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
ArtworkTheme(artwork) {
@@ -172,23 +182,23 @@ fun NowPlayingScreen(
ref = coverArt,
name = state.item?.title.orEmpty(),
thumbnailRef = thumbnailRef,
square = false,
modifier =
Modifier
.fillMaxHeight()
.aspectRatio(1f)
.shadow(elevation = 3.dp, shape = RoundedCornerShape(2.dp), clip = false)
.clip(RoundedCornerShape(2.dp)),
)
}
Text(
text = state.item?.title.orEmpty(),
style = MaterialTheme.typography.headlineSmall,
style = titleStyle,
maxLines = 1,
modifier = Modifier.padding(top = 28.dp).basicMarquee(),
)
Text(
text = listOfNotNull(state.item?.artist, state.item?.album).joinToString(" • "),
style = MaterialTheme.typography.bodyMedium,
style = subtitleStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
modifier = Modifier.padding(top = 6.dp).basicMarquee(),
@@ -206,7 +216,13 @@ fun NowPlayingScreen(
dragging = false
},
valueRange = 0f..state.durationMs.toFloat().coerceAtLeast(1f),
enabled = state.durationMs > 0,
track = { sliderState ->
SliderDefaults.Track(
sliderState = sliderState,
thumbTrackGapSize = 0.dp,
drawStopIndicator = {},
)
},
modifier = Modifier.padding(top = 28.dp),
)
Row(