all: post-refactor cleanup
This commit is contained in:
parent
bfb1033ed7
commit
f8d1a880d4
11 changed files with 103 additions and 97 deletions
|
@ -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 {
|
||||
|
|
|
@ -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<Song>
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -401,7 +401,7 @@ class RealAlbum(val raw: Raw, override val songs: List<RealSong>) : Album {
|
|||
val sortName: String?,
|
||||
/** @see Album.releaseType */
|
||||
val releaseType: ReleaseType?,
|
||||
/** @see Artist.Raw.name */
|
||||
/** @see RealArtist.Raw.name */
|
||||
val rawArtists: List<RealArtist.Raw>
|
||||
) {
|
||||
// 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<Music>): 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)
|
||||
}
|
||||
|
|
|
@ -202,9 +202,7 @@ private abstract class CacheDatabase : RoomDatabase() {
|
|||
@Dao
|
||||
private interface CacheDao {
|
||||
@Query("SELECT * FROM ${CachedSong.TABLE_NAME}") suspend fun readCache(): List<CachedSong>
|
||||
|
||||
@Query("DELETE FROM ${CachedSong.TABLE_NAME}") suspend fun nukeCache()
|
||||
|
||||
@Insert suspend fun insertCache(songs: List<CachedSong>)
|
||||
}
|
||||
|
||||
|
@ -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<String> = listOf(),
|
||||
/** @see Artist.Raw.musicBrainzId */
|
||||
/** @see RealSong.Raw.artistMusicBrainzIds */
|
||||
var artistMusicBrainzIds: List<String> = listOf(),
|
||||
/** @see Artist.Raw.name */
|
||||
/** @see RealSong.Raw.artistNames */
|
||||
var artistNames: List<String> = listOf(),
|
||||
/** @see Artist.Raw.sortName */
|
||||
/** @see RealSong.Raw.artistSortNames */
|
||||
var artistSortNames: List<String> = listOf(),
|
||||
/** @see Artist.Raw.musicBrainzId */
|
||||
/** @see RealSong.Raw.albumArtistMusicBrainzIds */
|
||||
var albumArtistMusicBrainzIds: List<String> = listOf(),
|
||||
/** @see Artist.Raw.name */
|
||||
/** @see RealSong.Raw.albumArtistNames */
|
||||
var albumArtistNames: List<String> = listOf(),
|
||||
/** @see Artist.Raw.sortName */
|
||||
/** @see RealSong.Raw.albumArtistSortNames */
|
||||
var albumArtistSortNames: List<String> = listOf(),
|
||||
/** @see Genre.Raw.name */
|
||||
/** @see RealSong.Raw.genreNames */
|
||||
var genreNames: List<String> = listOf()
|
||||
) {
|
||||
fun copyToRaw(rawSong: RealSong.Raw): CachedSong {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ interface Library {
|
|||
}
|
||||
|
||||
private class RealLibrary(rawSongs: List<RealSong.Raw>, 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<RealSong.Raw>, 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<RealSong.Raw>, 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
|
||||
|
|
|
@ -96,7 +96,7 @@ fun List<String>.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<String> {
|
||||
|
|
|
@ -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<Song>): Queue.ChangeResult {
|
||||
if (orderedMapping.isEmpty()) {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -12,13 +12,14 @@
|
|||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
style="@style/Widget.Auxio.TextView.Header"
|
||||
android:id="@+id/dirs_mode_header"
|
||||
style="@style/Widget.Auxio.TextView.Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large"
|
||||
android:text="@string/set_dirs_mode" />
|
||||
android:text="@string/set_dirs_mode"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
|
|
|
@ -51,23 +51,23 @@ class MusicTest {
|
|||
@Test
|
||||
fun albumRaw_equals_inconsistentCase() {
|
||||
val a =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = null,
|
||||
name = "Paraglow",
|
||||
sortName = null,
|
||||
releaseType = null,
|
||||
rawArtists =
|
||||
listOf(Artist.Raw(name = "Parannoul"), Artist.Raw(name = "Asian Glow")))
|
||||
listOf(RealArtist.Raw(name = "Parannoul"), RealArtist.Raw(name = "Asian Glow")))
|
||||
val b =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = null,
|
||||
name = "paraglow",
|
||||
sortName = null,
|
||||
releaseType = null,
|
||||
rawArtists =
|
||||
listOf(Artist.Raw(name = "Parannoul"), Artist.Raw(name = "Asian glow")))
|
||||
listOf(RealArtist.Raw(name = "Parannoul"), RealArtist.Raw(name = "Asian glow")))
|
||||
assertTrue(a == b)
|
||||
assertTrue(a.hashCode() == b.hashCode())
|
||||
}
|
||||
|
@ -75,21 +75,21 @@ class MusicTest {
|
|||
@Test
|
||||
fun albumRaw_equals_withMbids() {
|
||||
val a =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = UUID.fromString("c7b245c9-8099-32ea-af95-893acedde2cf"),
|
||||
name = "Weezer",
|
||||
sortName = "Blue Album",
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Weezer")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "Weezer")))
|
||||
val b =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = UUID.fromString("923d5ba6-7eee-3bce-bcb2-c913b2bd69d4"),
|
||||
name = "Weezer",
|
||||
sortName = "Green Album",
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Weezer")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "Weezer")))
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
@ -97,51 +97,51 @@ class MusicTest {
|
|||
@Test
|
||||
fun albumRaw_equals_inconsistentMbids() {
|
||||
val a =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = UUID.fromString("c7b245c9-8099-32ea-af95-893acedde2cf"),
|
||||
name = "Weezer",
|
||||
sortName = "Blue Album",
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Weezer")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "Weezer")))
|
||||
val b =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = null,
|
||||
name = "Weezer",
|
||||
sortName = "Green Album",
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Weezer")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "Weezer")))
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun albumRaw_equals_withArtists() {
|
||||
fun albumRaw_equals_withRealArtists() {
|
||||
val a =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = null,
|
||||
name = "Album",
|
||||
sortName = null,
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Artist A")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "RealArtist A")))
|
||||
val b =
|
||||
Album.Raw(
|
||||
RealAlbum.Raw(
|
||||
mediaStoreId = -1,
|
||||
musicBrainzId = null,
|
||||
name = "Album",
|
||||
sortName = null,
|
||||
releaseType = null,
|
||||
rawArtists = listOf(Artist.Raw(name = "Artist B")))
|
||||
rawArtists = listOf(RealArtist.Raw(name = "RealArtist B")))
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun artistRaw_equals_inconsistentCase() {
|
||||
val a = Artist.Raw(musicBrainzId = null, name = "Parannoul")
|
||||
val b = Artist.Raw(musicBrainzId = null, name = "parannoul")
|
||||
val a = RealArtist.Raw(musicBrainzId = null, name = "Parannoul")
|
||||
val b = RealArtist.Raw(musicBrainzId = null, name = "parannoul")
|
||||
assertTrue(a == b)
|
||||
assertTrue(a.hashCode() == b.hashCode())
|
||||
}
|
||||
|
@ -149,11 +149,11 @@ class MusicTest {
|
|||
@Test
|
||||
fun artistRaw_equals_withMbids() {
|
||||
val a =
|
||||
Artist.Raw(
|
||||
RealArtist.Raw(
|
||||
musicBrainzId = UUID.fromString("677325ef-d850-44bb-8258-0d69bbc0b3f7"),
|
||||
name = "Artist")
|
||||
val b =
|
||||
Artist.Raw(
|
||||
RealArtist.Raw(
|
||||
musicBrainzId = UUID.fromString("6b625592-d88d-48c8-ac1a-c5b476d78bcc"),
|
||||
name = "Artist")
|
||||
assertTrue(a != b)
|
||||
|
@ -163,50 +163,50 @@ class MusicTest {
|
|||
@Test
|
||||
fun artistRaw_equals_inconsistentMbids() {
|
||||
val a =
|
||||
Artist.Raw(
|
||||
RealArtist.Raw(
|
||||
musicBrainzId = UUID.fromString("677325ef-d850-44bb-8258-0d69bbc0b3f7"),
|
||||
name = "Artist")
|
||||
val b = Artist.Raw(musicBrainzId = null, name = "Artist")
|
||||
val b = RealArtist.Raw(musicBrainzId = null, name = "Artist")
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun artistRaw_equals_missingNames() {
|
||||
val a = Artist.Raw(name = null)
|
||||
val b = Artist.Raw(name = null)
|
||||
val a = RealArtist.Raw(name = null)
|
||||
val b = RealArtist.Raw(name = null)
|
||||
assertTrue(a == b)
|
||||
assertTrue(a.hashCode() == b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun artistRaw_equals_inconsistentNames() {
|
||||
val a = Artist.Raw(name = null)
|
||||
val b = Artist.Raw(name = "Parannoul")
|
||||
val a = RealArtist.Raw(name = null)
|
||||
val b = RealArtist.Raw(name = "Parannoul")
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genreRaw_equals_inconsistentCase() {
|
||||
val a = Genre.Raw("Future Garage")
|
||||
val b = Genre.Raw("future garage")
|
||||
val a = RealGenre.Raw("Future Garage")
|
||||
val b = RealGenre.Raw("future garage")
|
||||
assertTrue(a == b)
|
||||
assertTrue(a.hashCode() == b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genreRaw_equals_missingNames() {
|
||||
val a = Genre.Raw(name = null)
|
||||
val b = Genre.Raw(name = null)
|
||||
val a = RealGenre.Raw(name = null)
|
||||
val b = RealGenre.Raw(name = null)
|
||||
assertTrue(a == b)
|
||||
assertTrue(a.hashCode() == b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genreRaw_equals_inconsistentNames() {
|
||||
val a = Genre.Raw(name = null)
|
||||
val b = Genre.Raw(name = "Future Garage")
|
||||
val a = RealGenre.Raw(name = null)
|
||||
val b = RealGenre.Raw(name = "Future Garage")
|
||||
assertTrue(a != b)
|
||||
assertTrue(a.hashCode() != b.hashCode())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue