From 8a15868ba166cb3d424659a018d300e7027ec7c1 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sun, 28 Aug 2022 19:21:30 -0600 Subject: [PATCH] music: cache ids in map Make a map of ids to particular music items, which makes searching for music items much faster at the cost of higher memory usage. --- .../oxycblt/auxio/detail/DetailViewModel.kt | 11 ++--- .../org/oxycblt/auxio/music/MusicStore.kt | 44 ++++++++++++------- .../playback/state/PlaybackStateDatabase.kt | 14 ++---- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 2748c32f1..b024729ea 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -114,7 +114,7 @@ class DetailViewModel(application: Application) : fun setSongId(id: Long) { if (_currentSong.value?.run { song.id } == id) return val library = unlikelyToBeNull(musicStore.library) - val song = requireNotNull(library.songs.find { it.id == id }) { "Invalid song id provided" } + val song = requireNotNull(library.findSongById(id)) { "Invalid song id provided" } generateDetailSong(song) } @@ -126,8 +126,7 @@ class DetailViewModel(application: Application) : fun setAlbumId(id: Long) { if (_currentAlbum.value?.id == id) return val library = unlikelyToBeNull(musicStore.library) - val album = - requireNotNull(library.albums.find { it.id == id }) { "Invalid album id provided " } + val album = requireNotNull(library.findAlbumById(id)) { "Invalid album id provided " } _currentAlbum.value = album refreshAlbumData(album) @@ -136,8 +135,7 @@ class DetailViewModel(application: Application) : fun setArtistId(id: Long) { if (_currentArtist.value?.id == id) return val library = unlikelyToBeNull(musicStore.library) - val artist = - requireNotNull(library.artists.find { it.id == id }) { "Invalid artist id provided" } + val artist = requireNotNull(library.findArtistById(id)) { "Invalid artist id provided" } _currentArtist.value = artist refreshArtistData(artist) } @@ -145,8 +143,7 @@ class DetailViewModel(application: Application) : fun setGenreId(id: Long) { if (_currentGenre.value?.id == id) return val library = unlikelyToBeNull(musicStore.library) - val genre = - requireNotNull(library.genres.find { it.id == id }) { "Invalid genre id provided" } + val genre = requireNotNull(library.findGenreById(id)) { "Invalid genre id provided" } _currentGenre.value = genre refreshGenreData(genre) } diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index b0819d742..ff83df09d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -75,12 +75,37 @@ class MusicStore private constructor() { val albums: List, val songs: List ) { - /** Find a song in a faster manner by using the album ID as well.. */ - fun findSongFast(songId: Long, albumId: Long) = - albums.find { it.id == albumId }.run { songs.find { it.id == songId } } + private val genreIdMap = HashMap().apply { genres.forEach { put(it.id, it) } } + private val artistIdMap = + HashMap().apply { artists.forEach { put(it.id, it) } } + private val albumIdMap = HashMap().apply { albums.forEach { put(it.id, it) } } + private val songIdMap = HashMap().apply { songs.forEach { put(it.id, it) } } + + /** Find a [Song] by it's ID. Null if no song exists with that ID. */ + fun findSongById(songId: Long) = songIdMap[songId] + + /** Find a [Album] by it's ID. Null if no album exists with that ID. */ + fun findAlbumById(albumId: Long) = albumIdMap[albumId] + + /** Find a [Artist] by it's ID. Null if no artist exists with that ID. */ + fun findArtistById(artistId: Long) = artistIdMap[artistId] + + /** Find a [Genre] by it's ID. Null if no genre exists with that ID. */ + fun findGenreById(genreId: Long) = genreIdMap[genreId] + + /** Sanitize an old item to find the corresponding item in a new library. */ + fun sanitize(song: Song) = findSongById(song.id) + /** Sanitize an old item to find the corresponding item in a new library. */ + fun sanitize(songs: List) = songs.mapNotNull { sanitize(it) } + /** Sanitize an old item to find the corresponding item in a new library. */ + fun sanitize(album: Album) = findAlbumById(album.id) + /** Sanitize an old item to find the corresponding item in a new library. */ + fun sanitize(artist: Artist) = findArtistById(artist.id) + /** Sanitize an old item to find the corresponding item in a new library. */ + fun sanitize(genre: Genre) = findGenreById(genre.id) /** - * Find a song for a [uri], this is similar to [findSongFast], but with some kind of content + * Find a song for a [uri], this is similar to [findSong], but with some kind of content * uri. * @return The corresponding [Song] for this [uri], null if there isn't one. */ @@ -94,17 +119,6 @@ class MusicStore private constructor() { songs.find { it.path.name == displayName } } - - /** Sanitize an old item to find the corresponding item in a new library. */ - fun sanitize(song: Song) = songs.find { it.id == song.id } - /** Sanitize an old item to find the corresponding item in a new library. */ - fun sanitize(songs: List) = songs.mapNotNull { sanitize(it) } - /** Sanitize an old item to find the corresponding item in a new library. */ - fun sanitize(album: Album) = albums.find { it.id == album.id } - /** Sanitize an old item to find the corresponding item in a new library. */ - fun sanitize(artist: Artist) = artists.find { it.id == artist.id } - /** Sanitize an old item to find the corresponding item in a new library. */ - fun sanitize(genre: Genre) = genres.find { it.id == genre.id } } /** A callback for awaiting the loading of music. */ diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt index abd1f1504..e993229c0 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt @@ -116,9 +116,9 @@ class PlaybackStateDatabase private constructor(context: Context) : val parent = when (rawState.playbackMode) { PlaybackMode.ALL_SONGS -> null - PlaybackMode.IN_ALBUM -> library.albums.find { it.id == rawState.parentId } - PlaybackMode.IN_ARTIST -> library.artists.find { it.id == rawState.parentId } - PlaybackMode.IN_GENRE -> library.genres.find { it.id == rawState.parentId } + PlaybackMode.IN_ALBUM -> rawState.parentId?.let(library::findAlbumById) + PlaybackMode.IN_ARTIST -> rawState.parentId?.let(library::findArtistById) + PlaybackMode.IN_GENRE -> rawState.parentId?.let(library::findGenreById) } return SavedState( @@ -168,15 +168,9 @@ class PlaybackStateDatabase private constructor(context: Context) : readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor -> if (cursor.count == 0) return@queryAll - val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_ID) - val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_ID) - while (cursor.moveToNext()) { - library.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))?.let { - song -> - queue.add(song) - } + library.findSongById(cursor.getLong(songIndex))?.let(queue::add) } }