From 3e86cb094b90a29d5e4ddf7a4c72ccf96c66fee3 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 29 Oct 2022 20:44:12 +0700 Subject: [PATCH] PreferenceModel: Add subtitle provider to ListPreference (#8322) * PreferenceModel: Add subtitle provider to ListPreference So that it's possible to avoid value formatting when needed * cleanups --- .../more/settings/PreferenceItem.kt | 4 +- .../more/settings/PreferenceModel.kt | 22 ++++++++++- .../screen/SettingsAppearanceScreen.kt | 3 -- .../settings/screen/SettingsDownloadScreen.kt | 29 ++++---------- .../settings/screen/SettingsGeneralScreen.kt | 1 - .../settings/screen/SettingsLibraryScreen.kt | 39 ++++++------------- .../settings/screen/SettingsSecurityScreen.kt | 2 - .../settings/widget/ListPreferenceWidget.kt | 2 +- .../widget/MultiSelectListPreferenceWidget.kt | 2 +- 9 files changed, 44 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt index acfc83f33..75ef269ee 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt @@ -82,7 +82,7 @@ internal fun PreferenceItem( ListPreferenceWidget( value = value, title = item.title, - subtitle = item.subtitle, + subtitle = item.internalSubtitleProvider(value, item.entries), icon = item.icon, entries = item.entries, onValueChange = { newValue -> @@ -98,7 +98,7 @@ internal fun PreferenceItem( ListPreferenceWidget( value = item.value, title = item.title, - subtitle = item.subtitle, + subtitle = item.subtitleProvider(item.value, item.entries), icon = item.icon, entries = item.entries, onValueChange = { scope.launch { item.onValueChanged(it) } }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt index 89c88c7f7..ce0238407 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt @@ -1,7 +1,11 @@ package eu.kanade.presentation.more.settings +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource import eu.kanade.domain.ui.model.AppTheme +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.core.preference.Preference as PreferenceData @@ -47,6 +51,8 @@ sealed class Preference { val pref: PreferenceData, override val title: String, override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: T, entries: Map) -> 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 }, @@ -55,6 +61,10 @@ sealed class Preference { ) : 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) } /** @@ -64,6 +74,8 @@ sealed class Preference { val value: String, override val title: String, override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: String, entries: Map) -> 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 }, @@ -78,7 +90,15 @@ sealed class Preference { data class MultiSelectListPreference( val pref: PreferenceData>, override val title: String, - override val subtitle: String? = null, + override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: Set, entries: Map) -> String? = { v, e -> + val combined = remember(v) { + v.map { e[it] } + .takeIf { it.isNotEmpty() } + ?.joinToString() + } ?: stringResource(id = R.string.none) + subtitle?.format(combined) + }, override val icon: ImageVector? = null, override val enabled: Boolean = true, override val onValueChanged: suspend (newValue: Set) -> Boolean = { true }, 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 e423712ce..fa384feeb 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 @@ -72,7 +72,6 @@ class SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = themeModePref, title = stringResource(R.string.pref_theme_mode), - subtitle = "%s", entries = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mapOf( ThemeMode.SYSTEM to stringResource(R.string.theme_system), @@ -129,7 +128,6 @@ class SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = uiPreferences.relativeTime(), title = stringResource(R.string.pref_relative_format), - subtitle = "%s", entries = mapOf( 0 to stringResource(R.string.off), 2 to stringResource(R.string.pref_relative_time_short), @@ -139,7 +137,6 @@ class SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = uiPreferences.dateFormat(), title = stringResource(R.string.pref_date_format), - subtitle = "%s", entries = DateFormats .associateWith { val formattedDate = UiPreferences.dateFormat(it).format(now) 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 e8a1e313f..f606674be 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 @@ -10,7 +10,6 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -27,7 +26,6 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.R -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.runBlocking import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -101,9 +99,11 @@ class SettingsDownloadScreen : SearchableSettings { return Preference.PreferenceItem.ListPreference( pref = currentDirPref, title = stringResource(R.string.pref_download_directory), - subtitle = remember(currentDir) { - UniFile.fromUri(context, currentDir.toUri())?.filePath - } ?: stringResource(R.string.invalid_location, currentDir), + subtitleProvider = { value, _ -> + remember(value) { + UniFile.fromUri(context, value.toUri())?.filePath + } ?: stringResource(R.string.invalid_location, value) + }, entries = mapOf( defaultDirPair, customDirEntryKey to stringResource(R.string.custom_dir), @@ -173,25 +173,10 @@ class SettingsDownloadScreen : SearchableSettings { downloadPreferences: DownloadPreferences, categories: () -> List, ): Preference.PreferenceItem.MultiSelectListPreference { - val none = stringResource(R.string.none) - val pref = downloadPreferences.removeExcludeCategories() - val entries = categories().associate { it.id.toString() to it.visualName } - val subtitle by produceState(initialValue = "") { - pref.changes() - .stateIn(this) - .collect { mutable -> - value = mutable - .mapNotNull { id -> entries[id] } - .sortedBy { entries.values.indexOf(it) } - .joinToString() - .ifEmpty { none } - } - } return Preference.PreferenceItem.MultiSelectListPreference( - pref = pref, + pref = downloadPreferences.removeExcludeCategories(), title = stringResource(R.string.pref_remove_exclude_categories), - subtitle = subtitle, - entries = entries, + entries = categories().associate { it.id.toString() to it.visualName }, ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt index 906b8f4e1..62fc12a88 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt @@ -72,7 +72,6 @@ class SettingsGeneralScreen : SearchableSettings { Preference.PreferenceItem.BasicListPreference( value = currentLanguage, title = stringResource(R.string.pref_app_language), - subtitle = "%s", entries = langs, onValueChanged = { newValue -> currentLanguage = newValue 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 5230d141f..5ff503ae7 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 @@ -177,28 +177,6 @@ class SettingsLibraryScreen : SearchableSettings { val libraryUpdateInterval by libraryUpdateIntervalPref.collectAsState() - val deviceRestrictionEntries = mapOf( - DEVICE_ONLY_ON_WIFI to stringResource(R.string.connected_to_wifi), - DEVICE_NETWORK_NOT_METERED to stringResource(R.string.network_not_metered), - DEVICE_CHARGING to stringResource(R.string.charging), - DEVICE_BATTERY_NOT_LOW to stringResource(R.string.battery_not_low), - ) - val deviceRestrictions = libraryUpdateDeviceRestrictionPref.collectAsState() - .value - .sorted() - .map { deviceRestrictionEntries.getOrElse(it) { it } } - .let { if (it.isEmpty()) stringResource(R.string.none) else it.joinToString() } - - val mangaRestrictionEntries = mapOf( - MANGA_HAS_UNREAD to stringResource(R.string.pref_update_only_completely_read), - MANGA_NON_READ to stringResource(R.string.pref_update_only_started), - MANGA_NON_COMPLETED to stringResource(R.string.pref_update_only_non_completed), - ) - val mangaRestrictions = libraryUpdateMangaRestrictionPref.collectAsState() - .value - .map { mangaRestrictionEntries.getOrElse(it) { it } } - .let { if (it.isEmpty()) stringResource(R.string.none) else it.joinToString() } - val included by libraryUpdateCategoriesPref.collectAsState() val excluded by libraryUpdateCategoriesExcludePref.collectAsState() var showDialog by rememberSaveable { mutableStateOf(false) } @@ -224,7 +202,6 @@ class SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = libraryUpdateIntervalPref, title = stringResource(R.string.pref_library_update_interval), - subtitle = "%s", entries = mapOf( 0 to stringResource(R.string.update_never), 12 to stringResource(R.string.update_12hour), @@ -242,8 +219,13 @@ class SettingsLibraryScreen : SearchableSettings { pref = libraryUpdateDeviceRestrictionPref, enabled = libraryUpdateInterval > 0, title = stringResource(R.string.pref_library_update_restriction), - subtitle = stringResource(R.string.restrictions, deviceRestrictions), - entries = deviceRestrictionEntries, + subtitle = stringResource(R.string.restrictions), + entries = mapOf( + DEVICE_ONLY_ON_WIFI to stringResource(R.string.connected_to_wifi), + DEVICE_NETWORK_NOT_METERED to stringResource(R.string.network_not_metered), + DEVICE_CHARGING to stringResource(R.string.charging), + DEVICE_BATTERY_NOT_LOW to stringResource(R.string.battery_not_low), + ), onValueChanged = { // Post to event looper to allow the preference to be updated. ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) } @@ -253,8 +235,11 @@ class SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.MultiSelectListPreference( pref = libraryUpdateMangaRestrictionPref, title = stringResource(R.string.pref_library_update_manga_restriction), - subtitle = mangaRestrictions, - entries = mangaRestrictionEntries, + entries = mapOf( + MANGA_HAS_UNREAD to stringResource(R.string.pref_update_only_completely_read), + MANGA_NON_READ to stringResource(R.string.pref_update_only_started), + MANGA_NON_COMPLETED to stringResource(R.string.pref_update_only_non_completed), + ), ), Preference.PreferenceItem.TextPreference( title = stringResource(R.string.categories), 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 0ac7d20a9..1b7fa736c 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 @@ -49,7 +49,6 @@ class SettingsSecurityScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = securityPreferences.lockAppAfter(), title = stringResource(R.string.lock_when_idle), - subtitle = "%s", enabled = authSupported && useAuth, entries = LockAfterValues .associateWith { @@ -72,7 +71,6 @@ class SettingsSecurityScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = securityPreferences.secureScreen(), title = stringResource(R.string.secure_screen), - subtitle = "%s", entries = SecurityPreferences.SecureScreenMode.values() .associateWith { stringResource(it.titleResId) }, ), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt index 0ff1755b5..8c13cfb20 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt @@ -39,7 +39,7 @@ fun ListPreferenceWidget( TextPreferenceWidget( title = title, - subtitle = subtitle?.format(entries[value]), + subtitle = subtitle, icon = icon, onPreferenceClick = { showDialog(true) }, ) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt index 83a91468d..6aab4fd7c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt @@ -33,7 +33,7 @@ fun MultiSelectListPreferenceWidget( TextPreferenceWidget( title = preference.title, - subtitle = preference.subtitle, + subtitle = preference.subtitleProvider(values, preference.entries), icon = preference.icon, onPreferenceClick = { showDialog(true) }, )