From b4d10c9613e373d3488da862e85406f52787ce15 Mon Sep 17 00:00:00 2001 From: UlrichKu Date: Mon, 27 Feb 2023 14:07:28 +0100 Subject: [PATCH] 3204: Add an account based preference store (#3205) * 3204: Add an account based preference store * 3204: (related) reformat a bit, add todo * 3204: Use the preference data store for all three account settings * 3204: Move event handling to account settings handler * 3204: Correct includes * 3204: Appease linter * 3204: Appease linter again * 3204: Add an account based preference store * 3204: Use the preference data store for all three account settings * 3204: Move event handling to account settings handler * 3204: Correct includes * 3204: Add general "preference upgrade loop stepper"; use it for removing obsolete account settings (in shared) * 3204: Add missing spaces * 3204: Key is non-nullable * 3204: Upgrade to new settings migration code * 3204: Remove (commented) DI code --- .../keylesspalace/tusky/TuskyApplication.kt | 13 +++-- .../preference/AccountPreferencesFragment.kt | 56 ++++++------------- .../settings/AccountPreferenceHandler.kt | 35 ++++++++++++ .../tusky/settings/SettingsConstants.kt | 2 +- 4 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt index c755b217..ef5b8cab 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt @@ -69,9 +69,8 @@ class TuskyApplication : Application(), HasAndroidInjector { AppInjector.init(this) - // Migrate shared preference keys and defaults from version to version. The last - // version that did not have a SCHEMA_VERSION was 100, so that's the default. - val oldVersion = sharedPreferences.getInt(PrefKeys.SCHEMA_VERSION, 100) + // Migrate shared preference keys and defaults from version to version. + val oldVersion = sharedPreferences.getInt(PrefKeys.SCHEMA_VERSION, 0) if (oldVersion != SCHEMA_VERSION) { upgradeSharedPreferences(oldVersion, SCHEMA_VERSION) } @@ -105,7 +104,13 @@ class TuskyApplication : Application(), HasAndroidInjector { Log.d(TAG, "Upgrading shared preferences: $oldVersion -> $newVersion") val editor = sharedPreferences.edit() - // Future upgrade code goes here + if (oldVersion < 2023022701) { + // These preferences are (now) handled in AccountPreferenceHandler. Remove them from shared for clarity. + + editor.remove(PrefKeys.ALWAYS_OPEN_SPOILER) + editor.remove(PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA) + editor.remove(PrefKeys.MEDIA_PREVIEW_ENABLED) + } editor.putInt(PrefKeys.SCHEMA_VERSION, newVersion) editor.apply() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt index 0cbdacba..a863db48 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt @@ -36,13 +36,13 @@ import com.keylesspalace.tusky.components.followedtags.FollowedTagsActivity import com.keylesspalace.tusky.components.instancemute.InstanceListActivity import com.keylesspalace.tusky.components.login.LoginActivity import com.keylesspalace.tusky.components.notifications.currentAccountNeedsMigration -import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.settings.AccountPreferenceHandler import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.listPreference import com.keylesspalace.tusky.settings.makePreferenceScreen @@ -85,7 +85,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { colorInt = MaterialColors.getColor(context, R.attr.iconColor, Color.BLACK) } setOnPreferenceClickListener { - openNotificationPrefs() + openNotificationSystemPrefs() true } } @@ -184,8 +184,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { setEntryValues(R.array.post_privacy_values) key = PrefKeys.DEFAULT_POST_PRIVACY setSummaryProvider { entry } - val visibility = accountManager.activeAccount?.defaultPostPrivacy - ?: Status.Visibility.PUBLIC + val visibility = accountManager.activeAccount?.defaultPostPrivacy ?: Status.Visibility.PUBLIC value = visibility.serverString() setIcon(getIconForVisibility(visibility)) setOnPreferenceChangeListener { _, newValue -> @@ -224,8 +223,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { setIcon(R.drawable.ic_eye_24dp) key = PrefKeys.DEFAULT_MEDIA_SENSITIVITY isSingleLineTitle = false - val sensitivity = accountManager.activeAccount?.defaultMediaSensitivity - ?: false + val sensitivity = accountManager.activeAccount?.defaultMediaSensitivity ?: false setDefaultValue(sensitivity) setIcon(getIconForSensitivity(sensitivity)) setOnPreferenceChangeListener { _, newValue -> @@ -238,40 +236,29 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { } preferenceCategory(R.string.pref_title_timelines) { + // TODO having no activeAccount in this fragment does not really make sense, enforce it? + // All other locations here make it optional, however. + val accountPreferenceHandler = AccountPreferenceHandler(accountManager.activeAccount!!, accountManager, eventHub) + switchPreference { key = PrefKeys.MEDIA_PREVIEW_ENABLED setTitle(R.string.pref_title_show_media_preview) isSingleLineTitle = false - isChecked = accountManager.activeAccount?.mediaPreviewEnabled ?: true - setOnPreferenceChangeListener { _, newValue -> - updateAccount { it.mediaPreviewEnabled = newValue as Boolean } - eventHub.dispatch(PreferenceChangedEvent(key)) - true - } + preferenceDataStore = accountPreferenceHandler } switchPreference { key = PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA setTitle(R.string.pref_title_alway_show_sensitive_media) isSingleLineTitle = false - isChecked = accountManager.activeAccount?.alwaysShowSensitiveMedia ?: false - setOnPreferenceChangeListener { _, newValue -> - updateAccount { it.alwaysShowSensitiveMedia = newValue as Boolean } - eventHub.dispatch(PreferenceChangedEvent(key)) - true - } + preferenceDataStore = accountPreferenceHandler } switchPreference { key = PrefKeys.ALWAYS_OPEN_SPOILER setTitle(R.string.pref_title_alway_open_spoiler) isSingleLineTitle = false - isChecked = accountManager.activeAccount?.alwaysOpenSpoiler ?: false - setOnPreferenceChangeListener { _, newValue -> - updateAccount { it.alwaysOpenSpoiler = newValue as Boolean } - eventHub.dispatch(PreferenceChangedEvent(key)) - true - } + preferenceDataStore = accountPreferenceHandler } } @@ -279,10 +266,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { preference { setTitle(R.string.pref_title_public_filter_keywords) setOnPreferenceClickListener { - launchFilterActivity( - Filter.PUBLIC, - R.string.pref_title_public_filter_keywords - ) + launchFilterActivity(Filter.PUBLIC, R.string.pref_title_public_filter_keywords) true } } @@ -306,10 +290,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { preference { setTitle(R.string.pref_title_thread_filter_keywords) setOnPreferenceClickListener { - launchFilterActivity( - Filter.THREAD, - R.string.pref_title_thread_filter_keywords - ) + launchFilterActivity(Filter.THREAD, R.string.pref_title_thread_filter_keywords) true } } @@ -330,7 +311,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { requireActivity().setTitle(R.string.action_view_account_preferences) } - private fun openNotificationPrefs() { + private fun openNotificationSystemPrefs() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val intent = Intent() intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" @@ -345,14 +326,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { } } - private inline fun updateAccount(changer: (AccountEntity) -> Unit) { - accountManager.activeAccount?.let { account -> - changer(account) - accountManager.saveAccount(account) - } - } - private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null, language: String? = null) { + // TODO these could also be "datastore backed" preferences (a ServerPreferenceDataStore); follow-up of issue #3204 + mastodonApi.accountUpdateSource(visibility, sensitive, language) .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt new file mode 100644 index 00000000..3d6b0c15 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt @@ -0,0 +1,35 @@ +package com.keylesspalace.tusky.settings + +import androidx.preference.PreferenceDataStore +import com.keylesspalace.tusky.appstore.EventHub +import com.keylesspalace.tusky.appstore.PreferenceChangedEvent +import com.keylesspalace.tusky.db.AccountEntity +import com.keylesspalace.tusky.db.AccountManager + +class AccountPreferenceHandler( + private val account: AccountEntity, + private val accountManager: AccountManager, + private val eventHub: EventHub, +) : PreferenceDataStore() { + + override fun getBoolean(key: String, defValue: Boolean): Boolean { + return when (key) { + PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA -> account.alwaysShowSensitiveMedia + PrefKeys.ALWAYS_OPEN_SPOILER -> account.alwaysOpenSpoiler + PrefKeys.MEDIA_PREVIEW_ENABLED -> account.mediaPreviewEnabled + else -> defValue + } + } + + override fun putBoolean(key: String, value: Boolean) { + when (key) { + PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA -> account.alwaysShowSensitiveMedia = value + PrefKeys.ALWAYS_OPEN_SPOILER -> account.alwaysOpenSpoiler = value + PrefKeys.MEDIA_PREVIEW_ENABLED -> account.mediaPreviewEnabled = value + } + + accountManager.saveAccount(account) + + eventHub.dispatch(PreferenceChangedEvent(key)) + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt index 4b4f5f18..8b8d5c3d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -41,7 +41,7 @@ enum class AppTheme(val value: String) { * * - Adding a new preference that does not change the interpretation of an existing preference */ -const val SCHEMA_VERSION = 2023021501 +const val SCHEMA_VERSION = 2023022701 object PrefKeys { // Note: not all of these keys are actually used as SharedPreferences keys but we must give