From 9e9246ada3c48e5ab2871a0fc6071a3c9c59df2a Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Wed, 21 Aug 2019 11:38:14 -0400 Subject: [PATCH] [fenix] No issue: Clean up preferences code (https://github.com/mozilla-mobile/fenix/pull/4699) --- .../java/org/mozilla/fenix/ext/Fragment.kt | 3 + .../mozilla/fenix/settings/AboutFragment.kt | 15 +-- .../fenix/settings/AccessibilityFragment.kt | 66 +++++++---- .../settings/AccountAuthErrorPreference.kt | 9 +- .../fenix/settings/AccountPreference.kt | 9 +- .../fenix/settings/AccountProblemFragment.kt | 72 ++++++------ .../fenix/settings/DataChoicesFragment.kt | 15 +-- .../org/mozilla/fenix/settings/Extensions.kt | 85 ++++---------- .../mozilla/fenix/settings/PairFragment.kt | 4 +- .../mozilla/fenix/settings/PhoneFeature.kt | 85 +++++++------- .../fenix/settings/SearchEngineFragment.kt | 17 +-- .../fenix/settings/SettingsFragment.kt | 28 ++--- .../fenix/settings/SharedPreferenceUpdater.kt | 20 ++++ ...itePermissionsDetailsExceptionsFragment.kt | 3 +- .../fenix/settings/SitePermissionsFragment.kt | 42 ++----- ...tePermissionsManagePhoneFeatureFragment.kt | 19 +-- .../TextPercentageSeekBarPreference.kt | 110 +++++++++--------- .../mozilla/fenix/settings/ThemeFragment.kt | 9 +- .../settings/TrackingProtectionFragment.kt | 49 ++++---- .../fenix/settings/TurnOnSyncFragment.kt | 45 ++++--- .../account/AccountSettingsFragment.kt | 16 +-- .../quicksettings/QuickSettingsComponent.kt | 8 +- .../SitePermissionsRulesActionPreference.kt | 45 ------- .../java/org/mozilla/fenix/utils/Settings.kt | 43 ++++--- .../main/res/layout/fragment_turn_on_sync.xml | 8 +- ...itePermissionsRulesActionPreferenceTest.kt | 73 ------------ .../org/mozilla/fenix/utils/SettingsTest.kt | 33 +++--- 27 files changed, 381 insertions(+), 550 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/SharedPreferenceUpdater.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/settings/sharedpreferences/SitePermissionsRulesActionPreference.kt delete mode 100644 app/src/test/java/org/mozilla/fenix/settings/sharedpreferences/SitePermissionsRulesActionPreferenceTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt index 950e88cc95..48d2158bb9 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.ext import androidx.annotation.IdRes +import androidx.annotation.StringRes import androidx.fragment.app.Fragment import androidx.navigation.NavDirections import androidx.navigation.NavOptions @@ -29,3 +30,5 @@ fun Fragment.nav(@IdRes id: Int?, directions: NavDirections, extras: Navigator.E fun Fragment.nav(@IdRes id: Int?, directions: NavDirections, options: NavOptions) { findNavController(this).nav(id, directions, options) } + +fun Fragment.getPreferenceKey(@StringRes resourceId: Int): String = getString(resourceId) diff --git a/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt index 24e03a8def..0b4a536589 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt @@ -21,12 +21,18 @@ import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig +/** + * Displays the logo and information about the app, including library versions. + */ class AboutFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_about, container, false) } + /** + * Sets the activity title, displays library version strings, and sets up the [view_licenses_button]. + */ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -53,8 +59,8 @@ class AboutFragment : Fragment() { "" } - val buildDate = BuildConfig.BUILD_DATE val content = getString(R.string.about_content, appName) + val buildDate = BuildConfig.BUILD_DATE about_text.text = aboutText about_content.text = content @@ -62,12 +68,7 @@ class AboutFragment : Fragment() { view_licenses_button.setOnClickListener { startActivity(Intent(context, OssLicensesMenuActivity::class.java)) - OssLicensesMenuActivity.setActivityTitle( - getString( - R.string.open_source_licenses_title, - getString(R.string.app_name) - ) - ) + OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName)) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt index dfb2e0389e..6e539e0c84 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt @@ -6,44 +6,64 @@ package org.mozilla.fenix.settings import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R -import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.utils.Settings +/** + * Displays font size controls for accessibility. + * + * Includes an automatic font sizing toggle. When turned on, font sizing follows the Android device settings. + * When turned off, the font sizing can be controlled manually within the app. + */ class AccessibilityFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() (activity as AppCompatActivity).title = getString(R.string.preferences_accessibility) (activity as AppCompatActivity).supportActionBar?.show() - val textSizePreference = - findPreference(getString(R.string.pref_key_accessibility_font_scale)) - textSizePreference?.onPreferenceChangeListener = - Preference.OnPreferenceChangeListener { _, newValue -> - (newValue as? Int).let { - // Value is mapped from 0->30 in steps of 1 so let's convert to float in range 0.5->2.0 - val newTextScale = ((newValue as Int * STEP_SIZE) + MIN_SCALE_VALUE).toFloat() / PERCENT_TO_DECIMAL - Settings.getInstance(context!!).fontSizeFactor = newTextScale - requireComponents.core.engine.settings.fontSizeFactor = newTextScale - requireComponents.useCases.sessionUseCases.reload.invoke() - } - true - } + val textSizePreference = findPreference( + getPreferenceKey(R.string.pref_key_accessibility_font_scale) + ) + textSizePreference?.setOnPreferenceChangeListener { preference, newTextSize -> + val settings = Settings.getInstance(preference.context) + val components = preference.context.components + + // Value is mapped from 0->30 in steps of 1 so let's convert to float in range 0.5->2.0 + val newTextScale = ((newTextSize * STEP_SIZE) + MIN_SCALE_VALUE).toFloat() / PERCENT_TO_DECIMAL + + // Save new text scale value. We assume auto sizing is off if this change listener was called. + settings.fontSizeFactor = newTextScale + components.core.engine.settings.fontSizeFactor = newTextScale + + // Reload the current session to reflect the new text scale + components.useCases.sessionUseCases.reload() + true + } textSizePreference?.isVisible = !Settings.getInstance(context!!).shouldUseAutoSize val useAutoSizePreference = - findPreference(getString(R.string.pref_key_accessibility_auto_size)) - useAutoSizePreference?.setOnPreferenceChangeListener { _, newValue -> - Settings.getInstance(context!!).shouldUseAutoSize = newValue as Boolean - requireComponents.core.engine.settings.automaticFontSizeAdjustment = newValue - if (!newValue) { - requireComponents.core.engine.settings.fontSizeFactor = Settings.getInstance(context!!).fontSizeFactor + findPreference(getPreferenceKey(R.string.pref_key_accessibility_auto_size)) + useAutoSizePreference?.setOnPreferenceChangeListener { preference, useAutoSize -> + val settings = Settings.getInstance(preference.context) + val components = preference.context.components + + // Save the new setting value + settings.shouldUseAutoSize = useAutoSize + components.core.engine.settings.automaticFontSizeAdjustment = useAutoSize + + // If using manual sizing, update the engine settings with the local saved setting + if (!useAutoSize) { + components.core.engine.settings.fontSizeFactor = settings.fontSizeFactor } - textSizePreference?.isVisible = !newValue - requireComponents.useCases.sessionUseCases.reload.invoke() + // Show the manual sizing controls if automatic sizing is turned off. + textSizePreference?.isVisible = !useAutoSize + + // Reload the current session to reflect the new text scale + components.useCases.sessionUseCases.reload() true } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/AccountAuthErrorPreference.kt b/app/src/main/java/org/mozilla/fenix/settings/AccountAuthErrorPreference.kt index cde32e4618..33f298e108 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/AccountAuthErrorPreference.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/AccountAuthErrorPreference.kt @@ -6,8 +6,8 @@ package org.mozilla.fenix.settings import android.content.Context import android.util.AttributeSet -import android.view.View import android.widget.TextView +import androidx.core.view.isGone import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import org.mozilla.fenix.R @@ -32,10 +32,7 @@ class AccountAuthErrorPreference @JvmOverloads constructor( } private fun updateEmailView(email: String?) { - emailView?.text = email.orEmpty() - emailView?.visibility = when (email.isNullOrEmpty()) { - true -> View.GONE - false -> View.VISIBLE - } + emailView?.text = email + emailView?.isGone = email.isNullOrEmpty() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/AccountPreference.kt b/app/src/main/java/org/mozilla/fenix/settings/AccountPreference.kt index 32248ea1f3..558a8e6c08 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/AccountPreference.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/AccountPreference.kt @@ -6,8 +6,8 @@ package org.mozilla.fenix.settings import android.content.Context import android.util.AttributeSet -import android.view.View import android.widget.TextView +import androidx.core.view.isGone import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import org.mozilla.fenix.R @@ -49,10 +49,7 @@ class AccountPreference @JvmOverloads constructor( } private fun updateDisplayName(name: String?) { - displayNameView?.text = name.orEmpty() - displayNameView?.visibility = when (displayName.isNullOrEmpty()) { - true -> View.GONE - false -> View.VISIBLE - } + displayNameView?.text = name + displayNameView?.isGone = displayName.isNullOrEmpty() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/AccountProblemFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/AccountProblemFragment.kt index 35c6685e31..673b1f2b37 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/AccountProblemFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/AccountProblemFragment.kt @@ -7,7 +7,7 @@ package org.mozilla.fenix.settings import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import kotlinx.coroutines.launch @@ -20,66 +20,60 @@ import org.mozilla.fenix.ext.requireComponents class AccountProblemFragment : PreferenceFragmentCompat(), AccountObserver { + private val signInClickListener = Preference.OnPreferenceClickListener { + requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext()) + // TODO The sign-in web content populates session history, + // so pressing "back" after signing in won't take us back into the settings screen, but rather up the + // session history stack. + // We could auto-close this tab once we get to the end of the authentication process? + // Via an interceptor, perhaps. + true + } + + private val signOutClickListener = Preference.OnPreferenceClickListener { + nav( + R.id.accountProblemFragment, + AccountProblemFragmentDirections.actionAccountProblemFragmentToSignOutFragment() + ) + true + } + override fun onResume() { super.onResume() (activity as AppCompatActivity).title = getString(R.string.sync_reconnect) (activity as AppCompatActivity).supportActionBar?.show() + val accountManager = requireComponents.backgroundServices.accountManager + // We may have fixed our auth problem, in which case close this fragment. - if (requireComponents.backgroundServices.accountManager.authenticatedAccount() != null && - !requireComponents.backgroundServices.accountManager.accountNeedsReauth() - ) { - NavHostFragment.findNavController(this).popBackStack() + if (accountManager.authenticatedAccount() != null && !accountManager.accountNeedsReauth()) { + findNavController().popBackStack() return } - requireComponents.backgroundServices.accountManager.register(this, owner = this) + accountManager.register(this, owner = this) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.sync_problem, rootKey) val preferenceSignIn = - findPreference(context!!.getPreferenceKey(R.string.pref_key_sync_sign_in)) + findPreference(getPreferenceKey(R.string.pref_key_sync_sign_in)) val preferenceSignOut = - findPreference(context!!.getPreferenceKey(R.string.pref_key_sign_out)) - preferenceSignIn?.onPreferenceClickListener = getClickListenerForSignIn() - preferenceSignOut?.onPreferenceClickListener = getClickListenerForSignOut() - } - - private fun getClickListenerForSignIn(): Preference.OnPreferenceClickListener { - return Preference.OnPreferenceClickListener { - requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext()) - // TODO The sign-in web content populates session history, - // so pressing "back" after signing in won't take us back into the settings screen, but rather up the - // session history stack. - // We could auto-close this tab once we get to the end of the authentication process? - // Via an interceptor, perhaps. - true - } - } - - private fun getClickListenerForSignOut(): Preference.OnPreferenceClickListener { - return Preference.OnPreferenceClickListener { - nav( - R.id.accountProblemFragment, - AccountProblemFragmentDirections.actionAccountProblemFragmentToSignOutFragment() - ) - true - } + findPreference(getPreferenceKey(R.string.pref_key_sign_out)) + preferenceSignIn?.onPreferenceClickListener = signInClickListener + preferenceSignOut?.onPreferenceClickListener = signOutClickListener } // We're told our auth problems have been fixed; close this fragment. - override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) { - lifecycleScope.launch { - NavHostFragment.findNavController(this@AccountProblemFragment).popBackStack() - } - } + override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) = closeFragment() // We're told there are no more auth problems since there is no more account; close this fragment. - override fun onLoggedOut() { + override fun onLoggedOut() = closeFragment() + + private fun closeFragment() { lifecycleScope.launch { - NavHostFragment.findNavController(this@AccountProblemFragment).popBackStack() + findNavController().popBackStack() } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt index db218d85f9..78f18813e1 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt @@ -11,14 +11,18 @@ import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.utils.Settings +/** + * Lets the user toggle telemetry on/off. + */ class DataChoicesFragment : PreferenceFragmentCompat() { private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> when (key) { - getString(R.string.pref_key_telemetry) -> { + getPreferenceKey(R.string.pref_key_telemetry) -> { if (sharedPreferences.getBoolean(key, Settings.getInstance(requireContext()).isTelemetryEnabled)) { context?.components?.analytics?.metrics?.start() } else { @@ -51,16 +55,13 @@ class DataChoicesFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.data_choices_preferences, rootKey) - val telemetryPreference = findPreference(getString(R.string.pref_key_telemetry))?.apply { + findPreference(getPreferenceKey(R.string.pref_key_telemetry))?.apply { isChecked = Settings.getInstance(context).isTelemetryEnabled val appName = context.getString(R.string.app_name) summary = context.getString(R.string.preferences_usage_data_description, appName) - } - telemetryPreference?.setOnPreferenceChangeListener { preference, newValue -> - Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean) - .apply() - true + + onPreferenceChangeListener = SharedPreferenceUpdater() } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt index b93fdfc4bd..5175007fbb 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt @@ -4,82 +4,22 @@ package org.mozilla.fenix.settings -import android.content.Context import android.view.View import android.widget.RadioButton import android.widget.TextView import androidx.core.text.HtmlCompat +import androidx.preference.Preference import mozilla.components.feature.sitepermissions.SitePermissions -import mozilla.components.feature.sitepermissions.SitePermissionsRules import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative import org.mozilla.fenix.R import org.mozilla.fenix.ThemeManager -internal fun SitePermissionsRules.Action.toString(context: Context): String { - return when (this) { - SitePermissionsRules.Action.ASK_TO_ALLOW -> { - context.getString(R.string.preference_option_phone_feature_ask_to_allow) - } - SitePermissionsRules.Action.BLOCKED -> { - context.getString(R.string.preference_option_phone_feature_blocked) - } - } -} - -internal fun SitePermissions.Status.toString(context: Context): String { - return when (this) { - SitePermissions.Status.BLOCKED -> { - context.getString(R.string.preference_option_phone_feature_blocked) - } - SitePermissions.Status.NO_DECISION -> { - context.getString(R.string.preference_option_phone_feature_ask_to_allow) - } - SitePermissions.Status.ALLOWED -> { - context.getString(R.string.preference_option_phone_feature_allowed) - } - } -} - fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions { return when (featurePhone) { - PhoneFeature.CAMERA -> { - copy( - camera = camera.toggle() - ) - } - PhoneFeature.LOCATION -> { - copy( - location = location.toggle() - ) - } - PhoneFeature.MICROPHONE -> { - copy( - microphone = microphone.toggle() - ) - } - PhoneFeature.NOTIFICATION -> { - copy( - notification = notification.toggle() - ) - } - } -} - -fun PhoneFeature.getLabel(context: Context): String { - return when (this) { - PhoneFeature.CAMERA -> context.getString(R.string.preference_phone_feature_camera) - PhoneFeature.LOCATION -> context.getString(R.string.preference_phone_feature_location) - PhoneFeature.MICROPHONE -> context.getString(R.string.preference_phone_feature_microphone) - PhoneFeature.NOTIFICATION -> context.getString(R.string.preference_phone_feature_notification) - } -} - -fun PhoneFeature.getPreferenceKey(context: Context): String { - return when (this) { - PhoneFeature.CAMERA -> context.getString(R.string.pref_key_phone_feature_camera) - PhoneFeature.LOCATION -> context.getString(R.string.pref_key_phone_feature_location) - PhoneFeature.MICROPHONE -> context.getString(R.string.pref_key_phone_feature_microphone) - PhoneFeature.NOTIFICATION -> context.getString(R.string.pref_key_phone_feature_notification) + PhoneFeature.CAMERA -> copy(camera = camera.toggle()) + PhoneFeature.LOCATION -> copy(location = location.toggle()) + PhoneFeature.MICROPHONE -> copy(microphone = microphone.toggle()) + PhoneFeature.NOTIFICATION -> copy(notification = notification.toggle()) } } @@ -111,3 +51,18 @@ fun initBlockedByAndroidView(phoneFeature: PhoneFeature, blockedByAndroidView: V blockedByAndroidView.visibility = View.GONE } } + +/** + * Sets the callback to be invoked when this preference is changed by the user (but before + * the internal state has been updated). Allows the type of the preference to be specified. + * If the new value doesn't match the preference type the listener isn't called. + * + * @param onPreferenceChangeListener The callback to be invoked + */ +inline fun Preference.setOnPreferenceChangeListener( + crossinline onPreferenceChangeListener: (Preference, T) -> Boolean +) { + setOnPreferenceChangeListener { preference: Preference, newValue: Any -> + (newValue as? T)?.let { onPreferenceChangeListener(preference, it) } ?: false + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/PairFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/PairFragment.kt index cd9274beab..31146c468f 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/PairFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/PairFragment.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment import androidx.navigation.fragment.NavHostFragment.findNavController +import androidx.navigation.fragment.findNavController import kotlinx.android.synthetic.main.fragment_pair.* import mozilla.components.feature.qr.QrFeature import mozilla.components.support.base.feature.BackHandler @@ -68,8 +69,7 @@ class PairFragment : Fragment(), BackHandler { override fun onBackPressed(): Boolean { qrFeature.onBackPressed() - findNavController(this@PairFragment) - .popBackStack(R.id.turnOnSyncFragment, false) + findNavController().popBackStack(R.id.turnOnSyncFragment, false) return true } diff --git a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt index 415d673aef..2f233f28e8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt @@ -8,8 +8,12 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.RECORD_AUDIO import android.content.Context +import androidx.annotation.StringRes import mozilla.components.feature.sitepermissions.SitePermissions +import mozilla.components.feature.sitepermissions.SitePermissionsRules import mozilla.components.support.ktx.android.content.isPermissionGranted +import org.mozilla.fenix.R +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.utils.Settings import android.Manifest.permission.CAMERA as CAMERA_PERMISSION @@ -32,57 +36,50 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array) } fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings? = null): String { - val label = when (this) { - CAMERA -> { - sitePermissions?.camera?.toString(context) ?: settings - ?.sitePermissionsPhoneFeatureCameraAction - ?.toString(context) - } - LOCATION -> { - sitePermissions?.location?.toString(context) ?: settings - ?.sitePermissionsPhoneFeatureLocation - ?.toString(context) - } - MICROPHONE -> { - sitePermissions?.microphone?.toString(context) ?: settings - ?.sitePermissionsPhoneFeatureMicrophoneAction - ?.toString(context) - } - NOTIFICATION -> { - sitePermissions?.notification?.toString(context) ?: settings - ?.sitePermissionsPhoneFeatureNotificationAction - ?.toString(context) - } + @StringRes val stringRes = when (getStatus(sitePermissions, settings)) { + SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked + SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow + SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed } - return requireNotNull(label) + return context.getString(stringRes) } fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings? = null): SitePermissions.Status { - val status = when (this) { - CAMERA -> { - sitePermissions?.camera ?: settings - ?.sitePermissionsPhoneFeatureCameraAction - ?.toStatus() - } - LOCATION -> { - sitePermissions?.location ?: settings - ?.sitePermissionsPhoneFeatureLocation - ?.toStatus() - } - MICROPHONE -> { - sitePermissions?.microphone ?: settings - ?.sitePermissionsPhoneFeatureMicrophoneAction - ?.toStatus() - } - NOTIFICATION -> { - sitePermissions?.notification ?: settings - ?.sitePermissionsPhoneFeatureNotificationAction - ?.toStatus() - } - } + val status = getStatus(sitePermissions) ?: settings?.let(::getAction)?.toStatus() return requireNotNull(status) } + fun getLabel(context: Context): String { + return when (this) { + CAMERA -> context.getString(R.string.preference_phone_feature_camera) + LOCATION -> context.getString(R.string.preference_phone_feature_location) + MICROPHONE -> context.getString(R.string.preference_phone_feature_microphone) + NOTIFICATION -> context.getString(R.string.preference_phone_feature_notification) + } + } + + fun getPreferenceKey(context: Context): String { + return when (this) { + CAMERA -> context.getPreferenceKey(R.string.pref_key_phone_feature_camera) + LOCATION -> context.getPreferenceKey(R.string.pref_key_phone_feature_location) + MICROPHONE -> context.getPreferenceKey(R.string.pref_key_phone_feature_microphone) + NOTIFICATION -> context.getPreferenceKey(R.string.pref_key_phone_feature_notification) + } + } + + fun getAction(settings: Settings): SitePermissionsRules.Action = + settings.getSitePermissionsPhoneFeatureAction(this) + + private fun getStatus(sitePermissions: SitePermissions?): SitePermissions.Status? { + sitePermissions ?: return null + return when (this) { + CAMERA -> sitePermissions.camera + LOCATION -> sitePermissions.location + MICROPHONE -> sitePermissions.microphone + NOTIFICATION -> sitePermissions.notification + } + } + companion object { fun findFeatureBy(permissions: Array): PhoneFeature? { return PhoneFeature.values().find { feature -> diff --git a/app/src/main/java/org/mozilla/fenix/settings/SearchEngineFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SearchEngineFragment.kt index 7259899652..766f335686 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SearchEngineFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SearchEngineFragment.kt @@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.utils.Settings class SearchEngineFragment : PreferenceFragmentCompat() { @@ -23,25 +24,17 @@ class SearchEngineFragment : PreferenceFragmentCompat() { (activity as AppCompatActivity).supportActionBar?.show() val searchSuggestionsPreference = - findPreference(getString(R.string.pref_key_show_search_suggestions))?.apply { + findPreference(getPreferenceKey(R.string.pref_key_show_search_suggestions))?.apply { isChecked = Settings.getInstance(context).showSearchSuggestions } - searchSuggestionsPreference?.setOnPreferenceChangeListener { preference, newValue -> - Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean) - .apply() - true - } + searchSuggestionsPreference?.onPreferenceChangeListener = SharedPreferenceUpdater() val showVisitedSitesBookmarks = - findPreference(getString(R.string.pref_key_show_visited_sites_bookmarks))?.apply { + findPreference(getPreferenceKey(R.string.pref_key_show_visited_sites_bookmarks))?.apply { isChecked = Settings.getInstance(context).shouldShowVisitedSitesBookmarks } - showVisitedSitesBookmarks?.setOnPreferenceChangeListener { preference, newValue -> - Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean) - .apply() - true - } + showVisitedSitesBookmarks?.onPreferenceChangeListener = SharedPreferenceUpdater() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index 8559f1b8b5..d0e33a5957 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -90,7 +90,7 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener) if (SDK_INT <= Build.VERSION_CODES.M) { - findPreference(getString(R.string.pref_key_make_default_browser))?.apply { + findPreference(getPreferenceKey(R.string.pref_key_make_default_browser))?.apply { isVisible = false } } @@ -106,17 +106,17 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { (activity as AppCompatActivity).title = getString(R.string.settings_title) (activity as AppCompatActivity).supportActionBar?.show() val defaultBrowserPreference = - findPreference(getString(R.string.pref_key_make_default_browser)) + findPreference(getPreferenceKey(R.string.pref_key_make_default_browser)) defaultBrowserPreference?.updateSwitch() val searchEnginePreference = - findPreference(getString(R.string.pref_key_search_engine_settings)) + findPreference(getPreferenceKey(R.string.pref_key_search_engine_settings)) searchEnginePreference?.summary = context?.let { requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name } val trackingProtectionPreference = - findPreference(getString(R.string.pref_key_tracking_protection_settings)) + findPreference(getPreferenceKey(R.string.pref_key_tracking_protection_settings)) trackingProtectionPreference?.summary = context?.let { if (org.mozilla.fenix.utils.Settings.getInstance(it).shouldUseTrackingProtection) { getString(R.string.tracking_protection_on) @@ -126,12 +126,12 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { } val themesPreference = - findPreference(getString(R.string.pref_key_theme)) + findPreference(getPreferenceKey(R.string.pref_key_theme)) themesPreference?.summary = context?.let { org.mozilla.fenix.utils.Settings.getInstance(it).themeSettingString } - val aboutPreference = findPreference(getString(R.string.pref_key_about)) + val aboutPreference = findPreference(getPreferenceKey(R.string.pref_key_about)) val appName = getString(R.string.app_name) aboutPreference?.title = getString(R.string.preferences_about, appName) @@ -198,16 +198,16 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { navigateToThemeSettings() } resources.getString(pref_key_privacy_link) -> { - requireContext().apply { - val intent = SupportUtils.createCustomTabIntent(this, SupportUtils.PRIVACY_NOTICE_URL) + requireContext().let { context -> + val intent = SupportUtils.createCustomTabIntent(context, SupportUtils.PRIVACY_NOTICE_URL) startActivity(intent) } } resources.getString(pref_key_your_rights) -> { - requireContext().apply { + requireContext().let { context -> val intent = SupportUtils.createCustomTabIntent( - this, - SupportUtils.getSumoURLForTopic(context!!, SupportUtils.SumoTopic.YOUR_RIGHTS) + context, + SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS) ) startActivity(intent) } @@ -229,9 +229,9 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { } private fun setupPreferences() { - val makeDefaultBrowserKey = context!!.getPreferenceKey(pref_key_make_default_browser) - val leakKey = context!!.getPreferenceKey(pref_key_leakcanary) - val debuggingKey = context!!.getPreferenceKey(pref_key_remote_debugging) + val makeDefaultBrowserKey = getPreferenceKey(pref_key_make_default_browser) + val leakKey = getPreferenceKey(pref_key_leakcanary) + val debuggingKey = getPreferenceKey(pref_key_remote_debugging) val preferenceMakeDefaultBrowser = findPreference(makeDefaultBrowserKey) val preferenceLeakCanary = findPreference(leakKey) diff --git a/app/src/main/java/org/mozilla/fenix/settings/SharedPreferenceUpdater.kt b/app/src/main/java/org/mozilla/fenix/settings/SharedPreferenceUpdater.kt new file mode 100644 index 0000000000..d33c62959e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/SharedPreferenceUpdater.kt @@ -0,0 +1,20 @@ +package org.mozilla.fenix.settings + +import androidx.core.content.edit +import androidx.preference.Preference +import org.mozilla.fenix.utils.Settings + +/** + * Updates the corresponding [android.content.SharedPreferences] when the boolean [Preference] is changed. + * The preference key is used as the shared preference key. + */ +class SharedPreferenceUpdater : Preference.OnPreferenceChangeListener { + + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + val newBooleanValue = newValue as? Boolean ?: return false + Settings.getInstance(preference.context).preferences.edit { + putBoolean(preference.key, newBooleanValue) + } + return true + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsDetailsExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsDetailsExceptionsFragment.kt index 569b92c21d..3c8b5e3023 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsDetailsExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsDetailsExceptionsFragment.kt @@ -19,6 +19,7 @@ import org.jetbrains.anko.noButton import org.jetbrains.anko.yesButton import org.mozilla.fenix.R import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.settings.PhoneFeature.CAMERA import org.mozilla.fenix.settings.PhoneFeature.LOCATION import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE @@ -83,7 +84,7 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() { } private fun bindClearPermissionsButton() { - val keyPreference = getString(R.string.pref_key_exceptions_clear_site_permissions) + val keyPreference = getPreferenceKey(R.string.pref_key_exceptions_clear_site_permissions) val button: Preference = requireNotNull(findPreference(keyPreference)) button.onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt index 3113d69338..876845344f 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt @@ -11,10 +11,7 @@ import androidx.preference.Preference import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.PreferenceFragmentCompat import org.mozilla.fenix.R -import org.mozilla.fenix.settings.PhoneFeature.CAMERA -import org.mozilla.fenix.settings.PhoneFeature.LOCATION -import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE -import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION +import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.utils.Settings @SuppressWarnings("TooManyFunctions") @@ -36,13 +33,12 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { } private fun setupPreferences() { - bindCategoryPhoneFeatures() bindExceptions() } private fun bindExceptions() { - val keyExceptions = getString(R.string.pref_key_show_site_exceptions) + val keyExceptions = getPreferenceKey(R.string.pref_key_show_site_exceptions) val exceptionsCategory = requireNotNull(findPreference(keyExceptions)) exceptionsCategory.onPreferenceClickListener = OnPreferenceClickListener { @@ -53,33 +49,17 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { } private fun bindCategoryPhoneFeatures() { - val settings = Settings.getInstance(requireContext()) - - val cameraAction = settings - .sitePermissionsPhoneFeatureCameraAction - .toString(requireContext()) - - val locationAction = settings - .sitePermissionsPhoneFeatureLocation - .toString(requireContext()) - - val microPhoneAction = settings - .sitePermissionsPhoneFeatureMicrophoneAction - .toString(requireContext()) - - val notificationAction = settings - .sitePermissionsPhoneFeatureNotificationAction - .toString(requireContext()) - - initPhoneFeature(CAMERA, cameraAction) - initPhoneFeature(LOCATION, locationAction) - initPhoneFeature(MICROPHONE, microPhoneAction) - initPhoneFeature(NOTIFICATION, notificationAction) + PhoneFeature.values().forEach(::initPhoneFeature) } - private fun initPhoneFeature(phoneFeature: PhoneFeature, summary: String) { - val keyPreference = phoneFeature.getPreferenceKey(requireContext()) - val cameraPhoneFeatures: Preference = requireNotNull(findPreference(keyPreference)) + private fun initPhoneFeature(phoneFeature: PhoneFeature) { + val context = requireContext() + val settings = Settings.getInstance(context) + + val summary = phoneFeature.getActionLabel(context, settings = settings) + val preferenceKey = phoneFeature.getPreferenceKey(context) + + val cameraPhoneFeatures: Preference = requireNotNull(findPreference(preferenceKey)) cameraPhoneFeatures.summary = summary cameraPhoneFeatures.onPreferenceClickListener = OnPreferenceClickListener { diff --git a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeatureFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeatureFragment.kt index 0782de3f25..09389ca1c8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeatureFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeatureFragment.kt @@ -92,7 +92,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { } private fun RadioButton.restoreState(action: SitePermissionsRules.Action) { - if (phoneFeature.action == action) { + if (phoneFeature.getAction(settings) == action) { this.isChecked = true this.setStartCheckedIndicator() } @@ -119,16 +119,6 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { } } - private val PhoneFeature.action: SitePermissionsRules.Action - get() { - return when (phoneFeature) { - PhoneFeature.CAMERA -> settings.sitePermissionsPhoneFeatureCameraAction - PhoneFeature.LOCATION -> settings.sitePermissionsPhoneFeatureLocation - PhoneFeature.MICROPHONE -> settings.sitePermissionsPhoneFeatureMicrophoneAction - PhoneFeature.NOTIFICATION -> settings.sitePermissionsPhoneFeatureNotificationAction - } - } - private fun initSettingsButton(rootView: View) { val button = rootView.findViewById