Clean up storage usage info

- Show bar representation of used/total space
- Handle all mounted storages
- Also included a bunch of unrelated immutables changes, sorry
This commit is contained in:
arkon 2023-12-25 18:11:22 -05:00
parent 950b4a6c90
commit f31bc47757
20 changed files with 301 additions and 128 deletions

View File

@ -4,6 +4,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.core.preference.Preference as PreferenceData import tachiyomi.core.preference.Preference as PreferenceData
@ -64,20 +66,20 @@ sealed class Preference {
val pref: PreferenceData<T>, val pref: PreferenceData<T>,
override val title: String, override val title: String,
override val subtitle: String? = "%s", override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: T, entries: Map<T, String>) -> String? = val subtitleProvider: @Composable (value: T, entries: ImmutableMap<T, String>) -> String? =
{ v, e -> subtitle?.format(e[v]) }, { v, e -> subtitle?.format(e[v]) },
override val icon: ImageVector? = null, override val icon: ImageVector? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (newValue: T) -> Boolean = { true }, override val onValueChanged: suspend (newValue: T) -> Boolean = { true },
val entries: Map<T, String>, val entries: ImmutableMap<T, String>,
) : PreferenceItem<T>() { ) : PreferenceItem<T>() {
internal fun internalSet(newValue: Any) = pref.set(newValue as T) internal fun internalSet(newValue: Any) = pref.set(newValue as T)
internal suspend fun internalOnValueChanged(newValue: Any) = onValueChanged(newValue as T) internal suspend fun internalOnValueChanged(newValue: Any) = onValueChanged(newValue as T)
@Composable @Composable
internal fun internalSubtitleProvider(value: Any?, entries: Map<out Any?, String>) = internal fun internalSubtitleProvider(value: Any?, entries: ImmutableMap<out Any?, String>) =
subtitleProvider(value as T, entries as Map<T, String>) subtitleProvider(value as T, entries as ImmutableMap<T, String>)
} }
/** /**
@ -87,13 +89,13 @@ sealed class Preference {
val value: String, val value: String,
override val title: String, override val title: String,
override val subtitle: String? = "%s", override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: String, entries: Map<String, String>) -> String? = val subtitleProvider: @Composable (value: String, entries: ImmutableMap<String, String>) -> String? =
{ v, e -> subtitle?.format(e[v]) }, { v, e -> subtitle?.format(e[v]) },
override val icon: ImageVector? = null, override val icon: ImageVector? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }, override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
val entries: Map<String, String>, val entries: ImmutableMap<String, String>,
) : PreferenceItem<String>() ) : PreferenceItem<String>()
/** /**
@ -104,7 +106,10 @@ sealed class Preference {
val pref: PreferenceData<Set<String>>, val pref: PreferenceData<Set<String>>,
override val title: String, override val title: String,
override val subtitle: String? = "%s", override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: Set<String>, entries: Map<String, String>) -> String? = { v, e -> val subtitleProvider: @Composable (
value: Set<String>,
entries: ImmutableMap<String, String>,
) -> String? = { v, e ->
val combined = remember(v) { val combined = remember(v) {
v.map { e[it] } v.map { e[it] }
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
@ -116,7 +121,7 @@ sealed class Preference {
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (newValue: Set<String>) -> Boolean = { true }, override val onValueChanged: suspend (newValue: Set<String>) -> Boolean = { true },
val entries: Map<String, String>, val entries: ImmutableMap<String, String>,
) : PreferenceItem<Set<String>>() ) : PreferenceItem<Set<String>>()
/** /**
@ -170,6 +175,6 @@ sealed class Preference {
override val title: String, override val title: String,
override val enabled: Boolean = true, override val enabled: Boolean = true,
val preferenceItems: List<PreferenceItem<out Any>>, val preferenceItems: ImmutableList<PreferenceItem<out Any>>,
) : Preference() ) : Preference()
} }

View File

@ -51,6 +51,9 @@ import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.setDefaultSettings
import eu.kanade.tachiyomi.util.system.toast 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 kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import okhttp3.Headers import okhttp3.Headers
@ -149,7 +152,7 @@ object SettingsAdvancedScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_background_activity), title = stringResource(MR.strings.label_background_activity),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_disable_battery_optimization), title = stringResource(MR.strings.pref_disable_battery_optimization),
subtitle = stringResource(MR.strings.pref_disable_battery_optimization_summary), subtitle = stringResource(MR.strings.pref_disable_battery_optimization_summary),
@ -188,7 +191,7 @@ object SettingsAdvancedScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_data), title = stringResource(MR.strings.label_data),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_invalidate_download_cache), title = stringResource(MR.strings.pref_invalidate_download_cache),
subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary), subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary),
@ -218,7 +221,7 @@ object SettingsAdvancedScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_network), title = stringResource(MR.strings.label_network),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_clear_cookies), title = stringResource(MR.strings.pref_clear_cookies),
onClick = { onClick = {
@ -249,7 +252,7 @@ object SettingsAdvancedScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = networkPreferences.dohProvider(), pref = networkPreferences.dohProvider(),
title = stringResource(MR.strings.pref_dns_over_https), title = stringResource(MR.strings.pref_dns_over_https),
entries = mapOf( entries = persistentMapOf(
-1 to stringResource(MR.strings.disabled), -1 to stringResource(MR.strings.disabled),
PREF_DOH_CLOUDFLARE to "Cloudflare", PREF_DOH_CLOUDFLARE to "Cloudflare",
PREF_DOH_GOOGLE to "Google", PREF_DOH_GOOGLE to "Google",
@ -302,7 +305,7 @@ object SettingsAdvancedScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_library), title = stringResource(MR.strings.label_library),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_refresh_library_covers), title = stringResource(MR.strings.pref_refresh_library_covers),
onClick = { MetadataUpdateJob.startNow(context) }, onClick = { MetadataUpdateJob.startNow(context) },
@ -362,12 +365,13 @@ object SettingsAdvancedScreen : SearchableSettings {
} }
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_extensions), title = stringResource(MR.strings.label_extensions),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = extensionInstallerPref, pref = extensionInstallerPref,
title = stringResource(MR.strings.ext_installer_pref), title = stringResource(MR.strings.ext_installer_pref),
entries = extensionInstallerPref.entries entries = extensionInstallerPref.entries
.associateWith { stringResource(it.titleRes) }, .associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
onValueChanged = { onValueChanged = {
if (it == BasePreferences.ExtensionInstaller.SHIZUKU && if (it == BasePreferences.ExtensionInstaller.SHIZUKU &&
!context.isShizukuInstalled !context.isShizukuInstalled

View File

@ -24,6 +24,9 @@ import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.toast 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 org.xmlpull.v1.XmlPullParser
import tachiyomi.core.i18n.stringResource import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@ -66,7 +69,7 @@ object SettingsAppearanceScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_theme), title = stringResource(MR.strings.pref_category_theme),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.CustomPreference( Preference.PreferenceItem.CustomPreference(
title = stringResource(MR.strings.pref_app_theme), title = stringResource(MR.strings.pref_app_theme),
) { ) {
@ -127,7 +130,7 @@ object SettingsAppearanceScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_display), title = stringResource(MR.strings.pref_category_display),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.BasicListPreference( Preference.PreferenceItem.BasicListPreference(
value = currentLanguage, value = currentLanguage,
title = stringResource(MR.strings.pref_app_language), title = stringResource(MR.strings.pref_app_language),
@ -140,7 +143,9 @@ object SettingsAppearanceScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = uiPreferences.tabletUiMode(), pref = uiPreferences.tabletUiMode(),
title = stringResource(MR.strings.pref_tablet_ui_mode), title = stringResource(MR.strings.pref_tablet_ui_mode),
entries = TabletUiMode.entries.associateWith { stringResource(it.titleRes) }, entries = TabletUiMode.entries
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
onValueChanged = { onValueChanged = {
context.toast(MR.strings.requires_app_restart) context.toast(MR.strings.requires_app_restart)
true true
@ -153,7 +158,8 @@ object SettingsAppearanceScreen : SearchableSettings {
.associateWith { .associateWith {
val formattedDate = UiPreferences.dateFormat(it).format(now) val formattedDate = UiPreferences.dateFormat(it).format(now)
"${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)" "${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)"
}, }
.toImmutableMap(),
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = uiPreferences.relativeTime(), pref = uiPreferences.relativeTime(),
@ -167,7 +173,7 @@ object SettingsAppearanceScreen : SearchableSettings {
), ),
) )
} }
private fun getLangs(context: Context): Map<String, String> { private fun getLangs(context: Context): ImmutableMap<String, String> {
val langs = mutableListOf<Pair<String, String>>() val langs = mutableListOf<Pair<String, String>>()
val parser = context.resources.getXml(R.xml.locales_config) val parser = context.resources.getXml(R.xml.locales_config)
var eventType = parser.eventType var eventType = parser.eventType
@ -189,7 +195,7 @@ object SettingsAppearanceScreen : SearchableSettings {
langs.sortBy { it.second } langs.sortBy { it.second }
langs.add(0, Pair("", context.stringResource(MR.strings.label_default))) langs.add(0, Pair("", context.stringResource(MR.strings.label_default)))
return langs.toMap() return langs.toMap().toImmutableMap()
} }
} }

View File

@ -8,6 +8,7 @@ import androidx.fragment.app.FragmentActivity
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.core.i18n.stringResource import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@ -27,7 +28,7 @@ object SettingsBrowseScreen : SearchableSettings {
return listOf( return listOf(
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.label_sources), title = stringResource(MR.strings.label_sources),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.hideInLibraryItems(), pref = sourcePreferences.hideInLibraryItems(),
title = stringResource(MR.strings.pref_hide_in_library_items), title = stringResource(MR.strings.pref_hide_in_library_items),
@ -36,7 +37,7 @@ object SettingsBrowseScreen : SearchableSettings {
), ),
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_nsfw_content), title = stringResource(MR.strings.pref_category_nsfw_content),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.showNsfwSource(), pref = sourcePreferences.showNsfwSource(),
title = stringResource(MR.strings.pref_show_nsfw_source), title = stringResource(MR.strings.pref_show_nsfw_source),

View File

@ -3,12 +3,9 @@ package eu.kanade.presentation.more.settings.screen
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Environment
import android.text.format.Formatter
import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MultiChoiceSegmentedButtonRow 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.Preference
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen 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.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.BasePreferenceWidget
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
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.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.i18n.stringResource import tachiyomi.core.i18n.stringResource
import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.launchNonCancellable
@ -65,7 +64,7 @@ object SettingsDataScreen : SearchableSettings {
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = Injekt.get<BackupPreferences>()
val storagePreferences = Injekt.get<StoragePreferences>() val storagePreferences = Injekt.get<StoragePreferences>()
return listOf( return persistentListOf(
getStorageLocationPref(storagePreferences = storagePreferences), getStorageLocationPref(storagePreferences = storagePreferences),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)),
@ -142,7 +141,7 @@ object SettingsDataScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_backup), title = stringResource(MR.strings.label_backup),
preferenceItems = listOf( preferenceItems = persistentListOf(
// Manual actions // Manual actions
Preference.PreferenceItem.CustomPreference( Preference.PreferenceItem.CustomPreference(
title = stringResource(restorePreferenceKeyString), title = stringResource(restorePreferenceKeyString),
@ -177,7 +176,7 @@ object SettingsDataScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = backupPreferences.backupInterval(), pref = backupPreferences.backupInterval(),
title = stringResource(MR.strings.pref_backup_interval), title = stringResource(MR.strings.pref_backup_interval),
entries = mapOf( entries = persistentMapOf(
0 to stringResource(MR.strings.off), 0 to stringResource(MR.strings.off),
6 to stringResource(MR.strings.update_6hour), 6 to stringResource(MR.strings.update_6hour),
12 to stringResource(MR.strings.update_12hour), 12 to stringResource(MR.strings.update_12hour),
@ -200,8 +199,8 @@ object SettingsDataScreen : SearchableSettings {
@Composable @Composable
private fun getDataGroup(): Preference.PreferenceGroup { private fun getDataGroup(): Preference.PreferenceGroup {
val scope = rememberCoroutineScope()
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope()
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() } val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
val chapterCache = remember { Injekt.get<ChapterCache>() } val chapterCache = remember { Injekt.get<ChapterCache>() }
@ -210,8 +209,19 @@ object SettingsDataScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_data), title = stringResource(MR.strings.label_data),
preferenceItems = listOf( preferenceItems = persistentListOf(
getStorageInfoPref(cacheReadableSize), 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( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_clear_chapter_cache), 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)")
}
},
)
}
}
} }

View File

@ -12,6 +12,9 @@ import androidx.compose.ui.util.fastMap
import eu.kanade.presentation.category.visualName import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.TriStateListDialog 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 kotlinx.coroutines.runBlocking
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
@ -68,7 +71,7 @@ object SettingsDownloadScreen : SearchableSettings {
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_delete_chapters), title = stringResource(MR.strings.pref_category_delete_chapters),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = downloadPreferences.removeAfterMarkedAsRead(), pref = downloadPreferences.removeAfterMarkedAsRead(),
title = stringResource(MR.strings.pref_remove_after_marked_as_read), title = stringResource(MR.strings.pref_remove_after_marked_as_read),
@ -76,7 +79,7 @@ object SettingsDownloadScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = downloadPreferences.removeAfterReadSlots(), pref = downloadPreferences.removeAfterReadSlots(),
title = stringResource(MR.strings.pref_remove_after_read), title = stringResource(MR.strings.pref_remove_after_read),
entries = mapOf( entries = persistentMapOf(
-1 to stringResource(MR.strings.disabled), -1 to stringResource(MR.strings.disabled),
0 to stringResource(MR.strings.last_read_chapter), 0 to stringResource(MR.strings.last_read_chapter),
1 to stringResource(MR.strings.second_to_last), 1 to stringResource(MR.strings.second_to_last),
@ -105,7 +108,9 @@ object SettingsDownloadScreen : SearchableSettings {
return Preference.PreferenceItem.MultiSelectListPreference( return Preference.PreferenceItem.MultiSelectListPreference(
pref = downloadPreferences.removeExcludeCategories(), pref = downloadPreferences.removeExcludeCategories(),
title = stringResource(MR.strings.pref_remove_exclude_categories), 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( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_auto_download), title = stringResource(MR.strings.pref_category_auto_download),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = downloadNewChaptersPref, pref = downloadNewChaptersPref,
title = stringResource(MR.strings.pref_download_new), title = stringResource(MR.strings.pref_download_new),
@ -167,17 +172,19 @@ object SettingsDownloadScreen : SearchableSettings {
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.download_ahead), title = stringResource(MR.strings.download_ahead),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = downloadPreferences.autoDownloadWhileReading(), pref = downloadPreferences.autoDownloadWhileReading(),
title = stringResource(MR.strings.auto_download_while_reading), title = stringResource(MR.strings.auto_download_while_reading),
entries = listOf(0, 2, 3, 5, 10).associateWith { entries = listOf(0, 2, 3, 5, 10)
if (it == 0) { .associateWith {
stringResource(MR.strings.disabled) if (it == 0) {
} else { stringResource(MR.strings.disabled)
pluralStringResource(MR.plurals.next_unread_chapters, count = it, it) } else {
pluralStringResource(MR.plurals.next_unread_chapters, count = it, it)
}
} }
}, .toImmutableMap(),
), ),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.download_ahead_info)), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.download_ahead_info)),
), ),

View File

@ -20,6 +20,9 @@ import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.presentation.more.settings.widget.TriStateListDialog
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.ui.category.CategoryScreen 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.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
@ -79,7 +82,7 @@ object SettingsLibraryScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.categories), title = stringResource(MR.strings.categories),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.action_edit_categories), title = stringResource(MR.strings.action_edit_categories),
subtitle = pluralStringResource( subtitle = pluralStringResource(
@ -93,7 +96,7 @@ object SettingsLibraryScreen : SearchableSettings {
pref = libraryPreferences.defaultCategory(), pref = libraryPreferences.defaultCategory(),
title = stringResource(MR.strings.default_category), title = stringResource(MR.strings.default_category),
subtitle = selectedCategory?.visualName ?: stringResource(MR.strings.default_category_summary), subtitle = selectedCategory?.visualName ?: stringResource(MR.strings.default_category_summary),
entries = ids.zip(labels).toMap(), entries = ids.zip(labels).toMap().toImmutableMap(),
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = libraryPreferences.categorizedDisplaySettings(), pref = libraryPreferences.categorizedDisplaySettings(),
@ -146,11 +149,11 @@ object SettingsLibraryScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_library_update), title = stringResource(MR.strings.pref_category_library_update),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = autoUpdateIntervalPref, pref = autoUpdateIntervalPref,
title = stringResource(MR.strings.pref_library_update_interval), title = stringResource(MR.strings.pref_library_update_interval),
entries = mapOf( entries = persistentMapOf(
0 to stringResource(MR.strings.update_never), 0 to stringResource(MR.strings.update_never),
12 to stringResource(MR.strings.update_12hour), 12 to stringResource(MR.strings.update_12hour),
24 to stringResource(MR.strings.update_24hour), 24 to stringResource(MR.strings.update_24hour),
@ -168,7 +171,7 @@ object SettingsLibraryScreen : SearchableSettings {
enabled = autoUpdateInterval > 0, enabled = autoUpdateInterval > 0,
title = stringResource(MR.strings.pref_library_update_restriction), title = stringResource(MR.strings.pref_library_update_restriction),
subtitle = stringResource(MR.strings.restrictions), subtitle = stringResource(MR.strings.restrictions),
entries = mapOf( entries = persistentMapOf(
DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi), DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi),
DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered), DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered),
DEVICE_CHARGING to stringResource(MR.strings.charging), DEVICE_CHARGING to stringResource(MR.strings.charging),
@ -196,7 +199,7 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.MultiSelectListPreference( Preference.PreferenceItem.MultiSelectListPreference(
pref = libraryPreferences.autoUpdateMangaRestrictions(), pref = libraryPreferences.autoUpdateMangaRestrictions(),
title = stringResource(MR.strings.pref_library_update_manga_restriction), 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_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started), MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),
MANGA_NON_COMPLETED to stringResource(MR.strings.pref_update_only_non_completed), MANGA_NON_COMPLETED to stringResource(MR.strings.pref_update_only_non_completed),
@ -217,11 +220,11 @@ object SettingsLibraryScreen : SearchableSettings {
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_chapter_swipe), title = stringResource(MR.strings.pref_chapter_swipe),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = libraryPreferences.swipeToStartAction(), pref = libraryPreferences.swipeToStartAction(),
title = stringResource(MR.strings.pref_chapter_swipe_start), title = stringResource(MR.strings.pref_chapter_swipe_start),
entries = mapOf( entries = persistentMapOf(
LibraryPreferences.ChapterSwipeAction.Disabled to LibraryPreferences.ChapterSwipeAction.Disabled to
stringResource(MR.strings.disabled), stringResource(MR.strings.disabled),
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to LibraryPreferences.ChapterSwipeAction.ToggleBookmark to
@ -235,7 +238,7 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = libraryPreferences.swipeToEndAction(), pref = libraryPreferences.swipeToEndAction(),
title = stringResource(MR.strings.pref_chapter_swipe_end), title = stringResource(MR.strings.pref_chapter_swipe_end),
entries = mapOf( entries = persistentMapOf(
LibraryPreferences.ChapterSwipeAction.Disabled to LibraryPreferences.ChapterSwipeAction.Disabled to
stringResource(MR.strings.disabled), stringResource(MR.strings.disabled),
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to LibraryPreferences.ChapterSwipeAction.ToggleBookmark to

View File

@ -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.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode 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.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
@ -31,12 +34,13 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPref.defaultReadingMode(), pref = readerPref.defaultReadingMode(),
title = stringResource(MR.strings.pref_viewer_type), title = stringResource(MR.strings.pref_viewer_type),
entries = ReadingMode.entries.drop(1) entries = ReadingMode.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) }, .associate { it.flagValue to stringResource(it.stringRes) }
.toImmutableMap(),
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPref.doubleTapAnimSpeed(), pref = readerPref.doubleTapAnimSpeed(),
title = stringResource(MR.strings.pref_double_tap_anim_speed), title = stringResource(MR.strings.pref_double_tap_anim_speed),
entries = mapOf( entries = persistentMapOf(
1 to stringResource(MR.strings.double_tap_anim_speed_0), 1 to stringResource(MR.strings.double_tap_anim_speed_0),
500 to stringResource(MR.strings.double_tap_anim_speed_normal), 500 to stringResource(MR.strings.double_tap_anim_speed_normal),
250 to stringResource(MR.strings.double_tap_anim_speed_fast), 250 to stringResource(MR.strings.double_tap_anim_speed_fast),
@ -82,17 +86,18 @@ object SettingsReaderScreen : SearchableSettings {
val fullscreen by fullscreenPref.collectAsState() val fullscreen by fullscreenPref.collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_display), title = stringResource(MR.strings.pref_category_display),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.defaultOrientationType(), pref = readerPreferences.defaultOrientationType(),
title = stringResource(MR.strings.pref_rotation_type), title = stringResource(MR.strings.pref_rotation_type),
entries = ReaderOrientation.entries.drop(1) entries = ReaderOrientation.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) }, .associate { it.flagValue to stringResource(it.stringRes) }
.toImmutableMap(),
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.readerTheme(), pref = readerPreferences.readerTheme(),
title = stringResource(MR.strings.pref_reader_theme), title = stringResource(MR.strings.pref_reader_theme),
entries = mapOf( entries = persistentMapOf(
1 to stringResource(MR.strings.black_background), 1 to stringResource(MR.strings.black_background),
2 to stringResource(MR.strings.gray_background), 2 to stringResource(MR.strings.gray_background),
0 to stringResource(MR.strings.white_background), 0 to stringResource(MR.strings.white_background),
@ -126,7 +131,7 @@ object SettingsReaderScreen : SearchableSettings {
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_reading), title = stringResource(MR.strings.pref_category_reading),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.skipRead(), pref = readerPreferences.skipRead(),
title = stringResource(MR.strings.pref_skip_read_chapters), title = stringResource(MR.strings.pref_skip_read_chapters),
@ -161,23 +166,26 @@ object SettingsReaderScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pager_viewer), title = stringResource(MR.strings.pager_viewer),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = navModePref, pref = navModePref,
title = stringResource(MR.strings.pref_viewer_nav), title = stringResource(MR.strings.pref_viewer_nav),
entries = ReaderPreferences.TapZones entries = ReaderPreferences.TapZones
.mapIndexed { index, it -> index to stringResource(it) } .mapIndexed { index, it -> index to stringResource(it) }
.toMap(), .toMap()
.toImmutableMap(),
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.pagerNavInverted(), pref = readerPreferences.pagerNavInverted(),
title = stringResource(MR.strings.pref_read_with_tapping_inverted), title = stringResource(MR.strings.pref_read_with_tapping_inverted),
entries = listOf( entries = persistentListOf(
ReaderPreferences.TappingInvertMode.NONE, ReaderPreferences.TappingInvertMode.NONE,
ReaderPreferences.TappingInvertMode.HORIZONTAL, ReaderPreferences.TappingInvertMode.HORIZONTAL,
ReaderPreferences.TappingInvertMode.VERTICAL, ReaderPreferences.TappingInvertMode.VERTICAL,
ReaderPreferences.TappingInvertMode.BOTH, ReaderPreferences.TappingInvertMode.BOTH,
).associateWith { stringResource(it.titleRes) }, )
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
enabled = navMode != 5, enabled = navMode != 5,
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
@ -185,14 +193,16 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_image_scale_type), title = stringResource(MR.strings.pref_image_scale_type),
entries = ReaderPreferences.ImageScaleType entries = ReaderPreferences.ImageScaleType
.mapIndexed { index, it -> index + 1 to stringResource(it) } .mapIndexed { index, it -> index + 1 to stringResource(it) }
.toMap(), .toMap()
.toImmutableMap(),
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.zoomStart(), pref = readerPreferences.zoomStart(),
title = stringResource(MR.strings.pref_zoom_start), title = stringResource(MR.strings.pref_zoom_start),
entries = ReaderPreferences.ZoomStart entries = ReaderPreferences.ZoomStart
.mapIndexed { index, it -> index + 1 to stringResource(it) } .mapIndexed { index, it -> index + 1 to stringResource(it) }
.toMap(), .toMap()
.toImmutableMap(),
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.cropBorders(), pref = readerPreferences.cropBorders(),
@ -255,23 +265,26 @@ object SettingsReaderScreen : SearchableSettings {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.webtoon_viewer), title = stringResource(MR.strings.webtoon_viewer),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = navModePref, pref = navModePref,
title = stringResource(MR.strings.pref_viewer_nav), title = stringResource(MR.strings.pref_viewer_nav),
entries = ReaderPreferences.TapZones entries = ReaderPreferences.TapZones
.mapIndexed { index, it -> index to stringResource(it) } .mapIndexed { index, it -> index to stringResource(it) }
.toMap(), .toMap()
.toImmutableMap(),
), ),
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.webtoonNavInverted(), pref = readerPreferences.webtoonNavInverted(),
title = stringResource(MR.strings.pref_read_with_tapping_inverted), title = stringResource(MR.strings.pref_read_with_tapping_inverted),
entries = listOf( entries = persistentListOf(
ReaderPreferences.TappingInvertMode.NONE, ReaderPreferences.TappingInvertMode.NONE,
ReaderPreferences.TappingInvertMode.HORIZONTAL, ReaderPreferences.TappingInvertMode.HORIZONTAL,
ReaderPreferences.TappingInvertMode.VERTICAL, ReaderPreferences.TappingInvertMode.VERTICAL,
ReaderPreferences.TappingInvertMode.BOTH, ReaderPreferences.TappingInvertMode.BOTH,
).associateWith { stringResource(it.titleRes) }, )
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
enabled = navMode != 5, enabled = navMode != 5,
), ),
Preference.PreferenceItem.SliderPreference( Preference.PreferenceItem.SliderPreference(
@ -288,7 +301,7 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.readerHideThreshold(), pref = readerPreferences.readerHideThreshold(),
title = stringResource(MR.strings.pref_hide_threshold), title = stringResource(MR.strings.pref_hide_threshold),
entries = mapOf( entries = persistentMapOf(
ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest), ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest),
ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high), ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high),
ReaderPreferences.ReaderHideThreshold.LOW to stringResource(MR.strings.pref_low), ReaderPreferences.ReaderHideThreshold.LOW to stringResource(MR.strings.pref_low),
@ -341,7 +354,7 @@ object SettingsReaderScreen : SearchableSettings {
val readWithVolumeKeys by readWithVolumeKeysPref.collectAsState() val readWithVolumeKeys by readWithVolumeKeysPref.collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_reader_navigation), title = stringResource(MR.strings.pref_reader_navigation),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readWithVolumeKeysPref, pref = readWithVolumeKeysPref,
title = stringResource(MR.strings.pref_read_with_volume_keys), title = stringResource(MR.strings.pref_read_with_volume_keys),
@ -359,7 +372,7 @@ object SettingsReaderScreen : SearchableSettings {
private fun getActionsGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { private fun getActionsGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_reader_actions), title = stringResource(MR.strings.pref_reader_actions),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.readWithLongTap(), pref = readerPreferences.readWithLongTap(),
title = stringResource(MR.strings.pref_read_with_long_tap), title = stringResource(MR.strings.pref_read_with_long_tap),

View File

@ -10,6 +10,8 @@ import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported 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.core.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
@ -55,7 +57,8 @@ object SettingsSecurityScreen : SearchableSettings {
0 -> stringResource(MR.strings.lock_always) 0 -> stringResource(MR.strings.lock_always)
else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it) else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it)
} }
}, }
.toImmutableMap(),
onValueChanged = { onValueChanged = {
(context as FragmentActivity).authenticate( (context as FragmentActivity).authenticate(
title = context.stringResource(MR.strings.lock_when_idle), title = context.stringResource(MR.strings.lock_when_idle),
@ -70,14 +73,15 @@ object SettingsSecurityScreen : SearchableSettings {
pref = securityPreferences.secureScreen(), pref = securityPreferences.secureScreen(),
title = stringResource(MR.strings.secure_screen), title = stringResource(MR.strings.secure_screen),
entries = SecurityPreferences.SecureScreenMode.entries entries = SecurityPreferences.SecureScreenMode.entries
.associateWith { stringResource(it.titleRes) }, .associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
), ),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.secure_screen_summary)), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.secure_screen_summary)),
) )
} }
} }
private val LockAfterValues = listOf( private val LockAfterValues = persistentListOf(
0, // Always 0, // Always
1, 1,
2, 2,

View File

@ -51,6 +51,8 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast 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.launchIO
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
@ -125,7 +127,7 @@ object SettingsTrackingScreen : SearchableSettings {
), ),
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.services), title = stringResource(MR.strings.services),
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TrackerPreference( Preference.PreferenceItem.TrackerPreference(
title = trackerManager.myAnimeList.name, title = trackerManager.myAnimeList.name,
tracker = trackerManager.myAnimeList, tracker = trackerManager.myAnimeList,
@ -167,15 +169,17 @@ object SettingsTrackingScreen : SearchableSettings {
), ),
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.enhanced_services), title = stringResource(MR.strings.enhanced_services),
preferenceItems = enhancedTrackers.first preferenceItems = (
.map { service -> enhancedTrackers.first
Preference.PreferenceItem.TrackerPreference( .map { service ->
title = service.name, Preference.PreferenceItem.TrackerPreference(
tracker = service, title = service.name,
login = { (service as EnhancedTracker).loginNoop() }, tracker = service,
logout = service::logout, login = { (service as EnhancedTracker).loginNoop() },
) logout = service::logout,
} + listOf(Preference.PreferenceItem.InfoPreference(enhancedTrackerInfo)), )
} + listOf(Preference.PreferenceItem.InfoPreference(enhancedTrackerInfo))
).toImmutableList(),
), ),
) )
} }

View File

@ -36,6 +36,7 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.PersistentSet import kotlinx.collections.immutable.PersistentSet
import kotlinx.collections.immutable.minus import kotlinx.collections.immutable.minus
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.plus import kotlinx.collections.immutable.plus
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@ -170,7 +171,7 @@ private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel
) )
} }
private val BackupChoices = mapOf( private val BackupChoices = persistentMapOf(
BackupCreateFlags.BACKUP_CATEGORY to MR.strings.categories, BackupCreateFlags.BACKUP_CATEGORY to MR.strings.categories,
BackupCreateFlags.BACKUP_CHAPTER to MR.strings.chapters, BackupCreateFlags.BACKUP_CHAPTER to MR.strings.chapters,
BackupCreateFlags.BACKUP_TRACK to MR.strings.track, BackupCreateFlags.BACKUP_TRACK to MR.strings.track,

View File

@ -0,0 +1,117 @@
package eu.kanade.presentation.more.settings.screen.data
import android.text.format.Formatter
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.util.storage.DiskUtil
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import java.io.File
@Composable
fun StorageInfo(
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val storages = remember { DiskUtil.getExternalStorages(context) }
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
storages.forEach {
StorageInfo(it)
}
}
}
@Composable
private fun StorageInfo(
file: File,
) {
val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
val available = remember(file) { DiskUtil.getAvailableStorageSpace(file) }
val availableText = remember(available) { Formatter.formatFileSize(context, available) }
val total = remember(file) { DiskUtil.getTotalStorageSpace(file) }
val totalText = remember(total) { Formatter.formatFileSize(context, total) }
val cornerRadius = CornerRadius(100f, 100f)
val usedBarColor = MaterialTheme.colorScheme.primary
val totalBarColor = MaterialTheme.colorScheme.surfaceVariant
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = file.absolutePath,
fontWeight = FontWeight.Medium,
)
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(12.dp),
) {
drawRoundRect(
color = totalBarColor,
cornerRadius = cornerRadius,
)
drawPath(
path = Path().apply {
val pathSize = Size(
width = (1 - (available / total.toFloat())) * size.width,
height = size.height,
)
addRoundRect(
if (layoutDirection == LayoutDirection.Ltr) {
RoundRect(
rect = Rect(
offset = Offset(0f, 0f),
size = pathSize,
),
topLeft = cornerRadius,
bottomLeft = cornerRadius,
)
} else {
RoundRect(
rect = Rect(
offset = Offset(size.width - pathSize.width, 0f),
size = pathSize,
),
topRight = cornerRadius,
bottomRight = cornerRadius,
)
},
)
},
color = usedBarColor,
)
}
Text(
text = stringResource(MR.strings.available_disk_space_info, availableText, totalText),
)
}
}

View File

@ -15,6 +15,8 @@ import eu.kanade.presentation.more.settings.screen.about.AboutScreen
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.WebViewUtil
import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.guava.await import kotlinx.coroutines.guava.await
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@ -47,7 +49,7 @@ class DebugInfoScreen : Screen() {
private fun getAppInfoGroup(): Preference.PreferenceGroup { private fun getAppInfoGroup(): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = "App info", title = "App info",
preferenceItems = listOf( preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = "Version", title = "Version",
subtitle = AboutScreen.getVersionName(false), subtitle = AboutScreen.getVersionName(false),
@ -96,8 +98,8 @@ class DebugInfoScreen : Screen() {
} }
private fun getDeviceInfoGroup(): Preference.PreferenceGroup { private fun getDeviceInfoGroup(): Preference.PreferenceGroup {
val items = buildList { val items = persistentListOf<Preference.PreferenceItem<out Any>>().mutate {
add( it.add(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = "Model", title = "Model",
subtitle = "${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})", subtitle = "${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})",
@ -105,14 +107,14 @@ class DebugInfoScreen : Screen() {
) )
if (DeviceUtil.oneUiVersion != null) { if (DeviceUtil.oneUiVersion != null) {
add( it.add(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = "OneUI version", title = "OneUI version",
subtitle = "${DeviceUtil.oneUiVersion}", subtitle = "${DeviceUtil.oneUiVersion}",
), ),
) )
} else if (DeviceUtil.miuiMajorVersion != null) { } else if (DeviceUtil.miuiMajorVersion != null) {
add( it.add(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = "MIUI version", title = "MIUI version",
subtitle = "${DeviceUtil.miuiMajorVersion}", subtitle = "${DeviceUtil.miuiMajorVersion}",
@ -127,7 +129,7 @@ class DebugInfoScreen : Screen() {
} else { } else {
Build.VERSION.RELEASE Build.VERSION.RELEASE
} }
add( it.add(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = "Android version", title = "Android version",
subtitle = "$androidVersion (${Build.DISPLAY})", subtitle = "$androidVersion (${Build.DISPLAY})",

View File

@ -37,6 +37,7 @@ import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.database.models.toDomainChapter
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import kotlinx.collections.immutable.persistentMapOf
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.service.calculateChapterGap import tachiyomi.domain.chapter.service.calculateChapterGap
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@ -234,7 +235,7 @@ private fun ChapterText(
maxLines = 5, maxLines = 5,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
inlineContent = mapOf( inlineContent = persistentMapOf(
DownloadedIconContentId to InlineTextContent( DownloadedIconContentId to InlineTextContent(
Placeholder( Placeholder(
width = 22.sp, width = 22.sp,

View File

@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.presentation.theme.TachiyomiTheme
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
@ -233,7 +234,7 @@ private fun TrackStatusSelectorPreviews() {
TrackStatusSelector( TrackStatusSelector(
selection = 1, selection = 1,
onSelectionChange = {}, onSelectionChange = {},
selections = mapOf( selections = persistentMapOf(
// Anilist values // Anilist values
1 to MR.strings.reading, 1 to MR.strings.reading,
2 to MR.strings.plan_to_read, 2 to MR.strings.plan_to_read,

View File

@ -14,7 +14,6 @@ import okio.buffer
import okio.sink import okio.sink
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -26,9 +25,10 @@ import java.io.IOException
* *
* @param context the application context. * @param context the application context.
*/ */
class ChapterCache(private val context: Context) { class ChapterCache(
private val context: Context,
private val json: Json by injectLazy() private val json: Json,
) {
/** Cache class used for cache management. */ /** Cache class used for cache management. */
private val diskCache = DiskLruCache.open( private val diskCache = DiskLruCache.open(

View File

@ -111,7 +111,7 @@ class AppModule(val app: Application) : InjektModule {
ProtoBuf ProtoBuf
} }
addSingletonFactory { ChapterCache(app) } addSingletonFactory { ChapterCache(app, get()) }
addSingletonFactory { CoverCache(app) } addSingletonFactory { CoverCache(app) }
addSingletonFactory { NetworkHelper(app, get()) } addSingletonFactory { NetworkHelper(app, get()) }

View File

@ -3,13 +3,32 @@ package eu.kanade.tachiyomi.util.storage
import android.content.Context import android.content.Context
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.net.Uri import android.net.Uri
import android.os.Environment
import android.os.StatFs import android.os.StatFs
import androidx.core.content.ContextCompat
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.util.lang.Hash import eu.kanade.tachiyomi.util.lang.Hash
import java.io.File import java.io.File
object DiskUtil { object DiskUtil {
/**
* Returns the root folders of all the available external storages.
*/
fun getExternalStorages(context: Context): List<File> {
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 { fun hashKeyForDisk(key: String): String {
return Hash.md5(key) return Hash.md5(key)
} }

View File

@ -519,6 +519,7 @@
<string name="last_auto_backup_info">Last automatically backed up: %s</string> <string name="last_auto_backup_info">Last automatically backed up: %s</string>
<string name="label_data">Data</string> <string name="label_data">Data</string>
<string name="pref_storage_usage">Storage usage</string> <string name="pref_storage_usage">Storage usage</string>
<string name="available_disk_space_info">Available: %1$s / Total: %2$s</string>
<string name="pref_clear_chapter_cache">Clear chapter cache</string> <string name="pref_clear_chapter_cache">Clear chapter cache</string>
<string name="used_cache">Used: %1$s</string> <string name="used_cache">Used: %1$s</string>
<string name="cache_deleted">Cache cleared, %1$d files deleted</string> <string name="cache_deleted">Cache cleared, %1$d files deleted</string>

View File

@ -21,6 +21,7 @@ import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.collections.immutable.persistentMapOf
@Composable @Composable
fun BadgeGroup( fun BadgeGroup(
@ -66,7 +67,7 @@ fun Badge(
val text = buildAnnotatedString { val text = buildAnnotatedString {
appendInlineContent(iconContentPlaceholder) appendInlineContent(iconContentPlaceholder)
} }
val inlineContent = mapOf( val inlineContent = persistentMapOf(
Pair( Pair(
iconContentPlaceholder, iconContentPlaceholder,
InlineTextContent( InlineTextContent(