From 881fb58648397c38856d9223133e65992028b61b Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 18 Aug 2023 15:57:53 -0600 Subject: [PATCH] music: consider settings in equality Make it so that music items are meaningfully different when they were created under different settings. This resolves an issue where music information would not correctly update when separators or intelligent sorting would change. Resolves #546. --- CHANGELOG.md | 6 ++ .../auxio/music/device/DeviceMusicImpl.kt | 68 ++++++++++++++----- .../java/org/oxycblt/auxio/music/info/Name.kt | 10 +-- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd76d375..f3645510f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## dev + +#### What's Fixed +- Fixed app restart being required when changing intelligent sorting +or music separator settings + ## 3.2.0 #### What's New diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt index d513fcbff..d9f381ec9 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt @@ -53,8 +53,8 @@ import org.oxycblt.auxio.util.update */ class SongImpl( private val rawSong: RawSong, - nameFactory: Name.Known.Factory, - separators: Separators + private val nameFactory: Name.Known.Factory, + private val separators: Separators ) : Song { override val uid = // Attempt to use a MusicBrainz ID first before falling back to a hashed UID. @@ -110,17 +110,6 @@ class SongImpl( override val genres: List get() = _genres - private val hashCode = 31 * uid.hashCode() + rawSong.hashCode() - - override fun hashCode() = hashCode - - // TODO: I cant compare by raw information actually, as it also means that any settings - // configuration will be lost as well. - override fun equals(other: Any?) = - other is SongImpl && uid == other.uid && rawSong == other.rawSong - - override fun toString() = "Song(uid=$uid, name=$name)" - /** * The [RawAlbum] instances collated by the [Song]. This can be used to group [Song]s into an * [Album]. @@ -140,6 +129,8 @@ class SongImpl( */ val rawGenres: List + private var hashCode: Int = uid.hashCode() + init { val artistMusicBrainzIds = separators.split(rawSong.artistMusicBrainzIds) val artistNames = separators.split(rawSong.artistNames) @@ -190,8 +181,24 @@ class SongImpl( .mapTo(mutableSetOf()) { RawGenre(it) } .toList() .ifEmpty { listOf(RawGenre()) } + + hashCode = 31 * rawSong.hashCode() + hashCode = 31 * nameFactory.hashCode() } + override fun hashCode() = hashCode + + // Since equality on public-facing music models is not identical to the tag equality, + // we just compare raw instances and how they are interpreted. + override fun equals(other: Any?) = + other is SongImpl && + uid == other.uid && + nameFactory == other.nameFactory && + separators == other.separators && + rawSong == other.rawSong + + override fun toString() = "Song(uid=$uid, name=$name)" + /** * Links this [Song] with a parent [Album]. * @@ -259,7 +266,10 @@ class SongImpl( * @param nameFactory The [Name.Known.Factory] to interpret name information with. * @author Alexander Capehart (OxygenCobalt) */ -class AlbumImpl(grouping: Grouping, nameFactory: Name.Known.Factory) : Album { +class AlbumImpl( + grouping: Grouping, + private val nameFactory: Name.Known.Factory +) : Album { private val rawAlbum = grouping.raw.inner override val uid = @@ -322,13 +332,20 @@ class AlbumImpl(grouping: Grouping, nameFactory: Name.Known. dateAdded = earliestDateAdded hashCode = 31 * hashCode + rawAlbum.hashCode() + hashCode = 31 * nameFactory.hashCode() hashCode = 31 * hashCode + songs.hashCode() } override fun hashCode() = hashCode + // Since equality on public-facing music models is not identical to the tag equality, + // we just compare raw instances and how they are interpreted. override fun equals(other: Any?) = - other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs + other is AlbumImpl && + uid == other.uid && + rawAlbum == other.rawAlbum && + nameFactory == other.nameFactory && + songs == other.songs override fun toString() = "Album(uid=$uid, name=$name)" @@ -376,7 +393,10 @@ class AlbumImpl(grouping: Grouping, nameFactory: Name.Known. * @param nameFactory The [Name.Known.Factory] to interpret name information with. * @author Alexander Capehart (OxygenCobalt) */ -class ArtistImpl(grouping: Grouping, nameFactory: Name.Known.Factory) : Artist { +class ArtistImpl( + grouping: Grouping, + private val nameFactory: Name.Known.Factory +) : Artist { private val rawArtist = grouping.raw.inner override val uid = @@ -425,6 +445,7 @@ class ArtistImpl(grouping: Grouping, nameFactory: Name.Known.F durationMs = songs.sumOf { it.durationMs }.positiveOrNull() hashCode = 31 * hashCode + rawArtist.hashCode() + hashCode = 31 * hashCode + nameFactory.hashCode() hashCode = 31 * hashCode + songs.hashCode() } @@ -432,10 +453,13 @@ class ArtistImpl(grouping: Grouping, nameFactory: Name.Known.F // the same UID but different songs are not equal. override fun hashCode() = hashCode + // Since equality on public-facing music models is not identical to the tag equality, + // we just compare raw instances and how they are interpreted. override fun equals(other: Any?) = other is ArtistImpl && uid == other.uid && rawArtist == other.rawArtist && + nameFactory == other.nameFactory && songs == other.songs override fun toString() = "Artist(uid=$uid, name=$name)" @@ -473,7 +497,10 @@ class ArtistImpl(grouping: Grouping, nameFactory: Name.Known.F * @param nameFactory The [Name.Known.Factory] to interpret name information with. * @author Alexander Capehart (OxygenCobalt) */ -class GenreImpl(grouping: Grouping, nameFactory: Name.Known.Factory) : Genre { +class GenreImpl( + grouping: Grouping, + private val nameFactory: Name.Known.Factory +) : Genre { private val rawGenre = grouping.raw.inner override val uid = Music.UID.auxio(MusicType.GENRES) { update(rawGenre.name) } @@ -502,13 +529,18 @@ class GenreImpl(grouping: Grouping, nameFactory: Name.Known. durationMs = totalDuration hashCode = 31 * hashCode + rawGenre.hashCode() + hashCode = 31 * nameFactory.hashCode() hashCode = 31 * hashCode + songs.hashCode() } override fun hashCode() = hashCode override fun equals(other: Any?) = - other is GenreImpl && uid == other.uid && rawGenre == other.rawGenre && songs == other.songs + other is GenreImpl && + uid == other.uid && + rawGenre == other.rawGenre && + nameFactory == other.nameFactory && + songs == other.songs override fun toString() = "Genre(uid=$uid, name=$name)" diff --git a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt index b0f0b029d..09f4d8035 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt @@ -95,13 +95,13 @@ sealed interface Name : Comparable { * user-defined name configuration. * * @param settings The [MusicSettings] to use. - * @return A new [Factory] instance reflecting the configuration state. + * @return A [Factory] instance reflecting the configuration state. */ fun from(settings: MusicSettings) = if (settings.intelligentSorting) { - IntelligentKnownName.Factory() + IntelligentKnownName.Factory } else { - SimpleKnownName.Factory() + SimpleKnownName.Factory } } } @@ -149,7 +149,7 @@ data class SimpleKnownName(override val raw: String, override val sort: String?) return SortToken(collationKey, SortToken.Type.LEXICOGRAPHIC) } - class Factory : Name.Known.Factory { + data object Factory : Name.Known.Factory { override fun parse(raw: String, sort: String?) = SimpleKnownName(raw, sort) } } @@ -208,7 +208,7 @@ data class IntelligentKnownName(override val raw: String, override val sort: Str } } - class Factory : Name.Known.Factory { + data object Factory : Name.Known.Factory { override fun parse(raw: String, sort: String?) = IntelligentKnownName(raw, sort) }