Add ability to create manual backups with private preferences too
This commit is contained in:
parent
8735836498
commit
ccec5c3efe
9 changed files with 65 additions and 77 deletions
|
@ -154,14 +154,6 @@ private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel
|
|||
|
||||
@Immutable
|
||||
data class State(
|
||||
val options: BackupOptions = BackupOptions(
|
||||
libraryEntries = true,
|
||||
categories = true,
|
||||
chapters = true,
|
||||
tracking = true,
|
||||
history = true,
|
||||
appSettings = false,
|
||||
sourceSettings = false,
|
||||
),
|
||||
val options: BackupOptions = BackupOptions(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.hippo.unifile.UniFile
|
|||
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.util.lang.asBooleanArray
|
||||
import eu.kanade.tachiyomi.util.lang.asDataClass
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.isRunning
|
||||
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||
|
@ -47,9 +49,8 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
|
|||
|
||||
setForegroundSafely()
|
||||
|
||||
val options = inputData.getBooleanArray(OPTIONS_KEY)
|
||||
?.let { BackupOptions.fromBooleanArray(it) }
|
||||
?: BackupOptions.AutomaticDefaults
|
||||
val options: BackupOptions = inputData.getBooleanArray(OPTIONS_KEY)?.asDataClass()
|
||||
?: BackupOptions()
|
||||
|
||||
return try {
|
||||
val location = BackupCreator(context, isAutoBackup).backup(uri, options)
|
||||
|
@ -118,7 +119,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
|
|||
val inputData = workDataOf(
|
||||
IS_AUTO_BACKUP_KEY to false,
|
||||
LOCATION_URI_KEY to uri.toString(),
|
||||
OPTIONS_KEY to options.toBooleanArray(),
|
||||
OPTIONS_KEY to options.asBooleanArray(),
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<BackupCreateJob>()
|
||||
.addTag(TAG_MANUAL)
|
||||
|
|
|
@ -131,13 +131,13 @@ class BackupCreator(
|
|||
private fun backupAppPreferences(options: BackupOptions): List<BackupPreference> {
|
||||
if (!options.appSettings) return emptyList()
|
||||
|
||||
return preferenceBackupCreator.backupAppPreferences()
|
||||
return preferenceBackupCreator.backupAppPreferences(includePrivatePreferences = options.privateSettings)
|
||||
}
|
||||
|
||||
private fun backupSourcePreferences(options: BackupOptions): List<BackupSourcePreferences> {
|
||||
if (!options.sourceSettings) return emptyList()
|
||||
|
||||
return preferenceBackupCreator.backupSourcePreferences()
|
||||
return preferenceBackupCreator.backupSourcePreferences(includePrivatePreferences = options.privateSettings)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -12,75 +12,52 @@ data class BackupOptions(
|
|||
val history: Boolean = true,
|
||||
val appSettings: Boolean = true,
|
||||
val sourceSettings: Boolean = true,
|
||||
val privateSettings: Boolean = false,
|
||||
) {
|
||||
fun toBooleanArray() = booleanArrayOf(
|
||||
libraryEntries,
|
||||
categories,
|
||||
chapters,
|
||||
tracking,
|
||||
history,
|
||||
appSettings,
|
||||
sourceSettings,
|
||||
)
|
||||
|
||||
companion object {
|
||||
val AutomaticDefaults = BackupOptions(
|
||||
libraryEntries = true,
|
||||
categories = true,
|
||||
chapters = true,
|
||||
tracking = true,
|
||||
history = true,
|
||||
appSettings = true,
|
||||
sourceSettings = true,
|
||||
)
|
||||
|
||||
fun fromBooleanArray(booleanArray: BooleanArray) = BackupOptions(
|
||||
libraryEntries = booleanArray[0],
|
||||
categories = booleanArray[1],
|
||||
chapters = booleanArray[2],
|
||||
tracking = booleanArray[3],
|
||||
history = booleanArray[4],
|
||||
appSettings = booleanArray[5],
|
||||
sourceSettings = booleanArray[6],
|
||||
)
|
||||
|
||||
val entries = persistentListOf<BackupOptionEntry>(
|
||||
BackupOptionEntry(
|
||||
val entries = persistentListOf(
|
||||
Entry(
|
||||
label = MR.strings.categories,
|
||||
getter = BackupOptions::categories,
|
||||
setter = { options, enabled -> options.copy(categories = enabled) },
|
||||
),
|
||||
BackupOptionEntry(
|
||||
Entry(
|
||||
label = MR.strings.chapters,
|
||||
getter = BackupOptions::chapters,
|
||||
setter = { options, enabled -> options.copy(chapters = enabled) },
|
||||
),
|
||||
BackupOptionEntry(
|
||||
Entry(
|
||||
label = MR.strings.track,
|
||||
getter = BackupOptions::tracking,
|
||||
setter = { options, enabled -> options.copy(tracking = enabled) },
|
||||
),
|
||||
BackupOptionEntry(
|
||||
Entry(
|
||||
label = MR.strings.history,
|
||||
getter = BackupOptions::history,
|
||||
setter = { options, enabled -> options.copy(history = enabled) },
|
||||
),
|
||||
BackupOptionEntry(
|
||||
Entry(
|
||||
label = MR.strings.app_settings,
|
||||
getter = BackupOptions::appSettings,
|
||||
setter = { options, enabled -> options.copy(appSettings = enabled) },
|
||||
),
|
||||
BackupOptionEntry(
|
||||
Entry(
|
||||
label = MR.strings.source_settings,
|
||||
getter = BackupOptions::sourceSettings,
|
||||
setter = { options, enabled -> options.copy(sourceSettings = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = MR.strings.private_settings,
|
||||
getter = BackupOptions::privateSettings,
|
||||
setter = { options, enabled -> options.copy(privateSettings = enabled) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class BackupOptionEntry(
|
||||
val label: StringResource,
|
||||
val getter: (BackupOptions) -> Boolean,
|
||||
val setter: (BackupOptions, Boolean) -> BackupOptions,
|
||||
)
|
||||
data class Entry(
|
||||
val label: StringResource,
|
||||
val getter: (BackupOptions) -> Boolean,
|
||||
val setter: (BackupOptions, Boolean) -> BackupOptions,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,26 +22,27 @@ class PreferenceBackupCreator(
|
|||
private val preferenceStore: PreferenceStore = Injekt.get(),
|
||||
) {
|
||||
|
||||
fun backupAppPreferences(): List<BackupPreference> {
|
||||
fun backupAppPreferences(includePrivatePreferences: Boolean): List<BackupPreference> {
|
||||
return preferenceStore.getAll().toBackupPreferences()
|
||||
.withPrivatePreferences(includePrivatePreferences)
|
||||
}
|
||||
|
||||
fun backupSourcePreferences(): List<BackupSourcePreferences> {
|
||||
fun backupSourcePreferences(includePrivatePreferences: Boolean): List<BackupSourcePreferences> {
|
||||
return sourceManager.getCatalogueSources()
|
||||
.filterIsInstance<ConfigurableSource>()
|
||||
.map {
|
||||
BackupSourcePreferences(
|
||||
it.preferenceKey(),
|
||||
it.sourcePreferences().all.toBackupPreferences(),
|
||||
it.sourcePreferences().all.toBackupPreferences()
|
||||
.withPrivatePreferences(includePrivatePreferences),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
|
||||
return this.filterKeys {
|
||||
!Preference.isAppState(it) && !Preference.isPrivate(it)
|
||||
}
|
||||
return this
|
||||
.filterKeys { !Preference.isAppState(it) }
|
||||
.mapNotNull { (key, value) ->
|
||||
when (value) {
|
||||
is Int -> BackupPreference(key, IntPreferenceValue(value))
|
||||
|
@ -56,4 +57,11 @@ class PreferenceBackupCreator(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<BackupPreference>.withPrivatePreferences(include: Boolean) =
|
||||
if (include) {
|
||||
this
|
||||
} else {
|
||||
this.filter { !Preference.isPrivate(it.key) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import androidx.work.WorkerParameters
|
|||
import androidx.work.workDataOf
|
||||
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.util.lang.asBooleanArray
|
||||
import eu.kanade.tachiyomi.util.lang.asDataClass
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.isRunning
|
||||
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||
|
@ -30,8 +32,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
|||
|
||||
override suspend fun doWork(): Result {
|
||||
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
||||
val options = inputData.getBooleanArray(OPTIONS_KEY)
|
||||
?.let { RestoreOptions.fromBooleanArray(it) }
|
||||
val options: RestoreOptions? = inputData.getBooleanArray(OPTIONS_KEY)?.asDataClass()
|
||||
|
||||
if (uri == null || options == null) {
|
||||
return Result.failure()
|
||||
|
@ -84,7 +85,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
|||
val inputData = workDataOf(
|
||||
LOCATION_URI_KEY to uri.toString(),
|
||||
SYNC_KEY to sync,
|
||||
OPTIONS_KEY to options.toBooleanArray(),
|
||||
OPTIONS_KEY to options.asBooleanArray(),
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<BackupRestoreJob>()
|
||||
.addTag(TAG)
|
||||
|
|
|
@ -4,14 +4,4 @@ data class RestoreOptions(
|
|||
val appSettings: Boolean = true,
|
||||
val sourceSettings: Boolean = true,
|
||||
val library: Boolean = true,
|
||||
) {
|
||||
fun toBooleanArray() = booleanArrayOf(appSettings, sourceSettings, library)
|
||||
|
||||
companion object {
|
||||
fun fromBooleanArray(booleanArray: BooleanArray) = RestoreOptions(
|
||||
appSettings = booleanArray[0],
|
||||
sourceSettings = booleanArray[1],
|
||||
library = booleanArray[2],
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package eu.kanade.tachiyomi.util.lang
|
||||
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
fun <T : Any> T.asBooleanArray(): BooleanArray {
|
||||
return this::class.declaredMemberProperties
|
||||
.filterIsInstance<KProperty1<T, Boolean>>()
|
||||
.map { it.get(this) }
|
||||
.toBooleanArray()
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> BooleanArray.asDataClass(): T {
|
||||
val properties = T::class.declaredMemberProperties.filterIsInstance<KProperty1<T, Boolean>>()
|
||||
require(properties.size == this.size) { "Boolean array size does not match data class property count" }
|
||||
return T::class.primaryConstructor!!.call(*this.toTypedArray())
|
||||
}
|
|
@ -504,6 +504,7 @@
|
|||
<string name="backup_choice">What do you want to backup?</string>
|
||||
<string name="app_settings">App settings</string>
|
||||
<string name="source_settings">Source settings</string>
|
||||
<string name="private_settings">Include sensitive settings (e.g., tracker login tokens)</string>
|
||||
<string name="creating_backup">Creating backup</string>
|
||||
<string name="creating_backup_error">Backup failed</string>
|
||||
<string name="missing_storage_permission">Storage permissions not granted</string>
|
||||
|
|
Reference in a new issue