Merge pull request #2143 from 821938089/opt-backup-config

优化备份与恢复
pull/2146/head
kunfei 2 years ago committed by GitHub
commit e8f947b87a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      app/src/main/java/io/legado/app/help/AppWebDav.kt
  2. 5
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  3. 30
      app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt

@ -18,14 +18,14 @@ import io.legado.app.lib.webdav.WebDav
import io.legado.app.lib.webdav.WebDavException import io.legado.app.lib.webdav.WebDavException
import io.legado.app.lib.webdav.WebDavFile import io.legado.app.lib.webdav.WebDavFile
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.coroutines.coroutineContext
/** /**
* webDav初始化会访问网络,不要放到主线程 * webDav初始化会访问网络,不要放到主线程
@ -102,6 +102,7 @@ object AppWebDav {
suspend fun showRestoreDialog(context: Context) { suspend fun showRestoreDialog(context: Context) {
val names = withContext(IO) { getBackupNames() } val names = withContext(IO) { getBackupNames() }
if (names.isNotEmpty()) { if (names.isNotEmpty()) {
coroutineContext.ensureActive()
withContext(Main) { withContext(Main) {
context.selector( context.selector(
title = context.getString(R.string.select_restore_file), title = context.getString(R.string.select_restore_file),

@ -14,6 +14,7 @@ import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
@ -75,6 +76,7 @@ object Backup {
writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath) writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath)
writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath) writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath)
writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath) writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath)
ensureActive()
writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath) writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath)
writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath) writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath)
writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath) writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath)
@ -82,6 +84,7 @@ object Backup {
writeListToJson(appDb.txtTocRuleDao.all, "txtTocRule.json", backupPath) writeListToJson(appDb.txtTocRuleDao.all, "txtTocRule.json", backupPath)
writeListToJson(appDb.httpTTSDao.all, "httpTTS.json", backupPath) writeListToJson(appDb.httpTTSDao.all, "httpTTS.json", backupPath)
writeListToJson(appDb.keyboardAssistsDao.all, "keyboardAssists.json", backupPath) writeListToJson(appDb.keyboardAssistsDao.all, "keyboardAssists.json", backupPath)
ensureActive()
GSON.toJson(ReadBookConfig.configList).let { GSON.toJson(ReadBookConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName)
.writeText(it) .writeText(it)
@ -98,6 +101,7 @@ object Backup {
FileUtils.createFileIfNotExist(backupPath + File.separator + DirectLinkUpload.ruleFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + DirectLinkUpload.ruleFileName)
.writeText(GSON.toJson(it)) .writeText(GSON.toJson(it))
} }
ensureActive()
Preferences.getSharedPreferences(appCtx, backupPath, "config")?.let { sp -> Preferences.getSharedPreferences(appCtx, backupPath, "config")?.let { sp ->
val edit = sp.edit() val edit = sp.edit()
appCtx.defaultSharedPreferences.all.forEach { (key, value) -> appCtx.defaultSharedPreferences.all.forEach { (key, value) ->
@ -113,6 +117,7 @@ object Backup {
} }
edit.commit() edit.commit()
} }
ensureActive()
when { when {
path.isNullOrBlank() -> { path.isNullOrBlank() -> {
copyBackup(context.getExternalFilesDir(null)!!, false) copyBackup(context.getExternalFilesDir(null)!!, false)

@ -33,9 +33,10 @@ import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.document.HandleFileContract import io.legado.app.ui.document.HandleFileContract
import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.dialog.WaitDialog
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import splitties.init.appCtx import splitties.init.appCtx
import kotlin.collections.set import kotlin.collections.set
@ -44,6 +45,9 @@ class BackupConfigFragment : PreferenceFragment(),
MenuProvider { MenuProvider {
private val viewModel by activityViewModels<ConfigViewModel>() private val viewModel by activityViewModels<ConfigViewModel>()
private val waitDialog by lazy { WaitDialog(requireContext()) }
private var backupJob: Job? = null
private var restoreJob: Job? = null
private val selectBackupPath = registerForActivityResult(HandleFileContract()) { private val selectBackupPath = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
@ -245,7 +249,13 @@ class BackupConfigFragment : PreferenceFragment(),
val uri = Uri.parse(backupPath) val uri = Uri.parse(backupPath)
val doc = DocumentFile.fromTreeUri(requireContext(), uri) val doc = DocumentFile.fromTreeUri(requireContext(), uri)
if (doc?.canWrite() == true) { if (doc?.canWrite() == true) {
waitDialog.setText("备份中…")
waitDialog.setOnCancelListener {
backupJob?.cancel()
}
waitDialog.show()
Coroutine.async { Coroutine.async {
backupJob = coroutineContext[Job]
Backup.backup(requireContext(), backupPath) Backup.backup(requireContext(), backupPath)
}.onSuccess { }.onSuccess {
appCtx.toastOnUi(R.string.backup_success) appCtx.toastOnUi(R.string.backup_success)
@ -257,6 +267,8 @@ class BackupConfigFragment : PreferenceFragment(),
it.localizedMessage it.localizedMessage
) )
) )
}.onFinally(Main) {
waitDialog.dismiss()
} }
} else { } else {
backupDir.launch() backupDir.launch()
@ -272,7 +284,13 @@ class BackupConfigFragment : PreferenceFragment(),
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
waitDialog.setText("备份中…")
waitDialog.setOnCancelListener {
backupJob?.cancel()
}
waitDialog.show()
Coroutine.async { Coroutine.async {
backupJob = coroutineContext[Job]
AppConfig.backupPath = path AppConfig.backupPath = path
Backup.backup(requireContext(), path) Backup.backup(requireContext(), path)
}.onSuccess { }.onSuccess {
@ -280,13 +298,21 @@ class BackupConfigFragment : PreferenceFragment(),
}.onError { }.onError {
AppLog.put("备份出错\n${it.localizedMessage}", it) AppLog.put("备份出错\n${it.localizedMessage}", it)
appCtx.toastOnUi(appCtx.getString(R.string.backup_fail, it.localizedMessage)) appCtx.toastOnUi(appCtx.getString(R.string.backup_fail, it.localizedMessage))
}.onFinally(Main) {
waitDialog.dismiss()
} }
} }
.request() .request()
} }
fun restore() { fun restore() {
waitDialog.setText(R.string.loading)
waitDialog.setOnCancelListener {
restoreJob?.cancel()
}
waitDialog.show()
Coroutine.async { Coroutine.async {
restoreJob = coroutineContext[Job]
AppWebDav.showRestoreDialog(requireContext()) AppWebDav.showRestoreDialog(requireContext())
}.onError { }.onError {
alert { alert {
@ -297,6 +323,8 @@ class BackupConfigFragment : PreferenceFragment(),
} }
cancelButton() cancelButton()
} }
}.onFinally(Main) {
waitDialog.dismiss()
} }
} }

Loading…
Cancel
Save