Refactor backup/restore

Close #575
This commit is contained in:
MM20
2023-10-28 17:15:04 +02:00
parent c9d9b3a4e9
commit 47cf4e9483
24 changed files with 93 additions and 329 deletions
@@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.common
import android.net.Uri
import android.text.format.DateUtils
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -13,14 +12,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.backup.BackupCompatibility
import de.mm20.launcher2.backup.BackupComponent
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.LargeMessage
@@ -38,7 +35,6 @@ fun RestoreBackupSheet(
}
val state by viewModel.state
val selectedComponents by viewModel.selectedComponents
val compatibility by viewModel.compatibility
BottomSheetDialog(
@@ -52,7 +48,6 @@ fun RestoreBackupSheet(
if (state == RestoreBackupState.Ready && compatibility != BackupCompatibility.Incompatible) {
Button(
enabled = selectedComponents.isNotEmpty(),
onClick = { viewModel.restore() }) {
Text(stringResource(R.string.preference_restore))
}
@@ -138,60 +133,6 @@ fun RestoreBackupSheet(
)
)
}
Text(
stringResource(R.string.restore_select_components),
style = MaterialTheme.typography.titleSmall,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
)
val components by viewModel.availableComponents
for (component in components) {
Row(
modifier = Modifier
.clickable {
viewModel.toggleComponent(
component
)
}
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = when (component) {
BackupComponent.Favorites -> Icons.Rounded.Star
BackupComponent.Settings -> Icons.Rounded.Settings
BackupComponent.SearchActions -> Icons.Rounded.ArrowOutward
BackupComponent.Widgets -> Icons.Rounded.Widgets
BackupComponent.Customizations -> Icons.Rounded.Edit
BackupComponent.Themes -> Icons.Rounded.Palette
},
contentDescription = null
)
Text(
text = stringResource(
when (component) {
BackupComponent.Favorites -> R.string.backup_component_favorites
BackupComponent.Settings -> R.string.backup_component_settings
BackupComponent.SearchActions -> R.string.backup_component_searchactions
BackupComponent.Widgets -> R.string.backup_component_widgets
BackupComponent.Customizations -> R.string.backup_component_customizations
BackupComponent.Themes -> R.string.backup_component_themes
}
),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
)
Checkbox(
checked = selectedComponents.contains(
component
),
onCheckedChange = {
viewModel.toggleComponent(component)
}
)
}
}
}
}
}
@@ -5,7 +5,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.backup.BackupCompatibility
import de.mm20.launcher2.backup.BackupComponent
import de.mm20.launcher2.backup.BackupManager
import de.mm20.launcher2.backup.BackupMetadata
import kotlinx.coroutines.launch
@@ -21,9 +20,6 @@ class RestoreBackupSheetVM : ViewModel(), KoinComponent {
val state = mutableStateOf(RestoreBackupState.Parsing)
val metadata = mutableStateOf<BackupMetadata?>(null)
val compatibility = mutableStateOf<BackupCompatibility?>(null)
val selectedComponents = mutableStateOf(setOf<BackupComponent>())
val availableComponents = mutableStateOf(emptyList<BackupComponent>())
fun setInputUri(uri: Uri) {
restoreUri = uri
@@ -32,33 +28,20 @@ class RestoreBackupSheetVM : ViewModel(), KoinComponent {
val metadata = backupManager.readBackupMeta(uri)
if (metadata == null) {
state.value = RestoreBackupState.InvalidFile
availableComponents.value = emptyList()
} else {
state.value = RestoreBackupState.Ready
compatibility.value = backupManager.checkCompatibility(metadata)
availableComponents.value = metadata.components.toList().sortedBy { it.ordinal }
}
selectedComponents.value = metadata?.components ?: emptySet()
this@RestoreBackupSheetVM.metadata.value = metadata
}
}
fun toggleComponent(component: BackupComponent) {
val components = selectedComponents.value ?: emptySet()
if (components.contains(component)) {
selectedComponents.value = components - component
} else {
selectedComponents.value = components + component
}
}
fun restore() {
val components = selectedComponents.value ?: return
val uri = restoreUri ?: return
viewModelScope.launch {
state.value = RestoreBackupState.Restoring
backupManager.restore(uri, components)
backupManager.restore(uri)
state.value = RestoreBackupState.Restored
}
}
@@ -18,7 +18,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.backup.BackupComponent
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.LargeMessage
@@ -33,20 +32,23 @@ fun CreateBackupSheet(
val viewModel: CreateBackupSheetVM = viewModel()
LaunchedEffect(null) {
viewModel.reset()
}
val components by viewModel.selectedComponents
val state by viewModel.state
val backupLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument("application/vnd.de.mm20.launcher2.backup"),
onResult = {
if (it != null) viewModel.createBackup(it)
}
)
LaunchedEffect(null) {
viewModel.reset()
val fileName = "${
ZonedDateTime.now().format(
DateTimeFormatter.ISO_INSTANT
).replace(":", "_")
}.kvaesitso"
backupLauncher.launch(fileName)
}
val state by viewModel.state
BottomSheetDialog(
onDismissRequest = onDismissRequest,
@@ -58,7 +60,6 @@ fun CreateBackupSheet(
confirmButton = {
if (state == CreateBackupState.Ready) {
Button(
enabled = components.isNotEmpty(),
onClick = {
val fileName = "${
ZonedDateTime.now().format(
@@ -87,68 +88,7 @@ fun CreateBackupSheet(
) {
when (state) {
CreateBackupState.Ready -> {
Column {
Text(
stringResource(R.string.backup_select_components),
style = MaterialTheme.typography.titleSmall,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
)
BackupableComponent(
title = stringResource(R.string.backup_component_settings),
icon = Icons.Rounded.Settings,
checked = components.contains(BackupComponent.Settings),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.Settings)
}
)
BackupableComponent(
title = stringResource(R.string.backup_component_favorites),
icon = Icons.Rounded.Star,
checked = components.contains(BackupComponent.Favorites),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.Favorites)
}
)
BackupableComponent(
title = stringResource(R.string.backup_component_widgets),
icon = Icons.Rounded.Widgets,
checked = components.contains(BackupComponent.Widgets),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.Widgets)
}
)
BackupableComponent(
title = stringResource(R.string.backup_component_customizations),
icon = Icons.Rounded.Edit,
checked = components.contains(BackupComponent.Customizations),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.Customizations)
}
)
BackupableComponent(
title = stringResource(R.string.backup_component_searchactions),
icon = Icons.Rounded.TravelExplore,
checked = components.contains(BackupComponent.SearchActions),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.SearchActions)
}
)
BackupableComponent(
title = stringResource(R.string.backup_component_themes),
icon = Icons.Rounded.Palette,
checked = components.contains(BackupComponent.Themes),
onCheckedChange = {
viewModel.toggleComponent(BackupComponent.Themes)
}
)
SmallMessage(
modifier = Modifier
.padding(top = 8.dp)
.fillMaxWidth(),
icon = Icons.Rounded.Warning,
text = stringResource(R.string.backup_not_included)
)
}
}
CreateBackupState.BackingUp -> {
Box(
@@ -176,36 +116,3 @@ fun CreateBackupSheet(
}
}
@Composable
fun BackupableComponent(
title: String,
icon: ImageVector,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
) {
Row(
modifier = Modifier
.clickable {
onCheckedChange(!checked)
}
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null
)
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
)
Checkbox(
checked = checked,
onCheckedChange = onCheckedChange
)
}
}
@@ -4,7 +4,6 @@ import android.net.Uri
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.backup.BackupComponent
import de.mm20.launcher2.backup.BackupManager
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
@@ -16,30 +15,17 @@ class CreateBackupSheetVM : ViewModel(), KoinComponent {
val state = mutableStateOf(CreateBackupState.Ready)
val selectedComponents = mutableStateOf(BackupComponent.values().toSet())
fun reset() {
state.value = CreateBackupState.Ready
}
fun createBackup(uri: Uri) {
val components = selectedComponents.value ?: return
viewModelScope.launch {
state.value = CreateBackupState.BackingUp
backupManager.backup(uri, components)
backupManager.backup(uri)
state.value = CreateBackupState.BackedUp
}
}
fun toggleComponent(component: BackupComponent) {
val components = selectedComponents.value ?: emptySet()
if (components.contains(component)) {
selectedComponents.value = components - component
} else {
selectedComponents.value = components + component
}
}
}
enum class CreateBackupState {