music: back playlists with database
Finally persist playlists with a backing database.
This commit is contained in:
parent
0597fa876c
commit
c86970470f
7 changed files with 186 additions and 48 deletions
|
@ -299,8 +299,13 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun savePlaylistEdit() {
|
fun savePlaylistEdit() {
|
||||||
val playlist = _currentPlaylist.value ?: return
|
val playlist = _currentPlaylist.value ?: return
|
||||||
val editedPlaylist = (_editedPlaylist.value ?: return).also { _editedPlaylist.value = null }
|
val editedPlaylist = _editedPlaylist.value ?: return
|
||||||
musicRepository.rewritePlaylist(playlist, editedPlaylist)
|
viewModelScope.launch {
|
||||||
|
musicRepository.rewritePlaylist(playlist, editedPlaylist)
|
||||||
|
// TODO: The user could probably press some kind of button if they were fast enough.
|
||||||
|
// Think of a better way to handle this state.
|
||||||
|
_editedPlaylist.value = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -116,7 +116,7 @@ interface MusicRepository {
|
||||||
* @param name The name of the new [Playlist].
|
* @param name The name of the new [Playlist].
|
||||||
* @param songs The songs to populate the new [Playlist] with.
|
* @param songs The songs to populate the new [Playlist] with.
|
||||||
*/
|
*/
|
||||||
fun createPlaylist(name: String, songs: List<Song>)
|
suspend fun createPlaylist(name: String, songs: List<Song>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename a [Playlist].
|
* Rename a [Playlist].
|
||||||
|
@ -124,14 +124,14 @@ interface MusicRepository {
|
||||||
* @param playlist The [Playlist] to rename.
|
* @param playlist The [Playlist] to rename.
|
||||||
* @param name The name of the new [Playlist].
|
* @param name The name of the new [Playlist].
|
||||||
*/
|
*/
|
||||||
fun renamePlaylist(playlist: Playlist, name: String)
|
suspend fun renamePlaylist(playlist: Playlist, name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a [Playlist].
|
* Delete a [Playlist].
|
||||||
*
|
*
|
||||||
* @param playlist The playlist to delete.
|
* @param playlist The playlist to delete.
|
||||||
*/
|
*/
|
||||||
fun deletePlaylist(playlist: Playlist)
|
suspend fun deletePlaylist(playlist: Playlist)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given [Song]s to a [Playlist].
|
* Add the given [Song]s to a [Playlist].
|
||||||
|
@ -139,7 +139,7 @@ interface MusicRepository {
|
||||||
* @param songs The [Song]s to add to the [Playlist].
|
* @param songs The [Song]s to add to the [Playlist].
|
||||||
* @param playlist The [Playlist] to add to.
|
* @param playlist The [Playlist] to add to.
|
||||||
*/
|
*/
|
||||||
fun addToPlaylist(songs: List<Song>, playlist: Playlist)
|
suspend fun addToPlaylist(songs: List<Song>, playlist: Playlist)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the [Song]s of a [Playlist].
|
* Update the [Song]s of a [Playlist].
|
||||||
|
@ -147,7 +147,7 @@ interface MusicRepository {
|
||||||
* @param playlist The [Playlist] to update.
|
* @param playlist The [Playlist] to update.
|
||||||
* @param songs The new [Song]s to be contained in the [Playlist].
|
* @param songs The new [Song]s to be contained in the [Playlist].
|
||||||
*/
|
*/
|
||||||
fun rewritePlaylist(playlist: Playlist, songs: List<Song>)
|
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that a music loading operation is started by the current [IndexingWorker]. Does
|
* Request that a music loading operation is started by the current [IndexingWorker]. Does
|
||||||
|
@ -276,7 +276,7 @@ constructor(
|
||||||
(deviceLibrary?.run { findSong(uid) ?: findAlbum(uid) ?: findArtist(uid) ?: findGenre(uid) }
|
(deviceLibrary?.run { findSong(uid) ?: findAlbum(uid) ?: findArtist(uid) ?: findGenre(uid) }
|
||||||
?: userLibrary?.findPlaylist(uid))
|
?: userLibrary?.findPlaylist(uid))
|
||||||
|
|
||||||
override fun createPlaylist(name: String, songs: List<Song>) {
|
override suspend fun createPlaylist(name: String, songs: List<Song>) {
|
||||||
val userLibrary = userLibrary ?: return
|
val userLibrary = userLibrary ?: return
|
||||||
userLibrary.createPlaylist(name, songs)
|
userLibrary.createPlaylist(name, songs)
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
|
@ -285,7 +285,7 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renamePlaylist(playlist: Playlist, name: String) {
|
override suspend fun renamePlaylist(playlist: Playlist, name: String) {
|
||||||
val userLibrary = userLibrary ?: return
|
val userLibrary = userLibrary ?: return
|
||||||
userLibrary.renamePlaylist(playlist, name)
|
userLibrary.renamePlaylist(playlist, name)
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
|
@ -294,7 +294,7 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deletePlaylist(playlist: Playlist) {
|
override suspend fun deletePlaylist(playlist: Playlist) {
|
||||||
val userLibrary = userLibrary ?: return
|
val userLibrary = userLibrary ?: return
|
||||||
userLibrary.deletePlaylist(playlist)
|
userLibrary.deletePlaylist(playlist)
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
|
@ -303,7 +303,7 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addToPlaylist(songs: List<Song>, playlist: Playlist) {
|
override suspend fun addToPlaylist(songs: List<Song>, playlist: Playlist) {
|
||||||
val userLibrary = userLibrary ?: return
|
val userLibrary = userLibrary ?: return
|
||||||
userLibrary.addToPlaylist(playlist, songs)
|
userLibrary.addToPlaylist(playlist, songs)
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
|
@ -312,7 +312,7 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
|
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
|
||||||
val userLibrary = userLibrary ?: return
|
val userLibrary = userLibrary ?: return
|
||||||
userLibrary.rewritePlaylist(playlist, songs)
|
userLibrary.rewritePlaylist(playlist, songs)
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
|
|
|
@ -19,10 +19,13 @@
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.oxycblt.auxio.util.Event
|
import org.oxycblt.auxio.util.Event
|
||||||
import org.oxycblt.auxio.util.MutableEvent
|
import org.oxycblt.auxio.util.MutableEvent
|
||||||
|
|
||||||
|
@ -110,7 +113,7 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun createPlaylist(name: String? = null, songs: List<Song> = listOf()) {
|
fun createPlaylist(name: String? = null, songs: List<Song> = listOf()) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
musicRepository.createPlaylist(name, songs)
|
viewModelScope.launch(Dispatchers.IO) { musicRepository.createPlaylist(name, songs) }
|
||||||
} else {
|
} else {
|
||||||
_newPlaylistSongs.put(songs)
|
_newPlaylistSongs.put(songs)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +127,7 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun renamePlaylist(playlist: Playlist, name: String? = null) {
|
fun renamePlaylist(playlist: Playlist, name: String? = null) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
musicRepository.renamePlaylist(playlist, name)
|
viewModelScope.launch(Dispatchers.IO) { musicRepository.renamePlaylist(playlist, name) }
|
||||||
} else {
|
} else {
|
||||||
_playlistToRename.put(playlist)
|
_playlistToRename.put(playlist)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +142,7 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
|
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
|
||||||
if (rude) {
|
if (rude) {
|
||||||
musicRepository.deletePlaylist(playlist)
|
viewModelScope.launch(Dispatchers.IO) { musicRepository.deletePlaylist(playlist) }
|
||||||
} else {
|
} else {
|
||||||
_playlistToDelete.put(playlist)
|
_playlistToDelete.put(playlist)
|
||||||
}
|
}
|
||||||
|
@ -193,7 +196,7 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) {
|
fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) {
|
||||||
if (playlist != null) {
|
if (playlist != null) {
|
||||||
musicRepository.addToPlaylist(songs, playlist)
|
viewModelScope.launch(Dispatchers.IO) { musicRepository.addToPlaylist(songs, playlist) }
|
||||||
} else {
|
} else {
|
||||||
_songsToAdd.put(songs)
|
_songsToAdd.put(songs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ package org.oxycblt.auxio.music.user
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw playlist information persisted to [UserMusicDatabase].
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
data class RawPlaylist(
|
data class RawPlaylist(
|
||||||
@Embedded val playlistInfo: PlaylistInfo,
|
@Embedded val playlistInfo: PlaylistInfo,
|
||||||
@Relation(
|
@Relation(
|
||||||
|
@ -30,12 +34,26 @@ data class RawPlaylist(
|
||||||
val songs: List<PlaylistSong>
|
val songs: List<PlaylistSong>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UID and name information corresponding to a [RawPlaylist] entry.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
@Entity data class PlaylistInfo(@PrimaryKey val playlistUid: Music.UID, val name: String)
|
@Entity data class PlaylistInfo(@PrimaryKey val playlistUid: Music.UID, val name: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Song information corresponding to a [RawPlaylist] entry.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
@Entity data class PlaylistSong(@PrimaryKey val songUid: Music.UID)
|
@Entity data class PlaylistSong(@PrimaryKey val songUid: Music.UID)
|
||||||
|
|
||||||
@Entity(primaryKeys = ["playlistUid", "songUid"])
|
|
||||||
|
/**
|
||||||
|
* Links individual songs to a playlist entry.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
data class PlaylistSongCrossRef(
|
data class PlaylistSongCrossRef(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
val playlistUid: Music.UID,
|
val playlistUid: Music.UID,
|
||||||
@ColumnInfo(index = true) val songUid: Music.UID
|
val songUid: Music.UID
|
||||||
)
|
)
|
||||||
|
|
|
@ -57,11 +57,12 @@ interface UserLibrary {
|
||||||
/**
|
/**
|
||||||
* Create a new [UserLibrary].
|
* Create a new [UserLibrary].
|
||||||
*
|
*
|
||||||
* @param deviceLibrary Asynchronously populated [DeviceLibrary] that can be obtained later.
|
* @param deviceLibraryChannel Asynchronously populated [DeviceLibrary] that can be obtained
|
||||||
* This allows database information to be read before the actual instance is constructed.
|
* later. This allows database information to be read before the actual instance is
|
||||||
|
* constructed.
|
||||||
* @return A new [MutableUserLibrary] with the required implementation.
|
* @return A new [MutableUserLibrary] with the required implementation.
|
||||||
*/
|
*/
|
||||||
suspend fun read(deviceLibrary: Channel<DeviceLibrary>): MutableUserLibrary
|
suspend fun read(deviceLibraryChannel: Channel<DeviceLibrary>): MutableUserLibrary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ interface MutableUserLibrary : UserLibrary {
|
||||||
* @param name The name of the [Playlist].
|
* @param name The name of the [Playlist].
|
||||||
* @param songs The songs to place in the [Playlist].
|
* @param songs The songs to place in the [Playlist].
|
||||||
*/
|
*/
|
||||||
fun createPlaylist(name: String, songs: List<Song>)
|
suspend fun createPlaylist(name: String, songs: List<Song>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename a [Playlist].
|
* Rename a [Playlist].
|
||||||
|
@ -86,21 +87,21 @@ interface MutableUserLibrary : UserLibrary {
|
||||||
* @param playlist The [Playlist] to rename.
|
* @param playlist The [Playlist] to rename.
|
||||||
* @param name The name of the new [Playlist].
|
* @param name The name of the new [Playlist].
|
||||||
*/
|
*/
|
||||||
fun renamePlaylist(playlist: Playlist, name: String)
|
suspend fun renamePlaylist(playlist: Playlist, name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a [Playlist].
|
* Delete a [Playlist].
|
||||||
*
|
*
|
||||||
* @param playlist The playlist to delete.
|
* @param playlist The playlist to delete.
|
||||||
*/
|
*/
|
||||||
fun deletePlaylist(playlist: Playlist)
|
suspend fun deletePlaylist(playlist: Playlist)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add [Song]s to a [Playlist].
|
* Add [Song]s to a [Playlist].
|
||||||
*
|
*
|
||||||
* @param playlist The [Playlist] to add to. Must currently exist.
|
* @param playlist The [Playlist] to add to. Must currently exist.
|
||||||
*/
|
*/
|
||||||
fun addToPlaylist(playlist: Playlist, songs: List<Song>)
|
suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the [Song]s of a [Playlist].
|
* Update the [Song]s of a [Playlist].
|
||||||
|
@ -108,23 +109,32 @@ interface MutableUserLibrary : UserLibrary {
|
||||||
* @param playlist The [Playlist] to update.
|
* @param playlist The [Playlist] to update.
|
||||||
* @param songs The new [Song]s to be contained in the [Playlist].
|
* @param songs The new [Song]s to be contained in the [Playlist].
|
||||||
*/
|
*/
|
||||||
fun rewritePlaylist(playlist: Playlist, songs: List<Song>)
|
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>)
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserLibraryFactoryImpl
|
class UserLibraryFactoryImpl
|
||||||
@Inject
|
@Inject
|
||||||
constructor(private val playlistDao: PlaylistDao, private val musicSettings: MusicSettings) :
|
constructor(private val playlistDao: PlaylistDao, private val musicSettings: MusicSettings) :
|
||||||
UserLibrary.Factory {
|
UserLibrary.Factory {
|
||||||
override suspend fun read(deviceLibrary: Channel<DeviceLibrary>): MutableUserLibrary =
|
override suspend fun read(deviceLibraryChannel: Channel<DeviceLibrary>): MutableUserLibrary {
|
||||||
UserLibraryImpl(playlistDao, deviceLibrary.receive(), musicSettings)
|
// While were waiting for the library, read our playlists out.
|
||||||
|
val rawPlaylists = playlistDao.readRawPlaylists()
|
||||||
|
val deviceLibrary = deviceLibraryChannel.receive()
|
||||||
|
// Convert the database playlist information to actual usable playlists.
|
||||||
|
val playlistMap = mutableMapOf<Music.UID, PlaylistImpl>()
|
||||||
|
for (rawPlaylist in rawPlaylists) {
|
||||||
|
val playlistImpl = PlaylistImpl.fromRaw(rawPlaylist, deviceLibrary, musicSettings)
|
||||||
|
playlistMap[playlistImpl.uid] = playlistImpl
|
||||||
|
}
|
||||||
|
return UserLibraryImpl(playlistDao, playlistMap, musicSettings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserLibraryImpl(
|
private class UserLibraryImpl(
|
||||||
private val playlistDao: PlaylistDao,
|
private val playlistDao: PlaylistDao,
|
||||||
private val deviceLibrary: DeviceLibrary,
|
private val playlistMap: MutableMap<Music.UID, PlaylistImpl>,
|
||||||
private val musicSettings: MusicSettings
|
private val musicSettings: MusicSettings
|
||||||
) : MutableUserLibrary {
|
) : MutableUserLibrary {
|
||||||
private val playlistMap = mutableMapOf<Music.UID, PlaylistImpl>()
|
|
||||||
override val playlists: List<Playlist>
|
override val playlists: List<Playlist>
|
||||||
get() = playlistMap.values.toList()
|
get() = playlistMap.values.toList()
|
||||||
|
|
||||||
|
@ -132,35 +142,41 @@ private class UserLibraryImpl(
|
||||||
|
|
||||||
override fun findPlaylist(name: String) = playlistMap.values.find { it.name.raw == name }
|
override fun findPlaylist(name: String) = playlistMap.values.find { it.name.raw == name }
|
||||||
|
|
||||||
@Synchronized
|
override suspend fun createPlaylist(name: String, songs: List<Song>) {
|
||||||
override fun createPlaylist(name: String, songs: List<Song>) {
|
|
||||||
val playlistImpl = PlaylistImpl.from(name, songs, musicSettings)
|
val playlistImpl = PlaylistImpl.from(name, songs, musicSettings)
|
||||||
playlistMap[playlistImpl.uid] = playlistImpl
|
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
|
||||||
|
val rawPlaylist =
|
||||||
|
RawPlaylist(
|
||||||
|
PlaylistInfo(playlistImpl.uid, playlistImpl.name.raw),
|
||||||
|
playlistImpl.songs.map { PlaylistSong(it.uid) })
|
||||||
|
playlistDao.insertPlaylist(rawPlaylist)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
override suspend fun renamePlaylist(playlist: Playlist, name: String) {
|
||||||
override fun renamePlaylist(playlist: Playlist, name: String) {
|
|
||||||
val playlistImpl =
|
val playlistImpl =
|
||||||
requireNotNull(playlistMap[playlist.uid]) { "Cannot rename invalid playlist" }
|
requireNotNull(playlistMap[playlist.uid]) { "Cannot rename invalid playlist" }
|
||||||
playlistMap[playlist.uid] = playlistImpl.edit(name, musicSettings)
|
synchronized(this) { playlistMap[playlist.uid] = playlistImpl.edit(name, musicSettings) }
|
||||||
|
playlistDao.replacePlaylistInfo(PlaylistInfo(playlist.uid, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
override suspend fun deletePlaylist(playlist: Playlist) {
|
||||||
override fun deletePlaylist(playlist: Playlist) {
|
synchronized(this) {
|
||||||
requireNotNull(playlistMap.remove(playlist.uid)) { "Cannot remove invalid playlist" }
|
requireNotNull(playlistMap.remove(playlist.uid)) { "Cannot remove invalid playlist" }
|
||||||
|
}
|
||||||
|
playlistDao.deletePlaylist(playlist.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>) {
|
||||||
override fun addToPlaylist(playlist: Playlist, songs: List<Song>) {
|
|
||||||
val playlistImpl =
|
val playlistImpl =
|
||||||
requireNotNull(playlistMap[playlist.uid]) { "Cannot add to invalid playlist" }
|
requireNotNull(playlistMap[playlist.uid]) { "Cannot add to invalid playlist" }
|
||||||
playlistMap[playlist.uid] = playlistImpl.edit { addAll(songs) }
|
synchronized(this) { playlistMap[playlist.uid] = playlistImpl.edit { addAll(songs) } }
|
||||||
|
playlistDao.insertPlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
|
||||||
override fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
|
|
||||||
val playlistImpl =
|
val playlistImpl =
|
||||||
requireNotNull(playlistMap[playlist.uid]) { "Cannot rewrite invalid playlist" }
|
requireNotNull(playlistMap[playlist.uid]) { "Cannot rewrite invalid playlist" }
|
||||||
playlistMap[playlist.uid] = playlistImpl.edit(songs)
|
synchronized(this) { playlistMap[playlist.uid] = playlistImpl.edit(songs) }
|
||||||
|
playlistDao.replacePlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import dagger.hilt.components.SingletonComponent
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
interface UserModule {
|
interface UserModule {
|
||||||
@Binds fun userLibaryFactory(factory: UserLibraryFactoryImpl): UserLibrary.Factory
|
@Binds fun userLibraryFactory(factory: UserLibraryFactoryImpl): UserLibrary.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
|
|
@ -21,16 +21,112 @@ package org.oxycblt.auxio.music.user
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows persistence of all user-created music information.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
@Database(
|
@Database(
|
||||||
entities = [PlaylistInfo::class, PlaylistSong::class, PlaylistSongCrossRef::class],
|
entities = [PlaylistInfo::class, PlaylistSong::class, PlaylistSongCrossRef::class],
|
||||||
version = 28,
|
version = 30,
|
||||||
exportSchema = false)
|
exportSchema = false)
|
||||||
@TypeConverters(Music.UID.TypeConverters::class)
|
@TypeConverters(Music.UID.TypeConverters::class)
|
||||||
abstract class UserMusicDatabase : RoomDatabase() {
|
abstract class UserMusicDatabase : RoomDatabase() {
|
||||||
abstract fun playlistDao(): PlaylistDao
|
abstract fun playlistDao(): PlaylistDao
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Handle playlist defragmentation? I really don't want dead songs to accumulate in this
|
||||||
|
// database.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DAO for persisted playlist information.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
@Dao
|
@Dao
|
||||||
interface PlaylistDao {
|
interface PlaylistDao {
|
||||||
@Transaction @Query("SELECT * FROM PlaylistInfo") fun readRawPlaylists(): List<RawPlaylist>
|
/**
|
||||||
|
* Read out all playlists stored in the database.
|
||||||
|
* @return A list of [RawPlaylist] representing each playlist stored.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM PlaylistInfo")
|
||||||
|
suspend fun readRawPlaylists(): List<RawPlaylist>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new playlist.
|
||||||
|
* @param rawPlaylist The [RawPlaylist] to create.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
suspend fun insertPlaylist(rawPlaylist: RawPlaylist) {
|
||||||
|
insertInfo(rawPlaylist.playlistInfo)
|
||||||
|
insertSongs(rawPlaylist.songs)
|
||||||
|
insertRefs(
|
||||||
|
rawPlaylist.songs.map {
|
||||||
|
PlaylistSongCrossRef(
|
||||||
|
playlistUid = rawPlaylist.playlistInfo.playlistUid, songUid = it.songUid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the currently-stored [PlaylistInfo] for a playlist entry.
|
||||||
|
* @param playlistInfo The new [PlaylistInfo] to store.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
suspend fun replacePlaylistInfo(playlistInfo: PlaylistInfo) {
|
||||||
|
deleteInfo(playlistInfo.playlistUid)
|
||||||
|
insertInfo(playlistInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a playlist entry's [PlaylistInfo] and [PlaylistSong].
|
||||||
|
* @param playlistUid The [Music.UID] of the playlist to delete.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
suspend fun deletePlaylist(playlistUid: Music.UID) {
|
||||||
|
deleteInfo(playlistUid)
|
||||||
|
deleteRefs(playlistUid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert new song entries into a playlist.
|
||||||
|
* @param playlistUid The [Music.UID] of the playlist to insert into.
|
||||||
|
* @param songs The [PlaylistSong] representing each song to put into the playlist.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
suspend fun insertPlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||||
|
insertSongs(songs)
|
||||||
|
insertRefs(
|
||||||
|
songs.map { PlaylistSongCrossRef(playlistUid = playlistUid, songUid = it.songUid) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the currently-stored [Song]s of the current playlist entry.
|
||||||
|
* @param playlistUid The [Music.UID] of the playlist to update.
|
||||||
|
* @param songs The [PlaylistSong] representing the new list of songs to be placed in the playlist.
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
suspend fun replacePlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||||
|
deleteRefs(playlistUid)
|
||||||
|
insertSongs(songs)
|
||||||
|
insertRefs(
|
||||||
|
songs.map { PlaylistSongCrossRef(playlistUid = playlistUid, songUid = it.songUid) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Internal, do not use. */
|
||||||
|
@Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insertInfo(info: PlaylistInfo)
|
||||||
|
|
||||||
|
/** Internal, do not use. */
|
||||||
|
@Query("DELETE FROM PlaylistInfo where playlistUid = :playlistUid")
|
||||||
|
suspend fun deleteInfo(playlistUid: Music.UID)
|
||||||
|
|
||||||
|
/** Internal, do not use. */
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
suspend fun insertSongs(songs: List<PlaylistSong>)
|
||||||
|
|
||||||
|
/** Internal, do not use. */
|
||||||
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
|
suspend fun insertRefs(refs: List<PlaylistSongCrossRef>)
|
||||||
|
|
||||||
|
/** Internal, do not use. */
|
||||||
|
@Query("DELETE FROM PlaylistSongCrossRef where playlistUid = :playlistUid")
|
||||||
|
suspend fun deleteRefs(playlistUid: Music.UID)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue