Refactor backup option flags to normal data class of booleans

This commit is contained in:
arkon 2023-12-28 16:44:46 -05:00
parent 8b65fd5751
commit 8735836498
7 changed files with 133 additions and 83 deletions

View file

@ -29,16 +29,11 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.create.BackupCreator import eu.kanade.tachiyomi.data.backup.create.BackupCreator
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.PersistentSet
import kotlinx.collections.immutable.minus
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.plus
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.LabeledCheckbox
@ -101,13 +96,13 @@ class CreateBackupScreen : Screen() {
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium), modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
) )
} }
BackupChoices.forEach { (k, v) -> BackupOptions.entries.forEach { option ->
item { item {
LabeledCheckbox( LabeledCheckbox(
label = stringResource(v), label = stringResource(option.label),
checked = state.flags.contains(k), checked = option.getter(state.options),
onCheckedChange = { onCheckedChange = {
model.toggleFlag(k) model.toggle(option.setter, it)
}, },
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium), modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
) )
@ -145,37 +140,28 @@ class CreateBackupScreen : Screen() {
private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel.State>(State()) { private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel.State>(State()) {
fun toggleFlag(flag: Int) { fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
mutableState.update { mutableState.update {
if (it.flags.contains(flag)) { it.copy(
it.copy(flags = it.flags - flag) options = setter(it.options, enabled),
} else { )
it.copy(flags = it.flags + flag)
}
} }
} }
fun createBackup(context: Context, uri: Uri) { fun createBackup(context: Context, uri: Uri) {
val flags = state.value.flags.fold(initial = 0, operation = { a, b -> a or b }) BackupCreateJob.startNow(context, uri, state.value.options)
BackupCreateJob.startNow(context, uri, flags)
} }
@Immutable @Immutable
data class State( data class State(
val flags: PersistentSet<Int> = persistentSetOf( val options: BackupOptions = BackupOptions(
BackupCreateFlags.BACKUP_CATEGORY, libraryEntries = true,
BackupCreateFlags.BACKUP_CHAPTER, categories = true,
BackupCreateFlags.BACKUP_TRACK, chapters = true,
BackupCreateFlags.BACKUP_HISTORY, tracking = true,
history = true,
appSettings = false,
sourceSettings = false,
), ),
) )
} }
private val BackupChoices = persistentMapOf(
BackupCreateFlags.BACKUP_CATEGORY to MR.strings.categories,
BackupCreateFlags.BACKUP_CHAPTER to MR.strings.chapters,
BackupCreateFlags.BACKUP_TRACK to MR.strings.track,
BackupCreateFlags.BACKUP_HISTORY to MR.strings.history,
BackupCreateFlags.BACKUP_APP_PREFS to MR.strings.app_settings,
BackupCreateFlags.BACKUP_SOURCE_PREFS to MR.strings.source_settings,
)

View file

@ -1,17 +0,0 @@
package eu.kanade.tachiyomi.data.backup.create
internal object BackupCreateFlags {
const val BACKUP_CATEGORY = 0x1
const val BACKUP_CHAPTER = 0x2
const val BACKUP_HISTORY = 0x4
const val BACKUP_TRACK = 0x8
const val BACKUP_APP_PREFS = 0x10
const val BACKUP_SOURCE_PREFS = 0x20
const val AutomaticDefaults = BACKUP_CATEGORY or
BACKUP_CHAPTER or
BACKUP_HISTORY or
BACKUP_TRACK or
BACKUP_APP_PREFS or
BACKUP_SOURCE_PREFS
}

View file

@ -47,10 +47,12 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
setForegroundSafely() setForegroundSafely()
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults) val options = inputData.getBooleanArray(OPTIONS_KEY)
?.let { BackupOptions.fromBooleanArray(it) }
?: BackupOptions.AutomaticDefaults
return try { return try {
val location = BackupCreator(context, isAutoBackup).backup(uri, flags) val location = BackupCreator(context, isAutoBackup).backup(uri, options)
if (!isAutoBackup) { if (!isAutoBackup) {
notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!)
} }
@ -112,11 +114,11 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
} }
} }
fun startNow(context: Context, uri: Uri, flags: Int) { fun startNow(context: Context, uri: Uri, options: BackupOptions) {
val inputData = workDataOf( val inputData = workDataOf(
IS_AUTO_BACKUP_KEY to false, IS_AUTO_BACKUP_KEY to false,
LOCATION_URI_KEY to uri.toString(), LOCATION_URI_KEY to uri.toString(),
BACKUP_FLAGS_KEY to flags, OPTIONS_KEY to options.toBooleanArray(),
) )
val request = OneTimeWorkRequestBuilder<BackupCreateJob>() val request = OneTimeWorkRequestBuilder<BackupCreateJob>()
.addTag(TAG_MANUAL) .addTag(TAG_MANUAL)
@ -132,4 +134,4 @@ private const val TAG_MANUAL = "$TAG_AUTO:manual"
private const val IS_AUTO_BACKUP_KEY = "is_auto_backup" // Boolean private const val IS_AUTO_BACKUP_KEY = "is_auto_backup" // Boolean
private const val LOCATION_URI_KEY = "location_uri" // String private const val LOCATION_URI_KEY = "location_uri" // String
private const val BACKUP_FLAGS_KEY = "backup_flags" // Int private const val OPTIONS_KEY = "options" // BooleanArray

View file

@ -5,9 +5,6 @@ import android.net.Uri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.backup.BackupFileValidator import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_APP_PREFS
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CATEGORY
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_SOURCE_PREFS
import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator
import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator
import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator
@ -52,7 +49,7 @@ class BackupCreator(
private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(), private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
) { ) {
suspend fun backup(uri: Uri, flags: Int): String { suspend fun backup(uri: Uri, options: BackupOptions): String {
var file: UniFile? = null var file: UniFile? = null
try { try {
file = ( file = (
@ -80,11 +77,11 @@ class BackupCreator(
val databaseManga = getFavorites.await() val databaseManga = getFavorites.await()
val backup = Backup( val backup = Backup(
backupManga = backupMangas(databaseManga, flags), backupManga = backupMangas(databaseManga, options),
backupCategories = backupCategories(flags), backupCategories = backupCategories(options),
backupSources = backupSources(databaseManga), backupSources = backupSources(databaseManga),
backupPreferences = backupAppPreferences(flags), backupPreferences = backupAppPreferences(options),
backupSourcePreferences = backupSourcePreferences(flags), backupSourcePreferences = backupSourcePreferences(options),
) )
val byteArray = parser.encodeToByteArray(BackupSerializer, backup) val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
@ -117,28 +114,28 @@ class BackupCreator(
} }
} }
private suspend fun backupCategories(options: Int): List<BackupCategory> { private suspend fun backupCategories(options: BackupOptions): List<BackupCategory> {
if (options and BACKUP_CATEGORY != BACKUP_CATEGORY) return emptyList() if (!options.categories) return emptyList()
return categoriesBackupCreator.backupCategories() return categoriesBackupCreator.backupCategories()
} }
private suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> { private suspend fun backupMangas(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
return mangaBackupCreator.backupMangas(mangas, flags) return mangaBackupCreator.backupMangas(mangas, options)
} }
private fun backupSources(mangas: List<Manga>): List<BackupSource> { private fun backupSources(mangas: List<Manga>): List<BackupSource> {
return sourcesBackupCreator.backupSources(mangas) return sourcesBackupCreator.backupSources(mangas)
} }
private fun backupAppPreferences(flags: Int): List<BackupPreference> { private fun backupAppPreferences(options: BackupOptions): List<BackupPreference> {
if (flags and BACKUP_APP_PREFS != BACKUP_APP_PREFS) return emptyList() if (!options.appSettings) return emptyList()
return preferenceBackupCreator.backupAppPreferences() return preferenceBackupCreator.backupAppPreferences()
} }
private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> { private fun backupSourcePreferences(options: BackupOptions): List<BackupSourcePreferences> {
if (flags and BACKUP_SOURCE_PREFS != BACKUP_SOURCE_PREFS) return emptyList() if (!options.sourceSettings) return emptyList()
return preferenceBackupCreator.backupSourcePreferences() return preferenceBackupCreator.backupSourcePreferences()
} }

View file

@ -0,0 +1,86 @@
package eu.kanade.tachiyomi.data.backup.create
import dev.icerock.moko.resources.StringResource
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.i18n.MR
data class BackupOptions(
val libraryEntries: Boolean = true,
val categories: Boolean = true,
val chapters: Boolean = true,
val tracking: Boolean = true,
val history: Boolean = true,
val appSettings: Boolean = true,
val sourceSettings: Boolean = true,
) {
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(
label = MR.strings.categories,
getter = BackupOptions::categories,
setter = { options, enabled -> options.copy(categories = enabled) },
),
BackupOptionEntry(
label = MR.strings.chapters,
getter = BackupOptions::chapters,
setter = { options, enabled -> options.copy(chapters = enabled) },
),
BackupOptionEntry(
label = MR.strings.track,
getter = BackupOptions::tracking,
setter = { options, enabled -> options.copy(tracking = enabled) },
),
BackupOptionEntry(
label = MR.strings.history,
getter = BackupOptions::history,
setter = { options, enabled -> options.copy(history = enabled) },
),
BackupOptionEntry(
label = MR.strings.app_settings,
getter = BackupOptions::appSettings,
setter = { options, enabled -> options.copy(appSettings = enabled) },
),
BackupOptionEntry(
label = MR.strings.source_settings,
getter = BackupOptions::sourceSettings,
setter = { options, enabled -> options.copy(sourceSettings = enabled) },
),
)
}
}
data class BackupOptionEntry(
val label: StringResource,
val getter: (BackupOptions) -> Boolean,
val setter: (BackupOptions, Boolean) -> BackupOptions,
)

View file

@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.data.backup.create.creators package eu.kanade.tachiyomi.data.backup.create.creators
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.backup.models.BackupChapter import eu.kanade.tachiyomi.data.backup.models.BackupChapter
import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupManga
@ -20,18 +20,17 @@ class MangaBackupCreator(
private val getHistory: GetHistory = Injekt.get(), private val getHistory: GetHistory = Injekt.get(),
) { ) {
suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> { suspend fun backupMangas(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
return mangas.map { return mangas.map {
backupManga(it, flags) backupManga(it, options)
} }
} }
private suspend fun backupManga(manga: Manga, options: Int): BackupManga { private suspend fun backupManga(manga: Manga, options: BackupOptions): BackupManga {
// Entry for this manga // Entry for this manga
val mangaObject = manga.toBackupManga() val mangaObject = manga.toBackupManga()
// Check if user wants chapter information in backup if (options.chapters) {
if (options and BackupCreateFlags.BACKUP_CHAPTER == BackupCreateFlags.BACKUP_CHAPTER) {
// Backup all the chapters // Backup all the chapters
handler.awaitList { handler.awaitList {
chaptersQueries.getChaptersByMangaId( chaptersQueries.getChaptersByMangaId(
@ -44,8 +43,7 @@ class MangaBackupCreator(
?.let { mangaObject.chapters = it } ?.let { mangaObject.chapters = it }
} }
// Check if user wants category information in backup if (options.categories) {
if (options and BackupCreateFlags.BACKUP_CATEGORY == BackupCreateFlags.BACKUP_CATEGORY) {
// Backup categories for this manga // Backup categories for this manga
val categoriesForManga = getCategories.await(manga.id) val categoriesForManga = getCategories.await(manga.id)
if (categoriesForManga.isNotEmpty()) { if (categoriesForManga.isNotEmpty()) {
@ -53,16 +51,14 @@ class MangaBackupCreator(
} }
} }
// Check if user wants track information in backup if (options.tracking) {
if (options and BackupCreateFlags.BACKUP_TRACK == BackupCreateFlags.BACKUP_TRACK) {
val tracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id, backupTrackMapper) } val tracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id, backupTrackMapper) }
if (tracks.isNotEmpty()) { if (tracks.isNotEmpty()) {
mangaObject.tracking = tracks mangaObject.tracking = tracks
} }
} }
// Check if user wants history information in backup if (options.history) {
if (options and BackupCreateFlags.BACKUP_HISTORY == BackupCreateFlags.BACKUP_HISTORY) {
val historyByMangaId = getHistory.await(manga.id) val historyByMangaId = getHistory.await(manga.id)
if (historyByMangaId.isNotEmpty()) { if (historyByMangaId.isNotEmpty()) {
val history = historyByMangaId.map { history -> val history = historyByMangaId.map { history ->

View file

@ -40,7 +40,7 @@ class PreferenceBackupCreator(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> { private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
return this.filterKeys { return this.filterKeys {
!Preference.isPrivate(it) && !Preference.isAppState(it) !Preference.isAppState(it) && !Preference.isPrivate(it)
} }
.mapNotNull { (key, value) -> .mapNotNull { (key, value) ->
when (value) { when (value) {