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.
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_ALBUMS).mode) {
// 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
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.
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS).mode) {
// Name -> Use name
is Sort.Mode.ByName -> song.sortName.first().uppercase()
is Sort.Mode.ByName -> song.sortName?.run { first().uppercase() }
// Artist -> Use Artist Name
is Sort.Mode.ByArtist -> song.album.artist.sortName?.run { first().uppercase() }
// 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
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. */
abstract val rawName: String?
/** The name of this item used for sorting. Null if unknown. */
abstract val sortName: String?
/** The raw sorting name of this item. Null if not present. */
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
@ -99,8 +109,8 @@ data class Song(
return result
}
override val sortName: String
get() = rawName.withoutArticle
override val rawSortName: String?
get() = null
override fun resolveName(context: Context) = rawName
@ -196,8 +206,8 @@ data class Album(
return result
}
override val sortName: String
get() = rawName.withoutArticle
override val rawSortName: String?
get() = null
override fun resolveName(context: Context) = rawName
@ -238,8 +248,8 @@ data class Artist(
override val id: Long
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
override val sortName: String?
get() = rawName?.withoutArticle
override val rawSortName: String?
get() = null
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
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
override val sortName: String?
get() = rawName
override val rawSortName: String?
get() = null
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 -> {
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()
if (value.isNotEmpty()) {
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
// 4. ID3v2.3 Original Date, as it is like #1
// 5. ID3v2.3 Release Year, as it is the most common date type
audio.year
?: tags["TDOR"]?.iso8601year ?: tags["TDRC"]?.iso8601year ?: tags["TDRL"]?.iso8601year
?: tags["TORY"]?.year ?: tags["TYER"]?.year
(tags["TDOR"]?.iso8601year
?: tags["TDRC"]?.iso8601year ?: tags["TDRL"]?.iso8601year ?: tags["TORY"]?.year
?: tags["TYER"]?.year)
?.let { audio.year = it }
// Album
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
// 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!)
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
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:
// 1. ALBUMARTIST, which is the most common
// 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
tags["GENRE"]?.let { audio.genre = it }