diff --git a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt index 5ef863c51..6f7e9e3d7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt @@ -16,7 +16,7 @@ import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Label import androidx.compose.material.icons.outlined.QueryStats import androidx.compose.material.icons.outlined.Settings -import androidx.compose.material.icons.outlined.SettingsBackupRestore +import androidx.compose.material.icons.outlined.Storage import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -45,7 +45,7 @@ fun MoreScreen( onClickDownloadQueue: () -> Unit, onClickCategories: () -> Unit, onClickStats: () -> Unit, - onClickBackupAndRestore: () -> Unit, + onClickDataAndStorage: () -> Unit, onClickSettings: () -> Unit, onClickAbout: () -> Unit, ) { @@ -141,9 +141,9 @@ fun MoreScreen( } item { TextPreferenceWidget( - title = stringResource(R.string.label_backup), - icon = Icons.Outlined.SettingsBackupRestore, - onPreferenceClick = onClickBackupAndRestore, + title = stringResource(R.string.label_data_storage), + icon = Icons.Outlined.Storage, + onPreferenceClick = onClickDataAndStorage, ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 1b5d6a5c3..34e1981a9 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -31,7 +30,6 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.network.NetworkHelper @@ -61,7 +59,6 @@ import okhttp3.Headers import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.system.logcat -import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt @@ -183,40 +180,12 @@ object SettingsAdvancedScreen : SearchableSettings { @Composable private fun getDataGroup(): Preference.PreferenceGroup { - val scope = rememberCoroutineScope() val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val libraryPreferences = remember { Injekt.get() } - - val chapterCache = remember { Injekt.get() } - var readableSizeSema by remember { mutableIntStateOf(0) } - val readableSize = remember(readableSizeSema) { chapterCache.readableSize } return Preference.PreferenceGroup( title = stringResource(R.string.label_data), preferenceItems = listOf( - Preference.PreferenceItem.TextPreference( - title = stringResource(R.string.pref_clear_chapter_cache), - subtitle = stringResource(R.string.used_cache, readableSize), - onClick = { - scope.launchNonCancellable { - try { - val deletedFiles = chapterCache.clear() - withUIContext { - context.toast(context.getString(R.string.cache_deleted, deletedFiles)) - readableSizeSema++ - } - } catch (e: Throwable) { - logcat(LogPriority.ERROR, e) - withUIContext { context.toast(R.string.cache_delete_error) } - } - } - }, - ), - Preference.PreferenceItem.SwitchPreference( - pref = libraryPreferences.autoClearChapterCache(), - title = stringResource(R.string.pref_auto_clear_chapter_cache), - ), Preference.PreferenceItem.TextPreference( title = stringResource(R.string.pref_invalidate_download_cache), subtitle = stringResource(R.string.pref_invalidate_download_cache_summary), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt similarity index 86% rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index cb9c9907c..75c1cf467 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -31,8 +31,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.core.net.toUri -import com.hippo.unifile.UniFile import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.permissions.PermissionRequestHelper import eu.kanade.tachiyomi.R @@ -41,11 +39,17 @@ import eu.kanade.tachiyomi.data.backup.BackupCreateJob import eu.kanade.tachiyomi.data.backup.BackupFileValidator 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.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.launch +import logcat.LogPriority +import tachiyomi.core.util.lang.launchNonCancellable +import tachiyomi.core.util.lang.withUIContext +import tachiyomi.core.util.system.logcat import tachiyomi.domain.backup.service.BackupPreferences +import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.util.collectAsState @@ -54,12 +58,12 @@ import tachiyomi.presentation.core.util.isScrolledToStart import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -object SettingsBackupScreen : SearchableSettings { +object SettingsDataScreen : SearchableSettings { @ReadOnlyComposable @Composable @StringRes - override fun getTitleRes() = R.string.label_backup + override fun getTitleRes() = R.string.label_data_storage @Composable override fun getPreferences(): List { @@ -68,9 +72,49 @@ object SettingsBackupScreen : SearchableSettings { PermissionRequestHelper.requestStoragePermission() return listOf( - getCreateBackupPref(), - getRestoreBackupPref(), - getAutomaticBackupGroup(backupPreferences = backupPreferences), + getBackupAndRestoreGroup(backupPreferences = backupPreferences), + getDataGroup(), + ) + } + + @Composable + private fun getBackupAndRestoreGroup(backupPreferences: BackupPreferences): Preference.PreferenceGroup { + val context = LocalContext.current + val backupIntervalPref = backupPreferences.backupInterval() + val backupInterval by backupIntervalPref.collectAsState() + + return Preference.PreferenceGroup( + title = stringResource(R.string.label_backup), + preferenceItems = listOf( + // Manual actions + getCreateBackupPref(), + getRestoreBackupPref(), + + // Automatic backups + Preference.PreferenceItem.ListPreference( + pref = backupIntervalPref, + title = stringResource(R.string.pref_backup_interval), + entries = mapOf( + 0 to stringResource(R.string.off), + 6 to stringResource(R.string.update_6hour), + 12 to stringResource(R.string.update_12hour), + 24 to stringResource(R.string.update_24hour), + 48 to stringResource(R.string.update_48hour), + 168 to stringResource(R.string.update_weekly), + ), + onValueChanged = { + BackupCreateJob.setupTask(context, it) + true + }, + ), + Preference.PreferenceItem.ListPreference( + pref = backupPreferences.numberOfBackups(), + enabled = backupInterval != 0, + title = stringResource(R.string.pref_backup_slots), + entries = listOf(2, 3, 4, 5).associateWith { it.toString() }, + ), + Preference.PreferenceItem.InfoPreference(stringResource(R.string.backup_info)), + ), ) } @@ -318,70 +362,40 @@ object SettingsBackupScreen : SearchableSettings { } @Composable - private fun getAutomaticBackupGroup( - backupPreferences: BackupPreferences, - ): Preference.PreferenceGroup { + private fun getDataGroup(): Preference.PreferenceGroup { + val scope = rememberCoroutineScope() val context = LocalContext.current - val backupIntervalPref = backupPreferences.backupInterval() - val backupInterval by backupIntervalPref.collectAsState() - val backupDirPref = backupPreferences.backupsDirectory() - val backupDir by backupDirPref.collectAsState() - val pickBackupLocation = rememberLauncherForActivityResult( - contract = ActivityResultContracts.OpenDocumentTree(), - ) { uri -> - if (uri != null) { - val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION + val libraryPreferences = remember { Injekt.get() } - context.contentResolver.takePersistableUriPermission(uri, flags) - - val file = UniFile.fromUri(context, uri) - backupDirPref.set(file.uri.toString()) - } - } + val chapterCache = remember { Injekt.get() } + var readableSizeSema by remember { mutableIntStateOf(0) } + val readableSize = remember(readableSizeSema) { chapterCache.readableSize } return Preference.PreferenceGroup( - title = stringResource(R.string.pref_backup_service_category), + title = stringResource(R.string.label_data), preferenceItems = listOf( - Preference.PreferenceItem.ListPreference( - pref = backupIntervalPref, - title = stringResource(R.string.pref_backup_interval), - entries = mapOf( - 0 to stringResource(R.string.off), - 6 to stringResource(R.string.update_6hour), - 12 to stringResource(R.string.update_12hour), - 24 to stringResource(R.string.update_24hour), - 48 to stringResource(R.string.update_48hour), - 168 to stringResource(R.string.update_weekly), - ), - onValueChanged = { - BackupCreateJob.setupTask(context, it) - true - }, - ), Preference.PreferenceItem.TextPreference( - title = stringResource(R.string.pref_backup_directory), - enabled = backupInterval != 0, - subtitle = remember(backupDir) { - (UniFile.fromUri(context, backupDir.toUri())?.filePath)?.let { - "$it/automatic" - } - } ?: stringResource(R.string.invalid_location, backupDir), + title = stringResource(R.string.pref_clear_chapter_cache), + subtitle = stringResource(R.string.used_cache, readableSize), onClick = { - try { - pickBackupLocation.launch(null) - } catch (e: ActivityNotFoundException) { - context.toast(R.string.file_picker_error) + scope.launchNonCancellable { + try { + val deletedFiles = chapterCache.clear() + withUIContext { + context.toast(context.getString(R.string.cache_deleted, deletedFiles)) + readableSizeSema++ + } + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) + withUIContext { context.toast(R.string.cache_delete_error) } + } } }, ), - Preference.PreferenceItem.ListPreference( - pref = backupPreferences.numberOfBackups(), - enabled = backupInterval != 0, - title = stringResource(R.string.pref_backup_slots), - entries = listOf(2, 3, 4, 5).associateWith { it.toString() }, + Preference.PreferenceItem.SwitchPreference( + pref = libraryPreferences.autoClearChapterCache(), + title = stringResource(R.string.pref_auto_clear_chapter_cache), ), - Preference.PreferenceItem.InfoPreference(stringResource(R.string.backup_info)), ), ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 5f9c4e9b5..ea9c61a32 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -18,7 +18,7 @@ import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Palette import androidx.compose.material.icons.outlined.Search import androidx.compose.material.icons.outlined.Security -import androidx.compose.material.icons.outlined.SettingsBackupRestore +import androidx.compose.material.icons.outlined.Storage import androidx.compose.material.icons.outlined.Sync import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -208,10 +208,10 @@ object SettingsMainScreen : Screen() { screen = SettingsBrowseScreen, ), Item( - titleRes = R.string.label_backup, + titleRes = R.string.label_data_storage, subtitleRes = R.string.pref_backup_summary, - icon = Icons.Outlined.SettingsBackupRestore, - screen = SettingsBackupScreen, + icon = Icons.Outlined.Storage, + screen = SettingsDataScreen, ), Item( titleRes = R.string.pref_category_security, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt index 420722ccc..b67ac38c5 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt @@ -291,7 +291,7 @@ private val settingScreens = listOf( SettingsDownloadScreen, SettingsTrackingScreen, SettingsBrowseScreen, - SettingsBackupScreen, + SettingsDataScreen, SettingsSecurityScreen, SettingsAdvancedScreen, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt index 2870b8c44..8179fe68d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt @@ -71,7 +71,7 @@ object MoreTab : Tab { onClickDownloadQueue = { navigator.push(DownloadQueueScreen) }, onClickCategories = { navigator.push(CategoryScreen()) }, onClickStats = { navigator.push(StatsScreen()) }, - onClickBackupAndRestore = { navigator.push(SettingsScreen.toBackupScreen()) }, + onClickDataAndStorage = { navigator.push(SettingsScreen.toDataAndStorageScreen()) }, onClickSettings = { navigator.push(SettingsScreen.toMainScreen()) }, onClickAbout = { navigator.push(SettingsScreen.toAboutScreen()) }, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt index 5a6d05025..5b2dcedb2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt @@ -13,7 +13,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.more.settings.screen.SettingsAppearanceScreen -import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen +import eu.kanade.presentation.more.settings.screen.SettingsDataScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.util.DefaultNavigatorScreenTransition @@ -23,7 +23,7 @@ import eu.kanade.presentation.util.isTabletUi import tachiyomi.presentation.core.components.TwoPanelBox class SettingsScreen private constructor( - val toBackup: Boolean, + val toDataAndStorage: Boolean, val toAbout: Boolean, ) : Screen() { @@ -32,8 +32,8 @@ class SettingsScreen private constructor( val parentNavigator = LocalNavigator.currentOrThrow if (!isTabletUi()) { Navigator( - screen = if (toBackup) { - SettingsBackupScreen + screen = if (toDataAndStorage) { + SettingsDataScreen } else if (toAbout) { AboutScreen } else { @@ -54,8 +54,8 @@ class SettingsScreen private constructor( ) } else { Navigator( - screen = if (toBackup) { - SettingsBackupScreen + screen = if (toDataAndStorage) { + SettingsDataScreen } else if (toAbout) { AboutScreen } else { @@ -79,10 +79,10 @@ class SettingsScreen private constructor( } companion object { - fun toMainScreen() = SettingsScreen(toBackup = false, toAbout = false) + fun toMainScreen() = SettingsScreen(toDataAndStorage = false, toAbout = false) - fun toBackupScreen() = SettingsScreen(toBackup = true, toAbout = false) + fun toDataAndStorageScreen() = SettingsScreen(toDataAndStorage = true, toAbout = false) - fun toAboutScreen() = SettingsScreen(toBackup = false, toAbout = true) + fun toAboutScreen() = SettingsScreen(toDataAndStorage = false, toAbout = true) } } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 8c7ff8736..76fa240b7 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -24,6 +24,7 @@ History Sources Backup and restore + Data and storage Statistics Migrate Extensions @@ -181,7 +182,7 @@ Automatic download, download ahead One-way progress sync, enhanced sync Sources, extensions, global search - Manual & automatic backups + Manual & automatic backups, storage space App lock, secure screen Dump crash logs, battery optimizations @@ -467,15 +468,14 @@ Hide entries already in library - + Create backup Can be used to restore current library Restore backup Restore library from backup file Backup location - Automatic backups - Backup frequency - Maximum backups + Automatic backup frequency + Maximum automatic backups Backup created Invalid backup file Backup does not contain any library entries. @@ -509,7 +509,7 @@ Library sync complete - Network + Networking Clear cookies DNS over HTTPS (DoH) Default user agent string