From b2ac5b8c892a26d3c18dd13c2c2db400bc172a77 Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Tue, 4 Oct 2022 08:29:00 +0200 Subject: [PATCH] Add DatabaseSettings tab to SettingsScreen --- .../ui/screens/settings/DatabaseSettings.kt | 188 ++++++++++++++++++ .../ui/screens/settings/OtherSettings.kt | 127 ------------ .../ui/screens/settings/SettingsScreen.kt | 10 +- 3 files changed, 194 insertions(+), 131 deletions(-) create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt new file mode 100644 index 0000000..900eb6a --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt @@ -0,0 +1,188 @@ +package it.vfsfitvnm.vimusic.ui.screens.settings + +import android.annotation.SuppressLint +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.autoSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import it.vfsfitvnm.vimusic.Database +import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues +import it.vfsfitvnm.vimusic.checkpoint +import it.vfsfitvnm.vimusic.internal +import it.vfsfitvnm.vimusic.path +import it.vfsfitvnm.vimusic.query +import it.vfsfitvnm.vimusic.service.PlayerService +import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog +import it.vfsfitvnm.vimusic.ui.components.themed.Header +import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.utils.intent +import it.vfsfitvnm.vimusic.utils.produceSaveableState +import java.io.FileInputStream +import java.io.FileOutputStream +import java.text.SimpleDateFormat +import java.util.Date +import kotlin.system.exitProcess +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn + +@ExperimentalAnimationApi +@Composable +fun DatabaseSettings() { + val context = LocalContext.current + val (colorPalette) = LocalAppearance.current + + var isShowingRestoreDialog by rememberSaveable { + mutableStateOf(false) + } + + val queriesCount by produceSaveableState(initialValue = 0, stateSaver = autoSaver()) { + Database.queriesCount().flowOn(Dispatchers.IO).distinctUntilChanged().collect { value = it } + } + + val openDocumentContract = ActivityResultContracts.OpenDocument() + val createDocumentContract = ActivityResultContracts.CreateDocument("application/vnd.sqlite3") + + val backupLauncher = rememberLauncherForActivityResult(createDocumentContract) { uri -> + if (uri == null) return@rememberLauncherForActivityResult + + query { + Database.internal.checkpoint() + context.applicationContext.contentResolver.openOutputStream(uri) + ?.use { outputStream -> + FileInputStream(Database.internal.path).use { inputStream -> + inputStream.copyTo(outputStream) + } + } + } + } + + val restoreLauncher = rememberLauncherForActivityResult(openDocumentContract) { uri -> + if (uri == null) return@rememberLauncherForActivityResult + + query { + Database.internal.checkpoint() + Database.internal.close() + + FileOutputStream(Database.internal.path).use { outputStream -> + context.applicationContext.contentResolver.openInputStream(uri) + ?.use { inputStream -> + inputStream.copyTo(outputStream) + } + } + + context.stopService(context.intent()) + exitProcess(0) + } + } + + if (isShowingRestoreDialog) { + ConfirmationDialog( + text = "The application will automatically close itself to avoid problems after restoring the database.", + onDismiss = { + isShowingRestoreDialog = false + }, + onConfirm = { + val input = arrayOf( + "application/x-sqlite3", + "application/vnd.sqlite3", + "application/octet-stream" + ) + + if (openDocumentContract.createIntent(context, input) + .resolveActivity(context.packageManager) != null + ) { + restoreLauncher.launch(input) + } else { + Toast.makeText( + context, + "Can't read the database from the external storage", + Toast.LENGTH_SHORT + ).show() + } + }, + confirmText = "Ok" + ) + } + + Column( + modifier = Modifier + .background(colorPalette.background0) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(LocalPlayerAwarePaddingValues.current) + ) { + Header(title = "Database") + + SettingsEntryGroupText(title = "SEARCH HISTORY") + + SettingsEntry( + title = "Clear search history", + text = if (queriesCount > 0) { + "Delete $queriesCount search queries" + } else { + "History is empty" + }, + isEnabled = queriesCount > 0, + onClick = { + query { + Database.clearQueries() + } + } + ) + + SettingsGroupSpacer() + + SettingsEntryGroupText(title = "BACKUP") + + SettingsDescription(text = "Personal preferences (i.e. the theme mode) and the cache are excluded.") + + SettingsEntry( + title = "Backup", + text = "Export the database to the external storage", + onClick = { + @SuppressLint("SimpleDateFormat") + val dateFormat = SimpleDateFormat("yyyyMMddHHmmss") + val input = "vimusic_${dateFormat.format(Date())}.db" + + if (createDocumentContract.createIntent(context, input) + .resolveActivity(context.packageManager) != null + ) { + backupLauncher.launch(input) + } else { + Toast.makeText( + context, + "Can't copy the database to the external storage", + Toast.LENGTH_SHORT + ).show() + } + } + ) + + SettingsGroupSpacer() + + SettingsEntryGroupText(title = "RESTORE") + + SettingsDescription(text = "Existing data will be overwritten.") + + SettingsEntry( + title = "Restore", + text = "Import the database from the external storage", + onClick = { isShowingRestoreDialog = true } + ) + } +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt index 75868d1..381d7d7 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt @@ -16,34 +16,18 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues -import it.vfsfitvnm.vimusic.checkpoint -import it.vfsfitvnm.vimusic.internal -import it.vfsfitvnm.vimusic.path -import it.vfsfitvnm.vimusic.query -import it.vfsfitvnm.vimusic.service.PlayerService -import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance -import it.vfsfitvnm.vimusic.utils.intent import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey import it.vfsfitvnm.vimusic.utils.rememberPreference -import java.io.FileInputStream -import java.io.FileOutputStream -import java.text.SimpleDateFormat -import java.util.Date -import kotlin.system.exitProcess -import kotlinx.coroutines.Dispatchers @ExperimentalAnimationApi @Composable @@ -51,79 +35,17 @@ fun OtherSettings() { val context = LocalContext.current val (colorPalette) = LocalAppearance.current - val queriesCount by remember { - Database.queriesCount() - }.collectAsState(initial = 0, context = Dispatchers.IO) - var isInvincibilityEnabled by rememberPreference(isInvincibilityEnabledKey, false) var isIgnoringBatteryOptimizations by remember { mutableStateOf(context.isIgnoringBatteryOptimizations) } - var isShowingRestoreDialog by rememberSaveable { - mutableStateOf(false) - } - val activityResultLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { isIgnoringBatteryOptimizations = context.isIgnoringBatteryOptimizations } - val backupLauncher = - rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri -> - if (uri == null) return@rememberLauncherForActivityResult - - query { - Database.internal.checkpoint() - context.applicationContext.contentResolver.openOutputStream(uri) - ?.use { outputStream -> - FileInputStream(Database.internal.path).use { inputStream -> - inputStream.copyTo(outputStream) - } - } - } - } - - val restoreLauncher = - rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> - if (uri == null) return@rememberLauncherForActivityResult - - query { - Database.internal.checkpoint() - Database.internal.close() - - FileOutputStream(Database.internal.path).use { outputStream -> - context.applicationContext.contentResolver.openInputStream(uri) - ?.use { inputStream -> - inputStream.copyTo(outputStream) - } - } - - context.stopService(context.intent()) - exitProcess(0) - } - } - - if (isShowingRestoreDialog) { - ConfirmationDialog( - text = "The application will automatically close itself to avoid problems after restoring the database.", - onDismiss = { - isShowingRestoreDialog = false - }, - onConfirm = { - restoreLauncher.launch( - arrayOf( - "application/x-sqlite3", - "application/vnd.sqlite3", - "application/octet-stream" - ) - ) - }, - confirmText = "Ok" - ) - } - Column( modifier = Modifier .background(colorPalette.background0) @@ -133,25 +55,6 @@ fun OtherSettings() { ) { Header(title = "Other") - SettingsEntryGroupText(title = "SEARCH HISTORY") - - SettingsEntry( - title = "Clear search history", - text = if (queriesCount > 0) { - "Delete $queriesCount search queries" - } else { - "History is empty" - }, - isEnabled = queriesCount > 0, - onClick = { - query { - Database.clearQueries() - } - } - ) - - SettingsGroupSpacer() - SettingsEntryGroupText(title = "SERVICE LIFETIME") SettingsDescription(text = "If battery optimizations are applied, the playback notification can suddenly disappear when paused.") @@ -202,35 +105,5 @@ fun OtherSettings() { isChecked = isInvincibilityEnabled, onCheckedChange = { isInvincibilityEnabled = it } ) - - SettingsGroupSpacer() - - SettingsEntryGroupText(title = "BACKUP") - - SettingsDescription(text = "Personal preferences (i.e. the theme mode) and the cache are excluded.") - - SettingsEntry( - title = "Backup", - text = "Export the database to the external storage", - onClick = { - @SuppressLint("SimpleDateFormat") - val dateFormat = SimpleDateFormat("yyyyMMddHHmmss") - backupLauncher.launch("vimusic_${dateFormat.format(Date())}.db") - } - ) - - SettingsGroupSpacer() - - SettingsEntryGroupText(title = "RESTORE") - - SettingsDescription(text = "Existing data will be overwritten.") - - SettingsEntry( - title = "Restore", - text = "Import the database from the external storage", - onClick = { - isShowingRestoreDialog = true - } - ) } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt index 9c57f7b..db68aad 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt @@ -46,8 +46,9 @@ fun SettingsScreen() { Item(0, "Appearance", R.drawable.color_palette) Item(1, "Player", R.drawable.play) Item(2, "Cache", R.drawable.server) - Item(3, "Other", R.drawable.shapes) - Item(4, "About", R.drawable.information) + Item(3, "Database", R.drawable.server) + Item(4, "Other", R.drawable.shapes) + Item(5, "About", R.drawable.information) } ) { currentTabIndex -> saveableStateHolder.SaveableStateProvider(currentTabIndex) { @@ -55,8 +56,9 @@ fun SettingsScreen() { 0 -> AppearanceSettings() 1 -> PlayerSettings() 2 -> CacheSettings() - 3 -> OtherSettings() - 4 -> About() + 3 -> DatabaseSettings() + 4 -> OtherSettings() + 5 -> About() } } }