1
0
Fork 0

Add Tracking Protection Info Panel

master
Emily Kager 2019-09-10 13:29:21 -07:00 committed by Emily Kager
parent 9e83edcac5
commit 4485b7f647
57 changed files with 7331 additions and 143 deletions

View File

@ -425,6 +425,8 @@ dependencies {
implementation Deps.autodispose
implementation Deps.lottie
implementation Deps.adjust
implementation Deps.installreferrer // Required by Adjust

View File

@ -21,5 +21,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) {
FromBookmarks(R.id.bookmarkFragment),
FromHistory(R.id.historyFragment),
FromExceptions(R.id.exceptionsFragment),
FromAbout(R.id.aboutFragment)
FromAbout(R.id.aboutFragment),
FromTrackingProtection(R.id.trackingProtectionFragment)
}

View File

@ -37,6 +37,11 @@ object FeatureFlags {
*/
const val mediaIntegration = true
/**
* Displays the categories blocked by ETP in a panel in the toolbar
*/
val etpCategories = nightly or debug
/**
* Granular data deletion provides additional choices on the Delete Browsing Data
* setting screen for cookies, cached images and files, and site permissions.

View File

@ -56,6 +56,7 @@ import org.mozilla.fenix.library.history.HistoryFragmentDirections
import org.mozilla.fenix.search.SearchFragmentDirections
import org.mozilla.fenix.settings.AboutFragmentDirections
import org.mozilla.fenix.settings.SettingsFragmentDirections
import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections
import org.mozilla.fenix.share.ShareFragment
import org.mozilla.fenix.theme.DefaultThemeManager
import org.mozilla.fenix.theme.ThemeManager
@ -236,6 +237,10 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
)
BrowserDirection.FromAbout ->
AboutFragmentDirections.actionAboutFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromTrackingProtection ->
TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToBrowserFragment(
customTabSessionId
)
}
private fun load(

View File

@ -207,6 +207,10 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
showQuickSettingsDialog()
}
browserToolbarView.view.setOnTrackingProtectionClickedListener {
showTrackingProtectionPanel()
}
contextMenuFeature.set(
feature = ContextMenuFeature(
requireFragmentManager(),
@ -487,6 +491,8 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
protected abstract fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?)
protected abstract fun navToTrackingProtectionPanel(session: Session)
/**
* Returns the top and bottom margins.
*/
@ -530,6 +536,13 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
}
}
private fun showTrackingProtectionPanel() {
val session = getSessionById() ?: return
view?.let {
navToTrackingProtectionPanel(session)
}
}
/**
* Returns the current session.
*/

View File

@ -4,12 +4,17 @@
package org.mozilla.fenix.browser
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.RadioButton
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
@ -18,6 +23,7 @@ import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -31,6 +37,8 @@ import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
@ -42,8 +50,10 @@ import org.mozilla.fenix.components.toolbar.BrowserToolbarController
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.mvi.getManagedEmitter
@ -139,6 +149,18 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
).also { observer ->
getSessionById()?.register(observer, this, autoPause = true)
}
getSessionById()?.register(toolbarSessionObserver, this, autoPause = true)
}
private val toolbarSessionObserver = object : Session.Observer {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading &&
context!!.settings.shouldShowTrackingProtectionOnboarding &&
session.trackerBlockingEnabled
) {
showTrackingProtectionOnboarding()
}
}
}
override fun onResume() {
@ -181,14 +203,26 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
}
override fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?) {
val directions = BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = session.id,
url = session.url,
isSecured = session.securityInfo.secure,
isTrackingProtectionOn = session.trackerBlockingEnabled,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity()
)
val directions =
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = session.id,
url = session.url,
isSecured = session.securityInfo.secure,
isTrackingProtectionOn = session.trackerBlockingEnabled,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.browserFragment, directions)
}
override fun navToTrackingProtectionPanel(session: Session) {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment(
sessionId = session.id,
url = session.url,
trackingProtectionEnabled = session.trackerBlockingEnabled,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.browserFragment, directions)
}
@ -309,8 +343,44 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
}
}
private fun showTrackingProtectionOnboarding() {
if (!FeatureFlags.etpCategories) {
return
}
context?.let {
it.settings.incrementTrackingProtectionOnboardingCount()
val layout = LayoutInflater.from(it)
.inflate(R.layout.tracking_protection_onboarding_popup, null)
layout.onboarding_message.text =
it.getString(R.string.etp_onboarding_message, getString(R.string.app_name))
val trackingOnboarding =
PopupWindow(
layout,
TP_ONBOARDING_WIDTH.dpToPx(resources.displayMetrics),
LinearLayout.LayoutParams.WRAP_CONTENT,
true
)
val closeButton = layout.findViewById<ImageView>(R.id.close_onboarding)
closeButton.increaseTapArea(BUTTON_INCREASE_DPS)
closeButton.setOnClickListener {
trackingOnboarding.dismiss()
}
trackingOnboarding.isOutsideTouchable = true
trackingOnboarding.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
trackingOnboarding.showAtLocation(
browserToolbarView.view,
Gravity.BOTTOM or Gravity.START,
TP_ONBOARDING_X_OFFSET.dpToPx(resources.displayMetrics),
browserToolbarView.view.height
)
}
}
companion object {
private const val BUTTON_INCREASE_DPS = 12
private const val TP_ONBOARDING_X_OFFSET = 4
private const val SHARED_TRANSITION_MS = 200L
private const val TP_ONBOARDING_WIDTH = 256
private const val TAB_ITEM_TRANSITION_NAME = "tab_item"
const val REPORT_SITE_ISSUE_URL =
"https://webcompat.com/issues/new?url=%s&label=browser-fenix"

View File

@ -98,7 +98,10 @@ class Core(private val context: Context) {
withContext(Dispatchers.IO) {
sessionStorage.restore()
}?.let { snapshot ->
sessionManager.restore(snapshot, updateSelection = (sessionManager.selectedSession == null))
sessionManager.restore(
snapshot,
updateSelection = (sessionManager.selectedSession == null)
)
}
// Now that we have restored our previous state (if there's one) let's setup auto saving the state while
@ -151,7 +154,9 @@ class Core(private val context: Context) {
normalMode: Boolean = context.settings.shouldUseTrackingProtection,
privateMode: Boolean = true
): TrackingProtectionPolicy {
val trackingProtectionPolicy = TrackingProtectionPolicy.recommended()
val trackingProtectionPolicy =
if (context.settings.useStrictTrackingProtection) TrackingProtectionPolicy.strict() else
TrackingProtectionPolicy.recommended()
return when {
normalMode && privateMode -> trackingProtectionPolicy

View File

@ -8,6 +8,8 @@ import android.content.Context
import android.view.ViewGroup
import androidx.navigation.NavOptions
import androidx.navigation.Navigation
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import androidx.navigation.fragment.FragmentNavigator
import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
import mozilla.components.browser.session.SessionManager
@ -20,6 +22,7 @@ import mozilla.components.feature.toolbar.ToolbarPresenter
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.base.feature.LifecycleAwareFeature
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
@ -53,6 +56,26 @@ class ToolbarIntegration(
return@run
}
val task = LottieCompositionFactory
.fromRawRes(
context,
ThemeManager.resolveAttribute(R.attr.shieldLottieFile, context)
)
task.addListener { result ->
val lottieDrawable = LottieDrawable()
lottieDrawable.composition = result
toolbar.displayTrackingProtectionIcon =
context.settings.shouldUseTrackingProtection && FeatureFlags.etpCategories
toolbar.displaySeparatorView =
context.settings.shouldUseTrackingProtection && FeatureFlags.etpCategories
toolbar.setTrackingProtectionIcons(
iconOnNoTrackersBlocked = context.getDrawable(R.drawable.ic_tracking_protection_enabled)!!,
iconOnTrackersBlocked = lottieDrawable,
iconDisabledForSite = context.getDrawable(R.drawable.ic_tracking_protection_disabled)!!
)
}
val tabsAction = TabCounterToolbarButton(
sessionManager,
{

View File

@ -10,13 +10,18 @@ import android.view.Gravity
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.feature.customtabs.CustomTabsToolbarFeature
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.LifecycleAwareFeature
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarMenu
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.theme.ThemeManager
class CustomTabsIntegration(
context: Context,
@ -50,6 +55,25 @@ class CustomTabsIntegration(
// Hide the Quick Action Bar.
quickActionbar.visibility = View.GONE
val task = LottieCompositionFactory
.fromRawRes(
context,
ThemeManager.resolveAttribute(R.attr.shieldLottieFile, context)
)
task.addListener { result ->
val lottieDrawable = LottieDrawable()
lottieDrawable.composition = result
toolbar.displayTrackingProtectionIcon =
context.settings.shouldUseTrackingProtection && FeatureFlags.etpCategories
toolbar.displaySeparatorView = false
toolbar.setTrackingProtectionIcons(
iconOnNoTrackersBlocked = context.getDrawable(R.drawable.ic_tracking_protection_enabled)!!,
iconOnTrackersBlocked = lottieDrawable,
iconDisabledForSite = context.getDrawable(R.drawable.ic_tracking_protection_disabled)!!
)
}
}
private val customTabToolbarMenu by lazy {

View File

@ -78,6 +78,18 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
nav(R.id.externalAppBrowserFragment, directions)
}
override fun navToTrackingProtectionPanel(session: Session) {
val directions =
ExternalAppBrowserFragmentDirections
.actionExternalAppBrowserFragmentToTrackingProtectionPanelDialogFragment(
sessionId = session.id,
url = session.url,
trackingProtectionEnabled = session.trackerBlockingEnabled,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.externalAppBrowserFragment, directions)
}
override fun getEngineMargins(): Pair<Int, Int> {
val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
return toolbarSize to 0

View File

@ -0,0 +1,73 @@
/* 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.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import androidx.core.content.res.TypedArrayUtils
import androidx.core.content.withStyledAttributes
import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.increaseTapArea
class RadioButtonInfoPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : RadioButtonPreference(context, attrs) {
private var infoClickListener: (() -> Unit)? = null
private var infoView: ImageView? = null
fun onInfoClickListener(listener: (() -> Unit)) {
infoClickListener = listener
}
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
infoView?.alpha = if (enabled) FULL_ALPHA else HALF_ALPHA
infoView?.isEnabled = enabled
}
init {
layoutResource = R.layout.preference_widget_radiobutton_with_info
context.withStyledAttributes(
attrs,
androidx.preference.R.styleable.Preference,
TypedArrayUtils.getAttr(
context,
androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle
),
0
) {
val defaultValue = when {
hasValue(androidx.preference.R.styleable.Preference_defaultValue) ->
getBoolean(androidx.preference.R.styleable.Preference_defaultValue, false)
hasValue(androidx.preference.R.styleable.Preference_android_defaultValue) ->
getBoolean(
androidx.preference.R.styleable.Preference_android_defaultValue,
false
)
else -> false
}
setDefaultValue(defaultValue)
}
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
infoView = holder.findViewById(R.id.info_button) as ImageView
infoView?.increaseTapArea(EXTRA_TAP_AREA)
infoView?.setOnClickListener {
infoClickListener?.invoke()
}
infoView?.alpha = if (isEnabled) FULL_ALPHA else HALF_ALPHA
}
companion object {
const val EXTRA_TAP_AREA = 22
}
}

View File

@ -17,13 +17,14 @@ import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
class RadioButtonPreference @JvmOverloads constructor(
open class RadioButtonPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : Preference(context, attrs) {
private val radioGroups = mutableListOf<RadioButtonPreference>()
private lateinit var summaryView: TextView
private lateinit var radioButton: RadioButton
private var summaryView: TextView? = null
private var titleView: TextView? = null
private var radioButton: RadioButton? = null
private var shouldSummaryBeParsedAsHtmlContent: Boolean = true
private var defaultValue: Boolean = false
private var clickListener: (() -> Unit)? = null
@ -34,14 +35,21 @@ class RadioButtonPreference @JvmOverloads constructor(
context.withStyledAttributes(
attrs,
androidx.preference.R.styleable.Preference,
getAttr(context, androidx.preference.R.attr.preferenceStyle, android.R.attr.preferenceStyle),
getAttr(
context,
androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle
),
0
) {
defaultValue = when {
hasValue(androidx.preference.R.styleable.Preference_defaultValue) ->
getBoolean(androidx.preference.R.styleable.Preference_defaultValue, false)
hasValue(androidx.preference.R.styleable.Preference_android_defaultValue) ->
getBoolean(androidx.preference.R.styleable.Preference_android_defaultValue, false)
getBoolean(
androidx.preference.R.styleable.Preference_android_defaultValue,
false
)
else -> false
}
}
@ -55,6 +63,17 @@ class RadioButtonPreference @JvmOverloads constructor(
clickListener = listener
}
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
if (!enabled) {
summaryView?.alpha = HALF_ALPHA
titleView?.alpha = HALF_ALPHA
} else {
summaryView?.alpha = FULL_ALPHA
titleView?.alpha = FULL_ALPHA
}
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
@ -75,44 +94,53 @@ class RadioButtonPreference @JvmOverloads constructor(
private fun updateRadioValue(isChecked: Boolean) {
persistBoolean(isChecked)
radioButton.isChecked = isChecked
radioButton?.isChecked = isChecked
context.settings.preferences.edit().putBoolean(key, isChecked)
.apply()
}
private fun bindRadioButton(holder: PreferenceViewHolder) {
radioButton = holder.findViewById(R.id.radio_button) as RadioButton
radioButton.isChecked = context.settings.preferences.getBoolean(key, false)
radioButton.setStartCheckedIndicator()
radioButton?.isChecked = context.settings.preferences.getBoolean(key, defaultValue)
radioButton?.setStartCheckedIndicator()
}
private fun toggleRadioGroups() {
if (radioButton.isChecked) {
if (radioButton?.isChecked == true) {
radioGroups.forEach { it.updateRadioValue(false) }
}
}
private fun bindTitle(holder: PreferenceViewHolder) {
val titleView = holder.findViewById(R.id.title) as TextView
titleView = holder.findViewById(R.id.title) as TextView
titleView?.alpha = if (isEnabled) FULL_ALPHA else HALF_ALPHA
if (!title.isNullOrEmpty()) {
titleView.text = title
titleView?.text = title
}
}
private fun bindSummaryView(holder: PreferenceViewHolder) {
summaryView = holder.findViewById(R.id.widget_summary) as TextView
if (!summary.isNullOrEmpty()) {
summaryView.text = if (shouldSummaryBeParsedAsHtmlContent) {
HtmlCompat.fromHtml(summary.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT)
} else {
summary
}
summaryView?.alpha = if (isEnabled) FULL_ALPHA else HALF_ALPHA
summaryView?.let {
if (!summary.isNullOrEmpty()) {
it.text = if (shouldSummaryBeParsedAsHtmlContent) {
HtmlCompat.fromHtml(summary.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT)
} else {
summary
}
summaryView.visibility = View.VISIBLE
} else {
summaryView.visibility = View.GONE
it.visibility = View.VISIBLE
} else {
it.visibility = View.GONE
}
}
}
companion object {
const val HALF_ALPHA = 0.5F
const val FULL_ALPHA = 1F
}
}

View File

@ -10,9 +10,12 @@ import androidx.navigation.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.settings
/**
@ -22,10 +25,13 @@ import org.mozilla.fenix.ext.settings
class TrackingProtectionFragment : PreferenceFragmentCompat() {
private val exceptionsClickListener = Preference.OnPreferenceClickListener {
val directions = TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
val directions =
TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
view!!.findNavController().navigate(directions)
true
}
private lateinit var radioStrict: RadioButtonInfoPreference
private lateinit var radioStandard: RadioButtonInfoPreference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tracking_protection_preferences, rootKey)
@ -33,7 +39,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
override fun onResume() {
super.onResume()
activity?.title = getString(R.string.preferences_tracking_protection)
activity?.title = getString(R.string.preference_enhanced_tracking_protection)
(activity as AppCompatActivity).supportActionBar?.show()
// Tracking Protection Switch
@ -42,7 +48,8 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
preferenceTP?.isChecked = context!!.settings.shouldUseTrackingProtection
preferenceTP?.setOnPreferenceChangeListener<Boolean> { preference, trackingProtectionOn ->
preference.context.settings.shouldUseTrackingProtection = trackingProtectionOn
preference.context.settings.shouldUseTrackingProtection =
trackingProtectionOn
with(preference.context.components) {
val policy = core.createTrackingProtectionPolicy(trackingProtectionOn)
useCases.settingsUseCases.updateTrackingProtection(policy)
@ -51,8 +58,72 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
true
}
bindStrict()
bindRecommended()
setupRadioGroups()
val trackingProtectionLearnMore =
context!!.getPreferenceKey(R.string.pref_key_etp_learn_more)
val learnMorePreference = findPreference<Preference>(trackingProtectionLearnMore)
learnMorePreference?.setOnPreferenceClickListener {
(activity as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
(SupportUtils.SumoTopic.TRACKING_PROTECTION),
newTab = true,
from = BrowserDirection.FromTrackingProtection
)
true
}
learnMorePreference?.summary = getString(
R.string.preference_enhanced_tracking_protection_explanation,
getString(R.string.app_name)
)
val exceptions = getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
val preferenceExceptions = findPreference<Preference>(exceptions)
preferenceExceptions?.onPreferenceClickListener = exceptionsClickListener
}
private fun bindStrict() {
val keyStrict = getString(R.string.pref_key_tracking_protection_strict)
radioStrict = requireNotNull(findPreference(keyStrict))
radioStrict.onInfoClickListener {
nav(
R.id.trackingProtectionFragment,
TrackingProtectionFragmentDirections
.actionTrackingProtectionFragmentToTrackingProtectionBlockingFragment(true)
)
}
radioStrict.onClickListener {
updateTrackingProtectionPolicy()
}
}
private fun bindRecommended() {
val keyStandard = getString(R.string.pref_key_tracking_protection_standard)
radioStandard = requireNotNull(findPreference(keyStandard))
radioStandard.onInfoClickListener {
nav(
R.id.trackingProtectionFragment,
TrackingProtectionFragmentDirections
.actionTrackingProtectionFragmentToTrackingProtectionBlockingFragment(false)
)
}
radioStandard.onClickListener {
updateTrackingProtectionPolicy()
}
}
private fun updateTrackingProtectionPolicy() {
context?.components?.let {
val policy = it.core.createTrackingProtectionPolicy()
it.useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
it.useCases.sessionUseCases.reload.invoke()
}
}
private fun setupRadioGroups() {
radioStandard.addToRadioGroup(radioStrict)
radioStrict.addToRadioGroup(radioStandard)
}
}

View File

@ -9,9 +9,11 @@ import android.widget.CompoundButton
import android.widget.Switch
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import io.reactivex.Observer
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
@ -20,15 +22,23 @@ class TrackingProtectionSettingView(
private val actionEmitter: Observer<QuickSettingsAction>
) : View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private val trackingProtectionSwitch: Switch = container.findViewById(R.id.tracking_protection)
private val trackingProtectionAction: TextView = container.findViewById(R.id.tracking_protection_action)
private val trackingProtectionAction: TextView =
container.findViewById(R.id.tracking_protection_action)
private val trackingProtectionSettingView: ConstraintLayout =
container.findViewById(R.id.tracking_protection_view)
init {
trackingProtectionSwitch.putCompoundDrawablesRelativeWithIntrinsicBounds(
start = AppCompatResources.getDrawable(container.context, R.drawable.ic_tracking_protection)
start = AppCompatResources.getDrawable(
container.context,
R.drawable.ic_tracking_protection
)
)
}
fun bind(isTrackingProtectionOn: Boolean) {
trackingProtectionSettingView.visibility =
if (FeatureFlags.etpCategories) View.GONE else View.VISIBLE
val globalTPSetting = trackingProtectionSwitch.context.settings.shouldUseTrackingProtection
trackingProtectionAction.isVisible = !globalTPSetting

View File

@ -0,0 +1,51 @@
/* 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.trackingprotection
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.withStyledAttributes
import kotlinx.android.synthetic.main.switch_with_description.view.*
import kotlinx.android.synthetic.main.tracking_protection_category.view.switchItemDescription
import kotlinx.android.synthetic.main.tracking_protection_category.view.switchItemTitle
import org.mozilla.fenix.R
class SwitchWithDescription @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs) {
init {
LayoutInflater.from(context).inflate(R.layout.switch_with_description, this, true)
context.withStyledAttributes(attrs, R.styleable.SwitchWithDescription, defStyleAttr, 0) {
val id = getResourceId(
R.styleable.SwitchWithDescription_switchIcon,
R.drawable.ic_tracking_protection
)
switch_widget?.setCompoundDrawablesWithIntrinsicBounds(
resources.getDrawable(
id,
context.theme
), null, null, null
)
switchItemTitle?.text = resources.getString(
getResourceId(
R.styleable.SwitchWithDescription_switchTitle,
R.string.preference_enhanced_tracking_protection
)
)
switchItemDescription?.text = resources.getString(
getResourceId(
R.styleable.SwitchWithDescription_switchDescription,
R.string.preference_enhanced_tracking_protection_explanation
)
)
}
}
}

View File

@ -0,0 +1,49 @@
/* 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.trackingprotection
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_tracking_protection_blocking.*
import org.mozilla.fenix.R
class TrackingProtectionBlockingFragment : Fragment() {
private val safeArguments get() = requireNotNull(arguments)
private val isStrict: Boolean by lazy {
TrackingProtectionBlockingFragmentArgs.fromBundle(safeArguments).strictMode
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_tracking_protection_blocking, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (isStrict) {
category_tracking_content.visibility = View.VISIBLE
} else {
category_tracking_content.visibility = View.GONE
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).title =
getString(
if (isStrict) R.string.preference_enhanced_tracking_protection_strict else
R.string.preference_enhanced_tracking_protection_standard
)
(activity as AppCompatActivity).supportActionBar?.show()
}
}

View File

@ -0,0 +1,48 @@
/* 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.trackingprotection
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.withStyledAttributes
import kotlinx.android.synthetic.main.tracking_protection_category.view.*
import org.mozilla.fenix.R
class TrackingProtectionCategoryItem @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.tracking_protection_category, this, true)
context.withStyledAttributes(
attrs,
R.styleable.TrackingProtectionCategory,
defStyleAttr,
0
) {
val id = getResourceId(
R.styleable.TrackingProtectionCategory_categoryItemIcon,
R.drawable.ic_cryptominers
)
switchIcon?.background = resources.getDrawable(id, context.theme)
switchItemTitle?.text = resources.getString(
getResourceId(
R.styleable.TrackingProtectionCategory_categoryItemTitle,
R.string.etp_cookies_title
)
)
switchItemDescription?.text = resources.getString(
getResourceId(
R.styleable.TrackingProtectionCategory_categoryItemDescription,
R.string.etp_cookies_description
)
)
}
}
}

View File

@ -0,0 +1,216 @@
/* 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.trackingprotection
import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.appcompat.view.ContextThemeWrapper
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.whenStarted
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import kotlinx.android.synthetic.main.fragment_tracking_protection.view.*
import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.lib.state.ext.observe
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.exceptions.ExceptionDomains
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import java.net.MalformedURLException
import java.net.URL
class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHandler {
private val safeArguments get() = requireNotNull(arguments)
private val sessionId: String by lazy {
TrackingProtectionPanelDialogFragmentArgs.fromBundle(
safeArguments
).sessionId
}
private val url: String by lazy {
TrackingProtectionPanelDialogFragmentArgs.fromBundle(safeArguments).url
}
private val trackingProtectionEnabled: Boolean by lazy {
TrackingProtectionPanelDialogFragmentArgs.fromBundle(safeArguments)
.trackingProtectionEnabled
}
private val promptGravity: Int by lazy {
TrackingProtectionPanelDialogFragmentArgs.fromBundle(
safeArguments
).gravity
}
private fun inflateRootView(container: ViewGroup? = null): View {
val contextThemeWrapper = ContextThemeWrapper(
activity,
(activity as HomeActivity).themeManager.currentThemeResource
)
return LayoutInflater.from(contextThemeWrapper).inflate(
R.layout.fragment_tracking_protection,
container,
false
)
}
private lateinit var trackingProtectionStore: TrackingProtectionStore
private lateinit var trackingProtectionView: TrackingProtectionPanelView
private lateinit var trackingProtectionInteractor: TrackingProtectionPanelInteractor
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflateRootView(container)
val session = requireComponents.core.sessionManager.findSessionById(sessionId)
session?.register(sessionObserver, view = view)
trackingProtectionStore = StoreProvider.get(this) {
TrackingProtectionStore(
TrackingProtectionState(
url,
trackingProtectionEnabled,
session?.trackersBlocked ?: listOf(),
session?.trackersLoaded ?: listOf(),
TrackingProtectionState.Mode.Normal
)
)
}
trackingProtectionInteractor = TrackingProtectionPanelInteractor(
trackingProtectionStore,
::toggleTrackingProtection,
::openTrackingProtectionSettings
)
trackingProtectionView =
TrackingProtectionPanelView(view.fragment_tp, trackingProtectionInteractor)
return view
}
private val sessionObserver = object : Session.Observer {
override fun onUrlChanged(session: Session, url: String) {
trackingProtectionStore.dispatch(
TrackingProtectionAction.UrlChange(url)
)
}
override fun onTrackerBlocked(session: Session, tracker: Tracker, all: List<Tracker>) {
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerListChange(all)
)
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerLoadedListChange(session.trackersLoaded)
)
}
override fun onTrackerLoaded(session: Session, tracker: Tracker, all: List<Tracker>) {
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerListChange(session.trackersBlocked)
)
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerLoadedListChange(all)
)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
trackingProtectionStore.observe(view) {
viewLifecycleOwner.lifecycleScope.launch {
whenStarted {
trackingProtectionView.update(it)
}
}
}
}
private fun openTrackingProtectionSettings() {
nav(
R.id.trackingProtectionPanelDialogFragment,
TrackingProtectionPanelDialogFragmentDirections
.actionTrackingProtectionPanelDialogFragmentToTrackingProtectionFragment()
)
}
private fun toggleTrackingProtection(isEnabled: Boolean) {
context?.let {
val host = try {
URL(url).host
} catch (e: MalformedURLException) {
url
}
lifecycleScope.launch {
if (!ExceptionDomains.load(it).contains(host)) {
ExceptionDomains.add(it, host)
} else {
ExceptionDomains.remove(it, listOf(host))
}
}
it.components.useCases.sessionUseCases.reload.invoke()
}
trackingProtectionStore.dispatch(TrackingProtectionAction.TrackerBlockingChanged(isEnabled))
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return if (promptGravity == Gravity.BOTTOM) {
object : BottomSheetDialog(requireContext(), this.theme) {
override fun onBackPressed() {
this@TrackingProtectionPanelDialogFragment.onBackPressed()
}
}.apply {
setOnShowListener {
val bottomSheet =
findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as? FrameLayout
val behavior = BottomSheetBehavior.from(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
} else {
object : Dialog(requireContext()) {
override fun onBackPressed() {
this@TrackingProtectionPanelDialogFragment.onBackPressed()
}
}.applyCustomizationsForTopDialog(inflateRootView())
}
}
private fun Dialog.applyCustomizationsForTopDialog(rootView: View): Dialog {
addContentView(
rootView,
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
)
window?.apply {
setGravity(promptGravity)
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
// This must be called after addContentView, or it won't fully fill to the edge.
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
return this
}
override fun onBackPressed(): Boolean = trackingProtectionView.onBackPressed()
}

View File

@ -0,0 +1,31 @@
/* 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.trackingprotection
/**
* Interactor for the tracking protection panel
* Provides implementations for the TrackingProtectionPanelViewInteractor
*/
class TrackingProtectionPanelInteractor(
private val store: TrackingProtectionStore,
private val toggleTrackingProtection: (Boolean) -> Unit,
private val openTrackingProtectionSettings: () -> Unit
) : TrackingProtectionPanelViewInteractor {
override fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean) {
store.dispatch(TrackingProtectionAction.EnterDetailsMode(category, categoryBlocked))
}
override fun selectTrackingProtectionSettings() {
openTrackingProtectionSettings.invoke()
}
override fun trackingProtectionToggled(isEnabled: Boolean) {
toggleTrackingProtection.invoke(isEnabled)
}
override fun onBackPressed() {
store.dispatch(TrackingProtectionAction.ExitDetailsMode)
}
}

View File

@ -0,0 +1,265 @@
/* 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.trackingprotection
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_tracking_protection_panel.*
import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.url
import kotlinx.android.synthetic.main.switch_with_description.view.*
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.AD
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getHostFromUrl
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CROSS_SITE_TRACKING_COOKIES
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CRYPTOMINERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.FINGERPRINTERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.SOCIAL_MEDIA_TRACKERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.TRACKING_CONTENT
/**
* Interface for the TrackingProtectionPanelViewInteractor. This interface is implemented by objects that want
* to respond to user interaction on the TrackingProtectionPanelView
*/
interface TrackingProtectionPanelViewInteractor {
/**
* Called whenever the settings option is tapped
*/
fun selectTrackingProtectionSettings()
/**
* Called whenever the tracking protection toggle for this site is toggled
* @param isEnabled new status of session tracking protection
*/
fun trackingProtectionToggled(isEnabled: Boolean)
/**
* Called whenever back is pressed
*/
fun onBackPressed()
/**
* Called whenever an active tracking protection category is tapped
* @param category The Tracking Protection Category to view details about
* @param categoryBlocked The trackers from this category were blocked
*/
fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean)
}
/**
* View that contains and configures the Tracking Protection Panel
*/
class TrackingProtectionPanelView(
override val containerView: ViewGroup,
val interactor: TrackingProtectionPanelInteractor
) : LayoutContainer {
val view: ConstraintLayout = LayoutInflater.from(containerView.context)
.inflate(R.layout.component_tracking_protection_panel, containerView, true)
.findViewById(R.id.panel_wrapper)
private val context get() = view.context
var mode: TrackingProtectionState.Mode = TrackingProtectionState.Mode.Normal
private set
var trackers: List<Tracker> = listOf()
private set
var bucketedTrackers: HashMap<TrackingProtectionCategory, List<String>> = HashMap()
var loadedTrackers: List<Tracker> = listOf()
private set
var bucketedLoadedTrackers: HashMap<TrackingProtectionCategory, List<String>> = HashMap()
fun update(state: TrackingProtectionState) {
if (state.mode != mode) {
mode = state.mode
}
if (state.listTrackers != trackers) {
trackers = state.listTrackers
bucketedTrackers = getHashMapOfTrackersForCategory(state.listTrackers)
}
if (state.listTrackersLoaded != loadedTrackers) {
loadedTrackers = state.listTrackersLoaded
bucketedLoadedTrackers = getHashMapOfTrackersForCategory(state.listTrackersLoaded)
}
when (val mode = state.mode) {
is TrackingProtectionState.Mode.Normal -> setUIForNormalMode(state)
is TrackingProtectionState.Mode.Details -> setUIForDetailsMode(
mode.selectedCategory,
mode.categoryBlocked
)
}
}
private fun setUIForNormalMode(state: TrackingProtectionState) {
details_mode.visibility = View.GONE
normal_mode.visibility = View.VISIBLE
not_blocking_header.visibility =
if (bucketedLoadedTrackers.size == 0) View.GONE else View.VISIBLE
bindUrl(state.url)
bindTrackingProtectionInfo(state.isTrackingProtectionEnabled)
protection_settings.setOnClickListener {
interactor.selectTrackingProtectionSettings()
}
blocking_header.visibility =
if (bucketedTrackers.size == 0) View.GONE else View.VISIBLE
updateCategoryVisibility()
setCategoryClickListeners()
}
@Suppress("ComplexMethod")
private fun updateCategoryVisibility() {
cross_site_tracking.visibility = bucketedTrackers.getVisibility(CROSS_SITE_TRACKING_COOKIES)
social_media_trackers.visibility = bucketedTrackers.getVisibility(SOCIAL_MEDIA_TRACKERS)
fingerprinters.visibility = bucketedTrackers.getVisibility(FINGERPRINTERS)
tracking_content.visibility = bucketedTrackers.getVisibility(TRACKING_CONTENT)
cryptominers.visibility = bucketedTrackers.getVisibility(CRYPTOMINERS)
cross_site_tracking_loaded.visibility =
bucketedLoadedTrackers.getVisibility(CROSS_SITE_TRACKING_COOKIES)
social_media_trackers_loaded.visibility =
bucketedLoadedTrackers.getVisibility(SOCIAL_MEDIA_TRACKERS)
fingerprinters_loaded.visibility = bucketedLoadedTrackers.getVisibility(FINGERPRINTERS)
tracking_content_loaded.visibility = bucketedLoadedTrackers.getVisibility(TRACKING_CONTENT)
cryptominers_loaded.visibility = bucketedLoadedTrackers.getVisibility(CRYPTOMINERS)
}
private fun HashMap<TrackingProtectionCategory, List<String>>.getVisibility(
category: TrackingProtectionCategory
): Int = if (this[category]?.isNotEmpty() == true) View.VISIBLE else View.GONE
private fun setCategoryClickListeners() {
social_media_trackers.setOnClickListener {
interactor.openDetails(SOCIAL_MEDIA_TRACKERS, categoryBlocked = true)
}
fingerprinters.setOnClickListener {
interactor.openDetails(FINGERPRINTERS, categoryBlocked = true)
}
cross_site_tracking.setOnClickListener {
interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = true)
}
tracking_content.setOnClickListener {
interactor.openDetails(TRACKING_CONTENT, categoryBlocked = true)
}
cryptominers.setOnClickListener {
interactor.openDetails(CRYPTOMINERS, categoryBlocked = true)
}
social_media_trackers_loaded.setOnClickListener {
interactor.openDetails(SOCIAL_MEDIA_TRACKERS, categoryBlocked = false)
}
fingerprinters_loaded.setOnClickListener {
interactor.openDetails(FINGERPRINTERS, categoryBlocked = false)
}
cross_site_tracking_loaded.setOnClickListener {
interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = false)
}
tracking_content_loaded.setOnClickListener {
interactor.openDetails(TRACKING_CONTENT, categoryBlocked = false)
}
cryptominers_loaded.setOnClickListener {
interactor.openDetails(CRYPTOMINERS, categoryBlocked = false)
}
}
private fun setUIForDetailsMode(
category: TrackingProtectionCategory,
categoryBlocked: Boolean
) {
normal_mode.visibility = View.GONE
details_mode.visibility = View.VISIBLE
category_title.text = context.getString(category.title)
val stringList = bucketedTrackers[category]?.joinToString("\n")
blocking_text_list.text = stringList
category_description.text = context.getString(category.description)
details_blocking_header.text =
context.getString(
if (categoryBlocked) R.string.enhanced_tracking_protection_blocked else
R.string.enhanced_tracking_protection_allowed
)
details_back.setOnClickListener {
interactor.onBackPressed()
}
}
private fun getHashMapOfTrackersForCategory(
list: List<Tracker>
): HashMap<TrackingProtectionCategory, List<String>> {
val hashMap = HashMap<TrackingProtectionCategory, List<String>>()
items@ for (item in list) {
when {
item.trackingCategories.contains(CRYPTOMINING) -> {
hashMap[CRYPTOMINERS] =
(hashMap[CRYPTOMINERS]
?: listOf()).plus(item.url.getHostFromUrl() ?: item.url)
continue@items
}
item.trackingCategories.contains(FINGERPRINTING) -> {
hashMap[FINGERPRINTERS] =
(hashMap[FINGERPRINTERS]
?: listOf()).plus(item.url.getHostFromUrl() ?: item.url)
continue@items
}
item.trackingCategories.contains(SOCIAL) -> {
hashMap[SOCIAL_MEDIA_TRACKERS] =
(hashMap[SOCIAL_MEDIA_TRACKERS] ?: listOf()).plus(
item.url.getHostFromUrl() ?: item.url
)
continue@items
}
item.trackingCategories.contains(AD) ||
item.trackingCategories.contains(SOCIAL) ||
item.trackingCategories.contains(ANALYTICS) -> {
hashMap[TRACKING_CONTENT] =
(hashMap[TRACKING_CONTENT] ?: listOf()).plus(
item.url.getHostFromUrl() ?: item.url
)
continue@items
}
}
}
return hashMap
}
private fun bindUrl(url: String) {
this.url.text = url.toUri().hostWithoutCommonPrefixes
}
private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) {
tracking_protection.switchItemDescription.text =
context.getString(if (isTrackingProtectionOn) R.string.etp_panel_on else R.string.etp_panel_off)
tracking_protection.switch_widget.isChecked = isTrackingProtectionOn
tracking_protection.switch_widget.setOnCheckedChangeListener { _, isChecked ->
interactor.trackingProtectionToggled(isChecked)
}
}
fun onBackPressed(): Boolean {
return when (mode) {
is TrackingProtectionState.Mode.Details -> {
mode = TrackingProtectionState.Mode.Normal
interactor.onBackPressed()
true
}
else -> false
}
}
}

View File

@ -0,0 +1,129 @@
/* 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.trackingprotection
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import org.mozilla.fenix.R
/**
* The [Store] for holding the [TrackingProtectionState] and applying [TrackingProtectionAction]s.
*/
class TrackingProtectionStore(initialState: TrackingProtectionState) :
Store<TrackingProtectionState, TrackingProtectionAction>(
initialState,
::trackingProtectionStateReducer
)
/**
* Actions to dispatch through the `TrackingProtectionStore` to modify `TrackingProtectionState` through the reducer.
*/
sealed class TrackingProtectionAction : Action {
data class Change(
val url: String,
val isTrackingProtectionEnabled: Boolean,
val listTrackers: List<Tracker>,
val listTrackersLoaded: List<Tracker>,
val mode: TrackingProtectionState.Mode
) : TrackingProtectionAction()
data class UrlChange(val url: String) : TrackingProtectionAction()
data class TrackerListChange(val listTrackers: List<Tracker>) : TrackingProtectionAction()
data class TrackerLoadedListChange(val listTrackersLoaded: List<Tracker>) :
TrackingProtectionAction()
data class TrackerBlockingChanged(val isTrackingProtectionEnabled: Boolean) :
TrackingProtectionAction()
object ExitDetailsMode : TrackingProtectionAction()
data class EnterDetailsMode(
val category: TrackingProtectionCategory,
val categoryBlocked: Boolean
) :
TrackingProtectionAction()
}
/**
* The state for the Tracking Protection Panel
* @property url Current URL to display
* @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception)
* @property listTrackers List of currently blocked Trackers
* @property listTrackersLoaded List of currently not blocked Trackers
* @property mode Current Mode of TrackingProtection
*/
data class TrackingProtectionState(
val url: String,
val isTrackingProtectionEnabled: Boolean,
val listTrackers: List<Tracker>,
val listTrackersLoaded: List<Tracker>,
val mode: Mode
) : State {
sealed class Mode {
object Normal : Mode()
data class Details(
val selectedCategory: TrackingProtectionCategory,
val categoryBlocked: Boolean
) : Mode()
}
}
/**
* The 5 categories of Tracking Protection to display
*/
enum class TrackingProtectionCategory(val title: Int, val description: Int) {
SOCIAL_MEDIA_TRACKERS(
R.string.etp_social_media_trackers_title,
R.string.etp_social_media_trackers_description
),
CROSS_SITE_TRACKING_COOKIES(
R.string.etp_cookies_title,
R.string.etp_cookies_description
),
CRYPTOMINERS(
R.string.etp_cryptominers_title,
R.string.etp_cryptominers_description
),
FINGERPRINTERS(R.string.etp_fingerprinters_title, R.string.etp_fingerprinters_description),
TRACKING_CONTENT(R.string.etp_tracking_content_title, R.string.etp_tracking_content_description)
}
/**
* The TrackingProtectionState Reducer.
*/
fun trackingProtectionStateReducer(
state: TrackingProtectionState,
action: TrackingProtectionAction
): TrackingProtectionState {
return when (action) {
is TrackingProtectionAction.Change -> state.copy(
url = action.url,
isTrackingProtectionEnabled = action.isTrackingProtectionEnabled,
listTrackers = action.listTrackers,
mode = action.mode
)
is TrackingProtectionAction.UrlChange -> state.copy(
url = action.url
)
is TrackingProtectionAction.TrackerListChange -> state.copy(
listTrackers = action.listTrackers
)
is TrackingProtectionAction.TrackerLoadedListChange -> state.copy(
listTrackersLoaded = action.listTrackersLoaded
)
TrackingProtectionAction.ExitDetailsMode -> state.copy(
mode = TrackingProtectionState.Mode.Normal
)
is TrackingProtectionAction.EnterDetailsMode -> state.copy(
mode = TrackingProtectionState.Mode.Details(
action.category,
action.categoryBlocked
)
)
is TrackingProtectionAction.TrackerBlockingChanged ->
state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled)
}
}

View File

@ -29,9 +29,9 @@ class Settings private constructor(
context: Context,
private val isCrashReportEnabledInBuild: Boolean
) : PreferencesHolder {
companion object {
const val autoBounceMaximumCount = 2
const val trackingProtectionOnboardingMaximumCount = 2
const val FENIX_PREFERENCES = "fenix_preferences"
private const val BLOCKED_INT = 0
private const val ASK_TO_ALLOW_INT = 1
@ -80,7 +80,10 @@ class Settings private constructor(
val isCrashReportingEnabled: Boolean
get() = isCrashReportEnabledInBuild &&
preferences.getBoolean(appContext.getPreferenceKey(R.string.pref_key_crash_reporter), true)
preferences.getBoolean(
appContext.getPreferenceKey(R.string.pref_key_crash_reporter),
true
)
val isRemoteDebuggingEnabled by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_remote_debugging),
@ -97,6 +100,12 @@ class Settings private constructor(
default = true
)
private var trackingProtectionOnboardingShownThisSession = false
val shouldShowTrackingProtectionOnboarding: Boolean
get() = trackingProtectionOnboardingCount < trackingProtectionOnboardingMaximumCount &&
!trackingProtectionOnboardingShownThisSession
val shouldAutoBounceQuickActionSheet: Boolean
get() = autoBounceQuickActionSheetCount < autoBounceMaximumCount
@ -150,6 +159,11 @@ class Settings private constructor(
default = false
)
val useStrictTrackingProtection by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict),
false
)
val themeSettingString: String
get() = when {
shouldFollowDeviceTheme -> appContext.getString(R.string.preference_follow_device_theme)
@ -177,10 +191,27 @@ class Settings private constructor(
default = true
)
@VisibleForTesting(otherwise = PRIVATE)
internal val trackingProtectionOnboardingCount by intPreference(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding),
0
)
fun incrementTrackingProtectionOnboardingCount() {
trackingProtectionOnboardingShownThisSession = true
preferences.edit().putInt(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding),
trackingProtectionOnboardingCount + 1
).apply()
}
fun getSitePermissionsPhoneFeatureAction(feature: PhoneFeature) =
intToAction(preferences.getInt(feature.getPreferenceKey(appContext), ASK_TO_ALLOW_INT))
fun setSitePermissionsPhoneFeatureAction(feature: PhoneFeature, value: SitePermissionsRules.Action) {
fun setSitePermissionsPhoneFeatureAction(
feature: PhoneFeature,
value: SitePermissionsRules.Action
) {
preferences.edit().putInt(feature.getPreferenceKey(appContext), actionToInt(value)).apply()
}
@ -212,5 +243,8 @@ class Settings private constructor(
}
val searchWidgetInstalled: Boolean
get() = 0 < preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_search_widget_installed), 0)
get() = 0 < preferences.getInt(
appContext.getPreferenceKey(R.string.pref_key_search_widget_installed),
0
)
}

View File

@ -0,0 +1,12 @@
<?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="7"
android:viewportHeight="13">
<path
android:fillColor="?primaryText"
android:pathData="M0.9998,12.4877C0.5954,12.4877 0.2309,12.244 0.0761,11.8704C-0.0786,11.4967 0.0069,11.0667 0.2928,10.7807L4.5858,6.4877L0.2928,2.1947C-0.0862,1.8023 -0.0807,1.1786 0.305,0.7929C0.6907,0.4072 1.3144,0.4017 1.7068,0.7807L6.7068,5.7807C7.0972,6.1712 7.0972,6.8042 6.7068,7.1947L1.7068,12.1947C1.5193,12.3823 1.265,12.4877 0.9998,12.4877Z" />
</vector>

File diff suppressed because one or more lines are too long

View File

@ -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:pathData="M11,4V3a1,1 0,0 1,2 0v1h6v5h-6v12a1,1 0,0 1,-2 0V9H5.4C4.2,9 3.6,7.8 4.3,7l1.6,-2c0.5,-0.6 1.3,-1 2.2,-1H11z"
android:strokeLineJoin="round" />
</vector>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-18a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm0 7c0.6 0 1 0.4 1 1v4a1 1 0 0 1-2 0v-4c0-0.6 0.4-1 1-1zm0-4a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"
android:fillColor="?primaryText" />
<path
android:fillColor="?primaryText"
android:pathData="M12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-18a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm0 7c0.6 0 1 0.4 1 1v4a1 1 0 0 1-2 0v-4c0-0.6 0.4-1 1-1zm0-4a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
</vector>

View File

@ -0,0 +1,13 @@
<?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="?attr/primaryText"
android:pathData="M20.9,8.4c0.6,-1.3 0.1,-2.4 -0.8,-3.3 0.3,-1.8 -0.9,-2.5 -2.8,-3 -0.5,-0.2 -2.5,0 -3.3,0l-6,0.3C6,2.4 3.8,2 1.7,2v8.6h4.3c0.3,0.1 1,0.8 1.1,1 0.2,0.1 0.2,0.3 0.2,0.3s0.4,1 0.8,1.3c1.6,1.6 2.9,4.5 3.5,6.8 0.5,0.7 -1,1.8 0.9,2 1.7,0.2 2.4,-3 1.8,-4.3 -0.6,-1.3 -1.2,-3.8 0,-3.5l1,0.2c1.6,0.5 4,0.7 5.3,-0.1 1.2,-0.9 0.6,-1.6 0.4,-2.6 1,-1 0.8,-2.2 0,-3.3" />
</vector>

View File

@ -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="?attr/primaryText"
android:pathData="M19,21L5,21a3,3 0,0 1,-3 -3L2,6a3,3 0,0 1,3 -3h14a3,3 0,0 1,3 3v12a3,3 0,0 1,-3 3zM19,5L5,5a1,1 0,0 0,-1 1v12c0,0.6 0.4,1 1,1h14c0.6,0 1,-0.4 1,-1L20,6c0,-0.6 -0.4,-1 -1,-1zM18,17L6,17v-2c0,-0.2 0,-0.4 0.2,-0.5l2.5,-2.3c0.2,-0.2 0.4,-0.2 0.6,-0.1l2.3,1c0.2,0.2 0.5,0 0.7,-0.1l2.2,-2.8a0.6,0.6 0,0 1,1 0l2.4,3.4 0.1,0.4v3zM8.5,8a1.5,1.5 0,1 1,0 3,1.5 1.5,0 0,1 0,-3z"
android:strokeLineJoin="round" />
</vector>

View File

@ -3,25 +3,25 @@
- 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">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path android:pathData="M 18.8 2.8 C 19.8 1.8 21.1 3.3 20.2 4.2 L 3.4 21 L 2 19.6 L 18.8 2.8 Z"/>
<clip-path android:pathData="M 18.8 2.8 C 19.8 1.8 21.1 3.3 20.2 4.2 L 3.4 21 L 2 19.6 L 18.8 2.8 Z" />
<path
android:name="strike_thru_path"
android:pathData="M 20 1.6 L 21.4 3 L 5.2 19.2 C 4.2 20.2 2.8 18.8 3.8 17.8 L 20 1.6 Z"
android:fillColor="@color/disabled_text"
android:strokeWidth="1"/>
android:name="strike_thru_path"
android:fillColor="@color/disabled_text"
android:pathData="M 20 1.6 L 21.4 3 L 5.2 19.2 C 4.2 20.2 2.8 18.8 3.8 17.8 L 20 1.6 Z"
android:strokeWidth="1" />
</group>
<group>
<clip-path
android:name="strike_thru_mask"
android:pathData="M 0 0 L 0 24 L 24 24 L 24 0 L 0 0 Z M 21 2 L 23 4 L 6 21 L 4 19 L 21 2 Z"/>
android:name="strike_thru_mask"
android:pathData="M 0 0 L 0 24 L 24 24 L 24 0 L 0 0 Z M 21 2 L 23 4 L 6 21 L 4 19 L 21 2 Z" />
<path
android:name="icon"
android:fillColor="@color/disabled_text"
android:pathData="M20 6c0-1-0.8-1.9-1.8-2L12 3 5.8 4C4.8 4 4 5 4 6l0.1 5c0.3 3.2 1 5 2.5 7a8.4 8.4 0 0 0 5.3 3h0.2c2.1-0.3 4-1.4 5.3-3 1.6-2 2.2-3.8 2.5-7l0.1-5zm-2.1 4.8a10 10 0 0 1-2 6c-1 1.1-2.4 2-3.9 2.3a6.5 6.5 0 0 1-3.9-2.4 9.9 9.9 0 0 1-2-5.9 67.3 67.3 0 0 1 0-4.9L12 5l5.9 1 0.1 0.2-0.1 4.7zM8 7.6v3c0.3 2.7 0.8 3.7 1.7 5 0.6 0.6 1.4 1.2 2.3 1.4V7l-4 0.6z" />
android:name="icon"
android:fillColor="@color/disabled_text"
android:pathData="M20 6c0-1-0.8-1.9-1.8-2L12 3 5.8 4C4.8 4 4 5 4 6l0.1 5c0.3 3.2 1 5 2.5 7a8.4 8.4 0 0 0 5.3 3h0.2c2.1-0.3 4-1.4 5.3-3 1.6-2 2.2-3.8 2.5-7l0.1-5zm-2.1 4.8a10 10 0 0 1-2 6c-1 1.1-2.4 2-3.9 2.3a6.5 6.5 0 0 1-3.9-2.4 9.9 9.9 0 0 1-2-5.9 67.3 67.3 0 0 1 0-4.9L12 5l5.9 1 0.1 0.2-0.1 4.7zM8 7.6v3c0.3 2.7 0.8 3.7 1.7 5 0.6 0.6 1.4 1.2 2.3 1.4V7l-4 0.6z" />
</group>
</vector>

View File

@ -0,0 +1,20 @@
<?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/. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="45"
android:pivotX="-40%"
android:pivotY="87%"
android:toDegrees="45">
<shape android:shape="rectangle">
<stroke
android:width="10dp"
android:color="#0250BB" />
<solid android:color="#0250BB" />
</shape>
</rotate>
</item>
</layer-list>

View File

@ -0,0 +1,19 @@
<?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/. --><shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="225"
android:endColor="#0250BB"
android:startColor="#9059FF"
android:type="linear" />
<size
android:width="256dp"
android:height="152dp" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>

View File

@ -0,0 +1,259 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/panel_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?foundation">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/normal_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/url"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="https://wikipedia.org" />
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
android:id="@+id/tracking_protection"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:text="@string/preference_enhanced_tracking_protection"
app:layout_constraintTop_toBottomOf="@id/url"
app:switchDescription="@string/etp_panel_on"
app:switchIcon="@drawable/ic_tracking_protection"
app:switchTitle="@string/preference_enhanced_tracking_protection" />
<TextView
android:id="@+id/blocking_header"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height"
android:text="@string/enhanced_tracking_protection_blocked"
android:textColor="?attr/primaryText"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
<TextView
android:id="@+id/cross_site_tracking"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_cookies"
android:text="@string/etp_cookies_title"
app:layout_constraintTop_toBottomOf="@id/blocking_header" />
<TextView
android:id="@+id/fingerprinters"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_fingerprinters"
android:text="@string/etp_fingerprinters_title"
app:layout_constraintTop_toBottomOf="@id/cross_site_tracking" />
<TextView
android:id="@+id/cryptominers"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_cryptominers"
android:text="@string/etp_cryptominers_title"
app:layout_constraintTop_toBottomOf="@id/fingerprinters" />
<TextView
android:id="@+id/social_media_trackers"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_social_media_trackers"
android:text="@string/etp_social_media_trackers_title"
app:layout_constraintTop_toBottomOf="@id/cryptominers" />
<TextView
android:id="@+id/tracking_content"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_tracking_content"
android:text="@string/etp_tracking_content_title"
app:layout_constraintTop_toBottomOf="@id/social_media_trackers" />
<TextView
android:id="@+id/not_blocking_header"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height"
android:text="@string/enhanced_tracking_protection_allowed"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracking_content" />
<TextView
android:id="@+id/cross_site_tracking_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_cookies"
android:text="@string/etp_cookies_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/not_blocking_header" />
<TextView
android:id="@+id/fingerprinters_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_fingerprinters"
android:text="@string/etp_fingerprinters_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cross_site_tracking_loaded" />
<TextView
android:id="@+id/cryptominers_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_cryptominers"
android:text="@string/etp_cryptominers_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/fingerprinters_loaded" />
<TextView
android:id="@+id/social_media_trackers_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_social_media_trackers"
android:text="@string/etp_social_media_trackers_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cryptominers_loaded" />
<TextView
android:id="@+id/tracking_content_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_tracking_content"
android:text="@string/etp_tracking_content_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/social_media_trackers_loaded" />
<View
android:id="@+id/line_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?neutralFaded"
app:layout_constraintTop_toBottomOf="@id/tracking_content_loaded" />
<TextView
android:id="@+id/protection_settings"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_settings"
android:paddingEnd="24dp"
android:text="@string/etp_settings"
app:layout_constraintTop_toBottomOf="@id/line_divider" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/details_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/details_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:src="@drawable/mozac_ic_back"
android:tint="?attr/primaryText"
app:layout_constraintBottom_toBottomOf="@+id/category_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/category_title" />
<TextView
android:id="@+id/category_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="52dp"
android:layout_marginTop="11dp"
android:layout_marginEnd="19dp"
android:textColor="?attr/primaryText"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem" />
<TextView
android:id="@+id/category_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="52dp"
android:layout_marginEnd="19dp"
android:textColor="?secondaryText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/category_title"
tools:text="@tools:sample/lorem" />
<View
android:id="@+id/line_divider_details"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="8dp"
android:background="?neutralFaded"
app:layout_constraintTop_toBottomOf="@id/category_description" />
<TextView
android:id="@+id/details_blocking_header"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height"
android:layout_marginStart="52dp"
android:layout_marginEnd="26dp"
android:paddingStart="0dp"
android:text="@string/enhanced_tracking_protection_blocked"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/line_divider_details" />
<androidx.core.widget.NestedScrollView
android:id="@+id/blocking_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/details_blocking_header"
app:layout_constraintTop_toBottomOf="@id/details_blocking_header">
<TextView
android:id="@+id/blocking_text_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="52dp"
android:layout_marginEnd="26dp"
android:layout_marginBottom="12dp"
tools:text="@tools:sample/lorem/random" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,6 +7,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:text="@string/preferences_tracking_protection_exceptions_description"
android:text="@string/enhanced_tracking_protection_exceptions"
android:textColor="?primaryText"
android:textSize="16sp" />

View File

@ -22,22 +22,22 @@
style="@style/QuickSettingsText"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="https://wikipedia.org"
app:layout_constraintEnd_toEndOf="parent" />
tools:text="https://wikipedia.org" />
<TextView
android:id="@+id/security_info"
style="@style/QuickSettingsText.Icon"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/url"
tools:drawableStart="@drawable/mozac_ic_lock"
tools:drawableTint="@color/photonGreen50"
tools:text="Secure connection"
app:layout_constraintEnd_toEndOf="parent"/>
tools:text="Secure connection" />
<View
android:id="@+id/line_divider_security"
@ -49,62 +49,71 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/security_info" />
<Switch
android:id="@+id/tracking_protection"
style="@style/QuickSettingsText.Icon"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/tracking_protection_view"
android:layout_width="match_parent"
android:layout_height="@dimen/quicksettings_item_height"
tools:drawableStart="@drawable/ic_tracking_protection"
android:paddingEnd="24dp"
android:text="@string/preferences_tracking_protection"
app:layout_constraintBottom_toTopOf="@id/tracking_protection_action"
app:layout_constraintTop_toBottomOf="@id/line_divider_security" />
<TextView
android:id="@+id/tracking_protection_action"
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/preferences_tracking_protection_turned_off_globally"
android:textColor="?accentBright"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/report_site_issue_action"
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
<TextView
android:id="@+id/report_site_issue_action"
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/browser_menu_report_issue"
android:textColor="?accentUsedOnDarkBackground"
app:layout_constraintBottom_toTopOf="@id/line_divider"
app:layout_constraintTop_toBottomOf="@id/tracking_protection_action" />
<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="?neutralFaded"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/camera_icon"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintTop_toBottomOf="@id/line_divider_security">
<Switch
android:id="@+id/tracking_protection"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/quicksettings_item_height"
android:paddingEnd="24dp"
android:text="@string/preferences_tracking_protection"
app:layout_constraintBottom_toTopOf="@id/tracking_protection_action"
app:layout_constraintTop_toTopOf="parent"
tools:drawableStart="@drawable/ic_tracking_protection" />
<TextView
android:id="@+id/tracking_protection_action"
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/preferences_tracking_protection_turned_off_globally"
android:textColor="?accentBright"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/report_site_issue_action"
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
<TextView
android:id="@+id/report_site_issue_action"
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/browser_menu_report_issue"
android:textColor="?accentUsedOnDarkBackground"
app:layout_constraintBottom_toTopOf="@id/line_divider"
app:layout_constraintTop_toBottomOf="@id/tracking_protection_action" />
<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="?neutralFaded"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/camera_icon"
style="@style/QuickSettingsText.Icon"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
android:text="@string/preference_phone_feature_camera"
android:drawableStart="@drawable/ic_camera"
android:text="@string/preference_phone_feature_camera"
app:layout_constraintEnd_toStartOf="@+id/camera_action_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/line_divider"
app:layout_constraintEnd_toStartOf="@+id/camera_action_label"/>
app:layout_constraintTop_toBottomOf="@id/tracking_protection_view" />
<TextView
android:id="@+id/camera_action_label"
@ -112,7 +121,7 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/quicksettings_item_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/line_divider"
app:layout_constraintTop_toBottomOf="@id/tracking_protection_view"
tools:text="Allowed" />
<TextView
@ -120,11 +129,11 @@
style="@style/QuickSettingsText.Icon"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
android:text="@string/preference_phone_feature_microphone"
android:drawableStart="@drawable/ic_microphone"
android:text="@string/preference_phone_feature_microphone"
app:layout_constraintEnd_toStartOf="@+id/microphone_action_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/camera_icon"
app:layout_constraintEnd_toStartOf="@+id/microphone_action_label"/>
app:layout_constraintTop_toBottomOf="@id/camera_icon" />
<TextView
android:id="@+id/microphone_action_label"
@ -140,11 +149,11 @@
style="@style/QuickSettingsText.Icon"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
android:text="@string/preference_phone_feature_notification"
android:drawableStart="@drawable/ic_notifications"
android:text="@string/preference_phone_feature_notification"
app:layout_constraintEnd_toStartOf="@+id/notification_action_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/microphone_icon"
app:layout_constraintEnd_toStartOf="@+id/notification_action_label" />
app:layout_constraintTop_toBottomOf="@id/microphone_icon" />
<TextView
android:id="@+id/notification_action_label"
@ -160,11 +169,11 @@
style="@style/QuickSettingsText.Icon"
android:layout_width="0dp"
android:layout_height="@dimen/quicksettings_item_height"
android:text="@string/preference_phone_feature_location"
android:drawableStart="@drawable/ic_location"
android:text="@string/preference_phone_feature_location"
app:layout_constraintEnd_toStartOf="@+id/location_action_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/notification_icon"
app:layout_constraintEnd_toStartOf="@+id/location_action_label"/>
app:layout_constraintTop_toBottomOf="@id/notification_icon" />
<TextView
android:id="@+id/location_action_label"

View File

@ -0,0 +1,10 @@
<?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/. -->
<androidx.core.widget.NestedScrollView
android:id="@+id/fragment_tp"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"/>

View File

@ -0,0 +1,80 @@
<?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/. -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/details_blocking_header"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:paddingStart="0dp"
android:text="@string/enhanced_tracking_protection_blocked"
android:textStyle="bold" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_social_media"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:categoryItemDescription="@string/etp_social_media_trackers_description"
app:categoryItemIcon="@drawable/ic_social_media_trackers"
app:categoryItemTitle="@string/etp_social_media_trackers_title" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_cookies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:categoryItemDescription="@string/etp_cookies_description"
app:categoryItemIcon="@drawable/ic_cookies"
app:categoryItemTitle="@string/etp_cookies_title" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_cryptominers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:categoryItemDescription="@string/etp_cryptominers_description"
app:categoryItemIcon="@drawable/ic_cryptominers"
app:categoryItemTitle="@string/etp_cryptominers_title" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_fingerprinters"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:categoryItemDescription="@string/etp_fingerprinters_description"
app:categoryItemIcon="@drawable/ic_fingerprinters"
app:categoryItemTitle="@string/etp_fingerprinters_title" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_tracking_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:categoryItemDescription="@string/etp_tracking_content_description"
app:categoryItemIcon="@drawable/ic_tracking_content"
app:categoryItemTitle="@string/etp_tracking_content_title" />
</LinearLayout>
</ScrollView>

View File

@ -6,7 +6,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayout"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"

View File

@ -0,0 +1,76 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"
android:paddingTop="@dimen/radio_button_preference_vertical"
android:paddingBottom="@dimen/radio_button_preference_vertical">
<RadioButton
android:id="@+id/radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="42dp"
android:background="@android:color/transparent"
android:button="@null"
android:clickable="false"
android:drawablePadding="6dp"
android:focusable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:drawableStart="?android:attr/listChoiceIndicatorSingle" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center|start"
android:textAppearance="?android:attr/textAppearanceListItem"
app:layout_constraintBottom_toBottomOf="@id/radio_button"
app:layout_constraintEnd_toStartOf="@+id/vertical_divider"
app:layout_constraintStart_toEndOf="@+id/radio_button"
app:layout_constraintTop_toTopOf="@id/radio_button"
tools:text="Use recommended settings" />
<TextView
android:id="@+id/widget_summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toStartOf="@+id/vertical_divider"
app:layout_constraintStart_toStartOf="@+id/title"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="@tools:sample/lorem/random" />
<View
android:id="@+id/vertical_divider"
android:layout_width="1dp"
android:layout_height="0dp"
android:background="?neutralFaded"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/info_button"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/info_button"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="18dp"
android:layout_marginEnd="18dp"
android:src="@drawable/ic_info"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/vertical_divider"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,53 @@
<?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/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<TextView
android:id="@+id/switchItemTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginEnd="64dp"
android:clickable="false"
android:textAppearance="@style/ListItemTextStyle"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/switchItemDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/lorem" />
<TextView
android:id="@+id/switchItemDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:clickable="false"
android:textColor="?attr/secondaryText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/switchItemTitle"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@id/switchItemTitle"
app:layout_constraintTop_toBottomOf="@+id/switchItemTitle"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/lorem" />
<Switch
android:id="@+id/switch_widget"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:drawableStart="@drawable/ic_tracking_protection"
app:layout_constraintBottom_toBottomOf="@id/switchItemDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/switchItemTitle" />
</merge>

View File

@ -0,0 +1,57 @@
<?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/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/switchIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginTop="@dimen/library_item_icon_margin_vertical"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:layout_marginBottom="@dimen/library_item_icon_margin_vertical"
android:background="@drawable/ic_cryptominers"
android:clickable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/switchItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginTop="12dp"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:clickable="false"
android:textAppearance="@style/ListItemTextStyle"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/switchItemDescription"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@id/switchIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem" />
<TextView
android:id="@+id/switchItemDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:layout_marginBottom="18dp"
android:clickable="false"
android:textColor="?attr/secondaryText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@id/switchIcon"
app:layout_constraintTop_toBottomOf="@+id/switchItemTitle"
tools:text="@tools:sample/lorem/random" />
</merge>

View File

@ -0,0 +1,84 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:src="@drawable/ic_etp_artwork"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@android:id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:ellipsize="none"
android:letterSpacing="0.05"
android:scrollHorizontally="false"
android:singleLine="false"
android:text="@string/preference_enhanced_tracking_protection_explanation_title"
android:textAppearance="@style/Header16TextStyle"
android:textColor="@color/primary_text_dark_theme"
app:layout_constraintBottom_toTopOf="@android:id/summary"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@android:id/summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="none"
android:scrollHorizontally="false"
android:text="@string/preference_enhanced_tracking_protection_explanation"
android:textColor="@color/primary_text_dark_theme"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@android:id/title"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/learn_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ellipsize="none"
android:text="@string/preference_enhanced_tracking_protection_explanation_learn_more"
android:textColor="@color/primary_text_dark_theme"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@android:id/title"
app:layout_constraintTop_toBottomOf="@android:id/summary" />
<ImageView
android:layout_width="8dp"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_arrowhead_right"
android:tint="@color/primary_text_dark_theme"
app:layout_constraintBottom_toBottomOf="@id/learn_more"
app:layout_constraintStart_toEndOf="@id/learn_more"
app:layout_constraintTop_toTopOf="@id/learn_more" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="210dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,57 @@
<?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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/onboarding_popup_background"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/onboarding_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:lineSpacingExtra="2dp"
android:text="@string/etp_onboarding_message"
android:textColor="@color/primary_text_dark_theme"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/close_onboarding"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/close_onboarding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
android:src="@drawable/ic_close"
android:tint="@color/primary_text_dark_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="16dp"
android:importantForAccessibility="no"
android:rotation="180"
android:src="@drawable/ic_triangle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/message" />
</LinearLayout>

View File

@ -100,8 +100,8 @@
app:nullable="true" />
<argument
android:name="showShortcutEnginePicker"
app:argType="boolean"
android:defaultValue="false" />
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_searchFragment_to_searchEngineFragment"
app:destination="@id/searchEngineFragment"
@ -185,22 +185,28 @@
<action
android:id="@+id/action_browserFragment_to_quickSettingsSheetDialogFragment"
app:destination="@id/quickSettingsSheetDialogFragment" />
<action
android:id="@+id/action_browserFragment_to_trackingProtectionPanelDialogFragment"
app:destination="@id/trackingProtectionPanelDialogFragment" />
</fragment>
<fragment
android:id="@+id/externalAppBrowserFragment"
android:name="org.mozilla.fenix.customtabs.ExternalAppBrowserFragment"
tools:layout="@layout/fragment_browser">
android:id="@+id/externalAppBrowserFragment"
android:name="org.mozilla.fenix.customtabs.ExternalAppBrowserFragment"
tools:layout="@layout/fragment_browser">
<argument
android:name="activeSessionId"
app:argType="string"
app:nullable="true" />
android:name="activeSessionId"
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_externalAppBrowserFragment_to_shareFragment"
app:destination="@id/shareFragment" />
android:id="@+id/action_externalAppBrowserFragment_to_shareFragment"
app:destination="@id/shareFragment" />
<action
android:id="@+id/action_externalAppBrowserFragment_to_quickSettingsSheetDialogFragment"
app:destination="@id/quickSettingsSheetDialogFragment" />
android:id="@+id/action_externalAppBrowserFragment_to_quickSettingsSheetDialogFragment"
app:destination="@id/quickSettingsSheetDialogFragment" />
<action
android:id="@+id/action_externalAppBrowserFragment_to_trackingProtectionPanelDialogFragment"
app:destination="@id/trackingProtectionPanelDialogFragment" />
</fragment>
<fragment
@ -396,13 +402,12 @@
<fragment
android:id="@+id/pairFragment"
android:name="org.mozilla.fenix.settings.PairFragment"
android:label="@string/preferences_sync">
</fragment>
android:label="@string/preferences_sync"></fragment>
<fragment
android:id="@+id/aboutFragment"
android:name="org.mozilla.fenix.settings.AboutFragment"
android:label="AboutFragment" >
android:label="AboutFragment">
<action
android:id="@+id/action_aboutFragment_to_browserFragment"
app:destination="@id/browserFragment" />
@ -414,7 +419,7 @@
<action
android:id="@+id/action_crashReporterFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/nav_graph"/>
app:popUpTo="@id/nav_graph" />
<argument
android:name="crashIntent"
app:argType="android.content.Intent" />
@ -430,6 +435,12 @@
<action
android:id="@+id/action_trackingProtectionFragment_to_exceptionsFragment"
app:destination="@id/exceptionsFragment" />
<action
android:id="@+id/action_trackingProtectionFragment_to_trackingProtectionBlockingFragment"
app:destination="@id/trackingProtectionBlockingFragment" />
<action
android:id="@+id/action_trackingProtectionFragment_to_browserFragment"
app:destination="@id/browserFragment" />
</fragment>
<fragment
android:id="@+id/deleteBrowsingDataFragment"
@ -515,6 +526,36 @@
<dialog
android:id="@+id/signOutFragment"
android:name="org.mozilla.fenix.settings.SignOutFragment" />
<action android:id="@+id/action_global_shareFragment"
app:destination="@id/shareFragment"/>
<action
android:id="@+id/action_global_shareFragment"
app:destination="@id/shareFragment" />
<dialog
android:id="@+id/trackingProtectionPanelDialogFragment"
android:name="org.mozilla.fenix.trackingprotection.TrackingProtectionPanelDialogFragment"
android:label="TrackingProtectionPanelDialogFragment">
<argument
android:name="sessionId"
app:argType="string" />
<argument
android:name="url"
app:argType="string" />
<argument
android:name="trackingProtectionEnabled"
app:argType="boolean" />
<argument
android:name="gravity"
android:defaultValue="80"
app:argType="integer" />
<action
android:id="@+id/action_trackingProtectionPanelDialogFragment_to_trackingProtectionFragment"
app:destination="@id/trackingProtectionFragment" />
</dialog>
<fragment
android:id="@+id/trackingProtectionBlockingFragment"
android:name="org.mozilla.fenix.trackingprotection.TrackingProtectionBlockingFragment"
android:label="TrackingProtectionBlockingFragment">
<argument
android:name="strictMode"
app:argType="boolean" />
</fragment>
</navigation>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -33,12 +33,25 @@
<attr name="toolbarStartGradient" format="reference"/>
<attr name="toolbarCenterGradient" format="reference"/>
<attr name="toolbarEndGradient" format="reference"/>
<attr name="shieldLottieFile" format="reference" />
<declare-styleable name="LibraryListItem">
<attr name="listItemTitle" format="reference" />
<attr name="listItemIcon" format="reference" />
</declare-styleable>
<declare-styleable name="TrackingProtectionCategory">
<attr name="categoryItemTitle" format="reference" />
<attr name="categoryItemDescription" format="reference" />
<attr name="categoryItemIcon" format="reference" />
</declare-styleable>
<declare-styleable name="SwitchWithDescription">
<attr name="switchTitle" format="reference" />
<attr name="switchDescription" format="reference" />
<attr name="switchIcon" format="reference" />
</declare-styleable>
<declare-styleable name="DeleteBrowsingDataItem">
<attr name="deleteBrowsingDataItemTitle" format="reference" />
<attr name="deleteBrowsingDataItemSubtitle" format="reference" />

View File

@ -34,6 +34,7 @@
<!--Quick Settings-->
<dimen name="quicksettings_item_height">46dp</dimen>
<dimen name="tracking_protection_item_height">48dp</dimen>
<dimen name="design_quick_action_sheet_peek_height_min">64dp</dimen>
@ -51,4 +52,7 @@
<dimen name="sign_in_button_padding">10dp</dimen>
<dimen name="sign_in_button_margin_top">32dp</dimen>
<dimen name="sign_in_button_margin">16dp</dimen>
<!-- ETP Onboarding Popup -->
<dimen name="etp_onboarding_popup_width">256dp</dimen>
</resources>

View File

@ -77,10 +77,13 @@
<string name="pref_key_follow_device_theme" translatable="false">pref_key_follow_device_theme</string>
<!-- Tracking Protection Settings -->
<string name="pref_key_etp_learn_more" translatable="false">pref_key_etp_learn_more</string>
<string name="pref_key_tracking_protection_settings" translatable="false">pref_key_tracking_protection_settings</string>
<string name="pref_key_tracking_protection" translatable="false">pref_key_tracking_protection</string>
<string name="pref_key_tracking_protection_exceptions" translatable="false">pref_key_tracking_protection_exceptions</string>
<string name="pref_key_tracking_protection_standard" translatable="false">pref_key_tracking_protection_standard</string>
<string name="pref_key_tracking_protection_strict" translatable="false">pref_key_tracking_protection_strict</string>
<string name="pref_key_tracking_protection_onboarding" translatable="false">pref_key_tracking_protection_onboarding</string>
<!-- Quick Action Sheet -->
<string name="pref_key_bounce_quick_action" translatable="false">pref_key_bounce_quick_action</string>
<string name="pref_key_reader_mode_notification" translatable="false">pref_key_reader_mode_notification</string>

View File

@ -54,6 +54,7 @@
<item name="homeBackground">@color/foundation_normal_theme</item>
<item name="privateBrowsingButtonBackground">@android:color/transparent</item>
<item name="privateBrowsingButtonAccent">@color/primary_text_normal_theme</item>
<item name="shieldLottieFile">@raw/shield_json</item>
</style>
<style name="NormalTheme" parent="NormalThemeBase" />
@ -134,6 +135,7 @@
<item name="homeBackground">@drawable/private_home_background_gradient</item>
<item name="privateBrowsingButtonBackground">@color/primary_text_private_theme</item>
<item name="privateBrowsingButtonAccent">@color/above_private_theme</item>
<item name="shieldLottieFile">@raw/shield_json_dark</item>
</style>
@ -271,6 +273,7 @@
<item name="android:textColor">@color/state_list_text_color</item>
<item name="android:textSize">14sp</item>
<item name="android:paddingStart">16dp</item>
<item name="android:paddingEnd">16dp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_alignParentStart">true</item>
</style>

View File

@ -57,7 +57,7 @@
<androidx.preference.Preference
android:icon="@drawable/ic_tracking_protection_enabled"
android:key="@string/pref_key_tracking_protection_settings"
android:title="@string/preferences_tracking_protection" />
android:title="@string/preference_enhanced_tracking_protection" />
<androidx.preference.Preference
android:icon="@drawable/ic_permission"
android:key="@string/pref_key_site_permissions"

View File

@ -2,15 +2,34 @@
<!-- 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/. -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.preference.Preference
app:allowDividerBelow="false"
android:key="@string/pref_key_etp_learn_more"
android:layout="@layout/tracking_protection_learn_more_preference"
android:summary="@string/preference_enhanced_tracking_protection_explanation"
android:title="@string/preference_enhanced_tracking_protection_explanation_title" />
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_tracking_protection_enabled"
android:key="@string/pref_key_tracking_protection"
android:summary="@string/preferences_tracking_protection_description"
android:title="@string/preferences_tracking_protection" />
android:title="@string/preference_enhanced_tracking_protection" />
<org.mozilla.fenix.settings.RadioButtonInfoPreference
android:dependency="@string/pref_key_tracking_protection"
android:defaultValue="true"
android:key="@string/pref_key_tracking_protection_standard"
android:summary="@string/preference_enhanced_tracking_protection_standard_description"
android:title="@string/preference_enhanced_tracking_protection_standard" />
<org.mozilla.fenix.settings.RadioButtonInfoPreference
android:dependency="@string/pref_key_tracking_protection"
android:defaultValue="false"
android:key="@string/pref_key_tracking_protection_strict"
android:summary="@string/preference_enhanced_tracking_protection_strict_description"
android:title="@string/preference_enhanced_tracking_protection_strict" />
<Preference
android:icon="@drawable/ic_internet"
app:allowDividerAbove="true"
android:icon="@drawable/ic_info"
android:key="@string/pref_key_tracking_protection_exceptions"
android:title="@string/preferences_tracking_protection_exceptions" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,63 @@
/* 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.trackingprotection
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
import org.junit.Assert.assertEquals
class TrackingProtectionPanelInteractorTest {
@Test
fun openDetails() {
val store: TrackingProtectionStore = mockk(relaxed = true)
val interactor =
TrackingProtectionPanelInteractor(store, mockk(), mockk())
interactor.openDetails(TrackingProtectionCategory.FINGERPRINTERS, true)
verify {
store.dispatch(
TrackingProtectionAction.EnterDetailsMode(
TrackingProtectionCategory.FINGERPRINTERS,
true
)
)
}
}
@Test
fun selectTrackingProtectionSettings() {
var openSettings = false
val interactor = TrackingProtectionPanelInteractor(
mockk(),
mockk(),
{ openSettings = true }
)
interactor.selectTrackingProtectionSettings()
assertEquals(true, openSettings)
}
@Test
fun trackingProtectionToggled() {
var trackingProtectionNewValue: Boolean? = null
val interactor = TrackingProtectionPanelInteractor(
mockk(),
{ trackingProtectionNewValue = it },
mockk()
)
interactor.trackingProtectionToggled(true)
assertEquals(true, trackingProtectionNewValue)
}
@Test
fun onBackPressed() {
val store: TrackingProtectionStore = mockk(relaxed = true)
val interactor =
TrackingProtectionPanelInteractor(store, mockk(), mockk())
interactor.onBackPressed()
verify { store.dispatch(TrackingProtectionAction.ExitDetailsMode) }
}
}

View File

@ -0,0 +1,160 @@
/* 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.trackingprotection
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotSame
import kotlinx.coroutines.runBlocking
import mozilla.components.concept.engine.content.blocking.Tracker
import org.junit.Test
class TrackingProtectionStoreTest {
@Test
fun enterDetailsMode() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
store.dispatch(
TrackingProtectionAction.EnterDetailsMode(
TrackingProtectionCategory.FINGERPRINTERS,
true
)
)
.join()
assertNotSame(initialState, store.state)
assertEquals(
store.state.mode,
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true)
)
}
@Test
fun exitDetailsMode() = runBlocking {
val initialState = detailsState()
val store = TrackingProtectionStore(initialState)
store.dispatch(TrackingProtectionAction.ExitDetailsMode).join()
assertNotSame(initialState, store.state)
assertEquals(
store.state.mode,
TrackingProtectionState.Mode.Normal
)
}
@Test
fun trackerBlockingChanged() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
store.dispatch(TrackingProtectionAction.TrackerBlockingChanged(false)).join()
assertNotSame(initialState, store.state)
assertEquals(
store.state.mode,
TrackingProtectionState.Mode.Normal
)
assertEquals(
false,
store.state.isTrackingProtectionEnabled
)
}
@Test
fun trackerListChanged() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
val tracker = Tracker("url", listOf())
store.dispatch(TrackingProtectionAction.TrackerListChange(listOf(tracker))).join()
assertNotSame(initialState, store.state)
assertEquals(
listOf(tracker),
store.state.listTrackers
)
}
@Test
fun trackerLoadedListChanged() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
val tracker = Tracker("url", listOf())
store.dispatch(TrackingProtectionAction.TrackerLoadedListChange(listOf(tracker))).join()
assertNotSame(initialState, store.state)
assertEquals(
listOf(tracker),
store.state.listTrackersLoaded
)
}
@Test
fun urlChanged() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
store.dispatch(TrackingProtectionAction.UrlChange("newURL")).join()
assertNotSame(initialState, store.state)
assertEquals(
"newURL",
store.state.url
)
}
@Test
fun onChange() = runBlocking {
val initialState = defaultState()
val store = TrackingProtectionStore(initialState)
val tracker = Tracker("url", listOf())
store.dispatch(
TrackingProtectionAction.Change(
"newURL",
false,
listOf(tracker),
listOf(),
TrackingProtectionState.Mode.Details(
TrackingProtectionCategory.FINGERPRINTERS,
true
)
)
).join()
assertNotSame(initialState, store.state)
assertEquals(
"newURL",
store.state.url
)
assertEquals(
false,
store.state.isTrackingProtectionEnabled
)
assertEquals(
listOf<Tracker>(),
store.state.listTrackersLoaded
)
assertEquals(
listOf(tracker),
store.state.listTrackers
)
assertEquals(
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
store.state.mode
)
}
private fun defaultState(): TrackingProtectionState = TrackingProtectionState(
url = "www.mozilla.org",
isTrackingProtectionEnabled = true,
listTrackers = listOf(),
listTrackersLoaded = listOf(),
mode = TrackingProtectionState.Mode.Normal
)
private fun detailsState(): TrackingProtectionState = TrackingProtectionState(
url = "www.mozilla.org",
isTrackingProtectionEnabled = true,
listTrackers = listOf(),
listTrackersLoaded = listOf(),
mode = TrackingProtectionState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true)
)
}

View File

@ -64,6 +64,9 @@ object Versions {
const val robolectric = "4.2"
const val google_ads_id_version = "16.0.0"
const val airbnb_lottie = "3.0.7"
const val androidx_fragment_testing_version = "1.2.0-alpha02"
}
@ -217,4 +220,6 @@ object Deps {
const val google_ads_id = "com.google.android.gms:play-services-ads-identifier:${Versions.google_ads_id_version}"
const val androidx_fragment_testing = "androidx.fragment:fragment-testing:${Versions.androidx_fragment_testing_version}"
const val lottie = "com.airbnb.android:lottie:${Versions.airbnb_lottie}"
}