Fix: Artist selection by storing artist name in URI
Problem
The artist() method queries by MediaStore artist ID, but artists() generates synthetic URIs. They don't match, causing artist selection to fail.
Solution
Store the artist name in the URI path so artist() can extract and use it directly.
Implementation
Replace the artists() method with:
override fun artists(
providerIdentifier: ProviderIdentifier,
sortingRule: SortingRule,
) = providersManager.flatMapWithInstanceOf(providerIdentifier) {
// Get all audios to extract album artist information
contentResolver.queryFlow(
audiosUri,
audiosProjection,
).mapEachRowToAudio().mapLatest { audios ->
// Group by album artist, falling back to track artist
val artistGroups = audios
.groupBy { audio ->
audio.albumArtist ?: audio.artistName ?: "Unknown Artist"
}
// Sort based on sorting rule
val sortedArtistNames = when (sortingRule.strategy) {
SortingStrategy.NAME -> {
if (sortingRule.reverse) {
artistGroups.keys.sortedDescending()
} else {
artistGroups.keys.sorted()
}
}
else -> artistGroups.keys.sorted()
}
// Create Artist objects with custom URIs
sortedArtistNames.map { artistName ->
// Create a custom URI with artist name encoded
val customUri = Uri.Builder()
.scheme("content")
.authority("org.lineageos.twelve.artist")
.appendPath(artistName)
.build()
Artist.Builder(customUri)
.setName(artistName)
.build()
}
}.mapLatest {
Result.Success(it)
}
}
Replace the artist() method with:
override fun artist(artistUri: Uri) = withVolumeName(artistUri) { volumeName ->
// Extract artist name from custom URI
val artistName = artistUri.lastPathSegment ?: return@withVolumeName flowOf(
Result.Error(Error.NOT_FOUND)
)
// Get all audios
contentResolver.queryFlow(
getAudiosUri(volumeName),
audiosProjection,
).mapEachRowToAudio(volumeName).mapLatest { audios ->
// Filter audios by album artist or track artist
val matchingAudios = audios.filter { audio ->
(audio.albumArtist ?: audio.artistName ?: "Unknown Artist") == artistName
}
if (matchingAudios.isEmpty()) {
return@mapLatest Result.Error<Pair<Artist, ArtistWorks>, Error>(Error.NOT_FOUND)
}
// Get unique albums
val albumIds = matchingAudios
.map { it.albumUri }
.distinct()
.map { ContentUris.parseId(it).toString() }
// Query albums
contentResolver.queryFlow(
getAlbumsUri(volumeName),
albumsProjection,
bundleOf(
ContentResolver.QUERY_ARG_SQL_SELECTION to query {
MediaStore.Audio.AudioColumns._ID `in` List(albumIds.size) {
Query.ARG
}
},
ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS to albumIds.toTypedArray(),
)
).mapEachRowToAlbum(volumeName).mapLatest { albums ->
val artist = Artist.Builder(artistUri)
.setName(artistName)
.build()
val artistWorks = ArtistWorks(
albums,
listOf(),
listOf(),
)
Result.Success(artist to artistWorks)
}
}.flatMapLatest { it }
}
How it works
- artists() creates URIs with the artist name in the path
- artist() extracts the artist name from the URI
- Filters audios by album artist (with fallback to track artist)
- Returns albums for that artist
Testing
After implementing:
- Rebuild APK
- Tap on an artist - should now display correctly
- Verify compilation albums are grouped
- Check that artist details show all albums