From 3ceba8a36545a32de5e2f4b179fbf873e1235629 Mon Sep 17 00:00:00 2001 From: Honey Date: Tue, 1 Sep 2026 23:58:24 +0200 Subject: [PATCH] feat(android): partial-album detail gets Play/Shuffle + track 3-dot menu You can still enjoy an incomplete album: the downloaded-tracks section now has Abspielen + Zufall buttons (play/shuffle the downloaded songs) and each downloaded row gets the 3-dot context menu, threaded from both artist screens. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 + .../app/ui/screen/ArtistDetailScreen.kt | 1 + .../novasonic/app/ui/screen/ExploreScreen.kt | 1 + .../app/ui/screen/PartialAlbumScreen.kt | 49 +++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d4a2dd..a4ea78f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -571,6 +571,8 @@ Polish release: Android navigation and offline resilience, download accuracy for - **Android** — Romaji / Japanese sort support - **Web + Android** — Backend settings UI rework (move, reorganize) - **Web + Android** — yt-dlp backend update / maintenance +- **Web + Andoird** - Safe last played Queue, where it stopped and show last played song loaded (rn only on web kinda with the last song played loaded) +- **Android** - Andorid Auto Sync last played and queue too with Phone then, rn its not really syncing or safin what last played on Android Auto, also check if selecting a song to play on Android Auto works that song is getting played and in a playlist it will play the queue a this point then like it works already in Android and Web ### Known Bugs - **Android (lyrics)** — Synced-lyrics auto-scroll is **not smooth**: it follows playback and highlights the right line, but the list jumps to it rather than gliding. Web autoscroll is smooth. Not yet fixed. diff --git a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt index 57ff60b..b6f3c09 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt @@ -156,6 +156,7 @@ fun ArtistDetailScreen( onPreview = vm::togglePreview, onDownload = vm::showDownloadSheet, onBack = vm::dismissPartialAlbum, + onShowContextMenu = onShowContextMenu, ) return } diff --git a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ExploreScreen.kt b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ExploreScreen.kt index 9fb3fab..de5f62a 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ExploreScreen.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ExploreScreen.kt @@ -115,6 +115,7 @@ fun ExploreScreen( onPreview = vm::togglePreview, onDownload = vm::showDownloadSheet, onBack = vm::dismissPartialAlbum, + onShowContextMenu = onShowContextMenu, ) } albumTracks != null && albumItem != null -> { diff --git a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/PartialAlbumScreen.kt b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/PartialAlbumScreen.kt index a17c319..f53c155 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/PartialAlbumScreen.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/PartialAlbumScreen.kt @@ -5,9 +5,12 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.clickable import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Shuffle import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -41,6 +44,7 @@ fun PartialAlbumScreen( onPreview: (ItunesItem) -> Unit, onDownload: (ItunesItem) -> Unit, onBack: () -> Unit, + onShowContextMenu: ((Song) -> Unit)? = null, ) { val ia = partialAlbum.itunesAlbum val nd = partialAlbum.ndAlbum @@ -96,6 +100,50 @@ fun PartialAlbumScreen( .sortedBy { it.trackNumber ?: 999 } LazyColumn(Modifier.fillMaxSize()) { + // ── Play / Shuffle the downloaded tracks (you can still enjoy the album even when not + // every track is downloaded yet) ───────────────────────────────────────────────── + if (downloadedSongs.isNotEmpty()) { + item { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 4.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(10.dp)) + .background(NsAccent) + .clickable { onPlay(downloadedSongs, 0) } + .padding(vertical = 11.dp), + ) { + Icon(Icons.Default.PlayArrow, null, tint = NsBackground, modifier = Modifier.size(20.dp)) + Spacer(Modifier.width(6.dp)) + Text("Abspielen", fontFamily = Outfit, fontWeight = FontWeight.SemiBold, + fontSize = 14.sp, color = NsBackground) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(10.dp)) + .background(NsSurface2) + .clickable { onPlay(downloadedSongs.shuffled(), 0) } + .padding(vertical = 11.dp), + ) { + Icon(Icons.Default.Shuffle, null, tint = NsOnSurface, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Zufall", fontFamily = Outfit, fontWeight = FontWeight.SemiBold, + fontSize = 14.sp, color = NsOnSurface) + } + } + } + } + // ── Downloaded tracks ───────────────────────────────────────────── if (downloadedSongs.isNotEmpty()) { item { @@ -113,6 +161,7 @@ fun PartialAlbumScreen( starred = song.starred != null, onPlay = { onPlay(downloadedSongs, idx) }, onStarToggle = {}, + onMore = onShowContextMenu?.let { cb -> { cb(song) } }, ) } }