From 5b40da109b31d799aba46964538b2b75869cb322 Mon Sep 17 00:00:00 2001 From: Kelvin K Date: Tue, 25 Nov 2025 00:40:38 +0100 Subject: [PATCH] Artist thumbnail now fall back to album thumbnail --- .../main/LibraryArtistFragment.kt | 18 +++++++- .../platformplayer/states/StateLibrary.kt | 45 ++++++++++++++++++- .../viewholders/ArtistTileViewHolder.kt | 5 ++- app/src/main/res/layout/fragment_artist.xml | 2 +- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryArtistFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryArtistFragment.kt index ea9f6c49..6f2e81f5 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryArtistFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryArtistFragment.kt @@ -14,6 +14,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle @@ -22,6 +23,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 +import com.bumptech.glide.Glide import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UISlideOverlays @@ -359,7 +361,21 @@ class LibraryArtistFragment : MainFragment() { (_viewPager.adapter as ArtistViewPagerAdapter).artist = channel - _viewPager.adapter!!.notifyDataSetChanged() + _viewPager.adapter!!.notifyDataSetChanged(); + + val artistThumbnail = channel.getThumbnailOrAlbum(); + if(artistThumbnail != null) { + _creatorThumbnail.isVisible = true; + _creatorThumbnail.setThumbnail(channel.getThumbnailOrAlbum(), true, true); + Glide.with(_imageBanner) + .load(artistThumbnail) + .into(_imageBanner); + } + else { + _creatorThumbnail.isVisible = false; + Glide.with(_imageBanner).clear(_imageBanner); + } + this.channel = channel } diff --git a/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt b/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt index d4d3224f..2a5c4c8e 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt @@ -37,6 +37,8 @@ import java.io.File import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap class StateLibrary { @@ -488,6 +490,10 @@ class Artist { return AdhocPager({ listOf() }, getTracksPager(idLong)); } + fun getThumbnailOrAlbum(): String? { + return thumbnail ?: tryGetArtistThumbnail(id.toLongOrNull()); + } + companion object { val ID_UNKNOWN = "UNKNOWN"; val PROJECTION: Array = arrayOf(Artists._ID, @@ -495,6 +501,20 @@ class Artist { Artists.NUMBER_OF_TRACKS, Artists.NUMBER_OF_ALBUMS); + val thumbnailCache = ConcurrentHashMap(); + + fun tryGetArtistThumbnail(artistId: Long?): String? { + if(artistId == null) + return null; + if(thumbnailCache.containsKey(artistId)) + return thumbnailCache.get(artistId); + else { + val album = Album.getArtistAlbumWithThumbnail(artistId); + thumbnailCache.put(artistId, album?.thumbnail ?: ""); + return album?.thumbnail; + } + } + fun fromCursor(cursor: Cursor): Artist { val id = cursor.getString(0); val artist = cursor.getString(1); @@ -540,8 +560,11 @@ class Artist { cursor.moveToFirst(); val list = mutableListOf() while(!cursor.isAfterLast) { - list.add(fromCursor(cursor)); + val artist = fromCursor(cursor); cursor.moveToNext(); + if(artist.name == "") + continue; //TODO: Better way of detecting unknown? + list.add(artist); } return@use list; } @@ -685,6 +708,26 @@ class Album { return@use list; } } + fun getArtistAlbumWithThumbnail(artistId: Long): Album? { + val resolver = StateApp.instance.contextOrNull?.contentResolver; + if(resolver == null) { + Logger.w(TAG, "Album contentResolver not found"); + return null; + } + val cursor = resolver?.query( + MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, PROJECTION, "${MediaStore.Audio.Media.ARTIST_ID} = ?", arrayOf(artistId.toString()), + MediaStore.Audio.Albums.ALBUM + " ASC") ?: return null; + return cursor.use { + cursor.moveToFirst(); + while(!cursor.isAfterLast) { + val album = fromCursor(cursor); + if(album.thumbnail != null) + return album + cursor.moveToNext(); + } + return@use null; + } + } } } diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ArtistTileViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ArtistTileViewHolder.kt index 70fb0b4a..43eaea5e 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ArtistTileViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ArtistTileViewHolder.kt @@ -37,9 +37,10 @@ class ArtistTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder override fun bind(artist: Artist) { _artist = artist; _imageThumbnail?.let { - if (artist.thumbnail != null) + val thumbnail = artist.getThumbnailOrAlbum(); + if (thumbnail != null) Glide.with(it) - .load(artist.thumbnail) + .load(thumbnail) .placeholder(R.drawable.ic_artist) .into(it) else diff --git a/app/src/main/res/layout/fragment_artist.xml b/app/src/main/res/layout/fragment_artist.xml index 8110f62e..d727c042 100644 --- a/app/src/main/res/layout/fragment_artist.xml +++ b/app/src/main/res/layout/fragment_artist.xml @@ -60,7 +60,7 @@