mirror of
https://github.com/florisboard/florisboard.git
synced 2024-09-20 12:02:19 +02:00
Add restore data functionality
This commit is contained in:
parent
23dddfd16e
commit
96e7f2eeac
@ -156,9 +156,9 @@ dependencies {
|
||||
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
|
||||
implementation("androidx.navigation:navigation-compose:2.4.0-rc01")
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:0.20.2")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-datastore-model:0.1.0-beta02")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-datastore-ui:0.1.0-beta02")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-material-ui:0.1.0-beta02")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-datastore-model:0.1.0-beta03")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-datastore-ui:0.1.0-beta03")
|
||||
implementation("dev.patrickgold.jetpref:jetpref-material-ui:0.1.0-beta03")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||
implementation("androidx.room:room-runtime:2.4.0")
|
||||
|
@ -44,7 +44,7 @@ import dev.patrickgold.florisboard.ime.nlp.NlpManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.GlideTypingManager
|
||||
import dev.patrickgold.florisboard.res.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.res.io.deleteContentsRecursively
|
||||
import dev.patrickgold.jetpref.datastore.JetPrefManager
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import java.io.File
|
||||
import kotlin.Exception
|
||||
|
||||
@ -80,7 +80,7 @@ class FlorisApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
try {
|
||||
JetPrefManager.init(saveIntervalMs = 1_000)
|
||||
JetPref.configure(saveIntervalMs = 500)
|
||||
Flog.install(
|
||||
context = this,
|
||||
isFloggingEnabled = BuildConfig.DEBUG,
|
||||
@ -93,12 +93,12 @@ class FlorisApplication : Application() {
|
||||
if (AndroidVersion.ATLEAST_API24_N && !UserManagerCompat.isUserUnlocked(this)) {
|
||||
val context = createDeviceProtectedStorageContext()
|
||||
initICU(context)
|
||||
prefs.initializeForContext(context)
|
||||
prefs.initializeBlocking(context)
|
||||
registerReceiver(BootComplete(), IntentFilter(Intent.ACTION_USER_UNLOCKED))
|
||||
} else {
|
||||
initICU(this)
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
prefs.initializeForContext(this)
|
||||
prefs.initializeBlocking(this)
|
||||
clipboardManager.value.initializeForContext(this)
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ class FlorisApplication : Application() {
|
||||
flogError { e.toString() }
|
||||
}
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
prefs.initializeForContext(this@FlorisApplication)
|
||||
prefs.initializeBlocking(this@FlorisApplication)
|
||||
clipboardManager.value.initializeForContext(this@FlorisApplication)
|
||||
}
|
||||
}
|
||||
|
@ -144,12 +144,6 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
prefs.forceSyncToDisk()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun AppContent() {
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.prefs
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
@ -33,11 +31,10 @@ import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
|
||||
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.preferenceModel
|
||||
import java.time.LocalTime
|
||||
|
||||
fun florisPreferenceModel() = preferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val advanced = Advanced()
|
||||
@ -555,15 +552,13 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
default = extCoreTheme("floris_night"),
|
||||
serializer = ExtensionComponentName.Serializer,
|
||||
)
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
val sunriseTime = localTime(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime.of(6, 0),
|
||||
)
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
val sunsetTime = localTime(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime.of(18, 0),
|
||||
)
|
||||
//val sunriseTime = localTime(
|
||||
// key = "theme__sunrise_time",
|
||||
// default = LocalTime.of(6, 0),
|
||||
//)
|
||||
//val sunsetTime = localTime(
|
||||
// key = "theme__sunset_time",
|
||||
// default = LocalTime.of(18, 0),
|
||||
//)
|
||||
}
|
||||
}
|
||||
|
@ -46,15 +46,14 @@ import dev.patrickgold.florisboard.res.FileRegistry
|
||||
import dev.patrickgold.florisboard.res.ZipUtils
|
||||
import dev.patrickgold.florisboard.res.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.res.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.res.io.parentDir
|
||||
import dev.patrickgold.florisboard.res.io.subDir
|
||||
import dev.patrickgold.florisboard.res.io.subFile
|
||||
import dev.patrickgold.florisboard.res.io.writeJson
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
object Backup {
|
||||
const val FILE_PROVIDER_AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.file"
|
||||
const val METADATA_JSON_NAME = "backup_metadata.json"
|
||||
@ -128,8 +127,8 @@ fun BackupScreen() = FlorisScreen {
|
||||
fun prepareBackupWorkspace() {
|
||||
val workspace = cacheManager.backupAndRestore.new()
|
||||
if (backupFilesSelector.jetprefDatastore) {
|
||||
context.filesDir.parentDir!!.subDir("jetpref_datastore").let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir("jetpref_datastore"))
|
||||
context.jetprefDatastoreDir.let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
|
||||
}
|
||||
}
|
||||
val workspaceFilesDir = workspace.inputDir.subDir("files")
|
||||
@ -225,31 +224,43 @@ fun BackupScreen() = FlorisScreen {
|
||||
text = stringRes(R.string.backup_and_restore__back_up__destination_share_intent),
|
||||
)
|
||||
}
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
BackupFilesSelector(
|
||||
filesSelector = backupFilesSelector,
|
||||
title = stringRes(R.string.backup_and_restore__back_up__files),
|
||||
) {
|
||||
CheckboxListItem(
|
||||
onClick = { backupFilesSelector.jetprefDatastore = !backupFilesSelector.jetprefDatastore },
|
||||
checked = backupFilesSelector.jetprefDatastore,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_jetpref_datastore),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { backupFilesSelector.imeKeyboard = !backupFilesSelector.imeKeyboard },
|
||||
checked = backupFilesSelector.imeKeyboard,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_keyboard),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { backupFilesSelector.imeSpelling = !backupFilesSelector.imeSpelling },
|
||||
checked = backupFilesSelector.imeSpelling,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_spelling),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { backupFilesSelector.imeTheme = !backupFilesSelector.imeTheme },
|
||||
checked = backupFilesSelector.imeTheme,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_theme),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun BackupFilesSelector(
|
||||
modifier: Modifier = Modifier,
|
||||
filesSelector: Backup.FilesSelector,
|
||||
title: String,
|
||||
) {
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = title,
|
||||
) {
|
||||
CheckboxListItem(
|
||||
onClick = { filesSelector.jetprefDatastore = !filesSelector.jetprefDatastore },
|
||||
checked = filesSelector.jetprefDatastore,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_jetpref_datastore),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { filesSelector.imeKeyboard = !filesSelector.imeKeyboard },
|
||||
checked = filesSelector.imeKeyboard,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_keyboard),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { filesSelector.imeSpelling = !filesSelector.imeSpelling },
|
||||
checked = filesSelector.imeSpelling,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_spelling),
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = { filesSelector.imeTheme = !filesSelector.imeTheme },
|
||||
checked = filesSelector.imeTheme,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_theme),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -40,6 +41,7 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.res.stringRes
|
||||
import dev.patrickgold.florisboard.app.ui.components.CardDefaults
|
||||
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
|
||||
@ -53,8 +55,15 @@ import dev.patrickgold.florisboard.common.android.showLongToast
|
||||
import dev.patrickgold.florisboard.res.FileRegistry
|
||||
import dev.patrickgold.florisboard.res.ZipUtils
|
||||
import dev.patrickgold.florisboard.res.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.res.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.res.io.deleteContentsRecursively
|
||||
import dev.patrickgold.florisboard.res.io.readJson
|
||||
import dev.patrickgold.florisboard.res.io.subDir
|
||||
import dev.patrickgold.florisboard.res.io.subFile
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object Restore {
|
||||
const val MIN_VERSION_CODE = 64
|
||||
@ -76,7 +85,11 @@ fun RestoreScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
|
||||
val restoreFilesSelector = remember { Backup.FilesSelector() }
|
||||
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
|
||||
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
|
||||
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
|
||||
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
|
||||
var restoreWorkspace by remember {
|
||||
mutableStateOf<CacheManager.BackupAndRestoreWorkspace?>(null)
|
||||
}
|
||||
@ -115,6 +128,52 @@ fun RestoreScreen() = FlorisScreen {
|
||||
},
|
||||
)
|
||||
|
||||
suspend fun performRestore() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val workspace = restoreWorkspace!!
|
||||
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
|
||||
if (restoreFilesSelector.jetprefDatastore) {
|
||||
val datastoreFile = workspace.outputDir
|
||||
.subDir(JetPref.JETPREF_DIR_NAME)
|
||||
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
|
||||
if (datastoreFile.exists()) {
|
||||
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
|
||||
prefs.datastorePersistenceHandler?.persistPrefs()
|
||||
}
|
||||
}
|
||||
val workspaceFilesDir = workspace.outputDir.subDir("files")
|
||||
if (restoreFilesSelector.imeKeyboard) {
|
||||
val srcDir = workspaceFilesDir.subDir(ExtensionManager.IME_KEYBOARD_PATH)
|
||||
val dstDir = context.filesDir.subDir(ExtensionManager.IME_KEYBOARD_PATH)
|
||||
if (shouldReset) {
|
||||
dstDir.deleteContentsRecursively()
|
||||
}
|
||||
if (srcDir.exists()) {
|
||||
srcDir.copyRecursively(dstDir, overwrite = true)
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.imeSpelling) {
|
||||
val srcDir = workspaceFilesDir.subDir(ExtensionManager.IME_SPELLING_PATH)
|
||||
val dstDir = context.filesDir.subDir(ExtensionManager.IME_SPELLING_PATH)
|
||||
if (shouldReset) {
|
||||
dstDir.deleteContentsRecursively()
|
||||
}
|
||||
if (srcDir.exists()) {
|
||||
srcDir.copyRecursively(dstDir, overwrite = true)
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.imeTheme) {
|
||||
val srcDir = workspaceFilesDir.subDir(ExtensionManager.IME_THEME_PATH)
|
||||
val dstDir = context.filesDir.subDir(ExtensionManager.IME_THEME_PATH)
|
||||
if (shouldReset) {
|
||||
dstDir.deleteContentsRecursively()
|
||||
}
|
||||
if (srcDir.exists()) {
|
||||
srcDir.copyRecursively(dstDir, overwrite = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bottomBar {
|
||||
FlorisButtonBar {
|
||||
ButtonBarSpacer()
|
||||
@ -127,7 +186,15 @@ fun RestoreScreen() = FlorisScreen {
|
||||
)
|
||||
ButtonBarButton(
|
||||
onClick = {
|
||||
//
|
||||
restoreScope.launch(Dispatchers.Main) {
|
||||
try {
|
||||
performRestore()
|
||||
context.showLongToast(R.string.backup_and_restore__restore__success)
|
||||
navController.popBackStack()
|
||||
} catch (e: Throwable) {
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to e.localizedMessage)
|
||||
}
|
||||
}
|
||||
},
|
||||
text = stringRes(R.string.action__restore),
|
||||
enabled = restoreWorkspace != null && restoreWorkspace?.restoreErrorId == null,
|
||||
@ -155,23 +222,23 @@ fun RestoreScreen() = FlorisScreen {
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
|
||||
)
|
||||
}
|
||||
FlorisOutlinedButton(
|
||||
onClick = {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch(
|
||||
FileRegistry.BackupArchive.mediaType
|
||||
)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = stringRes(R.string.action__select_file),
|
||||
)
|
||||
val workspace = restoreWorkspace
|
||||
if (workspace == null) {
|
||||
FlorisOutlinedButton(
|
||||
onClick = {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch(
|
||||
FileRegistry.BackupArchive.mediaType
|
||||
)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = stringRes(R.string.action__select_file),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
@ -224,6 +291,12 @@ fun RestoreScreen() = FlorisScreen {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (workspace.restoreErrorId == null) {
|
||||
BackupFilesSelector(
|
||||
filesSelector = restoreFilesSelector,
|
||||
title = stringRes(R.string.backup_and_restore__restore__files),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +30,25 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.prefs.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.debug.*
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
private class SafePreferenceInstanceWrapper : ReadOnlyProperty<Any?, AppPrefs?> {
|
||||
val cachedPreferenceModel = try {
|
||||
florisPreferenceModel()
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): AppPrefs? {
|
||||
return cachedPreferenceModel?.getValue(thisRef, property)
|
||||
}
|
||||
}
|
||||
|
||||
class CrashDialogActivity : ComponentActivity() {
|
||||
private var stacktraces: List<CrashUtility.Stacktrace> = listOf()
|
||||
private var errorReport: StringBuilder = StringBuilder()
|
||||
private var prefs: AppPrefs? = null
|
||||
private val prefs by SafePreferenceInstanceWrapper()
|
||||
|
||||
private val stacktrace by lazy { findViewById<TextView>(R.id.stacktrace) }
|
||||
private val reportInstructions by lazy { findViewById<TextView>(R.id.report_instructions) }
|
||||
@ -48,13 +62,6 @@ class CrashDialogActivity : ComponentActivity() {
|
||||
val layout = layoutInflater.inflate(R.layout.crash_dialog, null)
|
||||
setContentView(layout)
|
||||
|
||||
// We secure the PrefHelper usage here because the PrefHelper could potentially be the
|
||||
// source of the crash, thus making the crash dialog unusable if not wrapped.
|
||||
try {
|
||||
prefs = florisPreferenceModel().preferenceModel
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
stacktraces = CrashUtility.getUnhandledStacktraces(this)
|
||||
errorReport.apply {
|
||||
appendLine("#### Environment information")
|
||||
|
@ -38,7 +38,6 @@ import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.common.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.res.ZipUtils
|
||||
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
|
||||
@ -53,7 +52,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import java.time.LocalTime
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
@ -235,18 +233,18 @@ class ThemeManager(context: Context) {
|
||||
prefs.theme.dayThemeId.get()
|
||||
}
|
||||
ThemeMode.FOLLOW_TIME -> {
|
||||
if (AndroidVersion.ATLEAST_API26_O) {
|
||||
val current = LocalTime.now()
|
||||
val sunrise = prefs.theme.sunriseTime.get()
|
||||
val sunset = prefs.theme.sunsetTime.get()
|
||||
if (current in sunrise..sunset) {
|
||||
prefs.theme.dayThemeId.get()
|
||||
} else {
|
||||
prefs.theme.nightThemeId.get()
|
||||
}
|
||||
} else {
|
||||
//if (AndroidVersion.ATLEAST_API26_O) {
|
||||
// val current = LocalTime.now()
|
||||
// val sunrise = prefs.theme.sunriseTime.get()
|
||||
// val sunset = prefs.theme.sunsetTime.get()
|
||||
// if (current in sunrise..sunset) {
|
||||
// prefs.theme.dayThemeId.get()
|
||||
// } else {
|
||||
// prefs.theme.nightThemeId.get()
|
||||
// }
|
||||
//} else {
|
||||
prefs.theme.nightThemeId.get()
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -430,7 +430,8 @@
|
||||
<string name="backup_and_restore__back_up__failure">Failed to export backup archive: {error_message}</string>
|
||||
<string name="backup_and_restore__restore__title">Restore data</string>
|
||||
<string name="backup_and_restore__restore__summary">Restore preferences and customizations from a backup archive</string>
|
||||
<string name="backup_and_restore__restore__metadata">Backup archive metadata</string>
|
||||
<string name="backup_and_restore__restore__files">Select what to restore</string>
|
||||
<string name="backup_and_restore__restore__metadata">Selected backup archive</string>
|
||||
<string name="backup_and_restore__restore__metadata_warn_different_version">This backup archive was generated in another version than current, which is generally supported. Be aware though that minor issues can occur or some preferences may not get transferred properly due to feature differences.</string>
|
||||
<string name="backup_and_restore__restore__metadata_warn_different_vendor">This backup archive was generated in a third-party app, which is generally not supported. Data losses may occur, restore at your own risk!</string>
|
||||
<string name="backup_and_restore__restore__metadata_error_invalid_metadata">This backup archive contains invalid metadata. Either it has been corrupted or poorly modified. Restoring from this archive is not possible, please select another one.</string>
|
||||
|
Loading…
Reference in New Issue
Block a user