Convert create backup dialog to a screen
Allows us more flexibility in adding more options/explanations in the future.
This commit is contained in:
parent
634ceeec50
commit
00b2853d3d
8 changed files with 209 additions and 174 deletions
|
@ -1,6 +1,5 @@
|
||||||
package eu.kanade.presentation.more.settings.screen
|
package eu.kanade.presentation.more.settings.screen
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
@ -13,11 +12,9 @@ import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -27,41 +24,34 @@ import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.toMutableStateList
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
|
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
||||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||||
import eu.kanade.presentation.permissions.PermissionRequestHelper
|
import eu.kanade.presentation.permissions.PermissionRequestHelper
|
||||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
|
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.launchNonCancellable
|
import tachiyomi.core.util.lang.launchNonCancellable
|
||||||
import tachiyomi.core.util.lang.withUIContext
|
import tachiyomi.core.util.lang.withUIContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
|
||||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
|
||||||
import tachiyomi.presentation.core.util.collectAsState
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
|
||||||
import tachiyomi.presentation.core.util.isScrolledToStart
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
@ -131,124 +121,11 @@ object SettingsDataScreen : SearchableSettings {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getCreateBackupPref(): Preference.PreferenceItem.TextPreference {
|
private fun getCreateBackupPref(): Preference.PreferenceItem.TextPreference {
|
||||||
val scope = rememberCoroutineScope()
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
var flag by rememberSaveable { mutableIntStateOf(0) }
|
|
||||||
val chooseBackupDir = rememberLauncherForActivityResult(
|
|
||||||
contract = ActivityResultContracts.CreateDocument("application/*"),
|
|
||||||
) {
|
|
||||||
if (it != null) {
|
|
||||||
context.contentResolver.takePersistableUriPermission(
|
|
||||||
it,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
|
||||||
)
|
|
||||||
BackupCreateJob.startNow(context, it, flag)
|
|
||||||
}
|
|
||||||
flag = 0
|
|
||||||
}
|
|
||||||
var showCreateDialog by rememberSaveable { mutableStateOf(false) }
|
|
||||||
if (showCreateDialog) {
|
|
||||||
CreateBackupDialog(
|
|
||||||
onConfirm = {
|
|
||||||
showCreateDialog = false
|
|
||||||
flag = it
|
|
||||||
try {
|
|
||||||
chooseBackupDir.launch(Backup.getFilename())
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
flag = 0
|
|
||||||
context.toast(R.string.file_picker_error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismissRequest = { showCreateDialog = false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Preference.PreferenceItem.TextPreference(
|
return Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(R.string.pref_create_backup),
|
title = stringResource(R.string.pref_create_backup),
|
||||||
subtitle = stringResource(R.string.pref_create_backup_summ),
|
subtitle = stringResource(R.string.pref_create_backup_summ),
|
||||||
onClick = {
|
onClick = { navigator.push(CreateBackupScreen()) },
|
||||||
scope.launch {
|
|
||||||
if (!BackupCreateJob.isManualJobRunning(context)) {
|
|
||||||
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
|
|
||||||
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
|
|
||||||
}
|
|
||||||
showCreateDialog = true
|
|
||||||
} else {
|
|
||||||
context.toast(R.string.backup_in_progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun CreateBackupDialog(
|
|
||||||
onConfirm: (flag: Int) -> Unit,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
) {
|
|
||||||
val choices = remember {
|
|
||||||
mapOf(
|
|
||||||
BackupConst.BACKUP_CATEGORY to R.string.categories,
|
|
||||||
BackupConst.BACKUP_CHAPTER to R.string.chapters,
|
|
||||||
BackupConst.BACKUP_TRACK to R.string.track,
|
|
||||||
BackupConst.BACKUP_HISTORY to R.string.history,
|
|
||||||
BackupConst.BACKUP_APP_PREFS to R.string.app_settings,
|
|
||||||
BackupConst.BACKUP_SOURCE_PREFS to R.string.source_settings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val flags = remember { choices.keys.toMutableStateList() }
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
title = { Text(text = stringResource(R.string.backup_choice)) },
|
|
||||||
text = {
|
|
||||||
Box {
|
|
||||||
val state = rememberLazyListState()
|
|
||||||
ScrollbarLazyColumn(state = state) {
|
|
||||||
item {
|
|
||||||
LabeledCheckbox(
|
|
||||||
label = stringResource(R.string.manga),
|
|
||||||
checked = true,
|
|
||||||
onCheckedChange = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
choices.forEach { (k, v) ->
|
|
||||||
item {
|
|
||||||
val isSelected = flags.contains(k)
|
|
||||||
LabeledCheckbox(
|
|
||||||
label = stringResource(v),
|
|
||||||
checked = isSelected,
|
|
||||||
onCheckedChange = {
|
|
||||||
if (it) {
|
|
||||||
flags.add(k)
|
|
||||||
} else {
|
|
||||||
flags.remove(k)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
|
||||||
if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(text = stringResource(R.string.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
val flag = flags.fold(initial = 0, operation = { a, b -> a or b })
|
|
||||||
onConfirm(flag)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.action_ok))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +213,7 @@ object SettingsDataScreen : SearchableSettings {
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
error = InvalidRestore(message = context.getString(R.string.file_null_uri_error))
|
context.toast(R.string.file_null_uri_error)
|
||||||
return@rememberLauncherForActivityResult
|
return@rememberLauncherForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
package eu.kanade.presentation.more.settings.screen.data
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags
|
||||||
|
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
|
||||||
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
||||||
|
class CreateBackupScreen : Screen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val model = rememberScreenModel { CreateBackupScreenModel() }
|
||||||
|
val state by model.state.collectAsState()
|
||||||
|
|
||||||
|
val chooseBackupDir = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.CreateDocument("application/*"),
|
||||||
|
) {
|
||||||
|
if (it != null) {
|
||||||
|
context.contentResolver.takePersistableUriPermission(
|
||||||
|
it,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
||||||
|
)
|
||||||
|
model.createBackup(context, it)
|
||||||
|
navigator.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(R.string.pref_create_backup),
|
||||||
|
navigateUp = navigator::pop,
|
||||||
|
scrollBehavior = it,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(contentPadding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = MaterialTheme.padding.medium),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
LabeledCheckbox(
|
||||||
|
label = stringResource(R.string.manga),
|
||||||
|
checked = true,
|
||||||
|
onCheckedChange = {},
|
||||||
|
enabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BackupChoices.forEach { (k, v) ->
|
||||||
|
item {
|
||||||
|
LabeledCheckbox(
|
||||||
|
label = stringResource(v),
|
||||||
|
checked = state.flags.contains(k),
|
||||||
|
onCheckedChange = {
|
||||||
|
model.toggleFlag(k)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
onClick = {
|
||||||
|
if (!BackupCreateJob.isManualJobRunning(context)) {
|
||||||
|
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
|
||||||
|
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
chooseBackupDir.launch(Backup.getFilename())
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
context.toast(R.string.file_picker_error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.toast(R.string.backup_in_progress)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.action_create),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel.State>(State()) {
|
||||||
|
|
||||||
|
fun toggleFlag(flag: Int) {
|
||||||
|
mutableState.update {
|
||||||
|
if (it.flags.contains(flag)) {
|
||||||
|
it.copy(flags = it.flags - flag)
|
||||||
|
} else {
|
||||||
|
it.copy(flags = it.flags + flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBackup(context: Context, uri: Uri) {
|
||||||
|
val flags = state.value.flags.fold(initial = 0, operation = { a, b -> a or b })
|
||||||
|
BackupCreateJob.startNow(context, uri, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class State(
|
||||||
|
val flags: Set<Int> = BackupChoices.keys,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val BackupChoices = mapOf(
|
||||||
|
BackupCreateFlags.BACKUP_CATEGORY to R.string.categories,
|
||||||
|
BackupCreateFlags.BACKUP_CHAPTER to R.string.chapters,
|
||||||
|
BackupCreateFlags.BACKUP_TRACK to R.string.track,
|
||||||
|
BackupCreateFlags.BACKUP_HISTORY to R.string.history,
|
||||||
|
BackupCreateFlags.BACKUP_APP_PREFS to R.string.app_settings,
|
||||||
|
BackupCreateFlags.BACKUP_SOURCE_PREFS to R.string.source_settings,
|
||||||
|
)
|
|
@ -1,24 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.backup
|
|
||||||
|
|
||||||
// Filter options
|
|
||||||
internal object BackupConst {
|
|
||||||
const val BACKUP_CATEGORY = 0x1
|
|
||||||
const val BACKUP_CATEGORY_MASK = 0x1
|
|
||||||
|
|
||||||
const val BACKUP_CHAPTER = 0x2
|
|
||||||
const val BACKUP_CHAPTER_MASK = 0x2
|
|
||||||
|
|
||||||
const val BACKUP_HISTORY = 0x4
|
|
||||||
const val BACKUP_HISTORY_MASK = 0x4
|
|
||||||
|
|
||||||
const val BACKUP_TRACK = 0x8
|
|
||||||
const val BACKUP_TRACK_MASK = 0x8
|
|
||||||
|
|
||||||
const val BACKUP_APP_PREFS = 0x10
|
|
||||||
const val BACKUP_APP_PREFS_MASK = 0x10
|
|
||||||
|
|
||||||
const val BACKUP_SOURCE_PREFS = 0x20
|
|
||||||
const val BACKUP_SOURCE_PREFS_MASK = 0x20
|
|
||||||
|
|
||||||
const val BACKUP_ALL = 0x3F
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package eu.kanade.tachiyomi.data.backup
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
|
||||||
val backupPreferences = Injekt.get<BackupPreferences>()
|
val backupPreferences = Injekt.get<BackupPreferences>()
|
||||||
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
||||||
?: backupPreferences.backupsDirectory().get().toUri()
|
?: backupPreferences.backupsDirectory().get().toUri()
|
||||||
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupConst.BACKUP_ALL)
|
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setForeground(getForegroundInfo())
|
setForeground(getForegroundInfo())
|
||||||
|
|
|
@ -5,18 +5,12 @@ import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_APP_PREFS
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS_MASK
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CATEGORY
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CHAPTER
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_HISTORY
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_SOURCE_PREFS
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER_MASK
|
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_TRACK
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS_MASK
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
|
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
|
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
|
||||||
|
@ -161,7 +155,7 @@ class BackupCreator(
|
||||||
*/
|
*/
|
||||||
private suspend fun backupCategories(options: Int): List<BackupCategory> {
|
private suspend fun backupCategories(options: Int): List<BackupCategory> {
|
||||||
// Check if user wants category information in backup
|
// Check if user wants category information in backup
|
||||||
return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
return if (options and BACKUP_CATEGORY == BACKUP_CATEGORY) {
|
||||||
getCategories.await()
|
getCategories.await()
|
||||||
.filterNot(Category::isSystemCategory)
|
.filterNot(Category::isSystemCategory)
|
||||||
.map(backupCategoryMapper)
|
.map(backupCategoryMapper)
|
||||||
|
@ -188,7 +182,7 @@ class BackupCreator(
|
||||||
val mangaObject = BackupManga.copyFrom(manga)
|
val mangaObject = BackupManga.copyFrom(manga)
|
||||||
|
|
||||||
// Check if user wants chapter information in backup
|
// Check if user wants chapter information in backup
|
||||||
if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) {
|
if (options and BACKUP_CHAPTER == BACKUP_CHAPTER) {
|
||||||
// Backup all the chapters
|
// Backup all the chapters
|
||||||
handler.awaitList {
|
handler.awaitList {
|
||||||
chaptersQueries.getChaptersByMangaId(
|
chaptersQueries.getChaptersByMangaId(
|
||||||
|
@ -202,7 +196,7 @@ class BackupCreator(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user wants category information in backup
|
// Check if user wants category information in backup
|
||||||
if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
if (options and BACKUP_CATEGORY == 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()) {
|
||||||
|
@ -211,7 +205,7 @@ class BackupCreator(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user wants track information in backup
|
// Check if user wants track information in backup
|
||||||
if (options and BACKUP_TRACK_MASK == BACKUP_TRACK) {
|
if (options and BACKUP_TRACK == 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
|
||||||
|
@ -219,7 +213,7 @@ class BackupCreator(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user wants history information in backup
|
// Check if user wants history information in backup
|
||||||
if (options and BACKUP_HISTORY_MASK == BACKUP_HISTORY) {
|
if (options and BACKUP_HISTORY == 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 ->
|
||||||
|
@ -236,13 +230,13 @@ class BackupCreator(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun backupAppPreferences(flags: Int): List<BackupPreference> {
|
private fun backupAppPreferences(flags: Int): List<BackupPreference> {
|
||||||
if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList()
|
if (flags and BACKUP_APP_PREFS != BACKUP_APP_PREFS) return emptyList()
|
||||||
|
|
||||||
return preferenceStore.getAll().toBackupPreferences()
|
return preferenceStore.getAll().toBackupPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> {
|
private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> {
|
||||||
if (flags and BACKUP_SOURCE_PREFS_MASK != BACKUP_SOURCE_PREFS) return emptyList()
|
if (flags and BACKUP_SOURCE_PREFS != BACKUP_SOURCE_PREFS) return emptyList()
|
||||||
|
|
||||||
return sourceManager.getCatalogueSources()
|
return sourceManager.getCatalogueSources()
|
||||||
.filterIsInstance<ConfigurableSource>()
|
.filterIsInstance<ConfigurableSource>()
|
||||||
|
|
|
@ -484,6 +484,7 @@
|
||||||
<string name="pref_backup_directory">Backup location</string>
|
<string name="pref_backup_directory">Backup location</string>
|
||||||
<string name="pref_backup_interval">Automatic backup frequency</string>
|
<string name="pref_backup_interval">Automatic backup frequency</string>
|
||||||
<string name="pref_backup_slots">Maximum automatic backups</string>
|
<string name="pref_backup_slots">Maximum automatic backups</string>
|
||||||
|
<string name="action_create">Create</string>
|
||||||
<string name="backup_created">Backup created</string>
|
<string name="backup_created">Backup created</string>
|
||||||
<string name="invalid_backup_file">Invalid backup file</string>
|
<string name="invalid_backup_file">Invalid backup file</string>
|
||||||
<string name="invalid_backup_file_missing_manga">Backup does not contain any library entries.</string>
|
<string name="invalid_backup_file_missing_manga">Backup does not contain any library entries.</string>
|
||||||
|
@ -880,7 +881,7 @@
|
||||||
<string name="file_select_cover">Select cover image</string>
|
<string name="file_select_cover">Select cover image</string>
|
||||||
<string name="file_select_backup">Select backup file</string>
|
<string name="file_select_backup">Select backup file</string>
|
||||||
<string name="file_picker_error">No file picker app found</string>
|
<string name="file_picker_error">No file picker app found</string>
|
||||||
<string name="file_null_uri_error">File picker failed to return file to app</string>
|
<string name="file_null_uri_error">No file selected</string>
|
||||||
|
|
||||||
<!--UpdateCheck-->
|
<!--UpdateCheck-->
|
||||||
<string name="update_check_confirm">Download</string>
|
<string name="update_check_confirm">Download</string>
|
||||||
|
|
|
@ -21,6 +21,7 @@ fun LabeledCheckbox(
|
||||||
label: String,
|
label: String,
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
enabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
@ -37,6 +38,7 @@ fun LabeledCheckbox(
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = null,
|
onCheckedChange = null,
|
||||||
|
enabled = enabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(text = label)
|
Text(text = label)
|
||||||
|
|
Reference in a new issue