diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt index c8f99b5930..fd8d12067c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt @@ -4,6 +4,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.graphics.vector.ImageVector import eu.kanade.tachiyomi.data.track.Tracker +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.core.preference.Preference as PreferenceData @@ -64,20 +66,20 @@ sealed class Preference { val pref: PreferenceData, override val title: String, override val subtitle: String? = "%s", - val subtitleProvider: @Composable (value: T, entries: Map) -> String? = + val subtitleProvider: @Composable (value: T, entries: ImmutableMap) -> String? = { v, e -> subtitle?.format(e[v]) }, override val icon: ImageVector? = null, override val enabled: Boolean = true, override val onValueChanged: suspend (newValue: T) -> Boolean = { true }, - val entries: Map, + val entries: ImmutableMap, ) : PreferenceItem() { internal fun internalSet(newValue: Any) = pref.set(newValue as T) internal suspend fun internalOnValueChanged(newValue: Any) = onValueChanged(newValue as T) @Composable - internal fun internalSubtitleProvider(value: Any?, entries: Map) = - subtitleProvider(value as T, entries as Map) + internal fun internalSubtitleProvider(value: Any?, entries: ImmutableMap) = + subtitleProvider(value as T, entries as ImmutableMap) } /** @@ -87,13 +89,13 @@ sealed class Preference { val value: String, override val title: String, override val subtitle: String? = "%s", - val subtitleProvider: @Composable (value: String, entries: Map) -> String? = + val subtitleProvider: @Composable (value: String, entries: ImmutableMap) -> String? = { v, e -> subtitle?.format(e[v]) }, override val icon: ImageVector? = null, override val enabled: Boolean = true, override val onValueChanged: suspend (newValue: String) -> Boolean = { true }, - val entries: Map, + val entries: ImmutableMap, ) : PreferenceItem() /** @@ -104,7 +106,10 @@ sealed class Preference { val pref: PreferenceData>, override val title: String, override val subtitle: String? = "%s", - val subtitleProvider: @Composable (value: Set, entries: Map) -> String? = { v, e -> + val subtitleProvider: @Composable ( + value: Set, + entries: ImmutableMap, + ) -> String? = { v, e -> val combined = remember(v) { v.map { e[it] } .takeIf { it.isNotEmpty() } @@ -116,7 +121,7 @@ sealed class Preference { override val enabled: Boolean = true, override val onValueChanged: suspend (newValue: Set) -> Boolean = { true }, - val entries: Map, + val entries: ImmutableMap, ) : PreferenceItem>() /** @@ -170,6 +175,6 @@ sealed class Preference { override val title: String, override val enabled: Boolean = true, - val preferenceItems: List>, + val preferenceItems: ImmutableList>, ) : Preference() } 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 56f6065674..921fd4ae4e 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 @@ -51,6 +51,9 @@ import eu.kanade.tachiyomi.util.system.isShizukuInstalled import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.toast +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.launch import logcat.LogPriority import okhttp3.Headers @@ -149,7 +152,7 @@ object SettingsAdvancedScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_background_activity), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_disable_battery_optimization), subtitle = stringResource(MR.strings.pref_disable_battery_optimization_summary), @@ -188,7 +191,7 @@ object SettingsAdvancedScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_data), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_invalidate_download_cache), subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary), @@ -218,7 +221,7 @@ object SettingsAdvancedScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_network), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_clear_cookies), onClick = { @@ -249,7 +252,7 @@ object SettingsAdvancedScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = networkPreferences.dohProvider(), title = stringResource(MR.strings.pref_dns_over_https), - entries = mapOf( + entries = persistentMapOf( -1 to stringResource(MR.strings.disabled), PREF_DOH_CLOUDFLARE to "Cloudflare", PREF_DOH_GOOGLE to "Google", @@ -302,7 +305,7 @@ object SettingsAdvancedScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_library), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_refresh_library_covers), onClick = { MetadataUpdateJob.startNow(context) }, @@ -362,12 +365,13 @@ object SettingsAdvancedScreen : SearchableSettings { } return Preference.PreferenceGroup( title = stringResource(MR.strings.label_extensions), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = extensionInstallerPref, title = stringResource(MR.strings.ext_installer_pref), entries = extensionInstallerPref.entries - .associateWith { stringResource(it.titleRes) }, + .associateWith { stringResource(it.titleRes) } + .toImmutableMap(), onValueChanged = { if (it == BasePreferences.ExtensionInstaller.SHIZUKU && !context.isShizukuInstalled diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 8523de930b..365e86b6fb 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -24,6 +24,9 @@ import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.toast +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableMap import org.xmlpull.v1.XmlPullParser import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR @@ -66,7 +69,7 @@ object SettingsAppearanceScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_theme), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.CustomPreference( title = stringResource(MR.strings.pref_app_theme), ) { @@ -127,7 +130,7 @@ object SettingsAppearanceScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_display), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.BasicListPreference( value = currentLanguage, title = stringResource(MR.strings.pref_app_language), @@ -140,7 +143,9 @@ object SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = uiPreferences.tabletUiMode(), title = stringResource(MR.strings.pref_tablet_ui_mode), - entries = TabletUiMode.entries.associateWith { stringResource(it.titleRes) }, + entries = TabletUiMode.entries + .associateWith { stringResource(it.titleRes) } + .toImmutableMap(), onValueChanged = { context.toast(MR.strings.requires_app_restart) true @@ -153,7 +158,8 @@ object SettingsAppearanceScreen : SearchableSettings { .associateWith { val formattedDate = UiPreferences.dateFormat(it).format(now) "${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)" - }, + } + .toImmutableMap(), ), Preference.PreferenceItem.SwitchPreference( pref = uiPreferences.relativeTime(), @@ -167,7 +173,7 @@ object SettingsAppearanceScreen : SearchableSettings { ), ) } - private fun getLangs(context: Context): Map { + private fun getLangs(context: Context): ImmutableMap { val langs = mutableListOf>() val parser = context.resources.getXml(R.xml.locales_config) var eventType = parser.eventType @@ -189,7 +195,7 @@ object SettingsAppearanceScreen : SearchableSettings { langs.sortBy { it.second } langs.add(0, Pair("", context.stringResource(MR.strings.label_default))) - return langs.toMap() + return langs.toMap().toImmutableMap() } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index 4d9aa2796a..61c1db21e7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.FragmentActivity import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.more.settings.Preference import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate +import kotlinx.collections.immutable.persistentListOf import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource @@ -27,7 +28,7 @@ object SettingsBrowseScreen : SearchableSettings { return listOf( Preference.PreferenceGroup( title = stringResource(MR.strings.label_sources), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = sourcePreferences.hideInLibraryItems(), title = stringResource(MR.strings.pref_hide_in_library_items), @@ -36,7 +37,7 @@ object SettingsBrowseScreen : SearchableSettings { ), Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_nsfw_content), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = sourcePreferences.showNsfwSource(), title = stringResource(MR.strings.pref_show_nsfw_source), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 25b5ca7637..aef277da05 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -3,12 +3,9 @@ package eu.kanade.presentation.more.settings.screen import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri -import android.os.Environment -import android.text.format.Formatter import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MultiChoiceSegmentedButtonRow @@ -31,13 +28,15 @@ import com.hippo.unifile.UniFile import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen +import eu.kanade.presentation.more.settings.screen.data.StorageInfo import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.cache.ChapterCache -import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.toast +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf import logcat.LogPriority import tachiyomi.core.i18n.stringResource import tachiyomi.core.util.lang.launchNonCancellable @@ -65,7 +64,7 @@ object SettingsDataScreen : SearchableSettings { val backupPreferences = Injekt.get() val storagePreferences = Injekt.get() - return listOf( + return persistentListOf( getStorageLocationPref(storagePreferences = storagePreferences), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)), @@ -142,7 +141,7 @@ object SettingsDataScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_backup), - preferenceItems = listOf( + preferenceItems = persistentListOf( // Manual actions Preference.PreferenceItem.CustomPreference( title = stringResource(restorePreferenceKeyString), @@ -177,7 +176,7 @@ object SettingsDataScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = backupPreferences.backupInterval(), title = stringResource(MR.strings.pref_backup_interval), - entries = mapOf( + entries = persistentMapOf( 0 to stringResource(MR.strings.off), 6 to stringResource(MR.strings.update_6hour), 12 to stringResource(MR.strings.update_12hour), @@ -200,8 +199,8 @@ object SettingsDataScreen : SearchableSettings { @Composable private fun getDataGroup(): Preference.PreferenceGroup { - val scope = rememberCoroutineScope() val context = LocalContext.current + val scope = rememberCoroutineScope() val libraryPreferences = remember { Injekt.get() } val chapterCache = remember { Injekt.get() } @@ -210,8 +209,19 @@ object SettingsDataScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.label_data), - preferenceItems = listOf( - getStorageInfoPref(cacheReadableSize), + preferenceItems = persistentListOf( + Preference.PreferenceItem.CustomPreference( + title = stringResource(MR.strings.pref_storage_usage), + ) { + BasePreferenceWidget( + title = stringResource(MR.strings.pref_storage_usage), + subcomponent = { + StorageInfo( + modifier = Modifier.padding(horizontal = PrefsHorizontalPadding), + ) + }, + ) + }, Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_clear_chapter_cache), @@ -238,31 +248,4 @@ object SettingsDataScreen : SearchableSettings { ), ) } - - @Composable - fun getStorageInfoPref( - chapterCacheReadableSize: String, - ): Preference.PreferenceItem.CustomPreference { - val context = LocalContext.current - val available = remember { - Formatter.formatFileSize(context, DiskUtil.getAvailableStorageSpace(Environment.getDataDirectory())) - } - val total = remember { - Formatter.formatFileSize(context, DiskUtil.getTotalStorageSpace(Environment.getDataDirectory())) - } - - return Preference.PreferenceItem.CustomPreference( - title = stringResource(MR.strings.pref_storage_usage), - ) { - BasePreferenceWidget( - title = stringResource(MR.strings.pref_storage_usage), - subcomponent = { - // TODO: downloads, SD cards, bar representation?, i18n - Box(modifier = Modifier.padding(horizontal = PrefsHorizontalPadding)) { - Text(text = "Available: $available / $total (chapter cache: $chapterCacheReadableSize)") - } - }, - ) - } - } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt index c9dd746f1e..072013415b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt @@ -12,6 +12,9 @@ import androidx.compose.ui.util.fastMap import eu.kanade.presentation.category.visualName import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.widget.TriStateListDialog +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.runBlocking import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.model.Category @@ -68,7 +71,7 @@ object SettingsDownloadScreen : SearchableSettings { ): Preference.PreferenceGroup { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_delete_chapters), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = downloadPreferences.removeAfterMarkedAsRead(), title = stringResource(MR.strings.pref_remove_after_marked_as_read), @@ -76,7 +79,7 @@ object SettingsDownloadScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = downloadPreferences.removeAfterReadSlots(), title = stringResource(MR.strings.pref_remove_after_read), - entries = mapOf( + entries = persistentMapOf( -1 to stringResource(MR.strings.disabled), 0 to stringResource(MR.strings.last_read_chapter), 1 to stringResource(MR.strings.second_to_last), @@ -105,7 +108,9 @@ object SettingsDownloadScreen : SearchableSettings { return Preference.PreferenceItem.MultiSelectListPreference( pref = downloadPreferences.removeExcludeCategories(), title = stringResource(MR.strings.pref_remove_exclude_categories), - entries = categories().associate { it.id.toString() to it.visualName }, + entries = categories() + .associate { it.id.toString() to it.visualName } + .toImmutableMap(), ) } @@ -142,7 +147,7 @@ object SettingsDownloadScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_auto_download), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = downloadNewChaptersPref, title = stringResource(MR.strings.pref_download_new), @@ -167,17 +172,19 @@ object SettingsDownloadScreen : SearchableSettings { ): Preference.PreferenceGroup { return Preference.PreferenceGroup( title = stringResource(MR.strings.download_ahead), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = downloadPreferences.autoDownloadWhileReading(), title = stringResource(MR.strings.auto_download_while_reading), - entries = listOf(0, 2, 3, 5, 10).associateWith { - if (it == 0) { - stringResource(MR.strings.disabled) - } else { - pluralStringResource(MR.plurals.next_unread_chapters, count = it, it) + entries = listOf(0, 2, 3, 5, 10) + .associateWith { + if (it == 0) { + stringResource(MR.strings.disabled) + } else { + pluralStringResource(MR.plurals.next_unread_chapters, count = it, it) + } } - }, + .toImmutableMap(), ), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.download_ahead_info)), ), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt index a12e5790ed..1ad7410be1 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt @@ -20,6 +20,9 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.ui.category.CategoryScreen +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import tachiyomi.domain.category.interactor.GetCategories @@ -79,7 +82,7 @@ object SettingsLibraryScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.categories), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.action_edit_categories), subtitle = pluralStringResource( @@ -93,7 +96,7 @@ object SettingsLibraryScreen : SearchableSettings { pref = libraryPreferences.defaultCategory(), title = stringResource(MR.strings.default_category), subtitle = selectedCategory?.visualName ?: stringResource(MR.strings.default_category_summary), - entries = ids.zip(labels).toMap(), + entries = ids.zip(labels).toMap().toImmutableMap(), ), Preference.PreferenceItem.SwitchPreference( pref = libraryPreferences.categorizedDisplaySettings(), @@ -146,11 +149,11 @@ object SettingsLibraryScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_library_update), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = autoUpdateIntervalPref, title = stringResource(MR.strings.pref_library_update_interval), - entries = mapOf( + entries = persistentMapOf( 0 to stringResource(MR.strings.update_never), 12 to stringResource(MR.strings.update_12hour), 24 to stringResource(MR.strings.update_24hour), @@ -168,7 +171,7 @@ object SettingsLibraryScreen : SearchableSettings { enabled = autoUpdateInterval > 0, title = stringResource(MR.strings.pref_library_update_restriction), subtitle = stringResource(MR.strings.restrictions), - entries = mapOf( + entries = persistentMapOf( DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi), DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered), DEVICE_CHARGING to stringResource(MR.strings.charging), @@ -196,7 +199,7 @@ object SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.MultiSelectListPreference( pref = libraryPreferences.autoUpdateMangaRestrictions(), title = stringResource(MR.strings.pref_library_update_manga_restriction), - entries = mapOf( + entries = persistentMapOf( MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read), MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started), MANGA_NON_COMPLETED to stringResource(MR.strings.pref_update_only_non_completed), @@ -217,11 +220,11 @@ object SettingsLibraryScreen : SearchableSettings { ): Preference.PreferenceGroup { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_chapter_swipe), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = libraryPreferences.swipeToStartAction(), title = stringResource(MR.strings.pref_chapter_swipe_start), - entries = mapOf( + entries = persistentMapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(MR.strings.disabled), LibraryPreferences.ChapterSwipeAction.ToggleBookmark to @@ -235,7 +238,7 @@ object SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = libraryPreferences.swipeToEndAction(), title = stringResource(MR.strings.pref_chapter_swipe_end), - entries = mapOf( + entries = persistentMapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(MR.strings.disabled), LibraryPreferences.ChapterSwipeAction.ToggleBookmark to diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index aac9259a1b..c28b12d481 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -10,6 +10,9 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState @@ -31,12 +34,13 @@ object SettingsReaderScreen : SearchableSettings { pref = readerPref.defaultReadingMode(), title = stringResource(MR.strings.pref_viewer_type), entries = ReadingMode.entries.drop(1) - .associate { it.flagValue to stringResource(it.stringRes) }, + .associate { it.flagValue to stringResource(it.stringRes) } + .toImmutableMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPref.doubleTapAnimSpeed(), title = stringResource(MR.strings.pref_double_tap_anim_speed), - entries = mapOf( + entries = persistentMapOf( 1 to stringResource(MR.strings.double_tap_anim_speed_0), 500 to stringResource(MR.strings.double_tap_anim_speed_normal), 250 to stringResource(MR.strings.double_tap_anim_speed_fast), @@ -82,17 +86,18 @@ object SettingsReaderScreen : SearchableSettings { val fullscreen by fullscreenPref.collectAsState() return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_display), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = readerPreferences.defaultOrientationType(), title = stringResource(MR.strings.pref_rotation_type), entries = ReaderOrientation.entries.drop(1) - .associate { it.flagValue to stringResource(it.stringRes) }, + .associate { it.flagValue to stringResource(it.stringRes) } + .toImmutableMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.readerTheme(), title = stringResource(MR.strings.pref_reader_theme), - entries = mapOf( + entries = persistentMapOf( 1 to stringResource(MR.strings.black_background), 2 to stringResource(MR.strings.gray_background), 0 to stringResource(MR.strings.white_background), @@ -126,7 +131,7 @@ object SettingsReaderScreen : SearchableSettings { private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_reading), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.skipRead(), title = stringResource(MR.strings.pref_skip_read_chapters), @@ -161,23 +166,26 @@ object SettingsReaderScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pager_viewer), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = navModePref, title = stringResource(MR.strings.pref_viewer_nav), entries = ReaderPreferences.TapZones .mapIndexed { index, it -> index to stringResource(it) } - .toMap(), + .toMap() + .toImmutableMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.pagerNavInverted(), title = stringResource(MR.strings.pref_read_with_tapping_inverted), - entries = listOf( + entries = persistentListOf( ReaderPreferences.TappingInvertMode.NONE, ReaderPreferences.TappingInvertMode.HORIZONTAL, ReaderPreferences.TappingInvertMode.VERTICAL, ReaderPreferences.TappingInvertMode.BOTH, - ).associateWith { stringResource(it.titleRes) }, + ) + .associateWith { stringResource(it.titleRes) } + .toImmutableMap(), enabled = navMode != 5, ), Preference.PreferenceItem.ListPreference( @@ -185,14 +193,16 @@ object SettingsReaderScreen : SearchableSettings { title = stringResource(MR.strings.pref_image_scale_type), entries = ReaderPreferences.ImageScaleType .mapIndexed { index, it -> index + 1 to stringResource(it) } - .toMap(), + .toMap() + .toImmutableMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.zoomStart(), title = stringResource(MR.strings.pref_zoom_start), entries = ReaderPreferences.ZoomStart .mapIndexed { index, it -> index + 1 to stringResource(it) } - .toMap(), + .toMap() + .toImmutableMap(), ), Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.cropBorders(), @@ -255,23 +265,26 @@ object SettingsReaderScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.webtoon_viewer), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.ListPreference( pref = navModePref, title = stringResource(MR.strings.pref_viewer_nav), entries = ReaderPreferences.TapZones .mapIndexed { index, it -> index to stringResource(it) } - .toMap(), + .toMap() + .toImmutableMap(), ), Preference.PreferenceItem.ListPreference( pref = readerPreferences.webtoonNavInverted(), title = stringResource(MR.strings.pref_read_with_tapping_inverted), - entries = listOf( + entries = persistentListOf( ReaderPreferences.TappingInvertMode.NONE, ReaderPreferences.TappingInvertMode.HORIZONTAL, ReaderPreferences.TappingInvertMode.VERTICAL, ReaderPreferences.TappingInvertMode.BOTH, - ).associateWith { stringResource(it.titleRes) }, + ) + .associateWith { stringResource(it.titleRes) } + .toImmutableMap(), enabled = navMode != 5, ), Preference.PreferenceItem.SliderPreference( @@ -288,7 +301,7 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = readerPreferences.readerHideThreshold(), title = stringResource(MR.strings.pref_hide_threshold), - entries = mapOf( + entries = persistentMapOf( ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest), ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high), ReaderPreferences.ReaderHideThreshold.LOW to stringResource(MR.strings.pref_low), @@ -341,7 +354,7 @@ object SettingsReaderScreen : SearchableSettings { val readWithVolumeKeys by readWithVolumeKeysPref.collectAsState() return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_reader_navigation), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = readWithVolumeKeysPref, title = stringResource(MR.strings.pref_read_with_volume_keys), @@ -359,7 +372,7 @@ object SettingsReaderScreen : SearchableSettings { private fun getActionsGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_reader_actions), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.readWithLongTap(), title = stringResource(MR.strings.pref_read_with_long_tap), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt index 0acb22083b..fb1e0932c5 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt @@ -10,6 +10,8 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableMap import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource @@ -55,7 +57,8 @@ object SettingsSecurityScreen : SearchableSettings { 0 -> stringResource(MR.strings.lock_always) else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it) } - }, + } + .toImmutableMap(), onValueChanged = { (context as FragmentActivity).authenticate( title = context.stringResource(MR.strings.lock_when_idle), @@ -70,14 +73,15 @@ object SettingsSecurityScreen : SearchableSettings { pref = securityPreferences.secureScreen(), title = stringResource(MR.strings.secure_screen), entries = SecurityPreferences.SecureScreenMode.entries - .associateWith { stringResource(it.titleRes) }, + .associateWith { stringResource(it.titleRes) } + .toImmutableMap(), ), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.secure_screen_summary)), ) } } -private val LockAfterValues = listOf( +private val LockAfterValues = persistentListOf( 0, // Always 1, 2, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt index 4aa7a26bde..f81f1ba5d3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt @@ -51,6 +51,8 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.withUIContext import tachiyomi.domain.source.service.SourceManager @@ -125,7 +127,7 @@ object SettingsTrackingScreen : SearchableSettings { ), Preference.PreferenceGroup( title = stringResource(MR.strings.services), - preferenceItems = listOf( + preferenceItems = persistentListOf( Preference.PreferenceItem.TrackerPreference( title = trackerManager.myAnimeList.name, tracker = trackerManager.myAnimeList, @@ -167,15 +169,17 @@ object SettingsTrackingScreen : SearchableSettings { ), Preference.PreferenceGroup( title = stringResource(MR.strings.enhanced_services), - preferenceItems = enhancedTrackers.first - .map { service -> - Preference.PreferenceItem.TrackerPreference( - title = service.name, - tracker = service, - login = { (service as EnhancedTracker).loginNoop() }, - logout = service::logout, - ) - } + listOf(Preference.PreferenceItem.InfoPreference(enhancedTrackerInfo)), + preferenceItems = ( + enhancedTrackers.first + .map { service -> + Preference.PreferenceItem.TrackerPreference( + title = service.name, + tracker = service, + login = { (service as EnhancedTracker).loginNoop() }, + logout = service::logout, + ) + } + listOf(Preference.PreferenceItem.InfoPreference(enhancedTrackerInfo)) + ).toImmutableList(), ), ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt index 1d1dfd1b52..9811a3cce3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt @@ -36,6 +36,7 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil 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 @@ -170,7 +171,7 @@ private class CreateBackupScreenModel : StateScreenModel>().mutate { + it.add( Preference.PreferenceItem.TextPreference( title = "Model", subtitle = "${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})", @@ -105,14 +107,14 @@ class DebugInfoScreen : Screen() { ) if (DeviceUtil.oneUiVersion != null) { - add( + it.add( Preference.PreferenceItem.TextPreference( title = "OneUI version", subtitle = "${DeviceUtil.oneUiVersion}", ), ) } else if (DeviceUtil.miuiMajorVersion != null) { - add( + it.add( Preference.PreferenceItem.TextPreference( title = "MIUI version", subtitle = "${DeviceUtil.miuiMajorVersion}", @@ -127,7 +129,7 @@ class DebugInfoScreen : Screen() { } else { Build.VERSION.RELEASE } - add( + it.add( Preference.PreferenceItem.TextPreference( title = "Android version", subtitle = "$androidVersion (${Build.DISPLAY})", diff --git a/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt b/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt index 2961529a39..d464382c3c 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt @@ -37,6 +37,7 @@ import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter +import kotlinx.collections.immutable.persistentMapOf import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.calculateChapterGap import tachiyomi.i18n.MR @@ -234,7 +235,7 @@ private fun ChapterText( maxLines = 5, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.titleLarge, - inlineContent = mapOf( + inlineContent = persistentMapOf( DownloadedIconContentId to InlineTextContent( Placeholder( width = 22.sp, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt index c7675fbe74..3531df8654 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp import dev.icerock.moko.resources.StringResource import eu.kanade.presentation.theme.TachiyomiTheme import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.ScrollbarLazyColumn @@ -233,7 +234,7 @@ private fun TrackStatusSelectorPreviews() { TrackStatusSelector( selection = 1, onSelectionChange = {}, - selections = mapOf( + selections = persistentMapOf( // Anilist values 1 to MR.strings.reading, 2 to MR.strings.plan_to_read, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index 3f155e7f52..56d6262c94 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -14,7 +14,6 @@ import okio.buffer import okio.sink import tachiyomi.core.util.system.logcat import tachiyomi.domain.chapter.model.Chapter -import uy.kohesive.injekt.injectLazy import java.io.File import java.io.IOException @@ -26,9 +25,10 @@ import java.io.IOException * * @param context the application context. */ -class ChapterCache(private val context: Context) { - - private val json: Json by injectLazy() +class ChapterCache( + private val context: Context, + private val json: Json, +) { /** Cache class used for cache management. */ private val diskCache = DiskLruCache.open( diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index 4e4abe744f..4e0866dade 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -111,7 +111,7 @@ class AppModule(val app: Application) : InjektModule { ProtoBuf } - addSingletonFactory { ChapterCache(app) } + addSingletonFactory { ChapterCache(app, get()) } addSingletonFactory { CoverCache(app) } addSingletonFactory { NetworkHelper(app, get()) } diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt b/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt index fc0e81e654..6d5d2ffb68 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt @@ -3,13 +3,32 @@ package eu.kanade.tachiyomi.util.storage import android.content.Context import android.media.MediaScannerConnection import android.net.Uri +import android.os.Environment import android.os.StatFs +import androidx.core.content.ContextCompat import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.util.lang.Hash import java.io.File object DiskUtil { + /** + * Returns the root folders of all the available external storages. + */ + fun getExternalStorages(context: Context): List { + return ContextCompat.getExternalFilesDirs(context, null) + .filterNotNull() + .mapNotNull { + val file = File(it.absolutePath.substringBefore("/Android/")) + val state = Environment.getExternalStorageState(file) + if (state == Environment.MEDIA_MOUNTED || state == Environment.MEDIA_MOUNTED_READ_ONLY) { + file + } else { + null + } + } + } + fun hashKeyForDisk(key: String): String { return Hash.md5(key) } diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 0961e47064..e702443871 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -519,6 +519,7 @@ Last automatically backed up: %s Data Storage usage + Available: %1$s / Total: %2$s Clear chapter cache Used: %1$s Cache cleared, %1$d files deleted diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Badges.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Badges.kt index ee2a05dac0..448ee64e29 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Badges.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Badges.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.text.PlaceholderVerticalAlign import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import kotlinx.collections.immutable.persistentMapOf @Composable fun BadgeGroup( @@ -66,7 +67,7 @@ fun Badge( val text = buildAnnotatedString { appendInlineContent(iconContentPlaceholder) } - val inlineContent = mapOf( + val inlineContent = persistentMapOf( Pair( iconContentPlaceholder, InlineTextContent(