music: prepare sort tags

Prepare support for sort tags by reworking how sortName is structured.

Instead of rawName and sortName, music must now implement rawName and
rawSortName. rawSortName will be checked first, followed by will
falling back to rawName.withoutArticle. This allows hard-coded tags to
be neatly implemented while avoiding an immediate copy.
This commit is contained in:
OxygenCobalt 2022-07-13 14:51:10 -06:00
parent 9921a39784
commit e8f94564b7
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
4 changed files with 32 additions and 20 deletions

View file

@ -58,7 +58,7 @@ class AlbumListFragment : HomeListFragment<Album>() {
// Change how we display the popup depending on the mode. // Change how we display the popup depending on the mode.
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_ALBUMS).mode) { return when (homeModel.getSortForDisplay(DisplayMode.SHOW_ALBUMS).mode) {
// By Name -> Use Name // By Name -> Use Name
is Sort.Mode.ByName -> album.sortName.first().uppercase() is Sort.Mode.ByName -> album.sortName?.run { first().uppercase() }
// By Artist -> Use Artist Name // By Artist -> Use Artist Name
is Sort.Mode.ByArtist -> album.artist.sortName?.run { first().uppercase() } is Sort.Mode.ByArtist -> album.artist.sortName?.run { first().uppercase() }

View file

@ -62,13 +62,13 @@ class SongListFragment : HomeListFragment<Song>() {
// based off the names of the parent objects and not the child objects. // based off the names of the parent objects and not the child objects.
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS).mode) { return when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS).mode) {
// Name -> Use name // Name -> Use name
is Sort.Mode.ByName -> song.sortName.first().uppercase() is Sort.Mode.ByName -> song.sortName?.run { first().uppercase() }
// Artist -> Use Artist Name // Artist -> Use Artist Name
is Sort.Mode.ByArtist -> song.album.artist.sortName?.run { first().uppercase() } is Sort.Mode.ByArtist -> song.album.artist.sortName?.run { first().uppercase() }
// Album -> Use Album Name // Album -> Use Album Name
is Sort.Mode.ByAlbum -> song.album.sortName.first().uppercase() is Sort.Mode.ByAlbum -> song.album.sortName?.run { first().uppercase() }
// Year -> Use Full Year // Year -> Use Full Year
is Sort.Mode.ByYear -> song.album.year?.toString() is Sort.Mode.ByYear -> song.album.year?.toString()

View file

@ -33,8 +33,18 @@ sealed class Music : Item() {
/** The raw name of this item. Null if unknown. */ /** The raw name of this item. Null if unknown. */
abstract val rawName: String? abstract val rawName: String?
/** The name of this item used for sorting. Null if unknown. */ /** The raw sorting name of this item. Null if not present. */
abstract val sortName: String? abstract val rawSortName: String?
/**
* The name of this item used for sorting. This will first use the sort tag for the item,
* followed by the name without a preceding article (The/A/An). In the case that the item has no
* name, this returns null.
*
* This should not be used outside of sorting and fast-scrolling.
*/
val sortName: String?
get() = rawSortName ?: rawName?.withoutArticle
/** /**
* Resolve a name from it's raw form to a form suitable to be shown in a ui. Ex. "unknown" would * Resolve a name from it's raw form to a form suitable to be shown in a ui. Ex. "unknown" would
@ -99,8 +109,8 @@ data class Song(
return result return result
} }
override val sortName: String override val rawSortName: String?
get() = rawName.withoutArticle get() = null
override fun resolveName(context: Context) = rawName override fun resolveName(context: Context) = rawName
@ -196,8 +206,8 @@ data class Album(
return result return result
} }
override val sortName: String override val rawSortName: String?
get() = rawName.withoutArticle get() = null
override fun resolveName(context: Context) = rawName override fun resolveName(context: Context) = rawName
@ -238,8 +248,8 @@ data class Artist(
override val id: Long override val id: Long
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong() get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
override val sortName: String? override val rawSortName: String?
get() = rawName?.withoutArticle get() = null
override fun resolveName(context: Context) = rawName ?: context.getString(R.string.def_artist) override fun resolveName(context: Context) = rawName ?: context.getString(R.string.def_artist)
@ -258,8 +268,8 @@ data class Genre(override val rawName: String?, override val songs: List<Song>)
override val id: Long override val id: Long
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong() get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
override val sortName: String? override val rawSortName: String?
get() = rawName get() = null
override fun resolveName(context: Context) = rawName ?: context.getString(R.string.def_genre) override fun resolveName(context: Context) = rawName ?: context.getString(R.string.def_genre)
} }

View file

@ -183,7 +183,8 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
} }
} }
is VorbisComment -> { is VorbisComment -> {
val id = tag.key.sanitize() // Vorbis comment keys can be in any case, make them uppercase for simplicity.
val id = tag.key.sanitize().uppercase()
val value = tag.value.sanitize() val value = tag.value.sanitize()
if (value.isNotEmpty()) { if (value.isNotEmpty()) {
vorbisTags[id] = value vorbisTags[id] = value
@ -223,9 +224,10 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
// 3. ID3v2.4 Release Date, as it is the second most common date type // 3. ID3v2.4 Release Date, as it is the second most common date type
// 4. ID3v2.3 Original Date, as it is like #1 // 4. ID3v2.3 Original Date, as it is like #1
// 5. ID3v2.3 Release Year, as it is the most common date type // 5. ID3v2.3 Release Year, as it is the most common date type
audio.year (tags["TDOR"]?.iso8601year
?: tags["TDOR"]?.iso8601year ?: tags["TDRC"]?.iso8601year ?: tags["TDRL"]?.iso8601year ?: tags["TDRC"]?.iso8601year ?: tags["TDRL"]?.iso8601year ?: tags["TORY"]?.year
?: tags["TORY"]?.year ?: tags["TYER"]?.year ?: tags["TYER"]?.year)
?.let { audio.year = it }
// Album // Album
tags["TALB"]?.let { audio.album = it } tags["TALB"]?.let { audio.album = it }
@ -256,8 +258,8 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
// 2. Date, as it is the most common date type // 2. Date, as it is the most common date type
// 3. Year, as old vorbis tags tended to use this (I know this because it's the only // 3. Year, as old vorbis tags tended to use this (I know this because it's the only
// tag that android supports, so it must be 15 years old or more!) // tag that android supports, so it must be 15 years old or more!)
audio.year = (tags["ORIGINALDATE"]?.iso8601year ?: tags["DATE"]?.iso8601year ?: tags["YEAR"]?.year)
tags["ORIGINALDATE"]?.iso8601year ?: tags["DATE"]?.iso8601year ?: tags["YEAR"]?.year ?.let { audio.year = it }
// Album // Album
tags["ALBUM"]?.let { audio.album = it } tags["ALBUM"]?.let { audio.album = it }
@ -268,7 +270,7 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
// Album artist. This actually comes into two flavors: // Album artist. This actually comes into two flavors:
// 1. ALBUMARTIST, which is the most common // 1. ALBUMARTIST, which is the most common
// 2. ALBUM ARTIST, which is present on older vorbis tags // 2. ALBUM ARTIST, which is present on older vorbis tags
audio.albumArtist = tags["ALBUMARTIST"] ?: tags["ALBUM ARTIST"] (tags["ALBUMARTIST"] ?: tags["ALBUM ARTIST"])?.let { audio.albumArtist = it }
// Genre, no ID3 rules here // Genre, no ID3 rules here
tags["GENRE"]?.let { audio.genre = it } tags["GENRE"]?.let { audio.genre = it }