mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: Finally add functionality for backup on composable data settings
This commit is contained in:
parent
27f42de963
commit
82ff1a49d5
7 changed files with 182 additions and 25 deletions
|
@ -28,12 +28,14 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.hippo.unifile.UniFile
|
||||
import dev.yokai.domain.storage.StorageManager
|
||||
import dev.yokai.domain.storage.StoragePreferences
|
||||
import dev.yokai.presentation.component.preference.Preference
|
||||
import dev.yokai.presentation.component.preference.storageLocationText
|
||||
import dev.yokai.presentation.component.preference.widget.BasePreferenceWidget
|
||||
import dev.yokai.presentation.component.preference.widget.PrefsHorizontalPadding
|
||||
import dev.yokai.presentation.settings.ComposableSettings
|
||||
import dev.yokai.presentation.settings.screen.data.CreateBackup
|
||||
import dev.yokai.presentation.settings.screen.data.RestoreBackup
|
||||
import dev.yokai.presentation.settings.screen.data.StorageInfo
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
@ -118,6 +120,7 @@ object SettingsDataScreen : ComposableSettings {
|
|||
val context = LocalContext.current
|
||||
val alertDialog = LocalAlertDialog.currentOrThrow
|
||||
val extensionManager = remember { Injekt.get<ExtensionManager>() }
|
||||
val storageManager = remember { Injekt.get<StorageManager>() }
|
||||
|
||||
val chooseBackup = rememberLauncherForActivityResult(
|
||||
object : ActivityResultContracts.GetContent() {
|
||||
|
@ -129,7 +132,7 @@ object SettingsDataScreen : ComposableSettings {
|
|||
},
|
||||
) {
|
||||
if (it == null) {
|
||||
context.toast(R.string.invalid_location_generic)
|
||||
context.toast(R.string.backup_restore_invalid_uri)
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
|
||||
|
@ -174,7 +177,19 @@ object SettingsDataScreen : ComposableSettings {
|
|||
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
context.toast("Not yet available")
|
||||
val dir = storageManager.getBackupsDirectory()
|
||||
if (dir == null) {
|
||||
context.toast(R.string.invalid_location_generic)
|
||||
return@SegmentedButton
|
||||
}
|
||||
|
||||
alertDialog.content = {
|
||||
CreateBackup(
|
||||
context = context,
|
||||
uri = dir.uri,
|
||||
onDismissRequest = { alertDialog.content = null },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
context.toast(R.string.backup_in_progress)
|
||||
}
|
||||
|
|
|
@ -2,15 +2,31 @@ package dev.yokai.presentation.settings.screen.data
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.hippo.unifile.UniFile
|
||||
import dev.yokai.presentation.component.LabeledCheckbox
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator.Results
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun RestoreBackup(
|
||||
|
@ -73,12 +89,50 @@ fun RestoreBackup(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateBackup(
|
||||
fun CreateBackup(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
var options by remember { mutableStateOf(BackupOptions()) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {},
|
||||
confirmButton = {
|
||||
val actualUri =
|
||||
UniFile.fromUri(context, uri)?.createFile(Backup.getBackupFilename())?.uri ?: return@AlertDialog
|
||||
context.toast(R.string.creating_backup)
|
||||
BackupCreatorJob.startNow(context, actualUri, options)
|
||||
onDismissRequest()
|
||||
},
|
||||
text = {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
options(BackupOptions.getEntries(), options) { option, enabled ->
|
||||
options = option.setter(options, enabled)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun LazyListScope.options(
|
||||
options: ImmutableList<BackupOptions.Entry>,
|
||||
state: BackupOptions,
|
||||
onChange: (BackupOptions.Entry, Boolean) -> Unit,
|
||||
) {
|
||||
items(options.size) {
|
||||
val option = options[it]
|
||||
LabeledCheckbox(
|
||||
label = stringResource(option.label),
|
||||
checked = option.getter(state),
|
||||
onCheckedChange = {
|
||||
onChange(option, it)
|
||||
},
|
||||
enabled = option.enabled(state),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package eu.kanade.tachiyomi.data.backup.create
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
data class BackupOptions(
|
||||
val libraryEntries: Boolean = true,
|
||||
val category: Boolean = true,
|
||||
val chapter: Boolean = true,
|
||||
val categories: Boolean = true,
|
||||
val chapters: Boolean = true,
|
||||
val tracking: Boolean = true,
|
||||
val history: Boolean = true,
|
||||
val appPrefs: Boolean = true,
|
||||
|
@ -16,8 +18,8 @@ data class BackupOptions(
|
|||
) {
|
||||
fun asBooleanArray() = booleanArrayOf(
|
||||
libraryEntries,
|
||||
category,
|
||||
chapter,
|
||||
categories,
|
||||
chapters,
|
||||
tracking,
|
||||
history,
|
||||
appPrefs,
|
||||
|
@ -28,7 +30,7 @@ data class BackupOptions(
|
|||
)
|
||||
|
||||
companion object {
|
||||
fun getOptions() = arrayOf(
|
||||
fun getOptions() = persistentListOf(
|
||||
R.string.library_entries,
|
||||
R.string.categories,
|
||||
R.string.chapters,
|
||||
|
@ -41,6 +43,65 @@ data class BackupOptions(
|
|||
R.string.backup_private_pref,
|
||||
)
|
||||
|
||||
fun getEntries() = persistentListOf(
|
||||
Entry(
|
||||
label = R.string.library_entries,
|
||||
getter = BackupOptions::libraryEntries,
|
||||
setter = { options, enabled -> options.copy(libraryEntries = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.categories,
|
||||
getter = BackupOptions::categories,
|
||||
setter = { options, enabled -> options.copy(categories = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.chapters,
|
||||
getter = BackupOptions::chapters,
|
||||
setter = { options, enabled -> options.copy(chapters = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.tracking,
|
||||
getter = BackupOptions::tracking,
|
||||
setter = { options, enabled -> options.copy(tracking = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.history,
|
||||
getter = BackupOptions::history,
|
||||
setter = { options, enabled -> options.copy(history = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.custom_manga_info,
|
||||
getter = BackupOptions::customInfo,
|
||||
setter = { options, enabled -> options.copy(customInfo = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.all_read_manga,
|
||||
getter = BackupOptions::readManga,
|
||||
setter = { options, enabled -> options.copy(readManga = enabled) },
|
||||
enabled = { it.libraryEntries },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.app_settings,
|
||||
getter = BackupOptions::appPrefs,
|
||||
setter = { options, enabled -> options.copy(appPrefs = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.source_settings,
|
||||
getter = BackupOptions::sourcePrefs,
|
||||
setter = { options, enabled -> options.copy(sourcePrefs = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.backup_private_pref,
|
||||
getter = BackupOptions::includePrivate,
|
||||
setter = { options, enabled -> options.copy(includePrivate = enabled) },
|
||||
),
|
||||
)
|
||||
|
||||
fun fromBooleanArray(array: BooleanArray): BackupOptions = BackupOptions(
|
||||
array[0],
|
||||
array[1],
|
||||
|
@ -54,4 +115,11 @@ data class BackupOptions(
|
|||
array[9],
|
||||
)
|
||||
}
|
||||
|
||||
data class Entry(
|
||||
@StringRes val label: Int,
|
||||
val getter: (BackupOptions) -> Boolean,
|
||||
val setter: (BackupOptions, Boolean) -> BackupOptions,
|
||||
val enabled: (BackupOptions) -> Boolean = { true },
|
||||
)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class MangaBackupCreator(
|
|||
val mangaObject = BackupManga.copyFrom(manga, if (options.customInfo) customMangaManager else null)
|
||||
|
||||
// Check if user wants chapter information in backup
|
||||
if (options.chapter) {
|
||||
if (options.chapters) {
|
||||
// Backup all the chapters
|
||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||
if (chapters.isNotEmpty()) {
|
||||
|
@ -42,7 +42,7 @@ class MangaBackupCreator(
|
|||
}
|
||||
|
||||
// Check if user wants category information in backup
|
||||
if (options.category) {
|
||||
if (options.categories) {
|
||||
// Backup categories for this manga
|
||||
val categoriesForManga = db.getCategoriesForManga(manga).executeAsBlocking()
|
||||
if (categoriesForManga.isNotEmpty()) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.ui.main
|
||||
|
||||
import android.Manifest
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.animation.ValueAnimator
|
||||
|
@ -10,7 +9,6 @@ import android.app.assist.AssistContent
|
|||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
|
@ -39,7 +37,6 @@ import androidx.appcompat.view.menu.MenuItemImpl
|
|||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.net.toUri
|
||||
|
@ -111,6 +108,7 @@ import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
|||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
||||
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
|
||||
import eu.kanade.tachiyomi.util.showNotificationPermissionPrompt
|
||||
import eu.kanade.tachiyomi.util.system.contextCompatDrawable
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.e
|
||||
|
@ -1016,17 +1014,12 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
fun showNotificationPermissionPrompt(showAnyway: Boolean = false) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
||||
val notificationPermission = Manifest.permission.POST_NOTIFICATIONS
|
||||
val hasPermission = ActivityCompat.checkSelfPermission(this, notificationPermission)
|
||||
if (hasPermission != PackageManager.PERMISSION_GRANTED &&
|
||||
(!preferences.hasShownNotifPermission().get() || showAnyway)
|
||||
) {
|
||||
preferences.hasShownNotifPermission().set(true)
|
||||
requestNotificationPermissionLauncher.launch((notificationPermission))
|
||||
}
|
||||
}
|
||||
fun showNotificationPermissionPrompt(showAnyway: Boolean = false) =
|
||||
this.showNotificationPermissionPrompt(
|
||||
requestNotificationPermissionLauncher,
|
||||
showAnyway,
|
||||
preferences,
|
||||
)
|
||||
|
||||
fun showColourProfilePicker() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
|
|
|
@ -48,6 +48,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@Deprecated("Migrating to compose")
|
||||
class SettingsDataLegacyController : SettingsLegacyController() {
|
||||
|
||||
/**
|
||||
|
|
26
app/src/main/java/eu/kanade/tachiyomi/util/PermissionUtil.kt
Normal file
26
app/src/main/java/eu/kanade/tachiyomi/util/PermissionUtil.kt
Normal file
|
@ -0,0 +1,26 @@
|
|||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
fun Context.showNotificationPermissionPrompt(
|
||||
requestNotificationPermissionLauncher: ActivityResultLauncher<String>,
|
||||
showAnyway: Boolean = false,
|
||||
preferences: PreferencesHelper = Injekt.get(),
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
||||
val notificationPermission = Manifest.permission.POST_NOTIFICATIONS
|
||||
val hasPermission = this.checkSelfPermission(notificationPermission)
|
||||
if (hasPermission != PackageManager.PERMISSION_GRANTED &&
|
||||
(!preferences.hasShownNotifPermission().get() || showAnyway)
|
||||
) {
|
||||
preferences.hasShownNotifPermission().set(true)
|
||||
requestNotificationPermissionLauncher.launch((notificationPermission))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue