Use database executors instead of compose coroutines to launch database operations

This commit is contained in:
vfsfitvnm 2022-06-27 22:07:07 +02:00
parent 85f89ec799
commit 2cbb5a4ba4
11 changed files with 88 additions and 107 deletions

View file

@ -207,8 +207,19 @@ object Converters {
val Database.internal: RoomDatabase
get() = DatabaseInitializer.Instance
fun Database.checkpoint() {
internal.getOpenHelper().writableDatabase.run {
fun query(block: () -> Unit) = DatabaseInitializer.Instance.getQueryExecutor().execute(block)
fun transaction(block: () -> Unit) = with(DatabaseInitializer.Instance) {
getTransactionExecutor().execute {
runInTransaction(block)
}
}
val RoomDatabase.path: String
get() = getOpenHelper().writableDatabase.path
fun RoomDatabase.checkpoint() {
getOpenHelper().writableDatabase.run {
query("PRAGMA journal_mode").use { cursor ->
if (cursor.moveToFirst()) {
when (cursor.getString(0).lowercase()) {

View file

@ -41,8 +41,8 @@ import coil.request.ImageRequest
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.MainActivity
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.models.QueuedMediaItem
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.Outcome
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
@ -195,7 +195,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
val mediaItemIndex = player.currentMediaItemIndex
val mediaItemPosition = player.currentPosition
Database.internal.getQueryExecutor().execute {
query {
Database.clearQueue()
Database.insertQueue(
mediaItems.mapIndexed { index, mediaItem ->
@ -226,7 +226,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
val mediaItem =
eventTime.timeline.getWindow(eventTime.windowIndex, Timeline.Window()).mediaItem
Database.internal.getQueryExecutor().execute {
query {
Database.incrementTotalPlayTimeMs(mediaItem.mediaId, playbackStats.totalPlayTimeMs)
}
}
@ -443,7 +443,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
}
mediaItem?.let {
Database.internal.getQueryExecutor().execute {
query {
Database.insert(it)
}
}

View file

@ -16,10 +16,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.media3.common.MediaItem
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.empty
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongInPlaylist
import it.vfsfitvnm.vimusic.models.SongWithInfo
@ -39,13 +37,11 @@ fun InFavoritesMediaItemMenu(
modifier: Modifier = Modifier,
onDismiss: (() -> Unit)? = null
) {
val coroutineScope = rememberCoroutineScope()
NonQueuedMediaItemMenu(
mediaItem = song.asMediaItem,
onDismiss = onDismiss,
onRemoveFromFavorites = {
coroutineScope.launch(Dispatchers.IO) {
query {
Database.update(song.song.toggleLike())
}
},
@ -63,8 +59,6 @@ fun InHistoryMediaItemMenu(
val menuState = LocalMenuState.current
val binder = LocalPlayerServiceBinder.current
val coroutineScope = rememberCoroutineScope()
var isDeletingFromDatabase by remember {
mutableStateOf(false)
}
@ -77,8 +71,9 @@ fun InHistoryMediaItemMenu(
},
onConfirm = {
(onDismiss ?: menuState::hide).invoke()
binder?.cache?.removeResource(song.song.id)
coroutineScope.launch(Dispatchers.IO) {
query {
// Not sure we can to this here
binder?.cache?.removeResource(song.song.id)
Database.delete(song.song)
}
}
@ -104,26 +99,22 @@ fun InPlaylistMediaItemMenu(
modifier: Modifier = Modifier,
onDismiss: (() -> Unit)? = null
) {
val coroutineScope = rememberCoroutineScope()
NonQueuedMediaItemMenu(
mediaItem = song.asMediaItem,
onDismiss = onDismiss,
onRemoveFromPlaylist = {
coroutineScope.launch(Dispatchers.IO) {
Database.internal.runInTransaction {
Database.delete(
SongInPlaylist(
songId = song.song.id,
playlistId = playlistId,
position = positionInPlaylist
)
)
Database.decrementSongPositions(
transaction {
Database.delete(
SongInPlaylist(
songId = song.song.id,
playlistId = playlistId,
fromPosition = positionInPlaylist + 1
position = positionInPlaylist
)
}
)
Database.decrementSongPositions(
playlistId = playlistId,
fromPosition = positionInPlaylist + 1
)
}
},
modifier = modifier
@ -211,7 +202,6 @@ fun BaseMediaItemMenu(
onGlobalRouteEmitted: (() -> Unit)? = null,
) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val albumRoute = rememberPlaylistOrAlbumRoute()
val artistRoute = rememberArtistRoute()
@ -224,7 +214,7 @@ fun BaseMediaItemMenu(
onPlaySingle = onPlaySingle,
onEnqueue = onEnqueue,
onAddToPlaylist = { playlist, position ->
coroutineScope.launch(Dispatchers.IO) {
transaction {
val playlistId = Database.playlist(playlist.id)?.id ?: Database.insert(playlist)
Database.insert(mediaItem)
@ -498,6 +488,7 @@ fun MediaItemMenu(
icon = R.drawable.trash,
text = "Delete",
onClick = {
onDismiss()
onDeleteFromDatabase()
}
)

View file

@ -1,7 +1,6 @@
package it.vfsfitvnm.vimusic.ui.screens
import android.net.Uri
import android.util.Log
import androidx.compose.animation.*
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
@ -39,6 +38,7 @@ import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SearchQuery
import it.vfsfitvnm.vimusic.models.SongWithInfo
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
@ -50,7 +50,6 @@ import it.vfsfitvnm.vimusic.ui.views.PlaylistPreviewItem
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ExperimentalFoundationApi
@ -60,8 +59,6 @@ fun HomeScreen() {
val colorPalette = LocalColorPalette.current
val typography = LocalTypography.current
val coroutineScope = rememberCoroutineScope()
val lazyListState = rememberLazyListState()
val intentUriRoute = rememberIntentUriRoute()
@ -86,8 +83,6 @@ fun HomeScreen() {
}
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
Log.d("HomeScreen", "songCollection: ${songCollection.size}")
RouteHandler(
listenToGlobalEmitter = true,
transitionSpec = {
@ -122,7 +117,7 @@ fun HomeScreen() {
onSearch = { query ->
searchResultRoute(query)
coroutineScope.launch(Dispatchers.IO) {
query {
Database.insert(SearchQuery(query = query))
}
},
@ -173,7 +168,7 @@ fun HomeScreen() {
isCreatingANewPlaylist = false
},
onDone = { text ->
coroutineScope.launch(Dispatchers.IO) {
query {
Database.insert(Playlist(name = text))
}
}

View file

@ -23,9 +23,9 @@ import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongInPlaylist
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.Error
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.Message
@ -40,7 +40,6 @@ import it.vfsfitvnm.youtubemusic.Outcome
import it.vfsfitvnm.youtubemusic.YouTube
import it.vfsfitvnm.youtubemusic.toNullable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ExperimentalAnimationApi
@ -70,7 +69,6 @@ fun IntentUriScreen(uri: Uri) {
val density = LocalDensity.current
val binder = LocalPlayerServiceBinder.current
val coroutineScope = rememberCoroutineScope()
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
var items by remember(uri) {
@ -102,24 +100,22 @@ fun IntentUriScreen(uri: Uri) {
onDone = { text ->
menuState.hide()
coroutineScope.launch(Dispatchers.IO) {
Database.internal.runInTransaction {
val playlistId = Database.insert(Playlist(name = text))
transaction {
val playlistId = Database.insert(Playlist(name = text))
items.valueOrNull
?.map(YouTube.Item.Song::asMediaItem)
?.forEachIndexed { index, mediaItem ->
Database.insert(mediaItem)
items.valueOrNull
?.map(YouTube.Item.Song::asMediaItem)
?.forEachIndexed { index, mediaItem ->
Database.insert(mediaItem)
Database.insert(
SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
Database.insert(
SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
}
}
)
}
}
}
)

View file

@ -24,8 +24,7 @@ import androidx.compose.ui.unit.dp
import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
import it.vfsfitvnm.vimusic.models.SongInPlaylist
@ -39,7 +38,6 @@ import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@ExperimentalAnimationApi
@ -84,8 +82,6 @@ fun LocalPlaylistScreen(
}
}
val coroutineScope = rememberCoroutineScope()
val reorderingState = rememberReorderingState(playlistWithSongs.songs)
var isRenaming by rememberSaveable {
@ -100,7 +96,7 @@ fun LocalPlaylistScreen(
isRenaming = false
},
onDone = { text ->
coroutineScope.launch(Dispatchers.IO) {
query {
Database.update(playlistWithSongs.playlist.copy(name = text))
}
}
@ -118,7 +114,7 @@ fun LocalPlaylistScreen(
isDeleting = false
},
onConfirm = {
coroutineScope.launch(Dispatchers.IO) {
query {
Database.delete(playlistWithSongs.playlist)
}
pop()
@ -290,7 +286,7 @@ fun LocalPlaylistScreen(
)
},
onDragEnd = { reachedIndex ->
coroutineScope.launch(Dispatchers.IO) {
transaction {
if (index > reachedIndex) {
Database.incrementSongPositions(
playlistId = playlistWithSongs.playlist.id,

View file

@ -31,9 +31,9 @@ import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongInPlaylist
import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.OutcomeItem
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
@ -45,7 +45,6 @@ import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.Outcome
import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -164,26 +163,24 @@ fun PlaylistOrAlbumScreen(
menuState.hide()
playlistOrAlbum.valueOrNull?.let { album ->
coroutineScope.launch(Dispatchers.IO) {
Database.internal.runInTransaction {
val playlistId =
Database.insert(Playlist(name = album.title ?: "Unknown"))
transaction {
val playlistId =
Database.insert(Playlist(name = album.title ?: "Unknown"))
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
Database.insert(mediaItem)
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
Database.insert(mediaItem)
Database.insert(
SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
Database.insert(
SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
}
}
)
}
}
}
}

View file

@ -33,6 +33,7 @@ import androidx.core.net.toUri
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.OutcomeItem
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
@ -44,7 +45,6 @@ import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -107,8 +107,6 @@ fun SearchScreen(
val colorPalette = LocalColorPalette.current
val typography = LocalTypography.current
val coroutineScope = rememberCoroutineScope()
val isOpenableUrl = remember(textFieldValue.text) {
Regex("""https://(music|www|m)\.youtube.com/(watch|playlist).*""").matches(textFieldValue.text)
}
@ -280,7 +278,7 @@ fun SearchScreen(
colorFilter = ColorFilter.tint(colorPalette.darkGray),
modifier = Modifier
.clickable {
coroutineScope.launch(Dispatchers.IO) {
query {
Database.delete(searchQuery)
}
}

View file

@ -8,8 +8,11 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
@ -18,10 +21,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.checkpoint
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
@ -32,8 +33,6 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.FileInputStream
import java.io.FileOutputStream
import java.text.SimpleDateFormat
@ -67,17 +66,15 @@ fun BackupAndRestoreScreen() {
val typography = LocalTypography.current
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val backupLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri ->
if (uri == null) return@rememberLauncherForActivityResult
coroutineScope.launch(Dispatchers.IO) {
Database.checkpoint()
query {
Database.internal.checkpoint()
context.applicationContext.contentResolver.openOutputStream(uri)
?.use { outputStream ->
FileInputStream(Database.internal.getOpenHelper().writableDatabase.path).use { inputStream ->
FileInputStream(Database.internal.path).use { inputStream ->
inputStream.copyTo(outputStream)
}
}
@ -88,10 +85,10 @@ fun BackupAndRestoreScreen() {
rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri == null) return@rememberLauncherForActivityResult
coroutineScope.launch(Dispatchers.IO) {
query {
Database.internal.close()
FileOutputStream(Database.internal.getOpenHelper().writableDatabase.path).use { outputStream ->
FileOutputStream(Database.internal.path).use { outputStream ->
context.applicationContext.contentResolver.openInputStream(uri)
?.use { inputStream ->
inputStream.copyTo(outputStream)

View file

@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.media3.common.Player
import it.vfsfitvnm.route.Route
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.empty
@ -28,6 +27,7 @@ import it.vfsfitvnm.route.rememberRoute
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.models.Song
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.BottomSheet
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.screens.rememberLyricsRoute
@ -55,7 +55,6 @@ fun PlayerBottomSheet(
val colorPalette = LocalColorPalette.current
val typography = LocalTypography.current
val coroutineScope = rememberCoroutineScope()
val lyricsRoute = rememberLyricsRoute()
@ -209,7 +208,7 @@ fun PlayerBottomSheet(
}.map { lyrics ->
lyrics ?: ""
}.map { lyrics ->
withContext(Dispatchers.IO) {
query {
(song ?: mediaItem.let(Database::insert)).let {
Database.update(it.copy(lyrics = lyrics))
}
@ -230,7 +229,7 @@ fun PlayerBottomSheet(
},
onLyricsUpdate = { lyrics ->
val mediaItem = player?.currentMediaItem
coroutineScope.launch(Dispatchers.IO) {
query {
(song ?: mediaItem?.let(Database::insert))?.let {
Database.update(it.copy(lyrics = lyrics))
}

View file

@ -40,6 +40,7 @@ import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.*
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette
@ -537,7 +538,7 @@ fun PlayerView(
),
modifier = Modifier
.clickable {
coroutineScope.launch(Dispatchers.IO) {
query {
(song ?: playerState.mediaItem?.let(Database::insert))?.let {
Database.update(it.toggleLike())
}