diff --git a/app/build.gradle b/app/build.gradle index 594f354eb..c0a848117 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,7 @@ dependencies { implementation "androidx.preference:preference-ktx:1.2.0" // Database - def room_version = '2.4.3' + def room_version = '2.5.0' implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" implementation "androidx.room:room-ktx:$room_version" @@ -122,8 +122,8 @@ dependencies { // Development debugImplementation "com.squareup.leakcanary:leakcanary-android:2.9.1" testImplementation "junit:junit:4.13.2" - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } spotless { diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index c2659961e..4edcacc46 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -225,6 +225,7 @@ sealed interface Music : Item { * @author Alexander Capehart (OxygenCobalt) */ sealed interface MusicParent : Music { + /** The child [Song]s of this [MusicParent]. */ val songs: List } @@ -337,10 +338,6 @@ interface Album : MusicParent { /** * An abstract artist. These are actually a combination of the artist and album artist tags from * within the library, derived from [Song]s and [Album]s respectively. - * @param raw The [Artist.Raw] to derive the member data from. - * @param songAlbums A list of the [Song]s and [Album]s that are a part of this [Artist], either - * through artist or album artist tags. Providing [Song]s to the artist is optional. These instances - * will be linked to this [Artist]. * @author Alexander Capehart (OxygenCobalt) */ interface Artist : MusicParent { diff --git a/app/src/main/java/org/oxycblt/auxio/music/RealMusic.kt b/app/src/main/java/org/oxycblt/auxio/music/RealMusic.kt index 9b2f0b65b..0336a2b83 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/RealMusic.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/RealMusic.kt @@ -401,7 +401,7 @@ class RealAlbum(val raw: Raw, override val songs: List) : Album { val sortName: String?, /** @see Album.releaseType */ val releaseType: ReleaseType?, - /** @see Artist.Raw.name */ + /** @see RealArtist.Raw.name */ val rawArtists: List ) { // Albums are grouped as follows: @@ -694,29 +694,6 @@ fun MessageDigest.update(n: Int?) { } } -/** Cached collator instance re-used with [makeCollationKey]. */ -private val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY } - -/** - * Provided implementation to create a [CollationKey] in the way described by [collationKey]. This - * should be used in all overrides of all [CollationKey]. - * @param music The [Music] to create the [CollationKey] for. - * @return A [CollationKey] that follows the specification described by [collationKey]. - */ -private fun makeCollationKey(music: Music): CollationKey? { - val sortName = - (music.rawSortName ?: music.rawName)?.run { - when { - length > 5 && startsWith("the ", ignoreCase = true) -> substring(4) - length > 4 && startsWith("an ", ignoreCase = true) -> substring(3) - length > 3 && startsWith("a ", ignoreCase = true) -> substring(2) - else -> this - } - } - - return COLLATOR.getCollationKey(sortName) -} - /** * Join a list of [Music]'s resolved names into a string in a localized manner, using * [R.string.fmt_list]. @@ -737,3 +714,26 @@ private fun resolveNames(context: Context, values: List): String { } return joined } + +/** Cached collator instance re-used with [makeCollationKey]. */ +private val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY } + +/** + * Provided implementation to create a [CollationKey] in the way described by [Music.collationKey]. + * This should be used in all overrides of all [CollationKey]. + * @param music The [Music] to create the [CollationKey] for. + * @return A [CollationKey] that follows the specification described by [Music.collationKey]. + */ +private fun makeCollationKey(music: Music): CollationKey? { + val sortName = + (music.rawSortName ?: music.rawName)?.run { + when { + length > 5 && startsWith("the ", ignoreCase = true) -> substring(4) + length > 4 && startsWith("an ", ignoreCase = true) -> substring(3) + length > 3 && startsWith("a ", ignoreCase = true) -> substring(2) + else -> this + } + } + + return COLLATOR.getCollationKey(sortName) +} diff --git a/app/src/main/java/org/oxycblt/auxio/music/extractor/CacheExtractor.kt b/app/src/main/java/org/oxycblt/auxio/music/extractor/CacheExtractor.kt index e919d5f42..db6cfb87b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/extractor/CacheExtractor.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/extractor/CacheExtractor.kt @@ -202,9 +202,7 @@ private abstract class CacheDatabase : RoomDatabase() { @Dao private interface CacheDao { @Query("SELECT * FROM ${CachedSong.TABLE_NAME}") suspend fun readCache(): List - @Query("DELETE FROM ${CachedSong.TABLE_NAME}") suspend fun nukeCache() - @Insert suspend fun insertCache(songs: List) } @@ -216,49 +214,49 @@ private data class CachedSong( * unstable and should only be used for accessing the audio file. */ @PrimaryKey var mediaStoreId: Long, - /** @see Song.dateAdded */ + /** @see RealSong.Raw.dateAdded */ var dateAdded: Long, /** The latest date the [Song]'s audio file was modified, as a unix epoch timestamp. */ var dateModified: Long, - /** @see Song.size */ + /** @see RealSong.Raw.size */ var size: Long? = null, - /** @see Song.durationMs */ + /** @see RealSong.Raw */ var durationMs: Long, - /** @see Music.UID */ + /** @see RealSong.Raw.musicBrainzId */ var musicBrainzId: String? = null, - /** @see Music.rawName */ + /** @see RealSong.Raw.name */ var name: String, - /** @see Music.rawSortName */ + /** @see RealSong.Raw.sortName */ var sortName: String? = null, - /** @see Song.track */ + /** @see RealSong.Raw.track */ var track: Int? = null, - /** @see Disc.number */ + /** @see RealSong.Raw.name */ var disc: Int? = null, - /** @See Disc.name */ + /** @See RealSong.Raw.subtitle */ var subtitle: String? = null, - /** @see Song.date */ + /** @see RealSong.Raw.date */ var date: Date? = null, - /** @see Album.Raw.musicBrainzId */ + /** @see RealSong.Raw.albumMusicBrainzId */ var albumMusicBrainzId: String? = null, - /** @see Album.Raw.name */ + /** @see RealSong.Raw.albumName */ var albumName: String, - /** @see Album.Raw.sortName */ + /** @see RealSong.Raw.albumSortName */ var albumSortName: String? = null, - /** @see Album.Raw.releaseType */ + /** @see RealSong.Raw.releaseTypes */ var releaseTypes: List = listOf(), - /** @see Artist.Raw.musicBrainzId */ + /** @see RealSong.Raw.artistMusicBrainzIds */ var artistMusicBrainzIds: List = listOf(), - /** @see Artist.Raw.name */ + /** @see RealSong.Raw.artistNames */ var artistNames: List = listOf(), - /** @see Artist.Raw.sortName */ + /** @see RealSong.Raw.artistSortNames */ var artistSortNames: List = listOf(), - /** @see Artist.Raw.musicBrainzId */ + /** @see RealSong.Raw.albumArtistMusicBrainzIds */ var albumArtistMusicBrainzIds: List = listOf(), - /** @see Artist.Raw.name */ + /** @see RealSong.Raw.albumArtistNames */ var albumArtistNames: List = listOf(), - /** @see Artist.Raw.sortName */ + /** @see RealSong.Raw.albumArtistSortNames */ var albumArtistSortNames: List = listOf(), - /** @see Genre.Raw.name */ + /** @see RealSong.Raw.genreNames */ var genreNames: List = listOf() ) { fun copyToRaw(rawSong: RealSong.Raw): CachedSong { diff --git a/app/src/main/java/org/oxycblt/auxio/music/format/ReleaseType.kt b/app/src/main/java/org/oxycblt/auxio/music/format/ReleaseType.kt index 4c21d66b8..45ba2909f 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/format/ReleaseType.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/format/ReleaseType.kt @@ -125,7 +125,7 @@ sealed class ReleaseType { } /** - * A Mix-tape. These are usually [EP]-sized releases of music made to promote an [Artist] or a + * A Mix-tape. These are usually [EP]-sized releases of music made to promote an Artist or a * future release. */ object Mixtape : ReleaseType() { @@ -141,7 +141,7 @@ sealed class ReleaseType { /** A release consisting of a live performance */ LIVE, - /** A release consisting of another [Artist]s remix of a prior performance. */ + /** A release consisting of another Artists remix of a prior performance. */ REMIX } diff --git a/app/src/main/java/org/oxycblt/auxio/music/library/Library.kt b/app/src/main/java/org/oxycblt/auxio/music/library/Library.kt index a49ab9d52..02ed4c59f 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/library/Library.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/library/Library.kt @@ -86,8 +86,7 @@ interface Library { } private class RealLibrary(rawSongs: List, settings: MusicSettings) : Library { - override val songs = - Sort(Sort.Mode.ByName, true).songs(rawSongs.map { RealSong(it, settings) }.distinct()) + override val songs = buildSongs(rawSongs, settings) override val albums = buildAlbums(songs) override val artists = buildArtists(songs, albums) override val genres = buildGenres(songs) @@ -124,6 +123,16 @@ private class RealLibrary(rawSongs: List, settings: MusicSettings) songs.find { it.path.name == displayName && it.size == size } } + /** + * Build a list [RealSong]s from the given [RealSong.Raw]. + * @param rawSongs The [RealSong.Raw]s to build the [RealSong]s from. + * @param settings [MusicSettings] required to build [RealSong]s. + * @return A sorted list of [RealSong]s derived from the [RealSong.Raw] that should be suitable + * for grouping. + */ + private fun buildSongs(rawSongs: List, settings: MusicSettings) = + Sort(Sort.Mode.ByName, true).songs(rawSongs.map { RealSong(it, settings) }.distinct()) + /** * Build a list of [Album]s from the given [Song]s. * @param songs The [Song]s to build [Album]s from. These will be linked with their respective diff --git a/app/src/main/java/org/oxycblt/auxio/music/parsing/ParsingUtil.kt b/app/src/main/java/org/oxycblt/auxio/music/parsing/ParsingUtil.kt index db9c53945..9ab087dca 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/parsing/ParsingUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/parsing/ParsingUtil.kt @@ -96,7 +96,7 @@ fun List.correctWhitespace() = mapNotNull { it.correctWhitespace() } /** * Attempt to parse a string by the user's separator preferences. - * @param settings [Settings] required to obtain user separator configuration. + * @param settings [MusicSettings] required to obtain user separator configuration. * @return A list of one or more [String]s that were split up by the user-defined separators. */ private fun String.maybeParseBySeparators(settings: MusicSettings): List { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/Queue.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/Queue.kt index b9122fc3c..9a2b44eb9 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/Queue.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/Queue.kt @@ -186,8 +186,9 @@ class EditableQueue : Queue { /** * Add [Song]s to the top of the queue. Will start playback if nothing is playing. * @param songs The [Song]s to add. - * @return [ChangeResult.MAPPING] if added to an existing queue, or [ChangeResult.SONG] if there - * was no prior playback and these enqueued [Song]s start new playback. + * @return [Queue.ChangeResult.MAPPING] if added to an existing queue, or + * [Queue.ChangeResult.SONG] if there was no prior playback and these enqueued [Song]s start new + * playback. */ fun playNext(songs: List): Queue.ChangeResult { if (orderedMapping.isEmpty()) { diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index a80ca7706..05bde11d4 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -129,7 +129,7 @@ class WidgetComponent(private val context: Context) : /** * A condensed form of the playback state that is safe to use in AppWidgets. - * @param song [PlaybackStateManager.song] + * @param song [Queue.currentSong] * @param cover A pre-loaded album cover [Bitmap] for [song]. * @param isPlaying [PlaybackStateManager.playerState] * @param repeatMode [PlaybackStateManager.repeatMode] diff --git a/app/src/main/res/layout/dialog_music_dirs.xml b/app/src/main/res/layout/dialog_music_dirs.xml index df19b3a25..470433b70 100644 --- a/app/src/main/res/layout/dialog_music_dirs.xml +++ b/app/src/main/res/layout/dialog_music_dirs.xml @@ -12,13 +12,14 @@ android:layout_height="match_parent"> + android:text="@string/set_dirs_mode" + app:layout_constraintTop_toTopOf="parent" />