备份可选网络、本地、网络与本地同时备份
This commit is contained in:
@@ -14,7 +14,6 @@ import io.legado.app.help.DirectLinkUpload
|
|||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.LocalConfig
|
import io.legado.app.help.config.LocalConfig
|
||||||
import io.legado.app.help.config.OldThemeConfig
|
import io.legado.app.help.config.OldThemeConfig
|
||||||
import io.legado.app.help.config.PersonalizationThemeConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.model.BookCover
|
import io.legado.app.model.BookCover
|
||||||
@@ -84,7 +83,6 @@ object Backup {
|
|||||||
ReadBookConfig.configFileName,
|
ReadBookConfig.configFileName,
|
||||||
ReadBookConfig.shareConfigFileName,
|
ReadBookConfig.shareConfigFileName,
|
||||||
OldThemeConfig.configFileName,
|
OldThemeConfig.configFileName,
|
||||||
PersonalizationThemeConfig.configFileName,
|
|
||||||
BookCover.configFileName,
|
BookCover.configFileName,
|
||||||
"config.xml"
|
"config.xml"
|
||||||
)
|
)
|
||||||
@@ -125,15 +123,15 @@ object Backup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun backupLocked(context: Context, path: String?) {
|
suspend fun backupLocked(context: Context, path: String?, mode: String = "both") {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
withContext(IO) {
|
withContext(IO) {
|
||||||
backup(context, path)
|
backup(context, path, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun backup(context: Context, path: String?) {
|
private suspend fun backup(context: Context, path: String?, mode: String = "both") {
|
||||||
LogUtils.d(TAG, "开始备份 path:$path")
|
LogUtils.d(TAG, "开始备份 path:$path")
|
||||||
LocalConfig.lastBackup = System.currentTimeMillis()
|
LocalConfig.lastBackup = System.currentTimeMillis()
|
||||||
val aes = BackupAES()
|
val aes = BackupAES()
|
||||||
@@ -183,8 +181,6 @@ object Backup {
|
|||||||
FileUtils.createFileIfNotExist(backupPath + File.separator + BookCover.configFileName)
|
FileUtils.createFileIfNotExist(backupPath + File.separator + BookCover.configFileName)
|
||||||
.writeText(GSON.toJson(it))
|
.writeText(GSON.toJson(it))
|
||||||
}
|
}
|
||||||
FileUtils.createFileIfNotExist(backupPath + File.separator + PersonalizationThemeConfig.configFileName)
|
|
||||||
.writeText(PersonalizationThemeConfig.toJson())
|
|
||||||
currentCoroutineContext().ensureActive()
|
currentCoroutineContext().ensureActive()
|
||||||
appCtx.getSharedPreferences(backupPath, "config")?.let { sp ->
|
appCtx.getSharedPreferences(backupPath, "config")?.let { sp ->
|
||||||
sp.edit(commit = true) {
|
sp.edit(commit = true) {
|
||||||
@@ -223,23 +219,27 @@ object Backup {
|
|||||||
zipFileName
|
zipFileName
|
||||||
}
|
}
|
||||||
if (ZipUtils.zipFiles(paths, zipFilePath)) {
|
if (ZipUtils.zipFiles(paths, zipFilePath)) {
|
||||||
when {
|
if (mode == "both" || mode == "local") {
|
||||||
path.isNullOrBlank() -> {
|
when {
|
||||||
copyBackup(context.getExternalFilesDir(null)!!, backupFileName)
|
path.isNullOrBlank() -> {
|
||||||
}
|
copyBackup(context.getExternalFilesDir(null)!!, backupFileName)
|
||||||
|
}
|
||||||
|
|
||||||
path.isContentScheme() -> {
|
path.isContentScheme() -> {
|
||||||
copyBackup(context, path.toUri(), backupFileName)
|
copyBackup(context, path.toUri(), backupFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
copyBackup(File(path), backupFileName)
|
copyBackup(File(path), backupFileName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
if (mode == "both" || mode == "webdav") {
|
||||||
AppWebDav.backUpWebDav(zipFileName)
|
try {
|
||||||
} catch (e: Exception) {
|
AppWebDav.backUpWebDav(zipFileName)
|
||||||
AppLog.put("上传备份至webdav失败\n$e", e)
|
} catch (e: Exception) {
|
||||||
|
AppLog.put("上传备份至webdav失败\n$e", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileUtils.delete(backupPath)
|
FileUtils.delete(backupPath)
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ object BackupConfig {
|
|||||||
private const val readConfigKey = "readConfig"
|
private const val readConfigKey = "readConfig"
|
||||||
private const val themeConfigKey = "themeConfig"
|
private const val themeConfigKey = "themeConfig"
|
||||||
private const val coverConfigKey = "coverConfig"
|
private const val coverConfigKey = "coverConfig"
|
||||||
private const val personalizationThemeConfigKey = "personalizationThemeConfig"
|
|
||||||
private const val localBookKey = "localBook"
|
private const val localBookKey = "localBook"
|
||||||
|
|
||||||
//配置忽略key
|
//配置忽略key
|
||||||
@@ -31,7 +30,6 @@ object BackupConfig {
|
|||||||
readConfigKey,
|
readConfigKey,
|
||||||
PreferKey.themeMode,
|
PreferKey.themeMode,
|
||||||
themeConfigKey,
|
themeConfigKey,
|
||||||
personalizationThemeConfigKey,
|
|
||||||
coverConfigKey,
|
coverConfigKey,
|
||||||
PreferKey.bookshelfLayout,
|
PreferKey.bookshelfLayout,
|
||||||
PreferKey.showRss,
|
PreferKey.showRss,
|
||||||
@@ -44,7 +42,6 @@ object BackupConfig {
|
|||||||
appCtx.getString(R.string.read_config),
|
appCtx.getString(R.string.read_config),
|
||||||
appCtx.getString(R.string.theme_mode),
|
appCtx.getString(R.string.theme_mode),
|
||||||
appCtx.getString(R.string.theme_config),
|
appCtx.getString(R.string.theme_config),
|
||||||
"深度个性化主题",
|
|
||||||
appCtx.getString(R.string.cover_config),
|
appCtx.getString(R.string.cover_config),
|
||||||
appCtx.getString(R.string.bookshelf_layout),
|
appCtx.getString(R.string.bookshelf_layout),
|
||||||
appCtx.getString(R.string.show_rss),
|
appCtx.getString(R.string.show_rss),
|
||||||
@@ -125,8 +122,6 @@ object BackupConfig {
|
|||||||
get() = ignoreConfig[themeConfigKey] == true
|
get() = ignoreConfig[themeConfigKey] == true
|
||||||
private val ignoreCoverConfig: Boolean
|
private val ignoreCoverConfig: Boolean
|
||||||
get() = ignoreConfig[coverConfigKey] == true
|
get() = ignoreConfig[coverConfigKey] == true
|
||||||
val ignorePersonalizationThemeConfig: Boolean
|
|
||||||
get() = ignoreConfig[personalizationThemeConfigKey] == true
|
|
||||||
private val ignoreBookshelfLayout: Boolean
|
private val ignoreBookshelfLayout: Boolean
|
||||||
get() = ignoreConfig[PreferKey.bookshelfLayout] == true
|
get() = ignoreConfig[PreferKey.bookshelfLayout] == true
|
||||||
private val ignoreShowRss: Boolean
|
private val ignoreShowRss: Boolean
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ object BackupConfig {
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var backupSyncMode by prefDelegate(
|
||||||
|
PreferKey.backupSyncMode,
|
||||||
|
"both"
|
||||||
|
)
|
||||||
|
|
||||||
var backupPath by prefDelegate<String?>(
|
var backupPath by prefDelegate<String?>(
|
||||||
PreferKey.backupPath,
|
PreferKey.backupPath,
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Cloud
|
||||||
|
import androidx.compose.material.icons.filled.Lan
|
||||||
|
import androidx.compose.material.icons.filled.PhoneAndroid
|
||||||
import androidx.compose.material.icons.filled.Visibility
|
import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -33,6 +36,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringArrayResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
@@ -54,7 +58,10 @@ import io.legado.app.ui.widget.components.card.SelectionItemCard
|
|||||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.OptionCard
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.OptionSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||||
|
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.InputSettingItem
|
import io.legado.app.ui.widget.components.settingItem.InputSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
@@ -90,6 +97,8 @@ fun BackupConfigScreen(
|
|||||||
var showBackupFilePicker by remember { mutableStateOf(false) }
|
var showBackupFilePicker by remember { mutableStateOf(false) }
|
||||||
var showRestoreFilePicker by remember { mutableStateOf(false) }
|
var showRestoreFilePicker by remember { mutableStateOf(false) }
|
||||||
var showRestoreSheet by remember { mutableStateOf(false) }
|
var showRestoreSheet by remember { mutableStateOf(false) }
|
||||||
|
var showRestoreOptionSheet by remember { mutableStateOf(false) }
|
||||||
|
var showBackupOptionSheet by remember { mutableStateOf(false) }
|
||||||
var backupNames by remember { mutableStateOf<List<String>>(emptyList()) }
|
var backupNames by remember { mutableStateOf<List<String>>(emptyList()) }
|
||||||
|
|
||||||
var showLoadingDialog by remember { mutableStateOf(false) }
|
var showLoadingDialog by remember { mutableStateOf(false) }
|
||||||
@@ -134,7 +143,7 @@ fun BackupConfigScreen(
|
|||||||
}
|
}
|
||||||
BackupConfig.backupPath = path
|
BackupConfig.backupPath = path
|
||||||
if (path.isNotEmpty()) {
|
if (path.isNotEmpty()) {
|
||||||
startBackup(path, context, viewModel, {
|
startBackup(path, context, viewModel, "both", {
|
||||||
showLoadingDialog = false
|
showLoadingDialog = false
|
||||||
scope.launch {
|
scope.launch {
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.backup_success))
|
snackbarHostState.showSnackbar(context.getString(R.string.backup_success))
|
||||||
@@ -285,6 +294,15 @@ fun BackupConfigScreen(
|
|||||||
checked = BackupConfig.autoCheckNewBackup,
|
checked = BackupConfig.autoCheckNewBackup,
|
||||||
onCheckedChange = { BackupConfig.autoCheckNewBackup = it }
|
onCheckedChange = { BackupConfig.autoCheckNewBackup = it }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DropdownListSettingItem(
|
||||||
|
title = stringResource(R.string.backup_sync_mode),
|
||||||
|
description = stringResource(R.string.backup_sync_mode_summary),
|
||||||
|
selectedValue = BackupConfig.backupSyncMode,
|
||||||
|
displayEntries = stringArrayResource(R.array.backup_sync_mode),
|
||||||
|
entryValues = stringArrayResource(R.array.backup_sync_mode_value),
|
||||||
|
onValueChange = { BackupConfig.backupSyncMode = it }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SplicedColumnGroup(title = stringResource(R.string.backup_restore)) {
|
SplicedColumnGroup(title = stringResource(R.string.backup_restore)) {
|
||||||
@@ -295,94 +313,16 @@ fun BackupConfigScreen(
|
|||||||
onClick = { showBackupFilePicker = true }
|
onClick = { showBackupFilePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
val backupText = stringResource(R.string.backup)
|
|
||||||
|
|
||||||
ClickableSettingItem(
|
ClickableSettingItem(
|
||||||
title = stringResource(R.string.backup),
|
title = stringResource(R.string.backup),
|
||||||
description = stringResource(R.string.backup_summary),
|
description = stringResource(R.string.backup_summary),
|
||||||
onClick = {
|
onClick = { showBackupOptionSheet = true }
|
||||||
val backupPath = BackupConfig.backupPath
|
|
||||||
if (backupPath.isNullOrEmpty()) {
|
|
||||||
showRestoreFilePicker = true
|
|
||||||
} else {
|
|
||||||
confirmDialogTitle = backupText
|
|
||||||
confirmDialogText = "确定要备份吗?"
|
|
||||||
onConfirmAction = {
|
|
||||||
if (backupPath.isContentScheme()) {
|
|
||||||
startBackup(backupPath, context, viewModel, {
|
|
||||||
showLoadingDialog = false
|
|
||||||
scope.launch {
|
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.backup_success))
|
|
||||||
}
|
|
||||||
}, { error ->
|
|
||||||
showLoadingDialog = false
|
|
||||||
scope.launch {
|
|
||||||
snackbarHostState.showSnackbar(
|
|
||||||
context.getString(
|
|
||||||
R.string.backup_fail,
|
|
||||||
error
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
PermissionsCompat.Builder()
|
|
||||||
.addPermissions(*Permissions.Group.STORAGE)
|
|
||||||
.rationale(R.string.tip_perm_request_storage)
|
|
||||||
.onGranted {
|
|
||||||
startBackup(backupPath, context, viewModel, {
|
|
||||||
showLoadingDialog = false
|
|
||||||
scope.launch {
|
|
||||||
snackbarHostState.showSnackbar(
|
|
||||||
context.getString(
|
|
||||||
R.string.backup_success
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}, { error ->
|
|
||||||
showLoadingDialog = false
|
|
||||||
scope.launch {
|
|
||||||
snackbarHostState.showSnackbar(
|
|
||||||
context.getString(
|
|
||||||
R.string.backup_fail,
|
|
||||||
error
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.request()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showConfirmDialog = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ClickableSettingItem(
|
ClickableSettingItem(
|
||||||
title = stringResource(R.string.restore),
|
title = stringResource(R.string.restore),
|
||||||
description = stringResource(R.string.restore_summary),
|
description = stringResource(R.string.restore_summary),
|
||||||
onClick = {
|
onClick = { showRestoreOptionSheet = true }
|
||||||
scope.launch {
|
|
||||||
showLoadingDialog = true
|
|
||||||
loadingText = "加载中"
|
|
||||||
try {
|
|
||||||
val names = viewModel.getBackupNames()
|
|
||||||
backupNames = names
|
|
||||||
showRestoreSheet = true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
confirmDialogTitle = "恢复"
|
|
||||||
confirmDialogText =
|
|
||||||
"WebDavError\n${e.localizedMessage}\n将从本地备份恢复。"
|
|
||||||
onConfirmAction = {
|
|
||||||
restoreFileLauncher.launch(arrayOf("application/zip"))
|
|
||||||
}
|
|
||||||
showConfirmDialog = true
|
|
||||||
} finally {
|
|
||||||
showLoadingDialog = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ClickableSettingItem(
|
ClickableSettingItem(
|
||||||
@@ -499,6 +439,84 @@ fun BackupConfigScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
OptionSheet(
|
||||||
|
show = showBackupOptionSheet,
|
||||||
|
onDismissRequest = { showBackupOptionSheet = false },
|
||||||
|
title = stringResource(R.string.backup)
|
||||||
|
) {
|
||||||
|
OptionCard(
|
||||||
|
icon = Icons.Default.PhoneAndroid,
|
||||||
|
text = stringResource(R.string.backup_to_local),
|
||||||
|
onClick = {
|
||||||
|
showBackupOptionSheet = false
|
||||||
|
executeBackup("local", context, viewModel, scope, snackbarHostState, {
|
||||||
|
showLoadingDialog = false
|
||||||
|
}, { showLoadingDialog = true; loadingText = it })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
OptionCard(
|
||||||
|
icon = Icons.Default.Cloud,
|
||||||
|
text = stringResource(R.string.backup_to_network),
|
||||||
|
onClick = {
|
||||||
|
showBackupOptionSheet = false
|
||||||
|
executeBackup("webdav", context, viewModel, scope, snackbarHostState, {
|
||||||
|
showLoadingDialog = false
|
||||||
|
}, { showLoadingDialog = true; loadingText = it })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
OptionCard(
|
||||||
|
icon = Icons.Default.Lan,
|
||||||
|
text = stringResource(R.string.backup_to_local_and_network),
|
||||||
|
onClick = {
|
||||||
|
showBackupOptionSheet = false
|
||||||
|
executeBackup("both", context, viewModel, scope, snackbarHostState, {
|
||||||
|
showLoadingDialog = false
|
||||||
|
}, { showLoadingDialog = true; loadingText = it })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionSheet(
|
||||||
|
show = showRestoreOptionSheet,
|
||||||
|
onDismissRequest = { showRestoreOptionSheet = false },
|
||||||
|
title = stringResource(R.string.restore)
|
||||||
|
) {
|
||||||
|
OptionCard(
|
||||||
|
icon = Icons.Default.PhoneAndroid,
|
||||||
|
text = "本地恢复",
|
||||||
|
onClick = {
|
||||||
|
showRestoreOptionSheet = false
|
||||||
|
restoreFileLauncher.launch(arrayOf("application/zip"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
OptionCard(
|
||||||
|
icon = Icons.Default.Cloud,
|
||||||
|
text = "网络恢复",
|
||||||
|
onClick = {
|
||||||
|
showRestoreOptionSheet = false
|
||||||
|
scope.launch {
|
||||||
|
showLoadingDialog = true
|
||||||
|
loadingText = "加载中"
|
||||||
|
try {
|
||||||
|
val names = viewModel.getBackupNames()
|
||||||
|
backupNames = names
|
||||||
|
showRestoreSheet = true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
confirmDialogTitle = "恢复"
|
||||||
|
confirmDialogText =
|
||||||
|
"WebDavError\n${e.localizedMessage}\n将从本地备份恢复。"
|
||||||
|
onConfirmAction = {
|
||||||
|
restoreFileLauncher.launch(arrayOf("application/zip"))
|
||||||
|
}
|
||||||
|
showConfirmDialog = true
|
||||||
|
} finally {
|
||||||
|
showLoadingDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
show = showRestoreSheet && backupNames.isNotEmpty(),
|
show = showRestoreSheet && backupNames.isNotEmpty(),
|
||||||
onDismissRequest = { showRestoreSheet = false },
|
onDismissRequest = { showRestoreSheet = false },
|
||||||
@@ -621,8 +639,57 @@ private fun startBackup(
|
|||||||
path: String,
|
path: String,
|
||||||
context: Context,
|
context: Context,
|
||||||
viewModel: BackupConfigViewModel,
|
viewModel: BackupConfigViewModel,
|
||||||
|
mode: String = "both",
|
||||||
onSuccess: () -> Unit,
|
onSuccess: () -> Unit,
|
||||||
onError: (String) -> Unit
|
onError: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
viewModel.backup(path, onSuccess, onError)
|
viewModel.backup(path, mode, onSuccess, onError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun executeBackup(
|
||||||
|
mode: String,
|
||||||
|
context: Context,
|
||||||
|
viewModel: BackupConfigViewModel,
|
||||||
|
scope: kotlinx.coroutines.CoroutineScope,
|
||||||
|
snackbarHostState: SnackbarHostState,
|
||||||
|
hideLoading: () -> Unit,
|
||||||
|
showLoading: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val backupPath = BackupConfig.backupPath
|
||||||
|
if (backupPath.isNullOrEmpty() && mode != "webdav") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val path = backupPath ?: ""
|
||||||
|
showLoading(context.getString(R.string.backup))
|
||||||
|
if (path.isNotEmpty() && !path.isContentScheme()) {
|
||||||
|
PermissionsCompat.Builder()
|
||||||
|
.addPermissions(*Permissions.Group.STORAGE)
|
||||||
|
.rationale(R.string.tip_perm_request_storage)
|
||||||
|
.onGranted {
|
||||||
|
startBackup(path, context, viewModel, mode, {
|
||||||
|
hideLoading()
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar(context.getString(R.string.backup_success))
|
||||||
|
}
|
||||||
|
}, { error ->
|
||||||
|
hideLoading()
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar(context.getString(R.string.backup_fail, error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.request()
|
||||||
|
} else {
|
||||||
|
startBackup(path, context, viewModel, mode, {
|
||||||
|
hideLoading()
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar(context.getString(R.string.backup_success))
|
||||||
|
}
|
||||||
|
}, { error ->
|
||||||
|
hideLoading()
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar(context.getString(R.string.backup_fail, error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ class BackupConfigViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun backup(backupPath: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
|
fun backup(backupPath: String, mode: String = "both", onSuccess: () -> Unit, onError: (String) -> Unit) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
Backup.backupLocked(appCtx, backupPath)
|
Backup.backupLocked(appCtx, backupPath, mode)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user