mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
make the now playing slider seekable and show full artwork
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="299.84"
|
||||
android:viewportHeight="219.51">
|
||||
<group android:scaleX="0.43"
|
||||
android:scaleY="0.3147989"
|
||||
android:translateX="85.4544"
|
||||
android:translateY="75.20425">
|
||||
<path
|
||||
android:pathData="m23.84,-0c-12.76,0 -23.44,13 -23.65,25.12 -0.42,79.03 0,160.73 0,170.4 0,12.59 9.8,23.99 23.41,23.99l253.58,0c11.4,0 22.66,-12.38 22.66,-22.79L299.84,24.12c0,-10.8 -10.24,-23.81 -21.84,-23.81 -11.6,0 -254.16,-0.31 -254.16,-0.31zM39.05,25.28c0,0 217.71,0.71 223.67,0.71 6.07,0 12.17,6.83 12.17,12.06l0,142.25c0,8.97 -6.97,14.63 -12.48,14.64l-33.42,0.07c-5.32,0.01 -9.93,-7.19 -11.35,-10.61 -1.89,-4.55 -11.08,-25.75 -12.1,-28.1 -0.6,-1.38 -2.9,-2.83 -5.89,-2.83L98.33,153.45c-2.68,0 -4.89,2.87 -5.67,4.71 -1.12,2.66 -9.02,21.39 -11.63,27.57 -2.73,6.46 -4.37,9.06 -11.47,9.06l-30.97,0c-7.44,0 -13.69,-7.17 -13.67,-15.18l0.28,-139.51c0,-6.88 5.54,-14.83 13.85,-14.83z"
|
||||
android:strokeWidth="0.9"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M90.09,95.03m-32.1,0a32.42,32.1 90,1 1,64.21 0a32.42,32.1 90,1 1,-64.21 0"
|
||||
android:strokeWidth="0.86"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M209.94,95.03m-32.1,0a32.42,32.1 90,1 1,64.21 0a32.42,32.1 90,1 1,-64.21 0"
|
||||
android:strokeWidth="0.86"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
</group>
|
||||
</vector>
|
||||
Reference in New Issue
Block a user