From 92ef7bfcfb025d2caca1ba9c457907d3903d64c3 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Thu, 24 Sep 2026 06:09:25 +0000 Subject: [PATCH] make the now playing slider seekable and show full artwork --- .../subtracks/playback/PlaybackController.kt | 5 +++- .../com/subtracks/ui/components/Components.kt | 27 ++++++++++++----- .../ui/components/HeroDetailScaffold.kt | 3 +- .../subtracks/ui/playback/NowPlayingScreen.kt | 24 ++++++++++++--- .../media3_notification_small_icon.xml | 29 +++++++++++++++++++ 5 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 app/src/main/res/drawable/media3_notification_small_icon.xml diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt index a0c01a09..1f4379a7 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt @@ -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( diff --git a/app/src/main/kotlin/com/subtracks/ui/components/Components.kt b/app/src/main/kotlin/com/subtracks/ui/components/Components.kt index 6bfb1a9b..c3b8a1bf 100644 --- a/app/src/main/kotlin/com/subtracks/ui/components/Components.kt +++ b/app/src/main/kotlin/com/subtracks/ui/components/Components.kt @@ -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(), ) } diff --git a/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt b/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt index 3dd43b0d..3af16cd2 100644 --- a/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt +++ b/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt @@ -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)), ) diff --git a/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt b/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt index d98bcbd0..ae46b429 100644 --- a/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt @@ -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( diff --git a/app/src/main/res/drawable/media3_notification_small_icon.xml b/app/src/main/res/drawable/media3_notification_small_icon.xml new file mode 100644 index 00000000..8d65de57 --- /dev/null +++ b/app/src/main/res/drawable/media3_notification_small_icon.xml @@ -0,0 +1,29 @@ + + + + + + +