playlist: add runtime boilerplate
Add the boilerplate code for the playlist system. Any functionality is runtime only and is not integrated in-app. Plan is to build the UI scaffold first followed by the backing playlist.
This commit is contained in:
parent
abeac90735
commit
e4339a76bf
5 changed files with 147 additions and 8 deletions
|
@ -144,9 +144,11 @@ interface Indexer {
|
||||||
*
|
*
|
||||||
* @param result The outcome of the music loading process.
|
* @param result The outcome of the music loading process.
|
||||||
*/
|
*/
|
||||||
data class Complete(val result: Result<Library>) : State()
|
data class Complete(val result: Result<Response>) : State()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Response(val result: Library, val playlists: List<Playlist>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current progress of the music loader. Usually encapsulated in a [State].
|
* Represents the current progress of the music loader. Usually encapsulated in a [State].
|
||||||
*
|
*
|
||||||
|
@ -237,7 +239,7 @@ constructor(
|
||||||
private val mediaStoreExtractor: MediaStoreExtractor,
|
private val mediaStoreExtractor: MediaStoreExtractor,
|
||||||
private val tagExtractor: TagExtractor
|
private val tagExtractor: TagExtractor
|
||||||
) : Indexer {
|
) : Indexer {
|
||||||
@Volatile private var lastResponse: Result<Library>? = null
|
@Volatile private var lastResponse: Result<Indexer.Response>? = null
|
||||||
@Volatile private var indexingState: Indexer.Indexing? = null
|
@Volatile private var indexingState: Indexer.Indexing? = null
|
||||||
@Volatile private var controller: Indexer.Controller? = null
|
@Volatile private var controller: Indexer.Controller? = null
|
||||||
@Volatile private var listener: Indexer.Listener? = null
|
@Volatile private var listener: Indexer.Listener? = null
|
||||||
|
@ -303,11 +305,11 @@ constructor(
|
||||||
val result =
|
val result =
|
||||||
try {
|
try {
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
val library = indexImpl(context, withCache, this)
|
val response = indexImpl(context, withCache, this)
|
||||||
logD(
|
logD(
|
||||||
"Music indexing completed successfully in " +
|
"Music indexing completed successfully in " +
|
||||||
"${System.currentTimeMillis() - start}ms")
|
"${System.currentTimeMillis() - start}ms")
|
||||||
Result.success(library)
|
Result.success(response)
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
// Got cancelled, propagate upwards to top-level co-routine.
|
// Got cancelled, propagate upwards to top-level co-routine.
|
||||||
logD("Loading routine was cancelled")
|
logD("Loading routine was cancelled")
|
||||||
|
@ -337,7 +339,7 @@ constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
withCache: Boolean,
|
withCache: Boolean,
|
||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
): Library {
|
): Indexer.Response {
|
||||||
if (ContextCompat.checkSelfPermission(context, Indexer.PERMISSION_READ_AUDIO) ==
|
if (ContextCompat.checkSelfPermission(context, Indexer.PERMISSION_READ_AUDIO) ==
|
||||||
PackageManager.PERMISSION_DENIED) {
|
PackageManager.PERMISSION_DENIED) {
|
||||||
logE("Permission check failed")
|
logE("Permission check failed")
|
||||||
|
@ -395,7 +397,7 @@ constructor(
|
||||||
if (cache == null || cache.invalidated) {
|
if (cache == null || cache.invalidated) {
|
||||||
cacheRepository.writeCache(rawSongs)
|
cacheRepository.writeCache(rawSongs)
|
||||||
}
|
}
|
||||||
return libraryJob.await()
|
return Indexer.Response(libraryJob.await(), listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -426,7 +428,7 @@ constructor(
|
||||||
* @param result The new [Result] to emit, representing the outcome of the music loading
|
* @param result The new [Result] to emit, representing the outcome of the music loading
|
||||||
* process.
|
* process.
|
||||||
*/
|
*/
|
||||||
private suspend fun emitCompletion(result: Result<Library>) {
|
private suspend fun emitCompletion(result: Result<Indexer.Response>) {
|
||||||
yield()
|
yield()
|
||||||
// Swap to the Main thread so that downstream callbacks don't crash from being on
|
// Swap to the Main thread so that downstream callbacks don't crash from being on
|
||||||
// a background thread. Does not occur in emitIndexing due to efficiency reasons.
|
// a background thread. Does not occur in emitIndexing due to efficiency reasons.
|
||||||
|
|
|
@ -40,7 +40,7 @@ import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
entities = [PlaybackState::class, QueueHeapItem::class, QueueMappingItem::class],
|
entities = [PlaybackState::class, QueueHeapItem::class, QueueMappingItem::class],
|
||||||
version = 27,
|
version = 27,
|
||||||
exportSchema = false)
|
exportSchema = false)
|
||||||
@TypeConverters(PersistenceDatabase.Converters::class)
|
@TypeConverters(Music.UID.Converter::class)
|
||||||
abstract class PersistenceDatabase : RoomDatabase() {
|
abstract class PersistenceDatabase : RoomDatabase() {
|
||||||
/**
|
/**
|
||||||
* Get the current [PlaybackStateDao].
|
* Get the current [PlaybackStateDao].
|
||||||
|
|
29
app/src/main/java/org/oxycblt/auxio/playlist/Playlist.kt
Normal file
29
app/src/main/java/org/oxycblt/auxio/playlist/Playlist.kt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* Playlist.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.playlist
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
|
interface Playlist {
|
||||||
|
val id: UUID
|
||||||
|
val name: String
|
||||||
|
val songs: List<Song>
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* PlaylistModule.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.playlist
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
interface PlaylistModule {
|
||||||
|
@Binds
|
||||||
|
fun playlistRepository(playlistRepositoryImpl: PlaylistRepositoryImpl): PlaylistRepository
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* PlaylistRepository.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.playlist
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
|
interface PlaylistRepository {
|
||||||
|
val playlists: List<Playlist>
|
||||||
|
suspend fun createPlaylist(name: String, songs: List<Song>)
|
||||||
|
suspend fun deletePlaylist(playlist: Playlist)
|
||||||
|
suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>)
|
||||||
|
suspend fun removeFromPlaylist(playlist: Playlist, song: Song)
|
||||||
|
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>)
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlaylistRepositoryImpl @Inject constructor() : PlaylistRepository {
|
||||||
|
private val playlistMap = mutableMapOf<UUID, PlaylistImpl>()
|
||||||
|
override val playlists: List<Playlist>
|
||||||
|
get() = playlistMap.values.toList()
|
||||||
|
|
||||||
|
override suspend fun createPlaylist(name: String, songs: List<Song>) {
|
||||||
|
val uuid = UUID.randomUUID()
|
||||||
|
playlistMap[uuid] = PlaylistImpl(uuid, name, songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deletePlaylist(playlist: Playlist) {
|
||||||
|
playlistMap.remove(playlist.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>) {
|
||||||
|
editPlaylist(playlist) {
|
||||||
|
addAll(songs)
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun removeFromPlaylist(playlist: Playlist, song: Song) {
|
||||||
|
editPlaylist(playlist) {
|
||||||
|
remove(song)
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
|
||||||
|
editPlaylist(playlist) { songs }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun editPlaylist(playlist: Playlist, edits: MutableList<Song>.() -> List<Song>) {
|
||||||
|
check(playlistMap.containsKey(playlist.id)) { "Invalid playlist argument provided" }
|
||||||
|
playlistMap[playlist.id] =
|
||||||
|
PlaylistImpl(playlist.id, playlist.name, edits(playlist.songs.toMutableList()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class PlaylistImpl(
|
||||||
|
override val id: UUID,
|
||||||
|
override val name: String,
|
||||||
|
override val songs: List<Song>
|
||||||
|
) : Playlist
|
Loading…
Reference in a new issue