Verified Commit dd134b49 authored by Matthew Finkel's avatar Matthew Finkel Committed by Pier Angelo Vendrame
Browse files

Add Security Level UI

Bug 40026: Implement Security Level settings

Bug 40026: Integrate Security Level settings
parent e350cd9e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ class Core(
                R.color.fx_mobile_layer_color_1
            ),
            httpsOnlyMode = context.settings().getHttpsOnlyMode(),
            torSecurityLevel = context.settings().torSecurityLevel().intRepresentation,
            spoofEnglish = context.settings().spoofEnglish
        )

+2 −0
Original line number Diff line number Diff line
@@ -566,6 +566,8 @@ class DefaultSessionControlController(
    }

    override fun handleOpenSecurityLevelSettingsClicked() {
        val directions = HomeFragmentDirections.actionGlobalTorSecurityLevelFragment()
        navController.nav(R.id.homeFragment, directions)
    }

    override fun handleReadPrivacyNoticeClicked() {
+39 −11
Original line number Diff line number Diff line
@@ -8,8 +8,11 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.TorOnboardingSecurityLevelBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.tor.SecurityLevel
import org.mozilla.fenix.tor.SecurityLevelUtil
import org.mozilla.fenix.utils.view.addToRadioGroup

class TorOnboardingSecurityLevelViewHolder(
@@ -17,12 +20,15 @@ class TorOnboardingSecurityLevelViewHolder(
    private val interactor: OnboardingInteractor
) : RecyclerView.ViewHolder(view) {

    private var _binding: TorOnboardingSecurityLevelBinding? = null
    private val binding get() = _binding!!

    private var standardSecurityLevel: OnboardingRadioButton
    private var saferSecurityLevel: OnboardingRadioButton
    private var safestSecurityLevel: OnboardingRadioButton

    init {
        val binding = TorOnboardingSecurityLevelBinding.bind(view)
        _binding = TorOnboardingSecurityLevelBinding.bind(view)
        binding.headerText.setOnboardingIcon(R.drawable.ic_onboarding_tracking_protection)

        standardSecurityLevel = binding.securityLevelStandardOption
@@ -34,34 +40,56 @@ class TorOnboardingSecurityLevelViewHolder(
        )

        binding.openSettingsButton.setOnClickListener {
            interactor.onOpenSettingsClicked()
            interactor.onOpenSecurityLevelSettingsClicked()
        }

        setupRadioGroup()
        setupRadioGroup(view)
    }

    private fun setupRadioGroup() {
    private fun setupRadioGroup(view: View) {

        addToRadioGroup(standardSecurityLevel, saferSecurityLevel, safestSecurityLevel)

        standardSecurityLevel.isChecked = true
        safestSecurityLevel.isChecked = false
        saferSecurityLevel.isChecked = false
        val securityLevel = try {
            SecurityLevelUtil.getSecurityLevelFromInt(
                view.context.components.core.engine.settings.torSecurityLevel
            )
        } catch (e: IllegalStateException) {
            SecurityLevel.STANDARD
        }

        standardSecurityLevel.isChecked = securityLevel == SecurityLevel.STANDARD
        safestSecurityLevel.isChecked = securityLevel == SecurityLevel.SAFEST
        saferSecurityLevel.isChecked = securityLevel == SecurityLevel.SAFER

        standardSecurityLevel.onClickListener {
            updateSecurityLevel()
            updateSecurityLevel(SecurityLevel.STANDARD, view)
        }

        saferSecurityLevel.onClickListener {
            updateSecurityLevel()
            updateSecurityLevel(SecurityLevel.SAFER, view)
        }

        safestSecurityLevel.onClickListener {
            updateSecurityLevel()
            updateSecurityLevel(SecurityLevel.SAFEST, view)
        }

        updateSecurityLevel(securityLevel, view)
    }

    private fun updateSecurityLevel() {
    private fun updateSecurityLevel(newLevel: SecurityLevel, view: View) {
        val resources = view.context.resources
        val securityLevel = when (newLevel) {
            SecurityLevel.STANDARD -> resources.getString(R.string.tor_security_level_standard_option)
            SecurityLevel.SAFER -> resources.getString(R.string.tor_security_level_safer_option)
            SecurityLevel.SAFEST -> resources.getString(R.string.tor_security_level_safest_option)
        }
        binding.currentLevel.text = resources.getString(
            R.string.tor_onboarding_chosen_security_level_label, securityLevel
        )
        view.context.components.let {
            it.core.engine.settings.torSecurityLevel = newLevel.intRepresentation
        }
    }

    companion object {
+3 −0
Original line number Diff line number Diff line
@@ -266,6 +266,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
            resources.getString(R.string.pref_key_tor_network_settings) -> {
                SettingsFragmentDirections.actionSettingsFragmentToTorNetworkSettingsFragment()
            }
            resources.getString(R.string.pref_key_tor_security_level_settings) -> {
                SettingsFragmentDirections.actionSettingsFragmentToTorSecurityLevelFragment()
            }
            resources.getString(R.string.pref_key_tracking_protection_settings) -> {
                TrackingProtection.etpSettings.record(NoExtras())
                SettingsFragmentDirections.actionSettingsFragmentToTrackingProtectionFragment()
+86 −0
Original line number Diff line number Diff line
/* 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

import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.tor.SecurityLevel
import org.mozilla.fenix.tor.SecurityLevelUtil
import org.mozilla.fenix.utils.view.GroupableRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
import org.mozilla.fenix.utils.view.uncheckAll

/**
 * Lets the user choose their security level
 */
@Suppress("SpreadOperator")
class TorSecurityLevelFragment : PreferenceFragmentCompat() {
    private val securityLevelRadioGroups = mutableListOf<GroupableRadioButton>()
    private var previousSecurityLevel: SecurityLevel? = null

    override fun onResume() {
        super.onResume()
        showToolbar(getString(R.string.preferences_tor_security_level_options))
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.tor_security_level_preferences, rootKey)

        val currentLevel: SecurityLevel? = context?.components?.let {
            try {
                SecurityLevelUtil.getSecurityLevelFromInt(
                    it.core.engine.settings.torSecurityLevel
                )
            } catch (e: IllegalStateException) {
                // The default state is 0. If we get an invalid state then
                // default to Standard (4).
                SecurityLevel.STANDARD
            }
        }

        if (currentLevel == null) {
            throw IllegalStateException("context or Components is null.")
        }

        val radioSafer = bindSecurityLevelRadio(SecurityLevel.SAFER)
        val radioSafest = bindSecurityLevelRadio(SecurityLevel.SAFEST)
        val radioStandard = bindSecurityLevelRadio(SecurityLevel.STANDARD)

        securityLevelRadioGroups.addAll(mutableListOf(radioSafer, radioSafest, radioStandard))
        // `*` is Kotlin's "spread" operator, for expanding an Array as a vararg.
        addToRadioGroup(*securityLevelRadioGroups.toTypedArray())

        securityLevelRadioGroups.uncheckAll()
        val securityLevelRadioButton = requirePreference<RadioButtonPreference>(currentLevel.preferenceKey)
        // Cache this for later comparison in the OnPreferenceChangeListener
        previousSecurityLevel = currentLevel
        securityLevelRadioButton.setCheckedWithoutClickListener(true)
    }

    private fun bindSecurityLevelRadio(
        level: SecurityLevel
    ): RadioButtonPreference {
        val radio = requirePreference<RadioButtonPreference>(level.preferenceKey)

        radio.summary = getString(level.contentDescriptionRes)

        radio.apply {
            setOnPreferenceChangeListener<Boolean> { preference, isChecked ->
                if (isChecked && (previousSecurityLevel!! != level)) {
                    preference.context.components.core.engine.settings.torSecurityLevel =
                        level.intRepresentation
                    previousSecurityLevel = level
                }
                true
            }
        }

        return radio
    }
}
Loading