music: extend equality impls

Extend the music equals/hashCode implementations to take into account
the raw music.

This way, items that change in non-UID metadata are actually considered
different at runtime while UIDs can still persist the data in a stable
manner.
This commit is contained in:
Alexander Capehart 2023-05-17 15:45:09 -06:00
parent d0a68353a7
commit 06885ba264
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 36 additions and 34 deletions

View file

@ -127,7 +127,7 @@ class PlaylistDetailFragment :
true true
} }
R.id.action_delete -> { R.id.action_delete -> {
musicModel.createPlaylist() musicModel.deletePlaylist(currentPlaylist)
true true
} }
else -> false else -> false

View file

@ -34,10 +34,6 @@ import org.oxycblt.auxio.util.toUuidOrNull
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import org.oxycblt.auxio.util.update import org.oxycblt.auxio.util.update
// TODO: Entirely rework music equality such that it's not completely UID-focused and actually
// takes metadata into account
// TODO: Reduce need for raw objects to save some memory
/** /**
* Library-backed implementation of [Song]. * Library-backed implementation of [Song].
* *
@ -45,7 +41,7 @@ import org.oxycblt.auxio.util.update
* @param musicSettings [MusicSettings] to for user parsing configuration. * @param musicSettings [MusicSettings] to for user parsing configuration.
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song { class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Song {
override val uid = override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID. // Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
rawSong.musicBrainzId?.toUuidOrNull()?.let { Music.UID.musicBrainz(MusicMode.SONGS, it) } rawSong.musicBrainzId?.toUuidOrNull()?.let { Music.UID.musicBrainz(MusicMode.SONGS, it) }
@ -89,9 +85,9 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
override val album: Album override val album: Album
get() = unlikelyToBeNull(_album) get() = unlikelyToBeNull(_album)
// Note: Only compare by UID so songs that differ only in MBID are treated differently. override fun hashCode() = 31 * uid.hashCode() + rawSong.hashCode()
override fun hashCode() = uid.hashCode() override fun equals(other: Any?) =
override fun equals(other: Any?) = other is Song && uid == other.uid other is SongImpl && uid == other.uid && rawSong == other.rawSong
private val artistMusicBrainzIds = rawSong.artistMusicBrainzIds.parseMultiValue(musicSettings) private val artistMusicBrainzIds = rawSong.artistMusicBrainzIds.parseMultiValue(musicSettings)
private val artistNames = rawSong.artistNames.parseMultiValue(musicSettings) private val artistNames = rawSong.artistNames.parseMultiValue(musicSettings)
@ -248,11 +244,15 @@ class AlbumImpl(
override val durationMs: Long override val durationMs: Long
override val dateAdded: Long override val dateAdded: Long
// Note: Append song contents to MusicParent equality so that Groups with override fun hashCode(): Int {
// the same UID but different contents are not equal. var hashCode = uid.hashCode()
override fun hashCode() = 31 * uid.hashCode() + songs.hashCode() hashCode = 31 * hashCode + rawAlbum.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
return hashCode
}
override fun equals(other: Any?) = override fun equals(other: Any?) =
other is AlbumImpl && uid == other.uid && songs == other.songs other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs
private val _artists = mutableListOf<ArtistImpl>() private val _artists = mutableListOf<ArtistImpl>()
override val artists: List<Artist> override val artists: List<Artist>
@ -341,9 +341,18 @@ class ArtistImpl(
// Note: Append song contents to MusicParent equality so that artists with // Note: Append song contents to MusicParent equality so that artists with
// the same UID but different songs are not equal. // the same UID but different songs are not equal.
override fun hashCode() = 31 * uid.hashCode() + songs.hashCode() override fun hashCode(): Int {
var hashCode = uid.hashCode()
hashCode = 31 * hashCode + rawArtist.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
return hashCode
}
override fun equals(other: Any?) = override fun equals(other: Any?) =
other is ArtistImpl && uid == other.uid && songs == other.songs other is ArtistImpl &&
uid == other.uid &&
rawArtist == other.rawArtist &&
songs == other.songs
override lateinit var genres: List<Genre> override lateinit var genres: List<Genre>
@ -422,11 +431,15 @@ class GenreImpl(
override val artists: List<Artist> override val artists: List<Artist>
override val durationMs: Long override val durationMs: Long
// Note: Append song contents to MusicParent equality so that Groups with override fun hashCode(): Int {
// the same UID but different contents are not equal. var hashCode = uid.hashCode()
override fun hashCode() = 31 * uid.hashCode() + songs.hashCode() hashCode = 31 * hashCode + rawGenre.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
return hashCode
}
override fun equals(other: Any?) = override fun equals(other: Any?) =
other is GenreImpl && uid == other.uid && songs == other.songs other is GenreImpl && uid == other.uid && rawGenre == other.rawGenre && songs == other.songs
init { init {
val distinctAlbums = mutableSetOf<Album>() val distinctAlbums = mutableSetOf<Album>()

View file

@ -30,7 +30,7 @@ import org.oxycblt.auxio.music.metadata.*
* *
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class RawSong( data class RawSong(
/** /**
* The ID of the [SongImpl]'s audio file, obtained from MediaStore. Note that this ID is highly * The ID of the [SongImpl]'s audio file, obtained from MediaStore. Note that this ID is highly
* unstable and should only be used for accessing the audio file. * unstable and should only be used for accessing the audio file.
@ -95,7 +95,7 @@ class RawSong(
* *
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class RawAlbum( data class RawAlbum(
/** /**
* The ID of the [AlbumImpl]'s grouping, obtained from MediaStore. Note that this ID is highly * The ID of the [AlbumImpl]'s grouping, obtained from MediaStore. Note that this ID is highly
* unstable and should only be used for accessing the system-provided cover art. * unstable and should only be used for accessing the system-provided cover art.
@ -142,7 +142,7 @@ class RawAlbum(
* *
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class RawArtist( data class RawArtist(
/** @see Music.UID */ /** @see Music.UID */
val musicBrainzId: UUID? = null, val musicBrainzId: UUID? = null,
/** @see Music.name */ /** @see Music.name */
@ -184,7 +184,7 @@ class RawArtist(
* *
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class RawGenre( data class RawGenre(
/** @see Music.name */ /** @see Music.name */
val name: String? = null val name: String? = null
) { ) {

View file

@ -61,7 +61,6 @@ class NewPlaylistDialog : ViewBindingDialogFragment<DialogPlaylistNameBinding>()
} }
// TODO: Navigate to playlist if there are songs in it // TODO: Navigate to playlist if there are songs in it
musicModel.createPlaylist(name, pendingPlaylist.songs) musicModel.createPlaylist(name, pendingPlaylist.songs)
pickerModel.dropPendingAddition()
requireContext().showToast(R.string.lng_playlist_created) requireContext().showToast(R.string.lng_playlist_created)
findNavController().apply { findNavController().apply {
navigateUp() navigateUp()

View file

@ -156,11 +156,6 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
refreshPlaylistChoices(songs) refreshPlaylistChoices(songs)
} }
/** Drop any pending songs to add since a playlist has already been found for them. */
fun dropPendingAddition() {
_currentPendingSongs.value = null
}
private fun refreshPlaylistChoices(songs: List<Song>) { private fun refreshPlaylistChoices(songs: List<Song>) {
val userLibrary = musicRepository.userLibrary ?: return val userLibrary = musicRepository.userLibrary ?: return
_playlistChoices.value = _playlistChoices.value =

View file

@ -112,11 +112,6 @@ private class UserLibraryImpl(
override val playlists: List<Playlist> override val playlists: List<Playlist>
get() = playlistMap.values.toList() get() = playlistMap.values.toList()
init {
// TODO: Actually read playlists
createPlaylist("Playlist 1", deviceLibrary.songs.slice(58..200))
}
override fun findPlaylist(uid: Music.UID) = playlistMap[uid] override fun findPlaylist(uid: Music.UID) = playlistMap[uid]
override fun findPlaylist(name: String) = playlistMap.values.find { it.name.raw == name } override fun findPlaylist(name: String) = playlistMap.values.find { it.name.raw == name }