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

(cherry picked from commit 3e86cb094b90a29d5e4ddf7a4c72ccf96c66fee3)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt
This commit is contained in:
Ivan Iskandar 2022-10-29 20:44:12 +07:00 committed by Jobobby04
parent 2fe7fe1a3f
commit d53ebe5314
9 changed files with 44 additions and 60 deletions

View File

@ -92,7 +92,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 ->
@ -108,7 +108,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) } },

View File

@ -1,8 +1,12 @@
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 androidx.compose.ui.text.AnnotatedString
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
@ -48,6 +52,8 @@ sealed class Preference {
val pref: PreferenceData<T>,
override val title: String,
override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: T, entries: Map<T, String>) -> 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 },
@ -56,6 +62,10 @@ sealed class Preference {
) : PreferenceItem<T>() {
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<out Any?, String>) =
subtitleProvider(value as T, entries as Map<T, String>)
}
/**
@ -65,6 +75,8 @@ sealed class Preference {
val value: String,
override val title: String,
override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: String, entries: Map<String, String>) -> 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 },
@ -79,7 +91,15 @@ sealed class Preference {
data class MultiSelectListPreference(
val pref: PreferenceData<Set<String>>,
override val title: String,
override val subtitle: String? = null,
override val subtitle: String? = "%s",
val subtitleProvider: @Composable (value: Set<String>, entries: Map<String, String>) -> 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<String>) -> Boolean = { true },

View File

@ -75,7 +75,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),
@ -132,7 +131,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),
@ -142,7 +140,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)

View File

@ -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<Category>,
): 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 },
)
}

View File

@ -78,7 +78,6 @@ class SettingsGeneralScreen : SearchableSettings {
Preference.PreferenceItem.BasicListPreference(
value = currentLanguage,
title = stringResource(R.string.pref_app_language),
subtitle = "%s",
entries = langs,
onValueChanged = { newValue ->
currentLanguage = newValue

View File

@ -187,28 +187,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) }
@ -234,7 +212,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),
@ -252,8 +229,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) }
@ -263,8 +245,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),

View File

@ -71,7 +71,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 {
@ -94,7 +93,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) },
),

View File

@ -39,7 +39,7 @@ fun <T> ListPreferenceWidget(
TextPreferenceWidget(
title = title,
subtitle = subtitle?.format(entries[value]),
subtitle = subtitle,
icon = icon,
onPreferenceClick = { showDialog(true) },
)

View File

@ -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) },
)