refactor: improve preference bounds #115

Closed
septs wants to merge 4 commits from septs:preference-bounds into master
2 changed files with 35 additions and 45 deletions

View file

@ -6,7 +6,6 @@ import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.datastore.preferences.core.Preferences
import androidx.lifecycle.lifecycleScope
import androidx.preference.CheckBoxPreference
import androidx.preference.Preference
@ -14,7 +13,6 @@ import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import im.angry.openeuicc.common.R
import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@ -60,25 +58,25 @@ open class SettingsFragment: PreferenceFragmentCompat() {
}
findPreference<CheckBoxPreference>("pref_notifications_download")
?.bindBooleanFlow(preferenceRepository.notificationDownloadFlow, PreferenceKeys.NOTIFICATION_DOWNLOAD)
?.bindBooleanFlow(preferenceRepository.notificationDownloadFlow)
findPreference<CheckBoxPreference>("pref_notifications_delete")
?.bindBooleanFlow(preferenceRepository.notificationDeleteFlow, PreferenceKeys.NOTIFICATION_DELETE)
?.bindBooleanFlow(preferenceRepository.notificationDeleteFlow)
findPreference<CheckBoxPreference>("pref_notifications_switch")
?.bindBooleanFlow(preferenceRepository.notificationSwitchFlow, PreferenceKeys.NOTIFICATION_SWITCH)
?.bindBooleanFlow(preferenceRepository.notificationSwitchFlow)
findPreference<CheckBoxPreference>("pref_advanced_disable_safeguard_removable_esim")
?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow, PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM)
?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow)
findPreference<CheckBoxPreference>("pref_advanced_verbose_logging")
?.bindBooleanFlow(preferenceRepository.verboseLoggingFlow, PreferenceKeys.VERBOSE_LOGGING)
?.bindBooleanFlow(preferenceRepository.verboseLoggingFlow)
findPreference<CheckBoxPreference>("pref_developer_unfiltered_profile_list")
?.bindBooleanFlow(preferenceRepository.unfilteredProfileListFlow, PreferenceKeys.UNFILTERED_PROFILE_LIST)
?.bindBooleanFlow(preferenceRepository.unfilteredProfileListFlow)
findPreference<CheckBoxPreference>("pref_ignore_tls_certificate")
?.bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow, PreferenceKeys.IGNORE_TLS_CERTIFICATE)
?.bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow)
}
override fun onStart() {
@ -99,10 +97,7 @@ open class SettingsFragment: PreferenceFragmentCompat() {
if (numClicks == 7) {
lifecycleScope.launch {
preferenceRepository.updatePreference(
PreferenceKeys.DEVELOPER_OPTIONS_ENABLED,
true
)
preferenceRepository.developerOptionsEnabledFlow.emit(true)
lastToast?.cancel()
Toast.makeText(
@ -124,15 +119,13 @@ open class SettingsFragment: PreferenceFragmentCompat() {
return true
}
private fun CheckBoxPreference.bindBooleanFlow(flow: Flow<Boolean>, key: Preferences.Key<Boolean>) {
private fun CheckBoxPreference.bindBooleanFlow(flow: BoundPreference<Boolean>) {
lifecycleScope.launch {
flow.collect { isChecked = it }
}
setOnPreferenceChangeListener { _, newValue ->
runBlocking {
preferenceRepository.updatePreference(key, newValue as Boolean)
}
runBlocking { flow.emit(newValue as Boolean) }
true
}
}

View file

@ -9,6 +9,7 @@ import androidx.datastore.preferences.preferencesDataStore
import androidx.fragment.app.Fragment
import im.angry.openeuicc.OpenEuiccApplication
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.map
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "prefs")
@ -19,41 +20,37 @@ val Context.preferenceRepository: PreferenceRepository
val Fragment.preferenceRepository: PreferenceRepository
get() = requireContext().preferenceRepository
object PreferenceKeys {
// ---- Profile Notifications ----
val NOTIFICATION_DOWNLOAD = booleanPreferencesKey("notification_download")
val NOTIFICATION_DELETE = booleanPreferencesKey("notification_delete")
val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch")
// ---- Advanced ----
val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim")
val VERBOSE_LOGGING = booleanPreferencesKey("verbose_logging")
// ---- Developer Options ----
val DEVELOPER_OPTIONS_ENABLED = booleanPreferencesKey("developer_options_enabled")
val UNFILTERED_PROFILE_LIST = booleanPreferencesKey("unfiltered_profile_list")
val IGNORE_TLS_CERTIFICATE = booleanPreferencesKey("ignore_tls_certificate")
}
class PreferenceRepository(private val context: Context) {
// Expose flows so that we can also handle default values
// ---- Profile Notifications ----
val notificationDownloadFlow = bindFlow(PreferenceKeys.NOTIFICATION_DOWNLOAD, true)
val notificationDeleteFlow = bindFlow(PreferenceKeys.NOTIFICATION_DELETE, true)
val notificationSwitchFlow = bindFlow(PreferenceKeys.NOTIFICATION_SWITCH, false)
val notificationDownloadFlow = bindFlow("notification_download", true)
val notificationDeleteFlow = bindFlow("notification_delete", true)
val notificationSwitchFlow = bindFlow("notification_switch", false)
// ---- Advanced ----
val disableSafeguardFlow = bindFlow(PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM, false)
val verboseLoggingFlow = bindFlow(PreferenceKeys.VERBOSE_LOGGING, false)
val disableSafeguardFlow = bindFlow("disable_safeguard_removable_esim", false)
val verboseLoggingFlow = bindFlow("verbose_logging", false)
// ---- Developer Options ----
val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false)
val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false)
val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false)
val developerOptionsEnabledFlow = bindFlow("developer_options_enabled", false)
val unfilteredProfileListFlow = bindFlow("unfiltered_profile_list", false)
val ignoreTLSCertificateFlow = bindFlow("ignore_tls_certificate", false)
private fun <T> bindFlow(key: Preferences.Key<T>, defaultValue: T): Flow<T> =
context.dataStore.data.map { it[key] ?: defaultValue }
private fun bindFlow(name: String, defaultValue: Boolean) =
bindFlow(booleanPreferencesKey(name), defaultValue)
suspend fun <T> updatePreference(key: Preferences.Key<T>, value: T) =
context.dataStore.edit { it[key] = value }
private fun <T> bindFlow(key: Preferences.Key<T>, defaultValue: T) =
BoundPreference(context.dataStore, key, defaultValue)
}
class BoundPreference<T>(
private val store: DataStore<Preferences>,
private val key: Preferences.Key<T>,
defaultValue: T
) : Flow<T> {
private val flow = store.data.map { it[key] ?: defaultValue }
suspend fun emit(value: T) = store.edit { it[key] = value }
override suspend fun collect(collector: FlowCollector<T>) = flow.collect(collector)
}