diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8e402e6..1f8d71c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,6 +69,8 @@ dependencies { implementation("androidx.room:room-ktx:2.6.1") annotationProcessor("androidx.room:room-compiler:2.6.1") ksp("androidx.room:room-compiler:2.6.1") + // Gson + implementation("com.google.code.gson:gson:2.10.1") // Test testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") diff --git a/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt b/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt index c54ef08..2719822 100644 --- a/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt @@ -3,14 +3,29 @@ package cn.super12138.todo.views.settings import android.os.Bundle import android.os.Process import android.view.WindowManager +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatDelegate +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.preference.ListPreference +import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import cn.super12138.todo.R import cn.super12138.todo.databinding.ActivitySettingsBinding +import cn.super12138.todo.databinding.DialogBackupBinding +import cn.super12138.todo.databinding.DialogRestoreBinding +import cn.super12138.todo.logic.Repository +import cn.super12138.todo.logic.dao.ToDoRoom import cn.super12138.todo.views.BaseActivity +import cn.super12138.todo.views.progress.ProgressFragmentViewModel +import cn.super12138.todo.views.todo.ToDoFragmentViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException +import com.google.gson.reflect.TypeToken +import kotlinx.coroutines.launch import kotlin.system.exitProcess class SettingsActivity : BaseActivity() { @@ -19,7 +34,6 @@ class SettingsActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) @@ -37,8 +51,15 @@ class SettingsActivity : BaseActivity() { } class SettingsFragment : PreferenceFragmentCompat() { + private lateinit var backupBinding: DialogBackupBinding + private lateinit var restoreBinding: DialogRestoreBinding override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) + val gson = Gson() + val progressViewModel = + ViewModelProvider(requireActivity()).get(ProgressFragmentViewModel::class.java) + val todoViewModel = + ViewModelProvider(requireActivity()).get(ToDoFragmentViewModel::class.java) findPreference("dark_mode")?.apply { setOnPreferenceChangeListener { preference, newValue -> @@ -76,6 +97,79 @@ class SettingsActivity : BaseActivity() { true } } + + findPreference("backupDB")?.apply { + setOnPreferenceClickListener { + lifecycleScope.launch { + val data = Repository.getAll() + val jsonData = gson.toJson(data) + backupBinding = DialogBackupBinding.inflate(layoutInflater) + backupBinding.jsonOutput.text = jsonData + + activity?.let { + MaterialAlertDialogBuilder(it) + .setTitle(R.string.export_data) + .setView(backupBinding.root) + .setPositiveButton(R.string.ok, null) + .show() + } + } + true + } + } + + findPreference("restoreDB")?.apply { + setOnPreferenceClickListener { + restoreBinding = DialogRestoreBinding.inflate(layoutInflater) + activity?.let { it1 -> + val dialog = MaterialAlertDialogBuilder(it1) + .setTitle(R.string.restore_data) + .setView(restoreBinding.root) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .show() + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + val jsonInput = restoreBinding.jsonInput.editText?.text.toString() + if (jsonInput.isEmpty()) { + restoreBinding.jsonInput.error = + getString(R.string.please_paste_data) + return@setOnClickListener + } + + lifecycleScope.launch { + if (restoreBinding.overwriteData.isChecked) { + Repository.deleteAll() + } + try { + val listType = object : TypeToken>() {}.type + val taskList: List = + gson.fromJson(jsonInput, listType) + taskList.forEach { Repository.insert(it) } + dialog.dismiss() + MaterialAlertDialogBuilder(it1) + .setTitle(R.string.restore_successful) + .setMessage(R.string.restore_need_restart_app) + .setPositiveButton(R.string.ok) { _, _ -> + Process.killProcess(Process.myPid()) + exitProcess(10) + } + .setNegativeButton(R.string.cancel, null) + .setCancelable(false) + .show() + } catch (e: JsonSyntaxException) { + restoreBinding.jsonInput.error = + getString(R.string.json_data_incorrect) + } catch (e: Exception) { + restoreBinding.jsonInput.error = + getString(R.string.restore_failed) + } + } + } + } + true + } + } } } } \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_backup.xml b/app/src/main/res/layout/dialog_backup.xml new file mode 100644 index 0000000..c8a0c7e --- /dev/null +++ b/app/src/main/res/layout/dialog_backup.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_restore.xml b/app/src/main/res/layout/dialog_restore.xml new file mode 100644 index 0000000..8b88778 --- /dev/null +++ b/app/src/main/res/layout/dialog_restore.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_progress.xml b/app/src/main/res/layout/fragment_progress.xml index 068f4f9..94a74e9 100644 --- a/app/src/main/res/layout/fragment_progress.xml +++ b/app/src/main/res/layout/fragment_progress.xml @@ -2,7 +2,8 @@ + android:layout_height="match_parent" + android:animateLayoutChanges="true"> + app:trackThickness="10dp" /> 备份待办数据库 恢复数据库 恢复先前备份过的待办数据库 + 下方是数据库文件,请全选复制以导出 + 在下方粘贴已经导出的数据库 + 覆盖原有数据 + 粘贴导出的数据 + 导出数据 + 恢复数据 + 请粘贴数据 + 数据恢复成功 + 需要重启应用以应用恢复的数据 + JSON 数据格式错误 + 恢复失败,请检查是否重复恢复同一次备份的数据 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc4764c..0c82272 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,4 +49,15 @@ Backup to-do database Restore database Restore the previously backed up to-do database + The following is the database file, please select all and copy to export. + Paste the exported database below. + Overwrite old data + Paste exported data + Export data + Restore data + Please paste the data + Data restore successful + Need to restart the app to apply the recovered data + JSON data format is incorrect + Restore failed, please check if you are attempting to restore the same backup data again. \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 661136b..aab0fe2 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -63,7 +63,7 @@