For #1084 - Add tracking protection toggle to quick settings dialog

nightly-build-test
Emily Kager 5 years ago committed by Colin Lee
parent 3f906dc58a
commit 01a181975d

@ -517,10 +517,14 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance( val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance(
url = session.url, url = session.url,
isSecured = session.securityInfo.secure, isSecured = session.securityInfo.secure,
isTrackingProtectionOn = Settings.getInstance(context!!).shouldUseTrackingProtection,
sitePermissions = sitePermissions sitePermissions = sitePermissions
) )
quickSettingsSheet.sitePermissions = sitePermissions quickSettingsSheet.sitePermissions = sitePermissions
quickSettingsSheet.show(requireFragmentManager(), QuickSettingsSheetDialogFragment.FRAGMENT_TAG) quickSettingsSheet.show(
requireFragmentManager(),
QuickSettingsSheetDialogFragment.FRAGMENT_TAG
)
} }
} }
} }
@ -572,6 +576,6 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2 private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2
private const val REQUEST_CODE_APP_PERMISSIONS = 3 private const val REQUEST_CODE_APP_PERMISSIONS = 3
private const val TOOLBAR_HEIGHT = 56f private const val TOOLBAR_HEIGHT = 56f
private const val REPORT_SITE_ISSUE_URL = "https://webcompat.com/issues/new?url=%s&label=browser-fenix" const val REPORT_SITE_ISSUE_URL = "https://webcompat.com/issues/new?url=%s&label=browser-fenix"
} }
} }

@ -80,6 +80,16 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name
} }
val trackingProtectionPreference =
findPreference<Preference>(getString(R.string.pref_key_tracking_protection_settings))
trackingProtectionPreference?.summary = context?.let {
if (org.mozilla.fenix.utils.Settings.getInstance(it).shouldUseTrackingProtection) {
getString(R.string.tracking_protection_on)
} else {
getString(R.string.tracking_protection_off)
}
}
val themesPreference = val themesPreference =
findPreference<Preference>(getString(R.string.pref_key_theme)) findPreference<Preference>(getString(R.string.pref_key_theme))
themesPreference?.summary = context?.let { themesPreference?.summary = context?.let {

@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
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.ext.requireComponents
@ -40,6 +41,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
sessions.forEach { getEngineSession(it)?.enableTrackingProtection(policy) } sessions.forEach { getEngineSession(it)?.enableTrackingProtection(policy) }
} }
} }
requireContext().components.useCases.sessionUseCases.reload.invoke()
true true
} }

@ -35,6 +35,7 @@ class QuickSettingsComponent(
mode = QuickSettingsState.Mode.Normal( mode = QuickSettingsState.Mode.Normal(
change.url, change.url,
change.isSecured, change.isSecured,
change.isTrackingProtectionOn,
change.sitePermissions change.sitePermissions
) )
) )
@ -97,15 +98,27 @@ class QuickSettingsComponent(
data class QuickSettingsState(val mode: Mode) : ViewState { data class QuickSettingsState(val mode: Mode) : ViewState {
sealed class Mode { sealed class Mode {
data class Normal(val url: String, val isSecured: Boolean, val sitePermissions: SitePermissions?) : Mode() data class Normal(
data class ActionLabelUpdated(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) : val url: String,
val isSecured: Boolean,
val isTrackingProtectionOn: Boolean,
val sitePermissions: SitePermissions?
) : Mode()
data class ActionLabelUpdated(
val phoneFeature: PhoneFeature,
val sitePermissions: SitePermissions?
) :
Mode() Mode()
data class CheckPendingFeatureBlockedByAndroid(val sitePermissions: SitePermissions?) : Mode() data class CheckPendingFeatureBlockedByAndroid(val sitePermissions: SitePermissions?) :
Mode()
} }
} }
sealed class QuickSettingsAction : Action { sealed class QuickSettingsAction : Action {
data class SelectReportProblem(val url: String) : QuickSettingsAction()
data class ToggleTrackingProtection(val trackingProtection: Boolean) : QuickSettingsAction()
data class SelectBlockedByAndroid(val permissions: Array<String>) : QuickSettingsAction() data class SelectBlockedByAndroid(val permissions: Array<String>) : QuickSettingsAction()
data class TogglePermission(val featurePhone: PhoneFeature) : QuickSettingsAction() data class TogglePermission(val featurePhone: PhoneFeature) : QuickSettingsAction()
} }
@ -114,6 +127,7 @@ sealed class QuickSettingsChange : Change {
data class Change( data class Change(
val url: String, val url: String,
val isSecured: Boolean, val isSecured: Boolean,
val isTrackingProtectionOn: Boolean,
val sitePermissions: SitePermissions? val sitePermissions: SitePermissions?
) : QuickSettingsChange() ) : QuickSettingsChange()

@ -10,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.preference.PreferenceManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -17,7 +18,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragment
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter import org.mozilla.fenix.mvi.getManagedEmitter
@ -27,6 +30,7 @@ import kotlin.coroutines.CoroutineContext
private const val KEY_URL = "KEY_URL" private const val KEY_URL = "KEY_URL"
private const val KEY_IS_SECURED = "KEY_IS_SECURED" private const val KEY_IS_SECURED = "KEY_IS_SECURED"
private const val KEY_SITE_PERMISSIONS = "KEY_SITE_PERMISSIONS" private const val KEY_SITE_PERMISSIONS = "KEY_SITE_PERMISSIONS"
private const val KEY_IS_TP_ON = "KEY_IS_TP_ON"
private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4 private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4
@SuppressWarnings("TooManyFunctions") @SuppressWarnings("TooManyFunctions")
@ -34,6 +38,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS
private val safeArguments get() = requireNotNull(arguments) private val safeArguments get() = requireNotNull(arguments)
private val url: String by lazy { safeArguments.getString(KEY_URL) } private val url: String by lazy { safeArguments.getString(KEY_URL) }
private val isSecured: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SECURED) } private val isSecured: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SECURED) }
private val isTrackingProtectionOn: Boolean by lazy { safeArguments.getBoolean(KEY_IS_TP_ON) }
private lateinit var quickSettingsComponent: QuickSettingsComponent private lateinit var quickSettingsComponent: QuickSettingsComponent
private lateinit var job: Job private lateinit var job: Job
@ -59,7 +64,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS
quickSettingsComponent = QuickSettingsComponent( quickSettingsComponent = QuickSettingsComponent(
rootView as ConstraintLayout, ActionBusFactory.get(this), rootView as ConstraintLayout, ActionBusFactory.get(this),
QuickSettingsState( QuickSettingsState(
QuickSettingsState.Mode.Normal(url, isSecured, sitePermissions) QuickSettingsState.Mode.Normal(url, isSecured, isTrackingProtectionOn, sitePermissions)
) )
) )
} }
@ -70,6 +75,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS
fun newInstance( fun newInstance(
url: String, url: String,
isSecured: Boolean, isSecured: Boolean,
isTrackingProtectionOn: Boolean,
sitePermissions: SitePermissions? sitePermissions: SitePermissions?
): QuickSettingsSheetDialogFragment { ): QuickSettingsSheetDialogFragment {
@ -79,6 +85,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS
with(arguments) { with(arguments) {
putString(KEY_URL, url) putString(KEY_URL, url)
putBoolean(KEY_IS_SECURED, isSecured) putBoolean(KEY_IS_SECURED, isSecured)
putBoolean(KEY_IS_TP_ON, isTrackingProtectionOn)
putParcelable(KEY_SITE_PERMISSIONS, sitePermissions) putParcelable(KEY_SITE_PERMISSIONS, sitePermissions)
} }
fragment.arguments = arguments fragment.arguments = arguments
@ -110,6 +117,37 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS
is QuickSettingsAction.SelectBlockedByAndroid -> { is QuickSettingsAction.SelectBlockedByAndroid -> {
requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS) requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS)
} }
is QuickSettingsAction.SelectReportProblem -> {
launch(Dispatchers.Main) {
val reportUrl =
String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, it.url)
requireComponents.useCases.sessionUseCases.loadUrl.invoke(reportUrl)
}
}
is QuickSettingsAction.ToggleTrackingProtection -> {
val trackingEnabled = it.trackingProtection
with(requireComponents.core) {
val policy =
createTrackingProtectionPolicy(trackingEnabled)
PreferenceManager.getDefaultSharedPreferences(context).edit()
.putBoolean(
context!!.getString(R.string.pref_key_tracking_protection),
trackingEnabled
).apply()
engine.settings.trackingProtectionPolicy = policy
with(sessionManager) {
sessions.forEach {
getEngineSession(it)?.enableTrackingProtection(
policy
)
}
}
}
launch(Dispatchers.Main) {
requireContext().components.useCases.sessionUseCases.reload.invoke()
}
}
is QuickSettingsAction.TogglePermission -> { is QuickSettingsAction.TogglePermission -> {
launch { launch {

@ -8,6 +8,7 @@ import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Switch
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
@ -19,6 +20,7 @@ import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
import mozilla.components.support.ktx.kotlin.toUri import mozilla.components.support.ktx.kotlin.toUri
import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.UIView import org.mozilla.fenix.mvi.UIView
import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.PhoneFeature
@ -28,6 +30,7 @@ import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
@Suppress("TooManyFunctions")
class QuickSettingsUIView( class QuickSettingsUIView(
container: ViewGroup, container: ViewGroup,
actionEmitter: Observer<QuickSettingsAction>, actionEmitter: Observer<QuickSettingsAction>,
@ -38,6 +41,8 @@ class QuickSettingsUIView(
) { ) {
private val securityInfoLabel: TextView private val securityInfoLabel: TextView
private val urlLabel: TextView private val urlLabel: TextView
private val trackingProtectionSwitch: Switch
private val reportProblemAction: TextView
private val cameraActionLabel: TextView private val cameraActionLabel: TextView
private val cameraLabel: TextView private val cameraLabel: TextView
private val microphoneActionLabel: TextView private val microphoneActionLabel: TextView
@ -53,6 +58,8 @@ class QuickSettingsUIView(
init { init {
urlLabel = view.findViewById<AppCompatTextView>(R.id.url) urlLabel = view.findViewById<AppCompatTextView>(R.id.url)
securityInfoLabel = view.findViewById<AppCompatTextView>(R.id.security_info) securityInfoLabel = view.findViewById<AppCompatTextView>(R.id.security_info)
trackingProtectionSwitch = view.findViewById(R.id.tracking_protection)
reportProblemAction = view.findViewById(R.id.report_problem)
cameraActionLabel = view.findViewById<AppCompatTextView>(R.id.camera_action_label) cameraActionLabel = view.findViewById<AppCompatTextView>(R.id.camera_action_label)
cameraLabel = view.findViewById<AppCompatTextView>(R.id.camera_icon) cameraLabel = view.findViewById<AppCompatTextView>(R.id.camera_icon)
microphoneActionLabel = view.findViewById<AppCompatTextView>(R.id.microphone_action_label) microphoneActionLabel = view.findViewById<AppCompatTextView>(R.id.microphone_action_label)
@ -68,6 +75,8 @@ class QuickSettingsUIView(
is QuickSettingsState.Mode.Normal -> { is QuickSettingsState.Mode.Normal -> {
bindUrl(state.mode.url) bindUrl(state.mode.url)
bindSecurityInfo(state.mode.isSecured) bindSecurityInfo(state.mode.isSecured)
bindReportProblemAction(state.mode.url)
bindTrackingProtectionInfo(state.mode.isTrackingProtectionOn)
bindPhoneFeatureItem(cameraActionLabel, CAMERA, state.mode.sitePermissions) bindPhoneFeatureItem(cameraActionLabel, CAMERA, state.mode.sitePermissions)
bindPhoneFeatureItem(microphoneActionLabel, MICROPHONE, state.mode.sitePermissions) bindPhoneFeatureItem(microphoneActionLabel, MICROPHONE, state.mode.sitePermissions)
bindPhoneFeatureItem(notificationActionLabel, NOTIFICATION, state.mode.sitePermissions) bindPhoneFeatureItem(notificationActionLabel, NOTIFICATION, state.mode.sitePermissions)
@ -90,6 +99,38 @@ class QuickSettingsUIView(
urlLabel.text = url.toUri().hostWithoutCommonPrefixes urlLabel.text = url.toUri().hostWithoutCommonPrefixes
} }
private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) {
val drawableId =
if (isTrackingProtectionOn) R.drawable.ic_tracking_protection else
R.drawable.ic_tracking_protection_disabled
val drawableTint = if (isTrackingProtectionOn) DefaultThemeManager.resolveAttribute(
R.attr.primaryText,
context
) else DefaultThemeManager.resolveAttribute(R.attr.neutral, context)
val icon = AppCompatResources.getDrawable(context, drawableId)
val resolvedColor = ContextCompat.getColor(context, drawableTint)
icon?.setTint(resolvedColor)
trackingProtectionSwitch.setTextColor(resolvedColor)
trackingProtectionSwitch.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
trackingProtectionSwitch.isChecked = isTrackingProtectionOn
trackingProtectionSwitch.setOnCheckedChangeListener { _, isChecked ->
actionEmitter.onNext(
QuickSettingsAction.ToggleTrackingProtection(
isChecked
)
)
}
}
private fun bindReportProblemAction(url: String) {
reportProblemAction.setOnClickListener {
actionEmitter.onNext(
QuickSettingsAction.SelectReportProblem(url)
)
}
}
private fun bindSecurityInfo(isSecured: Boolean) { private fun bindSecurityInfo(isSecured: Boolean) {
val stringId: Int val stringId: Int
val drawableId: Int val drawableId: Int

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?primaryText"
android:fillType="evenOdd"
android:pathData="M17.9,10.825C17.92,10.583 17.939,10.228 17.955,9.8L19.994,7.757C19.988,8.963 19.958,10.284 19.888,11.017C19.584,14.191 18.966,15.932 17.437,17.911C16.1139,19.5759 14.209,20.6776 12.106,20.994L12,21.006L11.893,20.994C10.5742,20.796 9.3208,20.2893 8.235,19.515L9.683,18.067C10.3916,18.5151 11.1773,18.8277 12,18.989C13.5187,18.7058 14.8864,17.8899 15.857,16.688C17.153,15.01 17.631,13.641 17.9,10.825ZM20.207,3.793C20.5973,4.1812 20.6005,4.8119 20.214,5.204L5.214,20.204C4.9629,20.464 4.5911,20.5682 4.2415,20.4767C3.8919,20.3852 3.6188,20.1121 3.5273,19.7625C3.4358,19.4129 3.54,19.0411 3.8,18.79L5.8,16.79C4.7719,15.0326 4.1919,13.0494 4.111,11.015C4.012,9.973 4,7.767 4,6.1C4.0096,5.0498 4.7782,4.161 5.816,4L12,2.986L18.187,3.999C18.2894,4.0247 18.3897,4.0581 18.487,4.099L18.793,3.793C19.1835,3.4026 19.8165,3.4026 20.207,3.793ZM6.1,10.826C6.1631,12.3919 6.5575,13.9266 7.257,15.329L8.717,13.864C8.3635,12.8432 8.1528,11.7785 8.091,10.7C8.066,10.439 8.015,9.667 8,7.7L12,7.041L12,10.586L16.789,5.797L12,5.014L6.138,5.972C6.0646,5.9767 6.0058,6.0347 6,6.108C6,8.4 6.031,10.076 6.1,10.826Z" />
</vector>

@ -35,14 +35,48 @@
app:layout_constraintTop_toBottomOf="@id/url"/> app:layout_constraintTop_toBottomOf="@id/url"/>
<View <View
android:id="@+id/line_divider" android:id="@+id/line_divider_security"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginTop="8dp" android:layout_height="1dp"
android:layout_marginBottom="8dp" android:layout_marginTop="8dp"
android:layout_height="1dp" android:layout_marginBottom="8dp"
android:background="?neutral" android:background="?neutral"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/security_info"/> app:layout_constraintTop_toBottomOf="@id/security_info" />
<Switch
android:id="@+id/tracking_protection"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/quicksettings_item_height"
android:drawableStart="@drawable/ic_tracking_protection"
android:paddingEnd="24dp"
android:text="@string/preferences_tracking_protection"
app:layout_constraintBottom_toTopOf="@id/report_problem"
app:layout_constraintTop_toBottomOf="@id/line_divider_security" />
<TextView
android:id="@+id/report_problem"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/quicksettings_item_height"
android:gravity="top"
android:paddingStart="48dp"
android:text="@string/tracking_protection_report_problem"
android:textColor="?accentBright"
android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@id/line_divider"
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
<View
android:id="@+id/line_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?neutral"
app:layout_constraintBottom_toTopOf="@id/camera_icon"
app:layout_constraintStart_toStartOf="parent" />
<TextView <TextView
android:id="@+id/camera_icon" android:id="@+id/camera_icon"

@ -386,4 +386,10 @@
<string name="phone_feature_blocked_by_android">Blocked by Android</string> <string name="phone_feature_blocked_by_android">Blocked by Android</string>
<!-- Preference for showing a list of websites that the default configurations won't apply to them --> <!-- Preference for showing a list of websites that the default configurations won't apply to them -->
<string name="preference_exceptions">Exceptions</string> <string name="preference_exceptions">Exceptions</string>
<!-- Action in Quick Settings dialog to report a site problem related to tracking protection -->
<string name="tracking_protection_report_problem">Report a problem</string>
<!-- Summary of tracking protection preference if tracking protection is set to on -->
<string name="tracking_protection_on">On</string>
<!-- Summary of tracking protection preference if tracking protection is set to off -->
<string name="tracking_protection_off">Off</string>
</resources> </resources>

Loading…
Cancel
Save