mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
fix cover art sizing and downsampling
This commit is contained in:
@@ -4,9 +4,11 @@ import android.content.Context
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -19,12 +21,12 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.compose.AsyncImagePainter
|
|
||||||
import coil3.compose.LocalPlatformContext
|
import coil3.compose.LocalPlatformContext
|
||||||
import coil3.compose.rememberAsyncImagePainter
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
@@ -42,24 +44,85 @@ fun CoverArt(
|
|||||||
elevation: Dp = 0.dp,
|
elevation: Dp = 0.dp,
|
||||||
) {
|
) {
|
||||||
val context = LocalPlatformContext.current
|
val context = LocalPlatformContext.current
|
||||||
|
var failed by remember(ref, thumbnailRef) { mutableStateOf(false) }
|
||||||
var thumbnailLoaded by remember(ref, thumbnailRef) { mutableStateOf(false) }
|
var thumbnailLoaded by remember(ref, thumbnailRef) { mutableStateOf(false) }
|
||||||
val model = remember(ref, thumbnailRef) { ref?.let { imageRequest(context, it, crossfade = thumbnailRef != null) } }
|
val model = remember(ref, thumbnailRef) { ref?.let { imageRequest(context, it, crossfade = thumbnailRef != null) } }
|
||||||
val painter = rememberAsyncImagePainter(model)
|
val frameModifier =
|
||||||
val failed = painter.state is AsyncImagePainter.State.Error
|
Modifier
|
||||||
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)
|
|
||||||
.shadow(elevation, RoundedCornerShape(2.dp), clip = elevation > 0.dp)
|
.shadow(elevation, RoundedCornerShape(2.dp), clip = elevation > 0.dp)
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
|
||||||
|
if (square) {
|
||||||
|
Box(modifier.then(frameModifier)) {
|
||||||
|
CoverArtContent(
|
||||||
|
ref = ref,
|
||||||
|
name = name,
|
||||||
|
thumbnailRef = thumbnailRef,
|
||||||
|
showPlaceholder = showPlaceholder,
|
||||||
|
failed = failed,
|
||||||
|
thumbnailLoaded = thumbnailLoaded,
|
||||||
|
onThumbnailLoaded = { thumbnailLoaded = true },
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
) {
|
) {
|
||||||
|
if (ref != null) {
|
||||||
|
AsyncImage(
|
||||||
|
model = model,
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
onError = { failed = true },
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val painter =
|
||||||
|
rememberAsyncImagePainter(
|
||||||
|
model = model,
|
||||||
|
onError = { failed = true },
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
)
|
||||||
|
BoxWithConstraints(modifier, contentAlignment = Alignment.Center) {
|
||||||
|
val ratio = painter.intrinsicSize.ratioOrOne()
|
||||||
|
val width = if (ratio > maxWidth.value / maxHeight.value) maxWidth else maxHeight * ratio
|
||||||
|
Box(Modifier.size(width, width / ratio).then(frameModifier)) {
|
||||||
|
CoverArtContent(
|
||||||
|
ref = ref,
|
||||||
|
name = name,
|
||||||
|
thumbnailRef = thumbnailRef,
|
||||||
|
showPlaceholder = showPlaceholder,
|
||||||
|
failed = failed,
|
||||||
|
thumbnailLoaded = thumbnailLoaded,
|
||||||
|
onThumbnailLoaded = { thumbnailLoaded = true },
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
) {
|
||||||
|
if (ref != null) {
|
||||||
|
Image(
|
||||||
|
painter = painter,
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BoxScope.CoverArtContent(
|
||||||
|
ref: CoverArtRef?,
|
||||||
|
name: String,
|
||||||
|
thumbnailRef: CoverArtRef?,
|
||||||
|
showPlaceholder: Boolean,
|
||||||
|
failed: Boolean,
|
||||||
|
thumbnailLoaded: Boolean,
|
||||||
|
onThumbnailLoaded: () -> Unit,
|
||||||
|
contentScale: ContentScale,
|
||||||
|
main: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
val context = LocalPlatformContext.current
|
||||||
if (showPlaceholder || (failed && !thumbnailLoaded)) {
|
if (showPlaceholder || (failed && !thumbnailLoaded)) {
|
||||||
Text(
|
Text(
|
||||||
text = name.trim().take(1).uppercase(),
|
text = name.trim().take(1).uppercase(),
|
||||||
@@ -72,22 +135,16 @@ fun CoverArt(
|
|||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) },
|
model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) },
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = if (square) ContentScale.Crop else ContentScale.Fit,
|
contentScale = contentScale,
|
||||||
onSuccess = { thumbnailLoaded = true },
|
onSuccess = { onThumbnailLoaded() },
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (ref != null) {
|
main()
|
||||||
Image(
|
|
||||||
painter = painter,
|
|
||||||
contentDescription = null,
|
|
||||||
contentScale = if (square) ContentScale.Crop else ContentScale.Fit,
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Size.ratioOrOne(): Float = if (width > 0f && height > 0f && width.isFinite() && height.isFinite()) width / height else 1f
|
||||||
|
|
||||||
private fun imageRequest(
|
private fun imageRequest(
|
||||||
context: Context,
|
context: Context,
|
||||||
ref: CoverArtRef,
|
ref: CoverArtRef,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.rounded.Download
|
import androidx.compose.material.icons.rounded.Download
|
||||||
@@ -47,9 +46,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.draw.shadow
|
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -17,7 +15,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.rounded.QueueMusic
|
import androidx.compose.material.icons.automirrored.rounded.QueueMusic
|
||||||
@@ -46,7 +43,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
@@ -203,7 +199,7 @@ fun NowPlayingScreen(
|
|||||||
thumbnailRef = thumbnailRef,
|
thumbnailRef = thumbnailRef,
|
||||||
square = false,
|
square = false,
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
modifier = Modifier.fillMaxHeight(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
@@ -256,6 +252,7 @@ fun NowPlayingScreen(
|
|||||||
dragging = false
|
dragging = false
|
||||||
},
|
},
|
||||||
valueRange = 0f..state.durationMs.toFloat().coerceAtLeast(1f),
|
valueRange = 0f..state.durationMs.toFloat().coerceAtLeast(1f),
|
||||||
|
enabled = state.durationMs > 0,
|
||||||
modifier = Modifier.padding(top = 28.dp),
|
modifier = Modifier.padding(top = 28.dp),
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
Reference in New Issue
Block a user