1
0
Fork 0

No issue: Clean up preferences code (#4699)

master
Tiger Oakes 2019-08-21 11:38:14 -04:00 committed by Sawyer Blatz
parent c1b1c2f332
commit aab357845b
27 changed files with 381 additions and 550 deletions

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix.ext package org.mozilla.fenix.ext
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import androidx.navigation.NavOptions 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) { fun Fragment.nav(@IdRes id: Int?, directions: NavDirections, options: NavOptions) {
findNavController(this).nav(id, directions, options) findNavController(this).nav(id, directions, options)
} }
fun Fragment.getPreferenceKey(@StringRes resourceId: Int): String = getString(resourceId)

View File

@ -21,12 +21,18 @@ import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
/**
* Displays the logo and information about the app, including library versions.
*/
class AboutFragment : Fragment() { class AboutFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_about, container, false) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) 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 content = getString(R.string.about_content, appName)
val buildDate = BuildConfig.BUILD_DATE
about_text.text = aboutText about_text.text = aboutText
about_content.text = content about_content.text = content
@ -62,12 +68,7 @@ class AboutFragment : Fragment() {
view_licenses_button.setOnClickListener { view_licenses_button.setOnClickListener {
startActivity(Intent(context, OssLicensesMenuActivity::class.java)) startActivity(Intent(context, OssLicensesMenuActivity::class.java))
OssLicensesMenuActivity.setActivityTitle( OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName))
getString(
R.string.open_source_licenses_title,
getString(R.string.app_name)
)
)
} }
} }

View File

@ -6,44 +6,64 @@ package org.mozilla.fenix.settings
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import org.mozilla.fenix.R 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 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() { class AccessibilityFragment : PreferenceFragmentCompat() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
(activity as AppCompatActivity).title = getString(R.string.preferences_accessibility) (activity as AppCompatActivity).title = getString(R.string.preferences_accessibility)
(activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.show()
val textSizePreference =
findPreference<TextPercentageSeekBarPreference>(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<TextPercentageSeekBarPreference>(
getPreferenceKey(R.string.pref_key_accessibility_font_scale)
)
textSizePreference?.setOnPreferenceChangeListener<Int> { 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 textSizePreference?.isVisible = !Settings.getInstance(context!!).shouldUseAutoSize
val useAutoSizePreference = val useAutoSizePreference =
findPreference<SwitchPreference>(getString(R.string.pref_key_accessibility_auto_size)) findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_accessibility_auto_size))
useAutoSizePreference?.setOnPreferenceChangeListener { _, newValue -> useAutoSizePreference?.setOnPreferenceChangeListener<Boolean> { preference, useAutoSize ->
Settings.getInstance(context!!).shouldUseAutoSize = newValue as Boolean val settings = Settings.getInstance(preference.context)
requireComponents.core.engine.settings.automaticFontSizeAdjustment = newValue val components = preference.context.components
if (!newValue) {
requireComponents.core.engine.settings.fontSizeFactor = Settings.getInstance(context!!).fontSizeFactor // 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 // Show the manual sizing controls if automatic sizing is turned off.
requireComponents.useCases.sessionUseCases.reload.invoke() textSizePreference?.isVisible = !useAutoSize
// Reload the current session to reflect the new text scale
components.useCases.sessionUseCases.reload()
true true
} }
} }

View File

@ -6,8 +6,8 @@ package org.mozilla.fenix.settings
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isGone
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -32,10 +32,7 @@ class AccountAuthErrorPreference @JvmOverloads constructor(
} }
private fun updateEmailView(email: String?) { private fun updateEmailView(email: String?) {
emailView?.text = email.orEmpty() emailView?.text = email
emailView?.visibility = when (email.isNullOrEmpty()) { emailView?.isGone = email.isNullOrEmpty()
true -> View.GONE
false -> View.VISIBLE
}
} }
} }

View File

@ -6,8 +6,8 @@ package org.mozilla.fenix.settings
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isGone
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -49,10 +49,7 @@ class AccountPreference @JvmOverloads constructor(
} }
private fun updateDisplayName(name: String?) { private fun updateDisplayName(name: String?) {
displayNameView?.text = name.orEmpty() displayNameView?.text = name
displayNameView?.visibility = when (displayName.isNullOrEmpty()) { displayNameView?.isGone = displayName.isNullOrEmpty()
true -> View.GONE
false -> View.VISIBLE
}
} }
} }

View File

@ -7,7 +7,7 @@ package org.mozilla.fenix.settings
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -20,66 +20,60 @@ import org.mozilla.fenix.ext.requireComponents
class AccountProblemFragment : PreferenceFragmentCompat(), AccountObserver { 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() { override fun onResume() {
super.onResume() super.onResume()
(activity as AppCompatActivity).title = getString(R.string.sync_reconnect) (activity as AppCompatActivity).title = getString(R.string.sync_reconnect)
(activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.show()
val accountManager = requireComponents.backgroundServices.accountManager
// We may have fixed our auth problem, in which case close this fragment. // We may have fixed our auth problem, in which case close this fragment.
if (requireComponents.backgroundServices.accountManager.authenticatedAccount() != null && if (accountManager.authenticatedAccount() != null && !accountManager.accountNeedsReauth()) {
!requireComponents.backgroundServices.accountManager.accountNeedsReauth() findNavController().popBackStack()
) {
NavHostFragment.findNavController(this).popBackStack()
return return
} }
requireComponents.backgroundServices.accountManager.register(this, owner = this) accountManager.register(this, owner = this)
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.sync_problem, rootKey) setPreferencesFromResource(R.xml.sync_problem, rootKey)
val preferenceSignIn = val preferenceSignIn =
findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_sign_in)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_sync_sign_in))
val preferenceSignOut = val preferenceSignOut =
findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sign_out)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_sign_out))
preferenceSignIn?.onPreferenceClickListener = getClickListenerForSignIn() preferenceSignIn?.onPreferenceClickListener = signInClickListener
preferenceSignOut?.onPreferenceClickListener = getClickListenerForSignOut() preferenceSignOut?.onPreferenceClickListener = signOutClickListener
}
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
}
} }
// We're told our auth problems have been fixed; close this fragment. // We're told our auth problems have been fixed; close this fragment.
override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) { override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) = closeFragment()
lifecycleScope.launch {
NavHostFragment.findNavController(this@AccountProblemFragment).popBackStack()
}
}
// We're told there are no more auth problems since there is no more account; close this fragment. // 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 { lifecycleScope.launch {
NavHostFragment.findNavController(this@AccountProblemFragment).popBackStack() findNavController().popBackStack()
} }
} }
} }

View File

@ -11,14 +11,18 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
/**
* Lets the user toggle telemetry on/off.
*/
class DataChoicesFragment : PreferenceFragmentCompat() { class DataChoicesFragment : PreferenceFragmentCompat() {
private val preferenceChangeListener = private val preferenceChangeListener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) { when (key) {
getString(R.string.pref_key_telemetry) -> { getPreferenceKey(R.string.pref_key_telemetry) -> {
if (sharedPreferences.getBoolean(key, Settings.getInstance(requireContext()).isTelemetryEnabled)) { if (sharedPreferences.getBoolean(key, Settings.getInstance(requireContext()).isTelemetryEnabled)) {
context?.components?.analytics?.metrics?.start() context?.components?.analytics?.metrics?.start()
} else { } else {
@ -51,16 +55,13 @@ class DataChoicesFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.data_choices_preferences, rootKey) setPreferencesFromResource(R.xml.data_choices_preferences, rootKey)
val telemetryPreference = findPreference<SwitchPreference>(getString(R.string.pref_key_telemetry))?.apply { findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_telemetry))?.apply {
isChecked = Settings.getInstance(context).isTelemetryEnabled isChecked = Settings.getInstance(context).isTelemetryEnabled
val appName = context.getString(R.string.app_name) val appName = context.getString(R.string.app_name)
summary = context.getString(R.string.preferences_usage_data_description, appName) summary = context.getString(R.string.preferences_usage_data_description, appName)
}
telemetryPreference?.setOnPreferenceChangeListener { preference, newValue -> onPreferenceChangeListener = SharedPreferenceUpdater()
Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean)
.apply()
true
} }
} }
} }

View File

@ -4,82 +4,22 @@
package org.mozilla.fenix.settings package org.mozilla.fenix.settings
import android.content.Context
import android.view.View import android.view.View
import android.widget.RadioButton import android.widget.RadioButton
import android.widget.TextView import android.widget.TextView
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.preference.Preference
import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ThemeManager 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 { fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions {
return when (featurePhone) { return when (featurePhone) {
PhoneFeature.CAMERA -> { PhoneFeature.CAMERA -> copy(camera = camera.toggle())
copy( PhoneFeature.LOCATION -> copy(location = location.toggle())
camera = camera.toggle() PhoneFeature.MICROPHONE -> copy(microphone = microphone.toggle())
) PhoneFeature.NOTIFICATION -> copy(notification = notification.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)
} }
} }
@ -111,3 +51,18 @@ fun initBlockedByAndroidView(phoneFeature: PhoneFeature, blockedByAndroidView: V
blockedByAndroidView.visibility = View.GONE 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 <reified T> Preference.setOnPreferenceChangeListener(
crossinline onPreferenceChangeListener: (Preference, T) -> Boolean
) {
setOnPreferenceChangeListener { preference: Preference, newValue: Any ->
(newValue as? T)?.let { onPreferenceChangeListener(preference, it) } ?: false
}
}

View File

@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.NavHostFragment.findNavController import androidx.navigation.fragment.NavHostFragment.findNavController
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_pair.* import kotlinx.android.synthetic.main.fragment_pair.*
import mozilla.components.feature.qr.QrFeature import mozilla.components.feature.qr.QrFeature
import mozilla.components.support.base.feature.BackHandler import mozilla.components.support.base.feature.BackHandler
@ -68,8 +69,7 @@ class PairFragment : Fragment(), BackHandler {
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
qrFeature.onBackPressed() qrFeature.onBackPressed()
findNavController(this@PairFragment) findNavController().popBackStack(R.id.turnOnSyncFragment, false)
.popBackStack(R.id.turnOnSyncFragment, false)
return true return true
} }

View File

@ -8,8 +8,12 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.Manifest.permission.RECORD_AUDIO import android.Manifest.permission.RECORD_AUDIO
import android.content.Context import android.content.Context
import androidx.annotation.StringRes
import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import mozilla.components.support.ktx.android.content.isPermissionGranted 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 org.mozilla.fenix.utils.Settings
import android.Manifest.permission.CAMERA as CAMERA_PERMISSION import android.Manifest.permission.CAMERA as CAMERA_PERMISSION
@ -32,57 +36,50 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>)
} }
fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings? = null): String { fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings? = null): String {
val label = when (this) { @StringRes val stringRes = when (getStatus(sitePermissions, settings)) {
CAMERA -> { SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked
sitePermissions?.camera?.toString(context) ?: settings SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow
?.sitePermissionsPhoneFeatureCameraAction SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed
?.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)
}
} }
return requireNotNull(label) return context.getString(stringRes)
} }
fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings? = null): SitePermissions.Status { fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings? = null): SitePermissions.Status {
val status = when (this) { val status = getStatus(sitePermissions) ?: settings?.let(::getAction)?.toStatus()
CAMERA -> {
sitePermissions?.camera ?: settings
?.sitePermissionsPhoneFeatureCameraAction
?.toStatus()
}
LOCATION -> {
sitePermissions?.location ?: settings
?.sitePermissionsPhoneFeatureLocation
?.toStatus()
}
MICROPHONE -> {
sitePermissions?.microphone ?: settings
?.sitePermissionsPhoneFeatureMicrophoneAction
?.toStatus()
}
NOTIFICATION -> {
sitePermissions?.notification ?: settings
?.sitePermissionsPhoneFeatureNotificationAction
?.toStatus()
}
}
return requireNotNull(status) 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 { companion object {
fun findFeatureBy(permissions: Array<out String>): PhoneFeature? { fun findFeatureBy(permissions: Array<out String>): PhoneFeature? {
return PhoneFeature.values().find { feature -> return PhoneFeature.values().find { feature ->

View File

@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
class SearchEngineFragment : PreferenceFragmentCompat() { class SearchEngineFragment : PreferenceFragmentCompat() {
@ -23,25 +24,17 @@ class SearchEngineFragment : PreferenceFragmentCompat() {
(activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.show()
val searchSuggestionsPreference = val searchSuggestionsPreference =
findPreference<SwitchPreference>(getString(R.string.pref_key_show_search_suggestions))?.apply { findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_show_search_suggestions))?.apply {
isChecked = Settings.getInstance(context).showSearchSuggestions isChecked = Settings.getInstance(context).showSearchSuggestions
} }
searchSuggestionsPreference?.setOnPreferenceChangeListener { preference, newValue -> searchSuggestionsPreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean)
.apply()
true
}
val showVisitedSitesBookmarks = val showVisitedSitesBookmarks =
findPreference<SwitchPreference>(getString(R.string.pref_key_show_visited_sites_bookmarks))?.apply { findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_show_visited_sites_bookmarks))?.apply {
isChecked = Settings.getInstance(context).shouldShowVisitedSitesBookmarks isChecked = Settings.getInstance(context).shouldShowVisitedSitesBookmarks
} }
showVisitedSitesBookmarks?.setOnPreferenceChangeListener { preference, newValue -> showVisitedSitesBookmarks?.onPreferenceChangeListener = SharedPreferenceUpdater()
Settings.getInstance(preference.context).preferences.edit().putBoolean(preference.key, newValue as Boolean)
.apply()
true
}
} }
} }

View File

@ -90,7 +90,7 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver {
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener) preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
if (SDK_INT <= Build.VERSION_CODES.M) { if (SDK_INT <= Build.VERSION_CODES.M) {
findPreference<DefaultBrowserPreference>(getString(R.string.pref_key_make_default_browser))?.apply { findPreference<DefaultBrowserPreference>(getPreferenceKey(R.string.pref_key_make_default_browser))?.apply {
isVisible = false isVisible = false
} }
} }
@ -106,17 +106,17 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver {
(activity as AppCompatActivity).title = getString(R.string.settings_title) (activity as AppCompatActivity).title = getString(R.string.settings_title)
(activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.show()
val defaultBrowserPreference = val defaultBrowserPreference =
findPreference<DefaultBrowserPreference>(getString(R.string.pref_key_make_default_browser)) findPreference<DefaultBrowserPreference>(getPreferenceKey(R.string.pref_key_make_default_browser))
defaultBrowserPreference?.updateSwitch() defaultBrowserPreference?.updateSwitch()
val searchEnginePreference = val searchEnginePreference =
findPreference<Preference>(getString(R.string.pref_key_search_engine_settings)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_search_engine_settings))
searchEnginePreference?.summary = context?.let { searchEnginePreference?.summary = context?.let {
requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name
} }
val trackingProtectionPreference = val trackingProtectionPreference =
findPreference<Preference>(getString(R.string.pref_key_tracking_protection_settings)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_tracking_protection_settings))
trackingProtectionPreference?.summary = context?.let { trackingProtectionPreference?.summary = context?.let {
if (org.mozilla.fenix.utils.Settings.getInstance(it).shouldUseTrackingProtection) { if (org.mozilla.fenix.utils.Settings.getInstance(it).shouldUseTrackingProtection) {
getString(R.string.tracking_protection_on) getString(R.string.tracking_protection_on)
@ -126,12 +126,12 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver {
} }
val themesPreference = val themesPreference =
findPreference<Preference>(getString(R.string.pref_key_theme)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_theme))
themesPreference?.summary = context?.let { themesPreference?.summary = context?.let {
org.mozilla.fenix.utils.Settings.getInstance(it).themeSettingString org.mozilla.fenix.utils.Settings.getInstance(it).themeSettingString
} }
val aboutPreference = findPreference<Preference>(getString(R.string.pref_key_about)) val aboutPreference = findPreference<Preference>(getPreferenceKey(R.string.pref_key_about))
val appName = getString(R.string.app_name) val appName = getString(R.string.app_name)
aboutPreference?.title = getString(R.string.preferences_about, appName) aboutPreference?.title = getString(R.string.preferences_about, appName)
@ -198,16 +198,16 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver {
navigateToThemeSettings() navigateToThemeSettings()
} }
resources.getString(pref_key_privacy_link) -> { resources.getString(pref_key_privacy_link) -> {
requireContext().apply { requireContext().let { context ->
val intent = SupportUtils.createCustomTabIntent(this, SupportUtils.PRIVACY_NOTICE_URL) val intent = SupportUtils.createCustomTabIntent(context, SupportUtils.PRIVACY_NOTICE_URL)
startActivity(intent) startActivity(intent)
} }
} }
resources.getString(pref_key_your_rights) -> { resources.getString(pref_key_your_rights) -> {
requireContext().apply { requireContext().let { context ->
val intent = SupportUtils.createCustomTabIntent( val intent = SupportUtils.createCustomTabIntent(
this, context,
SupportUtils.getSumoURLForTopic(context!!, SupportUtils.SumoTopic.YOUR_RIGHTS) SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS)
) )
startActivity(intent) startActivity(intent)
} }
@ -229,9 +229,9 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver {
} }
private fun setupPreferences() { private fun setupPreferences() {
val makeDefaultBrowserKey = context!!.getPreferenceKey(pref_key_make_default_browser) val makeDefaultBrowserKey = getPreferenceKey(pref_key_make_default_browser)
val leakKey = context!!.getPreferenceKey(pref_key_leakcanary) val leakKey = getPreferenceKey(pref_key_leakcanary)
val debuggingKey = context!!.getPreferenceKey(pref_key_remote_debugging) val debuggingKey = getPreferenceKey(pref_key_remote_debugging)
val preferenceMakeDefaultBrowser = findPreference<Preference>(makeDefaultBrowserKey) val preferenceMakeDefaultBrowser = findPreference<Preference>(makeDefaultBrowserKey)
val preferenceLeakCanary = findPreference<Preference>(leakKey) val preferenceLeakCanary = findPreference<Preference>(leakKey)

View File

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

View File

@ -19,6 +19,7 @@ import org.jetbrains.anko.noButton
import org.jetbrains.anko.yesButton import org.jetbrains.anko.yesButton
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components 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.CAMERA
import org.mozilla.fenix.settings.PhoneFeature.LOCATION import org.mozilla.fenix.settings.PhoneFeature.LOCATION
import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
@ -83,7 +84,7 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() {
} }
private fun bindClearPermissionsButton() { 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)) val button: Preference = requireNotNull(findPreference(keyPreference))
button.onPreferenceClickListener = Preference.OnPreferenceClickListener { button.onPreferenceClickListener = Preference.OnPreferenceClickListener {

View File

@ -11,10 +11,7 @@ import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.Preference.OnPreferenceClickListener
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.settings.PhoneFeature.CAMERA import org.mozilla.fenix.ext.getPreferenceKey
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.utils.Settings import org.mozilla.fenix.utils.Settings
@SuppressWarnings("TooManyFunctions") @SuppressWarnings("TooManyFunctions")
@ -36,13 +33,12 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
} }
private fun setupPreferences() { private fun setupPreferences() {
bindCategoryPhoneFeatures() bindCategoryPhoneFeatures()
bindExceptions() bindExceptions()
} }
private fun 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<Preference>(findPreference(keyExceptions)) val exceptionsCategory = requireNotNull<Preference>(findPreference(keyExceptions))
exceptionsCategory.onPreferenceClickListener = OnPreferenceClickListener { exceptionsCategory.onPreferenceClickListener = OnPreferenceClickListener {
@ -53,33 +49,17 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
} }
private fun bindCategoryPhoneFeatures() { private fun bindCategoryPhoneFeatures() {
val settings = Settings.getInstance(requireContext()) PhoneFeature.values().forEach(::initPhoneFeature)
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)
} }
private fun initPhoneFeature(phoneFeature: PhoneFeature, summary: String) { private fun initPhoneFeature(phoneFeature: PhoneFeature) {
val keyPreference = phoneFeature.getPreferenceKey(requireContext()) val context = requireContext()
val cameraPhoneFeatures: Preference = requireNotNull(findPreference(keyPreference)) 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.summary = summary
cameraPhoneFeatures.onPreferenceClickListener = OnPreferenceClickListener { cameraPhoneFeatures.onPreferenceClickListener = OnPreferenceClickListener {

View File

@ -92,7 +92,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() {
} }
private fun RadioButton.restoreState(action: SitePermissionsRules.Action) { private fun RadioButton.restoreState(action: SitePermissionsRules.Action) {
if (phoneFeature.action == action) { if (phoneFeature.getAction(settings) == action) {
this.isChecked = true this.isChecked = true
this.setStartCheckedIndicator() 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) { private fun initSettingsButton(rootView: View) {
val button = rootView.findViewById<Button>(R.id.settings_button) val button = rootView.findViewById<Button>(R.id.settings_button)
button.setOnClickListener { button.setOnClickListener {
@ -144,11 +134,6 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() {
} }
private fun saveActionInSettings(action: SitePermissionsRules.Action) { private fun saveActionInSettings(action: SitePermissionsRules.Action) {
when (phoneFeature) { settings.setSitePermissionsPhoneFeatureAction(phoneFeature, action)
PhoneFeature.CAMERA -> settings.sitePermissionsPhoneFeatureCameraAction = action
PhoneFeature.LOCATION -> settings.sitePermissionsPhoneFeatureLocation = action
PhoneFeature.MICROPHONE -> settings.sitePermissionsPhoneFeatureMicrophoneAction = action
PhoneFeature.NOTIFICATION -> settings.sitePermissionsPhoneFeatureNotificationAction = action
}
} }
} }

View File

@ -39,23 +39,19 @@ import org.mozilla.fenix.R
import java.text.NumberFormat import java.text.NumberFormat
/** /**
* Preference based on android.preference.SeekBarPreference but uses support preference as a base * Preference based on android.preference.SeekBarPreference but uses support preference as a base.
* . It contains a title and a [SeekBar] and a SeekBar value [TextView] and an Example [TextView]. * It contains a title and a [SeekBar] and a SeekBar value [TextView] and an Example [TextView].
* The actual preference layout is customizable by setting `android:layout` on the * The actual preference layout is customizable by setting `android:layout` on the
* preference widget layout or `seekBarPreferenceStyle` attribute. * preference widget layout or `seekBarPreferenceStyle` attribute.
* *
*
* The [SeekBar] within the preference can be defined adjustable or not by setting `adjustable` attribute. * The [SeekBar] within the preference can be defined adjustable or not by setting `adjustable` attribute.
* If adjustable, the preference will be responsive to DPAD left/right keys. * If adjustable, the preference will be responsive to DPAD left/right keys.
* Otherwise, it skips those keys. * Otherwise, it skips those keys.
* *
*
* The [SeekBar] value view can be shown or disabled by setting `showSeekBarValue` * The [SeekBar] value view can be shown or disabled by setting `showSeekBarValue`
* attribute to true or false, respectively. * attribute to true or false, respectively.
* *
* * Other [SeekBar] specific attributes (e.g. `title, summary, defaultValue, min, max`)
* Other [SeekBar] specific attributes (e.g. `title, summary, defaultValue, min,
* max`)
* can be set directly on the preference widget layout. * can be set directly on the preference widget layout.
*/ */
class TextPercentageSeekBarPreference @JvmOverloads constructor( class TextPercentageSeekBarPreference @JvmOverloads constructor(
@ -64,21 +60,32 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
defStyleAttr: Int = R.attr.seekBarPreferenceStyle, defStyleAttr: Int = R.attr.seekBarPreferenceStyle,
defStyleRes: Int = 0 defStyleRes: Int = 0
) : Preference(context, attrs, defStyleAttr, defStyleRes) { ) : Preference(context, attrs, defStyleAttr, defStyleRes) {
internal /* synthetic access */ var mSeekBarValue: Int = 0 /* synthetic access */
internal /* synthetic access */ var mMin: Int = 0 internal var mSeekBarValue: Int = 0
/* synthetic access */
internal var mMin: Int = 0
private var mMax: Int = 0 private var mMax: Int = 0
private var mSeekBarIncrement: Int = 0 private var mSeekBarIncrement: Int = 0
internal /* synthetic access */ var mTrackingTouch: Boolean = false /* synthetic access */
internal /* synthetic access */ var mSeekBar: SeekBar? = null internal var mTrackingTouch: Boolean = false
/* synthetic access */
internal var mSeekBar: SeekBar? = null
private var mSeekBarValueTextView: TextView? = null private var mSeekBarValueTextView: TextView? = null
private var mExampleTextTextView: TextView? = null private var mExampleTextTextView: TextView? = null
// Whether the SeekBar should respond to the left/right keys /**
/* synthetic access */ var isAdjustable: Boolean = false * Whether the SeekBar should respond to the left/right keys
// Whether to show the SeekBar value TextView next to the bar */
/* synthetic access */
var isAdjustable: Boolean = false
/**
* Whether to show the SeekBar value TextView next to the bar
*/
private var mShowSeekBarValue: Boolean = false private var mShowSeekBarValue: Boolean = false
// Whether the SeekBarPreference should continuously save the Seekbar value while it is being /**
// dragged. * Whether the SeekBarPreference should continuously save the Seekbar value while it is being dragged.
/* synthetic access */ var updatesContinuously: Boolean = false */
/* synthetic access */
var updatesContinuously: Boolean = false
/** /**
* Listener reacting to the [SeekBar] changing value by the user * Listener reacting to the [SeekBar] changing value by the user
*/ */
@ -111,26 +118,21 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
* to be handled accordingly. * to be handled accordingly.
*/ */
private val mSeekBarKeyListener = View.OnKeyListener { _, keyCode, event -> private val mSeekBarKeyListener = View.OnKeyListener { _, keyCode, event ->
if (event.action != KeyEvent.ACTION_DOWN) { return@OnKeyListener if (event.action != KeyEvent.ACTION_DOWN) {
return@OnKeyListener false false
} } else if (!isAdjustable && (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
if (!isAdjustable && (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
// Right or left keys are pressed when in non-adjustable mode; Skip the keys. // Right or left keys are pressed when in non-adjustable mode; Skip the keys.
return@OnKeyListener false false
} } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
// We don't want to propagate the click keys down to the SeekBar view since it will
// We don't want to propagate the click keys down to the SeekBar view since it will // create the ripple effect for the thumb.
// create the ripple effect for the thumb. false
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) { } else if (mSeekBar == null) {
return@OnKeyListener false
}
if (mSeekBar == null) {
Log.e(TAG, "SeekBar view is null and hence cannot be adjusted.") Log.e(TAG, "SeekBar view is null and hence cannot be adjusted.")
return@OnKeyListener false false
} else {
mSeekBar!!.onKeyDown(keyCode, event)
} }
mSeekBar!!.onKeyDown(keyCode, event)
} }
/** /**
@ -158,13 +160,14 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
* from the default mKeyProgressIncrement value in [android.widget.AbsSeekBar]. * from the default mKeyProgressIncrement value in [android.widget.AbsSeekBar].
* @return The amount of increment on the [SeekBar] performed after each user's arrow * @return The amount of increment on the [SeekBar] performed after each user's arrow
* key press * key press
*
* Sets the increment amount on the [SeekBar] for each arrow key press.
* @param seekBarIncrement The amount to increment or decrement when the user presses an
* arrow key.
*/ */
var seekBarIncrement: Int var seekBarIncrement: Int
get() = mSeekBarIncrement get() = mSeekBarIncrement
/**
* Sets the increment amount on the [SeekBar] for each arrow key press.
* @param seekBarIncrement The amount to increment or decrement when the user presses an
* arrow key.
*/
set(seekBarIncrement) { set(seekBarIncrement) {
if (seekBarIncrement != mSeekBarIncrement) { if (seekBarIncrement != mSeekBarIncrement) {
mSeekBarIncrement = Math.min(mMax - mMin, Math.abs(seekBarIncrement)) mSeekBarIncrement = Math.min(mMax - mMin, Math.abs(seekBarIncrement))
@ -173,10 +176,7 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
} }
/** /**
* Gets the upper bound set on the [SeekBar]. * Gets/Sets the upper bound set on the [SeekBar].
* @return The upper bound set
* Sets the upper bound on the [SeekBar].
* @param max The upper bound to set
*/ */
var max: Int var max: Int
get() = mMax get() = mMax
@ -195,29 +195,27 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
* Gets whether the current [SeekBar] value is displayed to the user. * Gets whether the current [SeekBar] value is displayed to the user.
* @return Whether the current [SeekBar] value is displayed to the user * @return Whether the current [SeekBar] value is displayed to the user
* @see .setShowSeekBarValue * @see .setShowSeekBarValue
* Sets whether the current [SeekBar] value is displayed to the user.
* @param showSeekBarValue Whether the current [SeekBar] value is displayed to the user
* @see .getShowSeekBarValue
*/ */
var showSeekBarValue: Boolean var showSeekBarValue: Boolean
get() = mShowSeekBarValue get() = mShowSeekBarValue
/**
* Sets whether the current [SeekBar] value is displayed to the user.
* @param showSeekBarValue Whether the current [SeekBar] value is displayed to the user
* @see .getShowSeekBarValue
*/
set(showSeekBarValue) { set(showSeekBarValue) {
mShowSeekBarValue = showSeekBarValue mShowSeekBarValue = showSeekBarValue
notifyChanged() notifyChanged()
} }
/** /**
* Gets the current progress of the [SeekBar]. * Gets/Sets the current progress of the [SeekBar].
* @return The current progress of the [SeekBar]
* Sets the current progress of the [SeekBar].
* @param seekBarValue The current progress of the [SeekBar]
*/ */
var value: Int var value: Int
get() = mSeekBarValue get() = mSeekBarValue
set(seekBarValue) = setValueInternal(seekBarValue, true) set(seekBarValue) = setValueInternal(seekBarValue, true)
init { init {
val a = context.obtainStyledAttributes( val a = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes
) )
@ -308,7 +306,8 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
* Persist the [SeekBar]'s SeekBar value if callChangeListener returns true, otherwise * Persist the [SeekBar]'s SeekBar value if callChangeListener returns true, otherwise
* set the [SeekBar]'s value to the stored value. * set the [SeekBar]'s value to the stored value.
*/ */
internal /* synthetic access */ fun syncValueInternal(seekBar: SeekBar) { /* synthetic access */
internal fun syncValueInternal(seekBar: SeekBar) {
val seekBarValue = mMin + seekBar.progress val seekBarValue = mMin + seekBar.progress
if (seekBarValue != mSeekBarValue) { if (seekBarValue != mSeekBarValue) {
if (callChangeListener(seekBarValue)) { if (callChangeListener(seekBarValue)) {
@ -324,9 +323,10 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
/** /**
* Attempts to update the TextView label that displays the current value. * Attempts to update the TextView label that displays the current value.
* *
* @param value the value to display next to the [SeekBar] * @param labelValue the value to display next to the [SeekBar]
*/ */
internal /* synthetic access */ fun updateLabelValue(labelValue: Int) { /* synthetic access */
internal fun updateLabelValue(labelValue: Int) {
var value = labelValue var value = labelValue
if (mSeekBarValueTextView != null) { if (mSeekBarValueTextView != null) {
value = value * STEP_SIZE + MIN_VALUE value = value * STEP_SIZE + MIN_VALUE
@ -339,9 +339,9 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
/** /**
* Attempts to update the example TextView text with text scale size. * Attempts to update the example TextView text with text scale size.
* *
* @param value the value of text size * @param textValue the value of text size
*/ */
internal /* synthetic access */ fun updateExampleTextValue(textValue: Int) { internal fun updateExampleTextValue(textValue: Int) {
var value = textValue var value = textValue
if (mExampleTextTextView != null) { if (mExampleTextTextView != null) {
value = value * STEP_SIZE + MIN_VALUE value = value * STEP_SIZE + MIN_VALUE

View File

@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
class ThemeFragment : PreferenceFragmentCompat() { class ThemeFragment : PreferenceFragmentCompat() {
@ -60,7 +61,7 @@ class ThemeFragment : PreferenceFragmentCompat() {
} }
private fun bindLightTheme() { private fun bindLightTheme() {
val keyLightTheme = getString(R.string.pref_key_light_theme) val keyLightTheme = getPreferenceKey(R.string.pref_key_light_theme)
radioLightTheme = requireNotNull(findPreference(keyLightTheme)) radioLightTheme = requireNotNull(findPreference(keyLightTheme))
radioLightTheme.onClickListener { radioLightTheme.onClickListener {
setNewTheme(AppCompatDelegate.MODE_NIGHT_NO) setNewTheme(AppCompatDelegate.MODE_NIGHT_NO)
@ -70,7 +71,7 @@ class ThemeFragment : PreferenceFragmentCompat() {
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
// Suppressing erroneous lint warning about using MODE_NIGHT_AUTO_BATTERY, a likely library bug // Suppressing erroneous lint warning about using MODE_NIGHT_AUTO_BATTERY, a likely library bug
private fun bindAutoBatteryTheme() { private fun bindAutoBatteryTheme() {
val keyBatteryTheme = getString(R.string.pref_key_auto_battery_theme) val keyBatteryTheme = getPreferenceKey(R.string.pref_key_auto_battery_theme)
radioAutoBatteryTheme = requireNotNull(findPreference(keyBatteryTheme)) radioAutoBatteryTheme = requireNotNull(findPreference(keyBatteryTheme))
radioAutoBatteryTheme.onClickListener { radioAutoBatteryTheme.onClickListener {
setNewTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) setNewTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
@ -78,7 +79,7 @@ class ThemeFragment : PreferenceFragmentCompat() {
} }
private fun bindDarkTheme() { private fun bindDarkTheme() {
val keyDarkTheme = getString(R.string.pref_key_dark_theme) val keyDarkTheme = getPreferenceKey(R.string.pref_key_dark_theme)
radioDarkTheme = requireNotNull(findPreference(keyDarkTheme)) radioDarkTheme = requireNotNull(findPreference(keyDarkTheme))
radioDarkTheme.onClickListener { radioDarkTheme.onClickListener {
setNewTheme(AppCompatDelegate.MODE_NIGHT_YES) setNewTheme(AppCompatDelegate.MODE_NIGHT_YES)
@ -86,7 +87,7 @@ class ThemeFragment : PreferenceFragmentCompat() {
} }
private fun bindFollowDeviceTheme() { private fun bindFollowDeviceTheme() {
val keyDeviceTheme = getString(R.string.pref_key_follow_device_theme) val keyDeviceTheme = getPreferenceKey(R.string.pref_key_follow_device_theme)
radioFollowDeviceTheme = requireNotNull(findPreference(keyDeviceTheme)) radioFollowDeviceTheme = requireNotNull(findPreference(keyDeviceTheme))
if (SDK_INT >= Build.VERSION_CODES.P) { if (SDK_INT >= Build.VERSION_CODES.P) {
radioFollowDeviceTheme.onClickListener { radioFollowDeviceTheme.onClickListener {

View File

@ -6,16 +6,27 @@ package org.mozilla.fenix.settings
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation import androidx.navigation.findNavController
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
/**
* Displays the toggle for tracking protection and a button to open
* the tracking protection [org.mozilla.fenix.exceptions.ExceptionsFragment].
*/
class TrackingProtectionFragment : PreferenceFragmentCompat() { class TrackingProtectionFragment : PreferenceFragmentCompat() {
private val exceptionsClickListener = Preference.OnPreferenceClickListener {
val directions = TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
view!!.findNavController().navigate(directions)
true
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tracking_protection_preferences, rootKey) setPreferencesFromResource(R.xml.tracking_protection_preferences, rootKey)
} }
@ -26,34 +37,22 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
(activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.show()
// Tracking Protection Switch // Tracking Protection Switch
val trackingProtectionKey = val trackingProtectionKey = getPreferenceKey(R.string.pref_key_tracking_protection)
context!!.getPreferenceKey(R.string.pref_key_tracking_protection)
val preferenceTP = findPreference<SwitchPreference>(trackingProtectionKey) val preferenceTP = findPreference<SwitchPreference>(trackingProtectionKey)
preferenceTP?.isChecked = Settings.getInstance(context!!).shouldUseTrackingProtection preferenceTP?.isChecked = Settings.getInstance(context!!).shouldUseTrackingProtection
preferenceTP?.onPreferenceChangeListener = preferenceTP?.setOnPreferenceChangeListener<Boolean> { preference, trackingProtectionOn ->
Preference.OnPreferenceChangeListener { _, newValue -> Settings.getInstance(preference.context).shouldUseTrackingProtection = trackingProtectionOn
Settings.getInstance(requireContext()).shouldUseTrackingProtection = newValue as Boolean with(preference.context.components) {
with(requireComponents) { val policy = core.createTrackingProtectionPolicy(trackingProtectionOn)
val policy = core.createTrackingProtectionPolicy(newValue) useCases.settingsUseCases.updateTrackingProtection(policy)
useCases.settingsUseCases.updateTrackingProtection.invoke(policy) useCases.sessionUseCases.reload()
useCases.sessionUseCases.reload.invoke()
}
true
} }
val exceptions =
context!!.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
val preferenceExceptions = findPreference<Preference>(exceptions)
preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions()
}
private fun getClickListenerForExceptions(): Preference.OnPreferenceClickListener {
return Preference.OnPreferenceClickListener {
val directions =
TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
Navigation.findNavController(view!!).navigate(directions)
true true
} }
val exceptions = getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
val preferenceExceptions = findPreference<Preference>(exceptions)
preferenceExceptions?.onPreferenceClickListener = exceptionsClickListener
} }
} }

View File

@ -11,8 +11,8 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.Navigation import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment.findNavController import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_turn_on_sync.view.* import kotlinx.android.synthetic.main.fragment_turn_on_sync.view.*
import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.OAuthAccount
@ -23,6 +23,22 @@ import org.mozilla.fenix.ext.requireComponents
@SuppressWarnings("TooManyFunctions") @SuppressWarnings("TooManyFunctions")
class TurnOnSyncFragment : Fragment(), AccountObserver { class TurnOnSyncFragment : Fragment(), AccountObserver {
private val signInClickListener = View.OnClickListener {
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.
}
private val paringClickListener = View.OnClickListener {
val directions = TurnOnSyncFragmentDirections.actionTurnOnSyncFragmentToPairFragment()
view!!.findNavController().navigate(directions)
requireComponents.analytics.metrics.track(Event.SyncAuthScanPairing)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
requireComponents.analytics.metrics.track(Event.SyncAuthOpened) requireComponents.analytics.metrics.track(Event.SyncAuthOpened)
@ -36,7 +52,7 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (requireComponents.backgroundServices.accountManager.authenticatedAccount() != null) { if (requireComponents.backgroundServices.accountManager.authenticatedAccount() != null) {
findNavController(this).popBackStack() findNavController().popBackStack()
return return
} }
@ -47,8 +63,8 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_turn_on_sync, container, false) val view = inflater.inflate(R.layout.fragment_turn_on_sync, container, false)
view.signInScanButton.setOnClickListener(getClickListenerForPairing()) view.signInScanButton.setOnClickListener(paringClickListener)
view.signInEmailButton.setOnClickListener(getClickListenerForSignIn()) view.signInEmailButton.setOnClickListener(signInClickListener)
view.signInInstructions.text = HtmlCompat.fromHtml( view.signInInstructions.text = HtmlCompat.fromHtml(
getString(R.string.sign_in_instructions), getString(R.string.sign_in_instructions),
HtmlCompat.FROM_HTML_MODE_LEGACY HtmlCompat.FROM_HTML_MODE_LEGACY
@ -56,25 +72,6 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
return view return view
} }
private fun getClickListenerForSignIn(): View.OnClickListener {
return View.OnClickListener {
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.
}
}
private fun getClickListenerForPairing(): View.OnClickListener {
return View.OnClickListener {
val directions = TurnOnSyncFragmentDirections.actionTurnOnSyncFragmentToPairFragment()
Navigation.findNavController(view!!).navigate(directions)
requireComponents.analytics.metrics.track(Event.SyncAuthScanPairing)
}
}
override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) { override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) {
FenixSnackbar.make(view!!, FenixSnackbar.LENGTH_SHORT) FenixSnackbar.make(view!!, FenixSnackbar.LENGTH_SHORT)
.setText(requireContext().getString(R.string.sync_syncing_in_progress)) .setText(requireContext().getString(R.string.sync_syncing_in_progress))

View File

@ -113,12 +113,12 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
accountManager.register(accountStateObserver, this, true) accountManager.register(accountStateObserver, this, true)
// Sign out // Sign out
val signOut = context!!.getPreferenceKey(R.string.pref_key_sign_out) val signOut = getPreferenceKey(R.string.pref_key_sign_out)
val preferenceSignOut = findPreference<Preference>(signOut) val preferenceSignOut = findPreference<Preference>(signOut)
preferenceSignOut?.onPreferenceClickListener = getClickListenerForSignOut() preferenceSignOut?.onPreferenceClickListener = getClickListenerForSignOut()
// Sync now // Sync now
val syncNow = context!!.getPreferenceKey(R.string.pref_key_sync_now) val syncNow = getPreferenceKey(R.string.pref_key_sync_now)
val preferenceSyncNow = findPreference<Preference>(syncNow) val preferenceSyncNow = findPreference<Preference>(syncNow)
preferenceSyncNow?.let { preferenceSyncNow?.let {
it.onPreferenceClickListener = getClickListenerForSyncNow() it.onPreferenceClickListener = getClickListenerForSyncNow()
@ -134,7 +134,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
// Device Name // Device Name
val deviceConstellation = accountManager.authenticatedAccount()?.deviceConstellation() val deviceConstellation = accountManager.authenticatedAccount()?.deviceConstellation()
val deviceNameKey = context!!.getPreferenceKey(R.string.pref_key_sync_device_name) val deviceNameKey = getPreferenceKey(R.string.pref_key_sync_device_name)
findPreference<EditTextPreference>(deviceNameKey)?.apply { findPreference<EditTextPreference>(deviceNameKey)?.apply {
onPreferenceChangeListener = getChangeListenerForDeviceName() onPreferenceChangeListener = getChangeListenerForDeviceName()
deviceConstellation?.state()?.currentDevice?.let { device -> deviceConstellation?.state()?.currentDevice?.let { device ->
@ -211,7 +211,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
private val syncStatusObserver = object : SyncStatusObserver { private val syncStatusObserver = object : SyncStatusObserver {
override fun onStarted() { override fun onStarted() {
lifecycleScope.launch { lifecycleScope.launch {
val pref = findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_now)) val pref = findPreference<Preference>(getPreferenceKey(R.string.pref_key_sync_now))
view?.announceForAccessibility(getString(R.string.sync_syncing_in_progress)) view?.announceForAccessibility(getString(R.string.sync_syncing_in_progress))
pref?.title = getString(R.string.sync_syncing_in_progress) pref?.title = getString(R.string.sync_syncing_in_progress)
pref?.isEnabled = false pref?.isEnabled = false
@ -221,7 +221,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
// Sync stopped successfully. // Sync stopped successfully.
override fun onIdle() { override fun onIdle() {
lifecycleScope.launch { lifecycleScope.launch {
val pref = findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_now)) val pref = findPreference<Preference>(getPreferenceKey(R.string.pref_key_sync_now))
pref?.let { pref?.let {
pref.title = getString(R.string.preferences_sync_now) pref.title = getString(R.string.preferences_sync_now)
pref.isEnabled = true pref.isEnabled = true
@ -235,7 +235,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
// Sync stopped after encountering a problem. // Sync stopped after encountering a problem.
override fun onError(error: Exception?) { override fun onError(error: Exception?) {
lifecycleScope.launch { lifecycleScope.launch {
val pref = findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_now)) val pref = findPreference<Preference>(getPreferenceKey(R.string.pref_key_sync_now))
pref?.let { pref?.let {
pref.title = getString(R.string.preferences_sync_now) pref.title = getString(R.string.preferences_sync_now)
pref.isEnabled = true pref.isEnabled = true
@ -256,7 +256,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
} }
private fun updateDeviceName(state: AccountSettingsState) { private fun updateDeviceName(state: AccountSettingsState) {
val deviceNameKey = context!!.getPreferenceKey(R.string.pref_key_sync_device_name) val deviceNameKey = getPreferenceKey(R.string.pref_key_sync_device_name)
val preferenceDeviceName = findPreference<Preference>(deviceNameKey) val preferenceDeviceName = findPreference<Preference>(deviceNameKey)
preferenceDeviceName?.summary = state.deviceName preferenceDeviceName?.summary = state.deviceName
} }
@ -280,7 +280,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
) )
} }
val syncNow = context!!.getPreferenceKey(R.string.pref_key_sync_now) val syncNow = getPreferenceKey(R.string.pref_key_sync_now)
findPreference<Preference>(syncNow)?.summary = value findPreference<Preference>(syncNow)?.summary = value
} }

View File

@ -48,10 +48,10 @@ class QuickSettingsComponent(
return if (sitePermissions == null) { return if (sitePermissions == null) {
val settings = Settings.getInstance(context) val settings = Settings.getInstance(context)
val origin = requireNotNull(url.toUri().host) val origin = requireNotNull(url.toUri().host)
var location = settings.sitePermissionsPhoneFeatureLocation.toStatus() var location = settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION).toStatus()
var camera = settings.sitePermissionsPhoneFeatureCameraAction.toStatus() var camera = settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA).toStatus()
var microphone = settings.sitePermissionsPhoneFeatureMicrophoneAction.toStatus() var microphone = settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE).toStatus()
var notification = settings.sitePermissionsPhoneFeatureNotificationAction.toStatus() var notification = settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION).toStatus()
when (featurePhone) { when (featurePhone) {
PhoneFeature.CAMERA -> camera = camera.toggle() PhoneFeature.CAMERA -> camera = camera.toggle()

View File

@ -1,45 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.sharedpreferences
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import java.security.InvalidParameterException
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
private class SitePermissionsRulesActionPreference(
private val key: String
) : ReadWriteProperty<PreferencesHolder, SitePermissionsRules.Action> {
override fun getValue(thisRef: PreferencesHolder, property: KProperty<*>): SitePermissionsRules.Action =
intToAction(thisRef.preferences.getInt(key, ASK_TO_ALLOW_INT))
override fun setValue(thisRef: PreferencesHolder, property: KProperty<*>, value: SitePermissionsRules.Action) {
thisRef.preferences.edit().putInt(key, actionToInt(value)).apply()
}
companion object {
private const val BLOCKED_INT = 0
private const val ASK_TO_ALLOW_INT = 1
private fun actionToInt(action: SitePermissionsRules.Action) = when (action) {
SitePermissionsRules.Action.BLOCKED -> BLOCKED_INT
SitePermissionsRules.Action.ASK_TO_ALLOW -> ASK_TO_ALLOW_INT
}
private fun intToAction(action: Int) = when (action) {
BLOCKED_INT -> SitePermissionsRules.Action.BLOCKED
ASK_TO_ALLOW_INT -> SitePermissionsRules.Action.ASK_TO_ALLOW
else -> throw InvalidParameterException("$action is not a valid SitePermissionsRules.Action")
}
}
}
/**
* Property delegate for getting and setting a [SitePermissionsRules.Action] preference.
*/
fun sitePermissionsRulesActionPreference(
key: String
): ReadWriteProperty<PreferencesHolder, SitePermissionsRules.Action> = SitePermissionsRulesActionPreference(key)

View File

@ -14,9 +14,10 @@ import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.Config import org.mozilla.fenix.Config
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.settings.PhoneFeature
import org.mozilla.fenix.settings.sharedpreferences.PreferencesHolder import org.mozilla.fenix.settings.sharedpreferences.PreferencesHolder
import org.mozilla.fenix.settings.sharedpreferences.booleanPreference import org.mozilla.fenix.settings.sharedpreferences.booleanPreference
import org.mozilla.fenix.settings.sharedpreferences.sitePermissionsRulesActionPreference import java.security.InvalidParameterException
/** /**
* A simple wrapper for SharedPreferences that makes reading preference a little bit easier. * A simple wrapper for SharedPreferences that makes reading preference a little bit easier.
@ -29,6 +30,19 @@ class Settings private constructor(
companion object { companion object {
const val autoBounceMaximumCount = 2 const val autoBounceMaximumCount = 2
const val FENIX_PREFERENCES = "fenix_preferences" const val FENIX_PREFERENCES = "fenix_preferences"
private const val BLOCKED_INT = 0
private const val ASK_TO_ALLOW_INT = 1
private fun actionToInt(action: SitePermissionsRules.Action) = when (action) {
SitePermissionsRules.Action.BLOCKED -> BLOCKED_INT
SitePermissionsRules.Action.ASK_TO_ALLOW -> ASK_TO_ALLOW_INT
}
private fun intToAction(action: Int) = when (action) {
BLOCKED_INT -> SitePermissionsRules.Action.BLOCKED
ASK_TO_ALLOW_INT -> SitePermissionsRules.Action.ASK_TO_ALLOW
else -> throw InvalidParameterException("$action is not a valid SitePermissionsRules.Action")
}
var instance: Settings? = null var instance: Settings? = null
@ -147,28 +161,19 @@ class Settings private constructor(
default = true default = true
) )
var sitePermissionsPhoneFeatureCameraAction by sitePermissionsRulesActionPreference( fun getSitePermissionsPhoneFeatureAction(feature: PhoneFeature) =
appContext.getPreferenceKey(R.string.pref_key_phone_feature_camera) intToAction(preferences.getInt(feature.getPreferenceKey(appContext), ASK_TO_ALLOW_INT))
)
var sitePermissionsPhoneFeatureMicrophoneAction by sitePermissionsRulesActionPreference( fun setSitePermissionsPhoneFeatureAction(feature: PhoneFeature, value: SitePermissionsRules.Action) {
appContext.getPreferenceKey(R.string.pref_key_phone_feature_microphone) preferences.edit().putInt(feature.getPreferenceKey(appContext), actionToInt(value)).apply()
) }
var sitePermissionsPhoneFeatureNotificationAction by sitePermissionsRulesActionPreference(
appContext.getPreferenceKey(R.string.pref_key_phone_feature_notification)
)
var sitePermissionsPhoneFeatureLocation by sitePermissionsRulesActionPreference(
appContext.getPreferenceKey(R.string.pref_key_phone_feature_location)
)
fun getSitePermissionsCustomSettingsRules(): SitePermissionsRules { fun getSitePermissionsCustomSettingsRules(): SitePermissionsRules {
return SitePermissionsRules( return SitePermissionsRules(
notification = sitePermissionsPhoneFeatureNotificationAction, notification = getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION),
microphone = sitePermissionsPhoneFeatureMicrophoneAction, microphone = getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE),
location = sitePermissionsPhoneFeatureLocation, location = getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION),
camera = sitePermissionsPhoneFeatureCameraAction camera = getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA)
) )
} }

View File

@ -2,10 +2,12 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this - License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:fillViewport="true"> android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -45,7 +47,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:gravity="center" android:gravity="center"
android:text="@string/sign_in_instructions" tools:text="@string/sign_in_instructions"
android:textColor="?primaryText" android:textColor="?primaryText"
android:textSize="16sp" android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -84,4 +86,4 @@
app:layout_constraintTop_toBottomOf="@id/signInScanButton" /> app:layout_constraintTop_toBottomOf="@id/signInScanButton" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@ -1,73 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.sharedpreferences
import android.content.SharedPreferences
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class SitePermissionsRulesActionPreferenceTest {
private lateinit var sharedPrefs: SharedPreferences
private lateinit var editor: SharedPreferences.Editor
@Before
fun setup() {
sharedPrefs = mockk(relaxed = true)
editor = mockk()
every { sharedPrefs.edit() } returns editor
every { editor.putInt(any(), any()) } returns editor
every { editor.apply() } just runs
}
@Test
fun `getter returns action from shared preferences`() {
val holder = object : PreferencesHolder {
override val preferences = sharedPrefs
val test by sitePermissionsRulesActionPreference("test_preference_key")
}
every { sharedPrefs.getInt("test_preference_key", 1) } returns 0
assertEquals(SitePermissionsRules.Action.BLOCKED, holder.test)
verify { sharedPrefs.getInt("test_preference_key", 1) }
}
@Test
fun `setter applies boolean to shared preferences`() {
val holder = object : PreferencesHolder {
override val preferences = sharedPrefs
var test by sitePermissionsRulesActionPreference("pref")
}
holder.test = SitePermissionsRules.Action.BLOCKED
verify { editor.putInt("pref", 0) }
verify { editor.apply() }
holder.test = SitePermissionsRules.Action.ASK_TO_ALLOW
verify { editor.putInt("pref", 1) }
verify { editor.apply() }
}
@Test
fun `getter defaults to ASK_TO_ALLOW`() {
every { sharedPrefs.getInt("key", 1) } returns 1
val holder = object : PreferencesHolder {
override val preferences = sharedPrefs
val action by sitePermissionsRulesActionPreference("key")
}
assertEquals(SitePermissionsRules.Action.ASK_TO_ALLOW, holder.action)
verify { sharedPrefs.getInt("key", 1) }
}
}

View File

@ -18,6 +18,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.TestApplication import org.mozilla.fenix.TestApplication
import org.mozilla.fenix.ext.clearAndCommit import org.mozilla.fenix.ext.clearAndCommit
import org.mozilla.fenix.settings.PhoneFeature
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
@ObsoleteCoroutinesApi @ObsoleteCoroutinesApi
@ -231,52 +232,52 @@ class SettingsTest {
fun sitePermissionsPhoneFeatureCameraAction() { fun sitePermissionsPhoneFeatureCameraAction() {
// When just created // When just created
// Then // Then
assertEquals(ASK_TO_ALLOW, settings.sitePermissionsPhoneFeatureCameraAction) assertEquals(ASK_TO_ALLOW, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA))
// When // When
settings.sitePermissionsPhoneFeatureCameraAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA, BLOCKED)
// Then // Then
assertEquals(BLOCKED, settings.sitePermissionsPhoneFeatureCameraAction) assertEquals(BLOCKED, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA))
} }
@Test @Test
fun sitePermissionsPhoneFeatureMicrophoneAction() { fun sitePermissionsPhoneFeatureMicrophoneAction() {
// When just created // When just created
// Then // Then
assertEquals(ASK_TO_ALLOW, settings.sitePermissionsPhoneFeatureMicrophoneAction) assertEquals(ASK_TO_ALLOW, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE))
// When // When
settings.sitePermissionsPhoneFeatureMicrophoneAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE, BLOCKED)
// Then // Then
assertEquals(BLOCKED, settings.sitePermissionsPhoneFeatureMicrophoneAction) assertEquals(BLOCKED, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE))
} }
@Test @Test
fun sitePermissionsPhoneFeatureNotificationAction() { fun sitePermissionsPhoneFeatureNotificationAction() {
// When just created // When just created
// Then // Then
assertEquals(ASK_TO_ALLOW, settings.sitePermissionsPhoneFeatureNotificationAction) assertEquals(ASK_TO_ALLOW, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION))
// When // When
settings.sitePermissionsPhoneFeatureNotificationAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION, BLOCKED)
// Then // Then
assertEquals(BLOCKED, settings.sitePermissionsPhoneFeatureNotificationAction) assertEquals(BLOCKED, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION))
} }
@Test @Test
fun sitePermissionsPhoneFeatureLocation() { fun sitePermissionsPhoneFeatureLocation() {
// When just created // When just created
// Then // Then
assertEquals(ASK_TO_ALLOW, settings.sitePermissionsPhoneFeatureLocation) assertEquals(ASK_TO_ALLOW, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION))
// When // When
settings.sitePermissionsPhoneFeatureLocation = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION, BLOCKED)
// Then // Then
assertEquals(BLOCKED, settings.sitePermissionsPhoneFeatureLocation) assertEquals(BLOCKED, settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION))
} }
@Test @Test
@ -292,7 +293,7 @@ class SettingsTest {
@Test @Test
fun getSitePermissionsCustomSettingsRules_camera() { fun getSitePermissionsCustomSettingsRules_camera() {
// When // When
settings.sitePermissionsPhoneFeatureCameraAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA, BLOCKED)
// Then // Then
assertEquals( assertEquals(
@ -304,7 +305,7 @@ class SettingsTest {
@Test @Test
fun getSitePermissionsCustomSettingsRules_notification() { fun getSitePermissionsCustomSettingsRules_notification() {
// When // When
settings.sitePermissionsPhoneFeatureNotificationAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION, BLOCKED)
// Then // Then
assertEquals( assertEquals(
@ -316,7 +317,7 @@ class SettingsTest {
@Test @Test
fun getSitePermissionsCustomSettingsRules_location() { fun getSitePermissionsCustomSettingsRules_location() {
// When // When
settings.sitePermissionsPhoneFeatureLocation = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION, BLOCKED)
// Then // Then
assertEquals( assertEquals(
@ -328,7 +329,7 @@ class SettingsTest {
@Test @Test
fun getSitePermissionsCustomSettingsRules_microphone() { fun getSitePermissionsCustomSettingsRules_microphone() {
// When // When
settings.sitePermissionsPhoneFeatureMicrophoneAction = BLOCKED settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE, BLOCKED)
// Then // Then
assertEquals( assertEquals(