From 2b2026463b87e4ca63aa4a2a8830a4821cedb45e Mon Sep 17 00:00:00 2001 From: Robert Holak <423088+rholak@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:34:33 -0500 Subject: [PATCH 1/2] android: library: clarify track subtitles Artist pages showed track artist instead of album artist, while album pages repeated the album title in every track row. Allow callers to override PlaylistMemberRow subtitles. Artist pages now show album artist and album, falling back to track artist when needed; album pages show track artist only. Other callers retain their existing subtitles. Verified with :app:assembleDebug through the managed Gradle wrapper and installation on a USB phone. Both views were visually confirmed by the user. Unit tests were not run. --- .../main/java/stream/kleeamp/mobile/library/AlbumPane.kt | 1 + .../main/java/stream/kleeamp/mobile/library/ArtistPane.kt | 1 + .../main/java/stream/kleeamp/mobile/library/SongCover.kt | 3 ++- .../src/main/java/stream/kleeamp/mobile/model/Station.kt | 6 ++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/stream/kleeamp/mobile/library/AlbumPane.kt b/android/app/src/main/java/stream/kleeamp/mobile/library/AlbumPane.kt index 7a1980c5..ff2fdccb 100644 --- a/android/app/src/main/java/stream/kleeamp/mobile/library/AlbumPane.kt +++ b/android/app/src/main/java/stream/kleeamp/mobile/library/AlbumPane.kt @@ -126,6 +126,7 @@ fun LibraryAlbumPane( items(shown, key = { it.id }) { s -> PlaylistMemberRow( s = s, + subtitle = s.artist, current = current, playing = playing, queue = shown, diff --git a/android/app/src/main/java/stream/kleeamp/mobile/library/ArtistPane.kt b/android/app/src/main/java/stream/kleeamp/mobile/library/ArtistPane.kt index 67d23e86..102a5844 100644 --- a/android/app/src/main/java/stream/kleeamp/mobile/library/ArtistPane.kt +++ b/android/app/src/main/java/stream/kleeamp/mobile/library/ArtistPane.kt @@ -123,6 +123,7 @@ fun LibraryArtistPane( items(shown, key = { it.id }) { s -> PlaylistMemberRow( s = s, + subtitle = s.albumArtistAlbum, current = current, playing = playing, queue = shown, diff --git a/android/app/src/main/java/stream/kleeamp/mobile/library/SongCover.kt b/android/app/src/main/java/stream/kleeamp/mobile/library/SongCover.kt index d21002df..63bf6358 100644 --- a/android/app/src/main/java/stream/kleeamp/mobile/library/SongCover.kt +++ b/android/app/src/main/java/stream/kleeamp/mobile/library/SongCover.kt @@ -365,6 +365,7 @@ internal fun PlaylistMemberRow( queue: List, onPlay: (Station, List) -> Unit, onOpenMenu: (Station) -> Unit, + subtitle: String = s.artistAlbum, ) { val p = LocalPalette.current ListRow( @@ -385,7 +386,7 @@ internal fun PlaylistMemberRow( ) { Mono(s.name, KleeampType.rowPrimary, if (current?.url == s.url) p.accent else p.ink, maxLines = 1) Mono( - s.artistAlbum.ifBlank { + subtitle.ifBlank { when { s.source == StationSource.Local -> durationLabel(s.durationMs) s.source == StationSource.Podcast -> "podcast" diff --git a/android/app/src/main/java/stream/kleeamp/mobile/model/Station.kt b/android/app/src/main/java/stream/kleeamp/mobile/model/Station.kt index 1dd5e902..72d6dd87 100644 --- a/android/app/src/main/java/stream/kleeamp/mobile/model/Station.kt +++ b/android/app/src/main/java/stream/kleeamp/mobile/model/Station.kt @@ -50,6 +50,12 @@ data class Station( .filter { it.isNotBlank() } .joinToString(" · ") + /** `album artist · album`, falling back to track artist when album artist is blank. */ + val albumArtistAlbum: String + get() = listOf(albumArtist.ifBlank { artist }, album) + .filter { it.isNotBlank() } + .joinToString(" · ") + /** * A finite thing with an end, as opposed to a live stream. Decides whether * the queue is handed to the player as a real playlist it can advance From bd490887d0783f883809efc9da0d7002e52f15b1 Mon Sep 17 00:00:00 2001 From: Robert Holak <423088+rholak@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:36:56 -0500 Subject: [PATCH 2/2] android: library: test album artist subtitles The new album-artist subtitle formatter lacked automated coverage for metadata precedence and missing fields. Add tests for album-artist precedence, track-artist fallback, blank-field separator handling, and unchanged track-artist formatting. Verified :app:testDebugUnitTest through the managed Gradle wrapper and git diff --check. All checks passed. --- .../kleeamp/mobile/model/StationTest.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/android/app/src/test/java/stream/kleeamp/mobile/model/StationTest.kt b/android/app/src/test/java/stream/kleeamp/mobile/model/StationTest.kt index 8149755a..09de0e4b 100644 --- a/android/app/src/test/java/stream/kleeamp/mobile/model/StationTest.kt +++ b/android/app/src/test/java/stream/kleeamp/mobile/model/StationTest.kt @@ -1,5 +1,6 @@ package stream.kleeamp.mobile.model +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -12,6 +13,34 @@ class StationTest { source = source, ) + @Test fun albumArtistAlbumPrefersAlbumArtist() { + val song = station(StationSource.Local, "l1").copy( + artist = "Daft Punk / Gabrielle", + albumArtist = "Daft Punk", + album = "Alive 2007", + ) + assertEquals("Daft Punk · Alive 2007", song.albumArtistAlbum) + assertEquals("Daft Punk / Gabrielle · Alive 2007", song.artistAlbum) + } + + @Test fun albumArtistAlbumFallsBackToTrackArtist() { + val song = station(StationSource.Local, "l1").copy( + artist = "Track Artist", + album = "Album", + ) + assertEquals("Track Artist · Album", song.albumArtistAlbum) + assertEquals("Track Artist · Album", song.copy(albumArtist = " ").albumArtistAlbum) + } + + @Test fun albumArtistAlbumOmitsBlankFieldsAndSeparators() { + val song = station(StationSource.Local, "l1") + assertEquals("", song.albumArtistAlbum) + assertEquals("Album Artist", song.copy(albumArtist = "Album Artist").albumArtistAlbum) + assertEquals("Track Artist", song.copy(artist = "Track Artist").albumArtistAlbum) + assertEquals("Album", song.copy(album = "Album").albumArtistAlbum) + assertEquals("", song.copy(artist = " ", albumArtist = " ", album = " ").albumArtistAlbum) + } + @Test fun channelTrackIdIsATrack() { val track = station(StationSource.Cliamp, "cliamp:edm:42") assertTrue(track.isChannelTrack)