Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 63 additions & 28 deletions app/src/main/kotlin/me/echeung/moemoekyun/widget/RadioWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.echeung.moemoekyun.widget
import android.content.Context
import android.graphics.Bitmap
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand Down Expand Up @@ -42,6 +43,7 @@ import androidx.glance.text.TextStyle
import coil3.imageLoader
import coil3.request.ImageRequest
import coil3.request.SuccessResult
import coil3.request.allowHardware
import coil3.toBitmap
import me.echeung.moemoekyun.R

Expand Down Expand Up @@ -84,6 +86,9 @@ class RadioWidget : GlanceAppWidget() {
val showTitle = size.width >= SIZE_MEDIUM.width
val showArtistAndFav = size.width >= SIZE_LARGE.width

// SIZE_MEDIUM+ matches the collapsed player design; SIZE_SMALL stays compact.
val useLargeLayout = showTitle

Box(
modifier = GlanceModifier
.fillMaxSize()
Expand All @@ -92,26 +97,39 @@ class RadioWidget : GlanceAppWidget() {
contentAlignment = Alignment.CenterStart,
) {
Row(
modifier = GlanceModifier.fillMaxSize().padding(8.dp),
modifier = GlanceModifier
.fillMaxSize()
.padding(
horizontal = if (useLargeLayout) 12.dp else 8.dp,
vertical = if (useLargeLayout) 10.dp else 8.dp,
),
verticalAlignment = Alignment.CenterVertically,
) {
AlbumArtImage(albumArt)
AlbumArtImage(
bitmap = albumArt,
size = if (useLargeLayout) 56.dp else 40.dp,
cornerRadius = if (useLargeLayout) 8.dp else 6.dp,
)

Spacer(GlanceModifier.width(8.dp))
Spacer(GlanceModifier.width(if (useLargeLayout) 12.dp else 8.dp))

if (showTitle) {
// Matches collapsed player order: art → FAB → text → fav
PlayPauseButton(isPlaying, useFabStyle = true)

Spacer(GlanceModifier.width(12.dp))

Column(
modifier = GlanceModifier
.defaultWeight()
.fillMaxHeight()
.padding(horizontal = 4.dp),
.fillMaxHeight(),
verticalAlignment = Alignment.CenterVertically,
) {
if (title != null) {
Text(
text = title,
style = TextStyle(
fontSize = 13.sp,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = GlanceTheme.colors.onSurface,
),
Expand All @@ -121,7 +139,7 @@ class RadioWidget : GlanceAppWidget() {
Text(
text = artists,
style = TextStyle(
fontSize = 11.sp,
fontSize = 12.sp,
color = GlanceTheme.colors.secondary,
),
maxLines = 1,
Expand All @@ -134,23 +152,21 @@ class RadioWidget : GlanceAppWidget() {
)
}
}

if (showArtistAndFav && isAuthenticated && songId != null) {
FavoriteButton(songId, isFavorited)
}
} else {
Spacer(GlanceModifier.defaultWeight())
}

PlayPauseButton(isPlaying)

if (showArtistAndFav && isAuthenticated && songId != null) {
Spacer(GlanceModifier.width(4.dp))
FavoriteButton(songId, isFavorited)
PlayPauseButton(isPlaying, useFabStyle = false)
}
}
}
}

@Composable
private fun AlbumArtImage(bitmap: Bitmap?) {
val mod = GlanceModifier.size(40.dp).cornerRadius(6.dp)
private fun AlbumArtImage(bitmap: Bitmap?, size: Dp = 40.dp, cornerRadius: Dp = 6.dp) {
val mod = GlanceModifier.size(size).cornerRadius(cornerRadius)
if (bitmap != null) {
Image(
provider = ImageProvider(bitmap),
Expand All @@ -169,15 +185,34 @@ class RadioWidget : GlanceAppWidget() {
}

@Composable
private fun PlayPauseButton(isPlaying: Boolean) {
Image(
provider = ImageProvider(if (isPlaying) R.drawable.ic_pause_24dp else R.drawable.ic_play_arrow_24dp),
contentDescription = null,
colorFilter = ColorFilter.tint(GlanceTheme.colors.onSurface),
modifier = GlanceModifier
.size(40.dp)
.clickable(actionRunCallback<PlayPauseAction>()),
)
private fun PlayPauseButton(isPlaying: Boolean, useFabStyle: Boolean = false) {
val icon = ImageProvider(if (isPlaying) R.drawable.ic_pause_24dp else R.drawable.ic_play_arrow_24dp)
if (useFabStyle) {
Box(
modifier = GlanceModifier
.size(48.dp)
.cornerRadius(24.dp)
.background(GlanceTheme.colors.primary)
.clickable(actionRunCallback<PlayPauseAction>()),
contentAlignment = Alignment.Center,
) {
Image(
provider = icon,
contentDescription = null,
colorFilter = ColorFilter.tint(GlanceTheme.colors.onPrimary),
modifier = GlanceModifier.size(24.dp),
)
}
} else {
Image(
provider = icon,
contentDescription = null,
colorFilter = ColorFilter.tint(GlanceTheme.colors.onSurface),
modifier = GlanceModifier
.size(40.dp)
.clickable(actionRunCallback<PlayPauseAction>()),
)
}
}

@Composable
Expand All @@ -200,15 +235,15 @@ class RadioWidget : GlanceAppWidget() {

companion object {
private val SIZE_SMALL = DpSize(110.dp, 56.dp)
private val SIZE_MEDIUM = DpSize(180.dp, 56.dp)
private val SIZE_LARGE = DpSize(270.dp, 56.dp)
private val SIZE_MEDIUM = DpSize(180.dp, 80.dp)
private val SIZE_LARGE = DpSize(270.dp, 80.dp)
}
}

private suspend fun loadAlbumArt(context: Context, url: String?): Bitmap? {
if (url == null) return null
return try {
val request = ImageRequest.Builder(context).data(url).build()
val request = ImageRequest.Builder(context).data(url).allowHardware(false).build()
val result = context.imageLoader.execute(request)
(result as? SuccessResult)?.image?.toBitmap()
} catch (_: Exception) {
Expand Down