Allows us more flexibility in adding more options/explanations in the future.pull/10123/head
parent
634ceeec50
commit
00b2853d3d
@ -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
|
||||||
|
}
|
Loading…
Reference in new issue