Bug 1797577 - Add cookie banner handling panel to the toolbar.
This commit is contained in:
parent
e7a7712a6b
commit
cc666c8887
|
@ -6883,7 +6883,55 @@ cookie_banners:
|
|||
metadata:
|
||||
tags:
|
||||
- Privacy&Security
|
||||
|
||||
exception_added:
|
||||
type: event
|
||||
description: |
|
||||
A user added a cookie banner handling exception through
|
||||
the toggle in the protections panel.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: 118
|
||||
metadata:
|
||||
tags:
|
||||
- Privacy&Security
|
||||
exception_removed:
|
||||
type: event
|
||||
description: |
|
||||
A user removed a cookie banner handling
|
||||
exception through the toggle in the protections panel.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: 118
|
||||
metadata:
|
||||
tags:
|
||||
- Privacy&Security
|
||||
visited_panel:
|
||||
type: event
|
||||
description: A user visited the cookie banner toolbar panel
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: 118
|
||||
metadata:
|
||||
tags:
|
||||
- Privacy&Security
|
||||
site_permissions:
|
||||
prompt_shown:
|
||||
type: event
|
||||
|
|
|
@ -11,8 +11,12 @@ import android.view.ViewGroup
|
|||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.selector.findTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
|
@ -38,6 +42,7 @@ import org.mozilla.fenix.ext.nav
|
|||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.runIfFragmentIsAttached
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.shortcut.PwaOnboardingObserver
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
|
||||
|
@ -360,22 +365,35 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
|||
}
|
||||
|
||||
override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) {
|
||||
requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
|
||||
runIfFragmentIsAttached {
|
||||
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = getAppropriateLayoutGravity(),
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
val useCase = requireComponents.useCases.trackingProtectionUseCases
|
||||
FxNimbus.features.cookieBanners.recordExposure()
|
||||
useCase.containsException(tab.id) { hasTrackingProtectionException ->
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
val cookieBannersStorage = requireComponents.core.cookieBannersStorage
|
||||
val hasCookieBannerException = withContext(Dispatchers.IO) {
|
||||
cookieBannersStorage.hasException(
|
||||
tab.content.url,
|
||||
tab.content.private,
|
||||
)
|
||||
nav(R.id.browserFragment, directions)
|
||||
}
|
||||
runIfFragmentIsAttached {
|
||||
val isTrackingProtectionEnabled =
|
||||
tab.trackingProtection.enabled && !hasTrackingProtectionException
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = getAppropriateLayoutGravity(),
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = !hasCookieBannerException,
|
||||
)
|
||||
nav(R.id.browserFragment, directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import mozilla.components.browser.engine.gecko.GeckoEngine
|
||||
import mozilla.components.browser.engine.gecko.cookiebanners.GeckoCookieBannersStorage
|
||||
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
|
||||
import mozilla.components.browser.engine.gecko.permission.GeckoSitePermissionsStorage
|
||||
import mozilla.components.browser.icons.BrowserIcons
|
||||
|
@ -183,6 +184,8 @@ class Core(
|
|||
)
|
||||
}
|
||||
|
||||
val cookieBannersStorage by lazyMonitored { GeckoCookieBannersStorage(geckoRuntime) }
|
||||
|
||||
val geckoSitePermissionsStorage by lazyMonitored {
|
||||
GeckoSitePermissionsStorage(geckoRuntime, OnDiskSitePermissionsStorage(context))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,11 @@ import android.content.Intent
|
|||
import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||
import mozilla.components.concept.engine.manifest.WebAppManifestParser
|
||||
|
@ -29,6 +33,7 @@ import org.mozilla.fenix.R
|
|||
import org.mozilla.fenix.browser.BaseBrowserFragment
|
||||
import org.mozilla.fenix.browser.CustomTabContextMenuCandidate
|
||||
import org.mozilla.fenix.browser.FenixSnackbarDelegate
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
@ -159,21 +164,29 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||
}
|
||||
|
||||
override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) {
|
||||
val cookieBannersStorage = requireComponents.core.cookieBannersStorage
|
||||
requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
|
||||
runIfFragmentIsAttached {
|
||||
val directions = ExternalAppBrowserFragmentDirections
|
||||
.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = getAppropriateLayoutGravity(),
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains,
|
||||
)
|
||||
nav(R.id.externalAppBrowserFragment, directions)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val hasException =
|
||||
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
|
||||
withContext(Dispatchers.Main) {
|
||||
runIfFragmentIsAttached {
|
||||
val directions = ExternalAppBrowserFragmentDirections
|
||||
.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = getAppropriateLayoutGravity(),
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains,
|
||||
isCookieHandlingEnabled = !hasException,
|
||||
)
|
||||
nav(R.id.externalAppBrowserFragment, directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,6 +281,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
SettingsFragmentDirections.actionSettingsFragmentToHttpsOnlyFragment()
|
||||
}
|
||||
resources.getString(R.string.pref_key_cookie_banner_settings) -> {
|
||||
FxNimbus.features.cookieBanners.recordExposure()
|
||||
CookieBanners.visitedSetting.record(mozilla.components.service.glean.private.NoExtras())
|
||||
SettingsFragmentDirections.actionSettingsFragmentToCookieBannerFragment()
|
||||
}
|
||||
|
|
|
@ -7,7 +7,12 @@ package org.mozilla.fenix.settings.quicksettings
|
|||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
@ -29,9 +34,12 @@ interface ConnectionDetailsController {
|
|||
/**
|
||||
* Default behavior of [ConnectionDetailsController].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class DefaultConnectionDetailsController(
|
||||
private val context: Context,
|
||||
private val fragment: Fragment,
|
||||
private val ioScope: CoroutineScope,
|
||||
private val cookieBannersStorage: CookieBannersStorage,
|
||||
private val navController: () -> NavController,
|
||||
internal var sitePermissions: SitePermissions?,
|
||||
private val gravity: Int,
|
||||
|
@ -41,22 +49,30 @@ class DefaultConnectionDetailsController(
|
|||
override fun handleBackPressed() {
|
||||
getCurrentTab()?.let { tab ->
|
||||
context.components.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
|
||||
fragment.runIfFragmentIsAttached {
|
||||
navController().popBackStack()
|
||||
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
)
|
||||
navController().navigate(directions)
|
||||
ioScope.launch {
|
||||
val hasException =
|
||||
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
|
||||
withContext(Dispatchers.Main) {
|
||||
fragment.runIfFragmentIsAttached {
|
||||
navController().popBackStack()
|
||||
val isTrackingProtectionEnabled =
|
||||
tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = !hasException,
|
||||
)
|
||||
navController().navigate(directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,11 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.plus
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -39,6 +42,8 @@ class ConnectionPanelDialogFragment : FenixDialogFragment() {
|
|||
|
||||
val controller = DefaultConnectionDetailsController(
|
||||
context = requireContext(),
|
||||
ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO,
|
||||
cookieBannersStorage = requireComponents.core.cookieBannersStorage,
|
||||
fragment = this,
|
||||
navController = { findNavController() },
|
||||
sitePermissions = args.sitePermissions,
|
||||
|
|
|
@ -17,6 +17,7 @@ import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase
|
|||
import mozilla.components.support.base.feature.OnNeedToRequestPermissions
|
||||
import mozilla.components.support.ktx.kotlin.getOrigin
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.CookieBanners
|
||||
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.components.PermissionStorage
|
||||
|
@ -60,14 +61,19 @@ interface QuickSettingsController {
|
|||
fun handleAndroidPermissionGranted(feature: PhoneFeature)
|
||||
|
||||
/**
|
||||
* @see [TrackingProtectionInteractor.onTrackingProtectionToggled]
|
||||
* @see [ProtectionsInteractor.onTrackingProtectionToggled]
|
||||
*/
|
||||
fun handleTrackingProtectionToggled(isEnabled: Boolean)
|
||||
|
||||
/**
|
||||
* @see [TrackingProtectionInteractor.onDetailsClicked]
|
||||
* Navigates to the cookie banners details panel.
|
||||
*/
|
||||
fun handleDetailsClicked()
|
||||
fun handleCookieBannerHandlingDetailsClicked()
|
||||
|
||||
/**
|
||||
* Navigates to the tracking protection details panel.
|
||||
*/
|
||||
fun handleTrackingProtectionDetailsClicked()
|
||||
|
||||
/**
|
||||
* Navigates to the connection details. Called when a user clicks on the
|
||||
|
@ -201,15 +207,34 @@ class DefaultQuickSettingsController(
|
|||
)
|
||||
}
|
||||
|
||||
override fun handleDetailsClicked() {
|
||||
override fun handleCookieBannerHandlingDetailsClicked() {
|
||||
CookieBanners.visitedPanel.record(NoExtras())
|
||||
|
||||
navController.popBackStack()
|
||||
|
||||
val state = quickSettingsStore.state.trackingProtectionState
|
||||
val state = quickSettingsStore.state.protectionsState
|
||||
val directions = NavGraphDirections
|
||||
.actionGlobalCookieBannerProtectionPanelDialogFragment(
|
||||
sessionId = sessionId,
|
||||
url = state.url,
|
||||
trackingProtectionEnabled = state.isTrackingProtectionEnabled,
|
||||
cookieBannerHandlingEnabled = state.isCookieBannerHandlingEnabled,
|
||||
gravity = context.components.settings.toolbarPosition.androidGravity,
|
||||
sitePermissions = sitePermissions,
|
||||
)
|
||||
navController.navigate(directions)
|
||||
}
|
||||
|
||||
override fun handleTrackingProtectionDetailsClicked() {
|
||||
navController.popBackStack()
|
||||
|
||||
val state = quickSettingsStore.state.protectionsState
|
||||
val directions = NavGraphDirections
|
||||
.actionGlobalTrackingProtectionPanelDialogFragment(
|
||||
sessionId = sessionId,
|
||||
url = state.url,
|
||||
trackingProtectionEnabled = state.isTrackingProtectionEnabled,
|
||||
cookieBannerHandlingEnabled = state.isCookieBannerHandlingEnabled,
|
||||
gravity = context.components.settings.toolbarPosition.androidGravity,
|
||||
sitePermissions = sitePermissions,
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ package org.mozilla.fenix.settings.quicksettings
|
|||
|
||||
import mozilla.components.lib.state.Action
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
|
||||
/**
|
||||
* Parent [Action] for all the [QuickSettingsFragmentState] changes.
|
||||
|
@ -49,7 +49,7 @@ sealed class WebsitePermissionAction(open val updatedFeature: PhoneFeature) : Qu
|
|||
}
|
||||
|
||||
/**
|
||||
* All possible [TrackingProtectionState] changes as a result oof user / system interactions.
|
||||
* All possible [ProtectionsState] changes in the quick setting panel.
|
||||
*/
|
||||
sealed class TrackingProtectionAction : QuickSettingsFragmentAction() {
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.settings.quicksettings
|
||||
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
|
||||
/**
|
||||
* Parent Reducer for all [QuickSettingsFragmentState]s of all Views shown in this Fragment.
|
||||
|
@ -27,8 +27,8 @@ internal fun quickSettingsFragmentReducer(
|
|||
),
|
||||
)
|
||||
is TrackingProtectionAction -> state.copy(
|
||||
trackingProtectionState = TrackingProtectionStateReducer.reduce(
|
||||
state = state.trackingProtectionState,
|
||||
protectionsState = ProtectionsStateReducer.reduce(
|
||||
state = state.protectionsState,
|
||||
action = action,
|
||||
),
|
||||
)
|
||||
|
@ -67,15 +67,18 @@ object WebsitePermissionsStateReducer {
|
|||
}
|
||||
}
|
||||
|
||||
object TrackingProtectionStateReducer {
|
||||
/**
|
||||
* A reduce for [TrackingProtectionAction]s.
|
||||
*/
|
||||
object ProtectionsStateReducer {
|
||||
/**
|
||||
* Handles creating a new [TrackingProtectionState] based on the specific
|
||||
* Handles creating a new [ProtectionsState] based on the specific
|
||||
* [TrackingProtectionAction].
|
||||
*/
|
||||
fun reduce(
|
||||
state: TrackingProtectionState,
|
||||
state: ProtectionsState,
|
||||
action: TrackingProtectionAction,
|
||||
): TrackingProtectionState {
|
||||
): ProtectionsState {
|
||||
return when (action) {
|
||||
is TrackingProtectionAction.ToggleTrackingProtectionEnabled ->
|
||||
state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled)
|
||||
|
|
|
@ -14,18 +14,18 @@ import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayA
|
|||
import mozilla.components.lib.state.State
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* [State] containing all data displayed to the user by this Fragment.
|
||||
*
|
||||
* Partitioned further to contain mutiple states for each standalone View this Fragment holds.
|
||||
* Partitioned further to contain multiple states for each standalone View this Fragment holds.
|
||||
*/
|
||||
data class QuickSettingsFragmentState(
|
||||
val webInfoState: WebsiteInfoState,
|
||||
val websitePermissionsState: WebsitePermissionsState,
|
||||
val trackingProtectionState: TrackingProtectionState,
|
||||
val protectionsState: ProtectionsState,
|
||||
) : State
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,7 @@ import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Compa
|
|||
import org.mozilla.fenix.settings.quicksettings.WebsiteInfoState.Companion.createWebsiteInfoState
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import java.util.EnumMap
|
||||
|
||||
|
@ -69,6 +69,7 @@ class QuickSettingsFragmentStore(
|
|||
settings: Settings,
|
||||
sessionId: String,
|
||||
isTrackingProtectionEnabled: Boolean,
|
||||
isCookieHandlingEnabled: Boolean,
|
||||
) = QuickSettingsFragmentStore(
|
||||
QuickSettingsFragmentState(
|
||||
webInfoState = createWebsiteInfoState(
|
||||
|
@ -83,11 +84,12 @@ class QuickSettingsFragmentStore(
|
|||
permissionHighlights,
|
||||
settings,
|
||||
),
|
||||
trackingProtectionState = createTrackingProtectionState(
|
||||
protectionsState = createTrackingProtectionState(
|
||||
context,
|
||||
sessionId,
|
||||
websiteUrl,
|
||||
isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -123,14 +125,16 @@ class QuickSettingsFragmentStore(
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct an initial [TrackingProtectionState] to be rendered by
|
||||
* [TrackingProtectionView].
|
||||
* Construct an initial [ProtectionsState] to be rendered by
|
||||
* [ProtectionsView].
|
||||
*
|
||||
* @param context [Context] used for various Android interactions.
|
||||
* @param sessionId [String] The current session ID.
|
||||
* @param websiteUrl [String] the URL of the current web page.
|
||||
* @param isTrackingProtectionEnabled [Boolean] Current status of tracking protection
|
||||
* for this session.
|
||||
* @param isCookieHandlingEnabled [Boolean] Current status of cookie banner handling
|
||||
* for this session.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun createTrackingProtectionState(
|
||||
|
@ -138,13 +142,15 @@ class QuickSettingsFragmentStore(
|
|||
sessionId: String,
|
||||
websiteUrl: String,
|
||||
isTrackingProtectionEnabled: Boolean,
|
||||
): TrackingProtectionState {
|
||||
return TrackingProtectionState(
|
||||
isCookieHandlingEnabled: Boolean,
|
||||
): ProtectionsState {
|
||||
return ProtectionsState(
|
||||
tab = context.components.core.store.state.findTabOrCustomTab(sessionId),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieBannerHandlingEnabled = isCookieHandlingEnabled,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package org.mozilla.fenix.settings.quicksettings
|
||||
|
||||
import org.mozilla.fenix.settings.quicksettings.protections.ProtectionsInteractor
|
||||
|
||||
/**
|
||||
* [QuickSettingsSheetDialogFragment] interactor.
|
||||
*
|
||||
|
@ -15,7 +17,7 @@ package org.mozilla.fenix.settings.quicksettings
|
|||
*/
|
||||
class QuickSettingsInteractor(
|
||||
private val controller: QuickSettingsController,
|
||||
) : WebsitePermissionInteractor, TrackingProtectionInteractor, WebSiteInfoInteractor, ClearSiteDataViewInteractor {
|
||||
) : WebsitePermissionInteractor, ProtectionsInteractor, WebSiteInfoInteractor, ClearSiteDataViewInteractor {
|
||||
override fun onPermissionsShown() {
|
||||
controller.handlePermissionsShown()
|
||||
}
|
||||
|
@ -32,8 +34,12 @@ class QuickSettingsInteractor(
|
|||
controller.handleTrackingProtectionToggled(isEnabled)
|
||||
}
|
||||
|
||||
override fun onDetailsClicked() {
|
||||
controller.handleDetailsClicked()
|
||||
override fun onCookieBannerHandlingDetailsClicked() {
|
||||
controller.handleCookieBannerHandlingDetailsClicked()
|
||||
}
|
||||
|
||||
override fun onTrackingProtectionDetailsClicked() {
|
||||
controller.handleTrackingProtectionDetailsClicked()
|
||||
}
|
||||
|
||||
override fun onConnectionDetailsClicked() {
|
||||
|
|
|
@ -17,7 +17,6 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.plus
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||
|
@ -35,6 +34,7 @@ import org.mozilla.fenix.ext.components
|
|||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.protections.ProtectionsView
|
||||
|
||||
/**
|
||||
* Dialog that presents the user with information about
|
||||
|
@ -52,7 +52,7 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
|
|||
private lateinit var clearSiteDataView: ClearSiteDataView
|
||||
|
||||
@VisibleForTesting
|
||||
internal lateinit var trackingProtectionView: TrackingProtectionView
|
||||
internal lateinit var protectionsView: ProtectionsView
|
||||
|
||||
private lateinit var interactor: QuickSettingsInteractor
|
||||
|
||||
|
@ -91,6 +91,7 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
|
|||
permissionHighlights = args.permissionHighlights,
|
||||
sessionId = args.sessionId,
|
||||
isTrackingProtectionEnabled = args.isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = args.isCookieHandlingEnabled,
|
||||
)
|
||||
|
||||
quickSettingsController = DefaultQuickSettingsController(
|
||||
|
@ -115,8 +116,8 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
|
|||
websiteInfoView = WebsiteInfoView(binding.websiteInfoLayout, interactor = interactor)
|
||||
websitePermissionsView =
|
||||
WebsitePermissionsView(binding.websitePermissionsLayout, interactor)
|
||||
trackingProtectionView =
|
||||
TrackingProtectionView(binding.trackingProtectionLayout, interactor, context.settings())
|
||||
protectionsView =
|
||||
ProtectionsView(binding.trackingProtectionLayout, interactor, context.settings())
|
||||
clearSiteDataView = ClearSiteDataView(
|
||||
context = context,
|
||||
ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO,
|
||||
|
@ -135,7 +136,7 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
|
|||
consumeFrom(quickSettingsStore) {
|
||||
websiteInfoView.update(it.webInfoState)
|
||||
websitePermissionsView.update(it.websitePermissionsState)
|
||||
trackingProtectionView.update(it.trackingProtectionState)
|
||||
protectionsView.update(it.protectionsState)
|
||||
clearSiteDataView.update(it.webInfoState)
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +211,7 @@ class QuickSettingsSheetDialogFragment : FenixDialogFragment() {
|
|||
provideTrackingProtectionUseCases().fetchTrackingLogs(
|
||||
tab.id,
|
||||
onSuccess = { trackers ->
|
||||
trackingProtectionView.updateDetailsSection(trackers.isNotEmpty())
|
||||
protectionsView.updateDetailsSection(trackers.isNotEmpty())
|
||||
},
|
||||
onError = {
|
||||
Logger.error("QuickSettingsSheetDialogFragment - fetchTrackingLogs onError", it)
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.quicksettings
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.view.isVisible
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.QuicksettingsTrackingProtectionBinding
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* Contract declaring all possible user interactions with [TrackingProtectionView].
|
||||
*/
|
||||
interface TrackingProtectionInteractor {
|
||||
|
||||
/**
|
||||
* Called whenever the tracking protection toggle for this site is toggled.
|
||||
*
|
||||
* @param isEnabled Whether or not tracking protection is enabled.
|
||||
*/
|
||||
fun onTrackingProtectionToggled(isEnabled: Boolean)
|
||||
|
||||
/**
|
||||
* Navigates to the tracking protection preferences. Called when a user clicks on the
|
||||
* "Details" button.
|
||||
*/
|
||||
fun onDetailsClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* MVI View that displays the tracking protection toggle and navigation to additional tracking
|
||||
* protection details.
|
||||
*
|
||||
* @param containerView [ViewGroup] in which this View will inflate itself.
|
||||
* @param interactor [TrackingProtectionInteractor] which will have delegated to all user
|
||||
* @param settings [Settings] application settings.
|
||||
* interactions.
|
||||
*/
|
||||
class TrackingProtectionView(
|
||||
val containerView: ViewGroup,
|
||||
val interactor: TrackingProtectionInteractor,
|
||||
val settings: Settings,
|
||||
) {
|
||||
private val context = containerView.context
|
||||
|
||||
@VisibleForTesting
|
||||
internal val binding = QuicksettingsTrackingProtectionBinding.inflate(
|
||||
LayoutInflater.from(containerView.context),
|
||||
containerView,
|
||||
true,
|
||||
)
|
||||
fun update(state: TrackingProtectionState) {
|
||||
bindTrackingProtectionInfo(state.isTrackingProtectionEnabled)
|
||||
binding.root.isVisible = settings.shouldUseTrackingProtection
|
||||
binding.trackingProtectionDetails.setOnClickListener {
|
||||
interactor.onDetailsClicked()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateDetailsSection(show: Boolean) {
|
||||
binding.trackingProtectionDetails.isVisible = show
|
||||
}
|
||||
|
||||
private fun bindTrackingProtectionInfo(isTrackingProtectionEnabled: Boolean) {
|
||||
binding.trackingProtectionSwitch.trackingProtectionCategoryItemDescription.text =
|
||||
context.getString(if (isTrackingProtectionEnabled) R.string.etp_panel_on else R.string.etp_panel_off)
|
||||
binding.trackingProtectionSwitch.switchWidget.isChecked = isTrackingProtectionEnabled
|
||||
binding.trackingProtectionSwitch.switchWidget.jumpDrawablesToCurrentState()
|
||||
|
||||
binding.trackingProtectionSwitch.switchWidget.setOnCheckedChangeListener { _, isChecked ->
|
||||
interactor.onTrackingProtectionToggled(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* 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.quicksettings.protections
|
||||
|
||||
/**
|
||||
* Contract declaring all possible user interactions with [ProtectionsView].
|
||||
*/
|
||||
interface ProtectionsInteractor {
|
||||
|
||||
/**
|
||||
* Called whenever the tracking protection toggle for this site is toggled.
|
||||
*
|
||||
* @param isEnabled Whether or not tracking protection is enabled.
|
||||
*/
|
||||
fun onTrackingProtectionToggled(isEnabled: Boolean)
|
||||
|
||||
/**
|
||||
* Navigates to the tracking protection details panel.
|
||||
*/
|
||||
fun onCookieBannerHandlingDetailsClicked()
|
||||
|
||||
/**
|
||||
* Navigates to the tracking protection preferences. Called when a user clicks on the
|
||||
* "Details" button.
|
||||
*/
|
||||
fun onTrackingProtectionDetailsClicked()
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/* 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.quicksettings.protections
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.isVisible
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.QuicksettingsProtectionsPanelBinding
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* MVI View that displays the tracking protection, cookie banner handling toggles and the navigation
|
||||
* to additional tracking protection details.
|
||||
*
|
||||
* @param containerView [ViewGroup] in which this View will inflate itself.
|
||||
* @param interactor [ProtectionsInteractor] which will have delegated to all user
|
||||
* @param settings [Settings] application settings.
|
||||
* interactions.
|
||||
*/
|
||||
class ProtectionsView(
|
||||
val containerView: ViewGroup,
|
||||
val interactor: ProtectionsInteractor,
|
||||
val settings: Settings,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Allows changing what this View displays.
|
||||
*/
|
||||
fun update(state: ProtectionsState) {
|
||||
bindTrackingProtectionInfo(state.isTrackingProtectionEnabled)
|
||||
bindCookieBannerProtection(state.isCookieBannerHandlingEnabled)
|
||||
binding.trackingProtectionSwitch.isVisible = settings.shouldUseTrackingProtection
|
||||
binding.cookieBannerItem.isVisible = shouldShowCookieBanner
|
||||
|
||||
binding.trackingProtectionDetails.setOnClickListener {
|
||||
interactor.onTrackingProtectionDetailsClicked()
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun updateDetailsSection(show: Boolean) {
|
||||
binding.trackingProtectionDetails.isVisible = show
|
||||
}
|
||||
|
||||
private fun bindTrackingProtectionInfo(isTrackingProtectionEnabled: Boolean) {
|
||||
binding.trackingProtectionSwitch.isChecked = isTrackingProtectionEnabled
|
||||
binding.trackingProtectionSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
interactor.onTrackingProtectionToggled(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal val binding = QuicksettingsProtectionsPanelBinding.inflate(
|
||||
LayoutInflater.from(containerView.context),
|
||||
containerView,
|
||||
true,
|
||||
)
|
||||
|
||||
private val shouldShowCookieBanner: Boolean
|
||||
get() = settings.shouldShowCookieBannerUI && settings.shouldUseCookieBanner
|
||||
|
||||
private fun bindCookieBannerProtection(isCookieBannerHandlingEnabled: Boolean) {
|
||||
val context = binding.cookieBannerItem.context
|
||||
val label = context.getString(R.string.preferences_cookie_banner_reduction)
|
||||
val description = context.getString(
|
||||
if (isCookieBannerHandlingEnabled) {
|
||||
R.string.reduce_cookie_banner_on_for_site
|
||||
} else {
|
||||
R.string.reduce_cookie_banner_off_for_site
|
||||
},
|
||||
)
|
||||
val icon = if (isCookieBannerHandlingEnabled) {
|
||||
R.drawable.ic_cookies_enabled
|
||||
} else {
|
||||
R.drawable.ic_cookies_disabled
|
||||
}
|
||||
|
||||
binding.cookieBannerItem.apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
FirefoxTheme {
|
||||
CookieBannerItem(
|
||||
label = label,
|
||||
description = description,
|
||||
startIconPainter = painterResource(icon),
|
||||
endIconPainter = painterResource(R.drawable.ic_arrowhead_right),
|
||||
onClick = { interactor.onCookieBannerHandlingDetailsClicked() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CookieBannerItem(
|
||||
label: String,
|
||||
description: String,
|
||||
startIconPainter: Painter,
|
||||
endIconPainter: Painter,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { onClick() }
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = startIconPainter,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(horizontal = 0.dp),
|
||||
tint = FirefoxTheme.colors.iconPrimary,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
style = FirefoxTheme.typography.subtitle1,
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
text = description,
|
||||
color = FirefoxTheme.colors.textSecondary,
|
||||
style = FirefoxTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = 0.dp)
|
||||
.size(24.dp),
|
||||
painter = endIconPainter,
|
||||
contentDescription = null,
|
||||
tint = FirefoxTheme.colors.iconPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun CookieBannerItemPreview() {
|
||||
FirefoxTheme {
|
||||
Box(Modifier.background(FirefoxTheme.colors.layer1)) {
|
||||
CookieBannerItem(
|
||||
label = "Cookie Banner Reduction",
|
||||
description = "On for this site",
|
||||
startIconPainter = painterResource(R.drawable.ic_cookies_enabled),
|
||||
endIconPainter = painterResource(R.drawable.ic_arrowhead_right),
|
||||
onClick = { println("list item click") },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.CookieBanners
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.runIfFragmentIsAttached
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsAction
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsStore
|
||||
|
||||
/**
|
||||
* [CookieBannerDetailsController] controller.
|
||||
*
|
||||
* Delegated by View Interactors, handles container business logic and operates changes on it,
|
||||
* complex Android interactions or communication with other features.
|
||||
*/
|
||||
interface CookieBannerDetailsController {
|
||||
/**
|
||||
* @see [CookieBannerDetailsInteractor.onBackPressed]
|
||||
*/
|
||||
fun handleBackPressed()
|
||||
|
||||
/**
|
||||
* @see [CookieBannerDetailsInteractor.onTogglePressed]
|
||||
*/
|
||||
fun handleTogglePressed(isEnabled: Boolean)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behavior of [CookieBannerDetailsController].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class DefaultCookieBannerDetailsController(
|
||||
private val context: Context,
|
||||
private val fragment: Fragment,
|
||||
private val ioScope: CoroutineScope,
|
||||
internal val sessionId: String,
|
||||
private val browserStore: BrowserStore,
|
||||
internal val protectionsStore: ProtectionsStore,
|
||||
private val cookieBannersStorage: CookieBannersStorage,
|
||||
private val navController: () -> NavController,
|
||||
internal var sitePermissions: SitePermissions?,
|
||||
private val gravity: Int,
|
||||
private val getCurrentTab: () -> SessionState?,
|
||||
private val reload: SessionUseCases.ReloadUrlUseCase,
|
||||
) : CookieBannerDetailsController {
|
||||
|
||||
override fun handleBackPressed() {
|
||||
getCurrentTab()?.let { tab ->
|
||||
context.components.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
|
||||
ioScope.launch {
|
||||
val hasException =
|
||||
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
|
||||
withContext(Dispatchers.Main) {
|
||||
fragment.runIfFragmentIsAttached {
|
||||
navController().popBackStack()
|
||||
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = !hasException,
|
||||
)
|
||||
navController().navigate(directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleTogglePressed(isEnabled: Boolean) {
|
||||
val tab = requireNotNull(browserStore.state.findTabOrCustomTab(sessionId)) {
|
||||
"A session is required to update the cookie banner mode"
|
||||
}
|
||||
ioScope.launch {
|
||||
if (isEnabled) {
|
||||
cookieBannersStorage.removeException(
|
||||
uri = tab.content.url,
|
||||
privateBrowsing = tab.content.private,
|
||||
)
|
||||
CookieBanners.exceptionRemoved.record(NoExtras())
|
||||
} else {
|
||||
cookieBannersStorage.addException(uri = tab.content.url, privateBrowsing = tab.content.private)
|
||||
CookieBanners.exceptionAdded.record(NoExtras())
|
||||
}
|
||||
protectionsStore.dispatch(
|
||||
ProtectionsAction.ToggleCookieBannerHandlingProtectionEnabled(
|
||||
isEnabled,
|
||||
),
|
||||
)
|
||||
reload(tab.id)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
/**
|
||||
* Contract declaring all possible user interactions with [CookieBannerHandlingDetailsView].
|
||||
*/
|
||||
interface CookieBannerDetailsInteractor {
|
||||
/**
|
||||
* Called whenever back is pressed.
|
||||
*/
|
||||
fun onBackPressed() = Unit
|
||||
|
||||
/**
|
||||
* Called whenever the user press the toggle widget.
|
||||
*/
|
||||
fun onTogglePressed(vale: Boolean) = Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* [CookieBannerPanelDialogFragment] interactor.
|
||||
*
|
||||
* Implements callbacks for each of [CookieBannerPanelDialogFragment]'s Views declared possible user interactions,
|
||||
* delegates all such user events to the [CookieBannerDetailsController].
|
||||
*
|
||||
* @param controller [CookieBannerDetailsController] which will be delegated for all users interactions,
|
||||
* it expected to contain all business logic for how to act in response.
|
||||
*/
|
||||
class DefaultCookieBannerDetailsInteractor(
|
||||
private val controller: CookieBannerDetailsController,
|
||||
) : CookieBannerDetailsInteractor {
|
||||
|
||||
override fun onBackPressed() {
|
||||
controller.handleBackPressed()
|
||||
}
|
||||
|
||||
override fun onTogglePressed(vale: Boolean) {
|
||||
controller.handleTogglePressed(vale)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
import mozilla.components.support.ktx.kotlin.toShortUrl
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.ComponentCookieBannerDetailsPanelBinding
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
|
||||
/**
|
||||
* MVI View that knows how to display cookie banner handling details for a site.
|
||||
*
|
||||
* @param container [ViewGroup] in which this View will inflate itself.
|
||||
* @param publicSuffixList To show short url.
|
||||
* @param interactor [CookieBannerDetailsInteractor] which will have delegated to all user interactions.
|
||||
*/
|
||||
class CookieBannerHandlingDetailsView(
|
||||
container: ViewGroup,
|
||||
private val context: Context,
|
||||
private val publicSuffixList: PublicSuffixList,
|
||||
val interactor: CookieBannerDetailsInteractor,
|
||||
) {
|
||||
val binding = ComponentCookieBannerDetailsPanelBinding.inflate(
|
||||
LayoutInflater.from(container.context),
|
||||
container,
|
||||
true,
|
||||
)
|
||||
|
||||
/**
|
||||
* Allows changing what this View displays.
|
||||
*/
|
||||
fun update(state: ProtectionsState) {
|
||||
bindTitle(state.url, state.isCookieBannerHandlingEnabled)
|
||||
bindBackButtonListener()
|
||||
bindDescription(state.isCookieBannerHandlingEnabled)
|
||||
bindSwitch(state.isCookieBannerHandlingEnabled)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun bindTitle(url: String, isCookieBannerHandlingEnabled: Boolean) {
|
||||
val stringID =
|
||||
if (isCookieBannerHandlingEnabled) {
|
||||
R.string.reduce_cookie_banner_details_panel_title_off_for_site
|
||||
} else {
|
||||
R.string.reduce_cookie_banner_details_panel_title_on_for_site
|
||||
}
|
||||
val shortUrl = url.toShortUrl(publicSuffixList)
|
||||
binding.title.text = context.getString(stringID, shortUrl)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun bindDescription(isCookieBannerHandlingEnabled: Boolean) {
|
||||
val stringID =
|
||||
if (isCookieBannerHandlingEnabled) {
|
||||
R.string.reduce_cookie_banner_details_panel_description_off_for_site
|
||||
} else {
|
||||
R.string.reduce_cookie_banner_details_panel_description_on_for_site
|
||||
}
|
||||
binding.details.text = context.getString(stringID, context.getString(R.string.app_name))
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun bindBackButtonListener() {
|
||||
binding.navigateBack.setOnClickListener {
|
||||
interactor.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun bindSwitch(isCookieBannerHandlingEnabled: Boolean) {
|
||||
binding.cookieBannerSwitch.isChecked = isCookieBannerHandlingEnabled
|
||||
binding.cookieBannerSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
interactor.onTogglePressed(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.plus
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.android.FenixDialogFragment
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.databinding.FragmentCookieBannerHandlingDetailsDialogBinding
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsStore
|
||||
|
||||
/**
|
||||
* A [FenixDialogFragment] that contains all the cookie banner details for a given tab.
|
||||
*/
|
||||
class CookieBannerPanelDialogFragment : FenixDialogFragment() {
|
||||
@VisibleForTesting
|
||||
private lateinit var cookieBannersView: CookieBannerHandlingDetailsView
|
||||
private val args by navArgs<CookieBannerPanelDialogFragmentArgs>()
|
||||
private var _binding: FragmentCookieBannerHandlingDetailsDialogBinding? = null
|
||||
|
||||
override val gravity: Int get() = args.gravity
|
||||
override val layoutId: Int = R.layout.fragment_cookie_banner_handling_details_dialog
|
||||
|
||||
@VisibleForTesting
|
||||
internal lateinit var protectionsStore: ProtectionsStore
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
val store = requireComponents.core.store
|
||||
val rootView = inflateRootView(container)
|
||||
val tab = store.state.findTabOrCustomTab(provideCurrentTabId())
|
||||
|
||||
protectionsStore = StoreProvider.get(this) {
|
||||
ProtectionsStore(
|
||||
ProtectionsState(
|
||||
tab = tab,
|
||||
url = args.url,
|
||||
isTrackingProtectionEnabled = args.trackingProtectionEnabled,
|
||||
isCookieBannerHandlingEnabled = args.cookieBannerHandlingEnabled,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val controller = DefaultCookieBannerDetailsController(
|
||||
context = requireContext(),
|
||||
ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO,
|
||||
cookieBannersStorage = requireComponents.core.cookieBannersStorage,
|
||||
protectionsStore = protectionsStore,
|
||||
browserStore = requireComponents.core.store,
|
||||
fragment = this,
|
||||
sessionId = args.sessionId,
|
||||
reload = requireComponents.useCases.sessionUseCases.reload,
|
||||
navController = { findNavController() },
|
||||
sitePermissions = args.sitePermissions,
|
||||
gravity = args.gravity,
|
||||
getCurrentTab = ::getCurrentTab,
|
||||
)
|
||||
|
||||
_binding = FragmentCookieBannerHandlingDetailsDialogBinding.bind(rootView)
|
||||
|
||||
cookieBannersView = CookieBannerHandlingDetailsView(
|
||||
context = requireContext(),
|
||||
container = binding.cookieBannerDetailsInfoLayout,
|
||||
publicSuffixList = requireComponents.publicSuffixList,
|
||||
interactor = DefaultCookieBannerDetailsInteractor(controller),
|
||||
)
|
||||
|
||||
return rootView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
consumeFrom(protectionsStore) { state ->
|
||||
cookieBannersView.update(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun provideCurrentTabId(): String = args.sessionId
|
||||
|
||||
private fun getCurrentTab(): SessionState? {
|
||||
return requireComponents.core.store.state.findTabOrCustomTab(args.sessionId)
|
||||
}
|
||||
}
|
|
@ -13,57 +13,95 @@ import mozilla.components.lib.state.Store
|
|||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* The [Store] for holding the [TrackingProtectionState] and applying [TrackingProtectionAction]s.
|
||||
* The [Store] for holding the [ProtectionsState] and applying [ProtectionsAction]s.
|
||||
*/
|
||||
class TrackingProtectionStore(initialState: TrackingProtectionState) :
|
||||
Store<TrackingProtectionState, TrackingProtectionAction>(
|
||||
class ProtectionsStore(initialState: ProtectionsState) :
|
||||
Store<ProtectionsState, ProtectionsAction>(
|
||||
initialState,
|
||||
::trackingProtectionStateReducer,
|
||||
::protectionsStateReducer,
|
||||
)
|
||||
|
||||
/**
|
||||
* Actions to dispatch through the `TrackingProtectionStore` to modify `TrackingProtectionState` through the reducer.
|
||||
* Actions to dispatch through the `TrackingProtectionStore` to modify `ProtectionsState` through the reducer.
|
||||
*/
|
||||
sealed class TrackingProtectionAction : Action {
|
||||
sealed class ProtectionsAction : Action {
|
||||
/**
|
||||
* The values of the tracking protection view has been changed.
|
||||
*/
|
||||
data class Change(
|
||||
val url: String,
|
||||
val isTrackingProtectionEnabled: Boolean,
|
||||
val isCookieBannerHandlingEnabled: Boolean,
|
||||
val listTrackers: List<TrackerLog>,
|
||||
val mode: TrackingProtectionState.Mode,
|
||||
) : TrackingProtectionAction()
|
||||
val mode: ProtectionsState.Mode,
|
||||
) : ProtectionsAction()
|
||||
|
||||
data class UrlChange(val url: String) : TrackingProtectionAction()
|
||||
data class TrackerLogChange(val listTrackers: List<TrackerLog>) : TrackingProtectionAction()
|
||||
/**
|
||||
* Toggles the enabled state of cookie banner handling protection.
|
||||
*
|
||||
* @param isEnabled Whether or not cookie banner protection is enabled.
|
||||
*/
|
||||
data class ToggleCookieBannerHandlingProtectionEnabled(val isEnabled: Boolean) :
|
||||
ProtectionsAction()
|
||||
|
||||
object ExitDetailsMode : TrackingProtectionAction()
|
||||
/**
|
||||
* Indicates the url has changed.
|
||||
*/
|
||||
data class UrlChange(val url: String) : ProtectionsAction()
|
||||
|
||||
/**
|
||||
* Indicates the url has the list of trackers has been updated.
|
||||
*/
|
||||
data class TrackerLogChange(val listTrackers: List<TrackerLog>) : ProtectionsAction()
|
||||
|
||||
/**
|
||||
* Indicates the user is leaving the detailed view.
|
||||
*/
|
||||
object ExitDetailsMode : ProtectionsAction()
|
||||
|
||||
/**
|
||||
* Holds the data to show a detailed tracking protection view.
|
||||
*/
|
||||
data class EnterDetailsMode(
|
||||
val category: TrackingProtectionCategory,
|
||||
val categoryBlocked: Boolean,
|
||||
) :
|
||||
TrackingProtectionAction()
|
||||
) : ProtectionsAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* The state for the Tracking Protection Panel
|
||||
* The state for the Protections Panel
|
||||
* @property tab Current session to display
|
||||
* @property url Current URL to display
|
||||
* @property isTrackingProtectionEnabled Current status of tracking protection for this session
|
||||
* (ie is an exception)
|
||||
* @property isCookieBannerHandlingEnabled Current status of cookie banner handling protection
|
||||
* for this session (ie is an exception).
|
||||
* @property listTrackers Current Tracker Log list of blocked and loaded tracker categories
|
||||
* @property mode Current Mode of TrackingProtection
|
||||
* @property lastAccessedCategory Remembers the last accessed details category, used to move
|
||||
* accessibly focus after returning from details_mode
|
||||
*/
|
||||
data class TrackingProtectionState(
|
||||
data class ProtectionsState(
|
||||
val tab: SessionState?,
|
||||
val url: String,
|
||||
val isTrackingProtectionEnabled: Boolean,
|
||||
val isCookieBannerHandlingEnabled: Boolean,
|
||||
val listTrackers: List<TrackerLog>,
|
||||
val mode: Mode,
|
||||
val lastAccessedCategory: String,
|
||||
) : State {
|
||||
/**
|
||||
* Indicates the modes in which a tracking protection view could be in.
|
||||
*/
|
||||
sealed class Mode {
|
||||
/**
|
||||
* Indicates that tracking protection view should not be in detail mode.
|
||||
*/
|
||||
object Normal : Mode()
|
||||
|
||||
/**
|
||||
* Indicates that tracking protection view in detailed mode.
|
||||
*/
|
||||
data class Details(
|
||||
val selectedCategory: TrackingProtectionCategory,
|
||||
val categoryBlocked: Boolean,
|
||||
|
@ -105,32 +143,36 @@ enum class TrackingProtectionCategory(
|
|||
}
|
||||
|
||||
/**
|
||||
* The TrackingProtectionState Reducer.
|
||||
* The [ProtectionsState] reducer.
|
||||
*/
|
||||
fun trackingProtectionStateReducer(
|
||||
state: TrackingProtectionState,
|
||||
action: TrackingProtectionAction,
|
||||
): TrackingProtectionState {
|
||||
fun protectionsStateReducer(
|
||||
state: ProtectionsState,
|
||||
action: ProtectionsAction,
|
||||
): ProtectionsState {
|
||||
return when (action) {
|
||||
is TrackingProtectionAction.Change -> state.copy(
|
||||
is ProtectionsAction.Change -> state.copy(
|
||||
url = action.url,
|
||||
isTrackingProtectionEnabled = action.isTrackingProtectionEnabled,
|
||||
isCookieBannerHandlingEnabled = action.isCookieBannerHandlingEnabled,
|
||||
listTrackers = action.listTrackers,
|
||||
mode = action.mode,
|
||||
)
|
||||
is TrackingProtectionAction.UrlChange -> state.copy(
|
||||
is ProtectionsAction.UrlChange -> state.copy(
|
||||
url = action.url,
|
||||
)
|
||||
is TrackingProtectionAction.TrackerLogChange -> state.copy(listTrackers = action.listTrackers)
|
||||
TrackingProtectionAction.ExitDetailsMode -> state.copy(
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
is ProtectionsAction.TrackerLogChange -> state.copy(listTrackers = action.listTrackers)
|
||||
ProtectionsAction.ExitDetailsMode -> state.copy(
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
)
|
||||
is TrackingProtectionAction.EnterDetailsMode -> state.copy(
|
||||
mode = TrackingProtectionState.Mode.Details(
|
||||
is ProtectionsAction.EnterDetailsMode -> state.copy(
|
||||
mode = ProtectionsState.Mode.Details(
|
||||
action.category,
|
||||
action.categoryBlocked,
|
||||
),
|
||||
lastAccessedCategory = action.category.name,
|
||||
)
|
||||
is ProtectionsAction.ToggleCookieBannerHandlingProtectionEnabled -> state.copy(
|
||||
isCookieBannerHandlingEnabled = action.isEnabled,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -7,7 +7,9 @@ package org.mozilla.fenix.trackingprotection
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -15,42 +17,114 @@ import androidx.core.content.withStyledAttributes
|
|||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
private const val DEFAULT_DRAWABLE: Int = 0
|
||||
|
||||
/**
|
||||
* Add a [SwitchCompat] widget with description that will vary depending on switch status.
|
||||
*/
|
||||
class SwitchWithDescription @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
lateinit var switchWidget: SwitchCompat
|
||||
lateinit var trackingProtectionCategoryTitle: TextView
|
||||
lateinit var trackingProtectionCategoryItemDescription: TextView
|
||||
private lateinit var switchWidget: SwitchCompat
|
||||
private lateinit var titleWidget: TextView
|
||||
private lateinit var descriptionWidget: TextView
|
||||
private lateinit var descriptionOn: String
|
||||
private lateinit var descriptionOff: String
|
||||
private var iconOn: Int = 0
|
||||
private var iconOff: Int = 0
|
||||
private var shouldShowIcons: Boolean = true
|
||||
|
||||
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,
|
||||
)
|
||||
switchWidget = findViewById(R.id.switch_widget)
|
||||
trackingProtectionCategoryTitle = findViewById(R.id.trackingProtectionCategoryTitle)
|
||||
trackingProtectionCategoryItemDescription = findViewById(R.id.trackingProtectionCategoryItemDescription)
|
||||
switchWidget.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
start = AppCompatResources.getDrawable(context, id),
|
||||
titleWidget = findViewById(R.id.switch_with_description_title)
|
||||
descriptionWidget = findViewById(R.id.switch_with_description_description)
|
||||
|
||||
switchWidget.setOnCheckedChangeListener { _, isChecked ->
|
||||
onSwitchChange(isChecked)
|
||||
}
|
||||
|
||||
iconOn = getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchIconOn,
|
||||
DEFAULT_DRAWABLE,
|
||||
)
|
||||
trackingProtectionCategoryTitle.text = resources.getString(
|
||||
iconOff = getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchIconOff,
|
||||
DEFAULT_DRAWABLE,
|
||||
)
|
||||
|
||||
shouldShowIcons = getBoolean(
|
||||
R.styleable.SwitchWithDescription_switchShowIcon,
|
||||
true,
|
||||
)
|
||||
|
||||
descriptionOn = resources.getString(
|
||||
getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchDescriptionOn,
|
||||
R.string.empty_string,
|
||||
),
|
||||
)
|
||||
descriptionOff = resources.getString(
|
||||
getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchDescriptionOff,
|
||||
R.string.empty_string,
|
||||
),
|
||||
)
|
||||
|
||||
switchWidget.textOn = descriptionOn
|
||||
switchWidget.textOff = descriptionOff
|
||||
|
||||
titleWidget.text = resources.getString(
|
||||
getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchTitle,
|
||||
R.string.preference_enhanced_tracking_protection,
|
||||
),
|
||||
)
|
||||
trackingProtectionCategoryItemDescription.text = resources.getString(
|
||||
getResourceId(
|
||||
R.styleable.SwitchWithDescription_switchDescription,
|
||||
R.string.preference_enhanced_tracking_protection_explanation,
|
||||
R.string.empty_string,
|
||||
),
|
||||
)
|
||||
|
||||
if (shouldShowIcons) {
|
||||
switchWidget.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
start = AppCompatResources.getDrawable(context, iconOn),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a [CompoundButton.OnCheckedChangeListener] listener to the switch view.
|
||||
*/
|
||||
fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener) {
|
||||
switchWidget.setOnCheckedChangeListener { item, isChecked ->
|
||||
onSwitchChange(isChecked)
|
||||
listener.onCheckedChanged(item, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to query switch view isChecked state.
|
||||
*/
|
||||
var isChecked: Boolean
|
||||
get() = switchWidget.isChecked
|
||||
set(value) {
|
||||
switchWidget.isChecked = value
|
||||
onSwitchChange(value)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun onSwitchChange(isChecked: Boolean) {
|
||||
val newDescription = if (isChecked) descriptionOn else descriptionOff
|
||||
val newIcon = if (isChecked) iconOn else iconOff
|
||||
|
||||
if (shouldShowIcons) {
|
||||
switchWidget.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
start = AppCompatResources.getDrawable(context, newIcon),
|
||||
)
|
||||
}
|
||||
descriptionWidget.text = newDescription
|
||||
switchWidget.jumpDrawablesToCurrentState()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,10 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
|
@ -65,7 +66,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal lateinit var trackingProtectionStore: TrackingProtectionStore
|
||||
internal lateinit var protectionsStore: ProtectionsStore
|
||||
private lateinit var trackingProtectionView: TrackingProtectionPanelView
|
||||
private lateinit var trackingProtectionInteractor: TrackingProtectionPanelInteractor
|
||||
private lateinit var trackingProtectionUseCases: TrackingProtectionUseCases
|
||||
|
@ -84,14 +85,15 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
val view = inflateRootView(container)
|
||||
val tab = store.state.findTabOrCustomTab(provideCurrentTabId())
|
||||
|
||||
trackingProtectionStore = StoreProvider.get(this) {
|
||||
TrackingProtectionStore(
|
||||
TrackingProtectionState(
|
||||
protectionsStore = StoreProvider.get(this) {
|
||||
ProtectionsStore(
|
||||
ProtectionsState(
|
||||
tab = tab,
|
||||
url = args.url,
|
||||
isTrackingProtectionEnabled = args.trackingProtectionEnabled,
|
||||
isCookieBannerHandlingEnabled = args.cookieBannerHandlingEnabled,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
),
|
||||
)
|
||||
|
@ -99,7 +101,9 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
trackingProtectionInteractor = TrackingProtectionPanelInteractor(
|
||||
context = requireContext(),
|
||||
fragment = this,
|
||||
store = trackingProtectionStore,
|
||||
store = protectionsStore,
|
||||
ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO,
|
||||
cookieBannersStorage = requireComponents.core.cookieBannersStorage,
|
||||
navController = { findNavController() },
|
||||
openTrackingProtectionSettings = ::openTrackingProtectionSettings,
|
||||
openLearnMoreLink = ::handleLearnMoreClicked,
|
||||
|
@ -119,7 +123,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
trackingProtectionUseCases.fetchTrackingLogs(
|
||||
tab.id,
|
||||
onSuccess = {
|
||||
trackingProtectionStore.dispatch(TrackingProtectionAction.TrackerLogChange(it))
|
||||
protectionsStore.dispatch(ProtectionsAction.TrackerLogChange(it))
|
||||
},
|
||||
onError = {
|
||||
Logger.error("TrackingProtectionUseCases - fetchTrackingLogs onError", it)
|
||||
|
@ -133,7 +137,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
|
||||
observeUrlChange(store)
|
||||
observeTrackersChange(store)
|
||||
trackingProtectionStore.observe(view) {
|
||||
protectionsStore.observe(view) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
whenStarted {
|
||||
trackingProtectionView.update(it)
|
||||
|
@ -217,7 +221,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
state.findTabOrCustomTab(provideCurrentTabId())
|
||||
}.ifChanged { tab -> tab.content.url }
|
||||
.collect {
|
||||
trackingProtectionStore.dispatch(TrackingProtectionAction.UrlChange(it.content.url))
|
||||
protectionsStore.dispatch(ProtectionsAction.UrlChange(it.content.url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,12 @@ package org.mozilla.fenix.trackingprotection
|
|||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
@ -21,7 +26,9 @@ import org.mozilla.fenix.ext.runIfFragmentIsAttached
|
|||
class TrackingProtectionPanelInteractor(
|
||||
private val context: Context,
|
||||
private val fragment: Fragment,
|
||||
private val store: TrackingProtectionStore,
|
||||
private val store: ProtectionsStore,
|
||||
private val ioScope: CoroutineScope,
|
||||
private val cookieBannersStorage: CookieBannersStorage,
|
||||
private val navController: () -> NavController,
|
||||
private val openTrackingProtectionSettings: () -> Unit,
|
||||
private val openLearnMoreLink: () -> Unit,
|
||||
|
@ -31,7 +38,7 @@ class TrackingProtectionPanelInteractor(
|
|||
) : TrackingProtectionPanelViewInteractor {
|
||||
|
||||
override fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean) {
|
||||
store.dispatch(TrackingProtectionAction.EnterDetailsMode(category, categoryBlocked))
|
||||
store.dispatch(ProtectionsAction.EnterDetailsMode(category, categoryBlocked))
|
||||
}
|
||||
|
||||
override fun onLearnMoreClicked() {
|
||||
|
@ -45,28 +52,36 @@ class TrackingProtectionPanelInteractor(
|
|||
override fun onBackPressed() {
|
||||
getCurrentTab()?.let { tab ->
|
||||
context.components.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
|
||||
fragment.runIfFragmentIsAttached {
|
||||
navController().popBackStack()
|
||||
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
)
|
||||
navController().navigate(directions)
|
||||
ioScope.launch {
|
||||
val hasException =
|
||||
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
|
||||
withContext(Dispatchers.Main) {
|
||||
fragment.runIfFragmentIsAttached {
|
||||
navController().popBackStack()
|
||||
val isTrackingProtectionEnabled =
|
||||
tab.trackingProtection.enabled && !contains
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
|
||||
sessionId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
isSecured = tab.content.securityInfo.secure,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
certificateName = tab.content.securityInfo.issuer,
|
||||
permissionHighlights = tab.content.permissionHighlights,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = !hasException,
|
||||
)
|
||||
navController().navigate(directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onExitDetailMode() {
|
||||
store.dispatch(TrackingProtectionAction.ExitDetailsMode)
|
||||
store.dispatch(ProtectionsAction.ExitDetailsMode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class TrackingProtectionPanelView(
|
|||
|
||||
val view: ConstraintLayout = binding.panelWrapper
|
||||
|
||||
private var mode: TrackingProtectionState.Mode = TrackingProtectionState.Mode.Normal
|
||||
private var mode: ProtectionsState.Mode = ProtectionsState.Mode.Normal
|
||||
|
||||
private var bucketedTrackers = TrackerBuckets()
|
||||
|
||||
|
@ -106,13 +106,16 @@ class TrackingProtectionPanelView(
|
|||
setCategoryClickListeners()
|
||||
}
|
||||
|
||||
fun update(state: TrackingProtectionState) {
|
||||
/**
|
||||
* Updates the display mode of the Protection view.
|
||||
*/
|
||||
fun update(state: ProtectionsState) {
|
||||
mode = state.mode
|
||||
bucketedTrackers.updateIfNeeded(state.listTrackers)
|
||||
|
||||
when (val mode = state.mode) {
|
||||
is TrackingProtectionState.Mode.Normal -> setUIForNormalMode(state)
|
||||
is TrackingProtectionState.Mode.Details -> setUIForDetailsMode(
|
||||
is ProtectionsState.Mode.Normal -> setUIForNormalMode(state)
|
||||
is ProtectionsState.Mode.Details -> setUIForDetailsMode(
|
||||
mode.selectedCategory,
|
||||
mode.categoryBlocked,
|
||||
)
|
||||
|
@ -121,7 +124,7 @@ class TrackingProtectionPanelView(
|
|||
setAccessibilityViewHierarchy(binding.detailsBack, binding.categoryTitle)
|
||||
}
|
||||
|
||||
private fun setUIForNormalMode(state: TrackingProtectionState) {
|
||||
private fun setUIForNormalMode(state: ProtectionsState) {
|
||||
binding.detailsMode.visibility = View.GONE
|
||||
binding.normalMode.visibility = View.VISIBLE
|
||||
|
||||
|
@ -280,8 +283,8 @@ class TrackingProtectionPanelView(
|
|||
|
||||
fun onBackPressed(): Boolean {
|
||||
return when (mode) {
|
||||
is TrackingProtectionState.Mode.Details -> {
|
||||
mode = TrackingProtectionState.Mode.Normal
|
||||
is ProtectionsState.Mode.Details -> {
|
||||
mode = ProtectionsState.Mode.Normal
|
||||
interactor.onBackPressed()
|
||||
true
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/textPrimary"
|
||||
android:pathData="M15.379,8a0.142,0.142 0,0 0,0.02 0,7.978 7.978,0 0,1 -1.858,-0.356 0.981,0.981 0,0 1,-0.054 0.847,1 1,0 1,1 -1.735,-0.994 0.981,0.981 0,0 1,0.481 -0.407c-0.069,-0.036 -0.13,-0.083 -0.2,-0.121l-5.42,5.418a0.977,0.977 0,0 1,-0.4 0.476,0.85 0.85,0 0,1 -0.117,0.04l-2.065,2.066c0.509,0.219 1,0.24 1.161,-0.147 -0.424,1.025 2.823,1.668 2.822,0.558 0,1.11 3.246,0.461 2.821,-0.564 0.425,1.025 3.175,-0.816 2.39,-1.6 0.785,0.784 2.621,-1.97 1.6,-2.394 1.021,0.424 1.664,-2.822 0.554,-2.822zM10.306,13a1,1 0,1 1,1 -1,1 1,0 0,1 -1,1zM14.707,1.293a1,1 0,0 0,-1.414 0L9.679,4.907A7.942,7.942 0,0 1,8 0.61v0.025C8,-0.474 4.753,0.174 5.179,1.2 4.753,0.174 2,2.016 2.788,2.8 2,2.016 0.167,4.77 1.193,5.193 0.167,4.77 -0.476,8.016 0.634,8.015c-1.11,0 -0.461,3.247 0.564,2.821 -0.639,0.265 -0.163,1.428 0.475,2.077l-0.38,0.38a1,1 0,1 0,1.414 1.414l12,-12a1,1 0,0 0,0 -1.414zM5.707,2.993a1,1 0,1 1,-1 1A1,1 0,0 1,5.706 3zM2.524,7.508a1,1 0,1 1,0.37 1.364,1 1,0 0,1 -0.37,-1.364zM7.293,7.293z"/>
|
||||
android:pathData="M7,6V6.404L5.558,4.962C6.903,3.723 8.601,2.861 10.489,2.56L11.054,3.058C11.022,3.344 10.999,3.633 10.999,3.928C10.999,7.474 13.307,10.477 16.5,11.529V12.751L17,13.251H18.5L19,12.751V11.934H19.006C19.649,11.934 20.264,11.833 20.862,11.689L21.492,12.244C21.416,14.61 20.47,16.752 18.972,18.376L13.25,12.654V11.25L12.75,10.75H11.346L8.596,8H9L9.5,7.5V6L9,5.5H7.5L7,6ZM18.159,21.78C18.305,21.926 18.497,22 18.689,22C18.881,22 19.073,21.927 19.219,21.78C19.512,21.487 19.512,21.012 19.219,20.719L3.134,4.634C2.841,4.341 2.366,4.341 2.073,4.634C1.78,4.927 1.78,5.402 2.073,5.695L3.697,7.319C2.931,8.684 2.492,10.256 2.492,11.934C2.492,17.185 6.749,21.442 12,21.442C13.679,21.442 15.253,21.005 16.619,20.241L18.159,21.78ZM5,12.75V11.25L5.5,10.75H7L7.5,11.25V12.75L7,13.25H5.5L5,12.75ZM9.5,18L9,18.5H7.5L7,18V16.5L7.5,16H9L9.5,16.5V18Z"/>
|
||||
</vector>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/textPrimary"
|
||||
android:pathData="M15.379,8a0.142,0.142 0,0 0,0.02 0,7.978 7.978,0 0,1 -1.858,-0.356 0.981,0.981 0,0 1,-0.054 0.847,1 1,0 1,1 -1.735,-0.994 0.981,0.981 0,0 1,0.481 -0.407A8.02,8.02 0,0 1,8 0.61v0.025C8,-0.474 4.753,0.174 5.179,1.2 4.753,0.174 2,2.016 2.788,2.8 2,2.016 0.167,4.77 1.193,5.193 0.167,4.77 -0.476,8.016 0.634,8.015c-1.11,0 -0.461,3.247 0.564,2.821 -1.025,0.426 0.817,3.175 1.6,2.39 -0.784,0.785 1.969,2.621 2.393,1.6 -0.424,1.025 2.823,1.668 2.822,0.558 0,1.11 3.246,0.461 2.821,-0.564 0.425,1.025 3.175,-0.816 2.39,-1.6 0.785,0.784 2.621,-1.97 1.6,-2.394 1.022,0.42 1.665,-2.826 0.555,-2.826zM4.259,8.5a1,1 0,1 1,-0.37 -1.365,1 1,0 0,1 0.37,1.365zM6.214,12.861a1,1 0,1 1,0.36 -1.367,1 1,0 0,1 -0.36,1.369zM5.706,5a1,1 0,1 1,1 -1,1 1,0 0,1 -1,1zM8,9a1,1 0,1 1,1 -1,1 1,0 0,1 -1,1zM10.306,13a1,1 0,1 1,1 -1,1 1,0 0,1 -1,1z" />
|
||||
android:pathData="M20.862,11.69a7.886,7.886 0,0 1,-1.856 0.244L19,11.934v0.817l-0.5,0.5L17,13.251l-0.5,-0.5L16.5,11.53c-3.193,-1.052 -5.501,-4.055 -5.501,-7.6 0,-0.296 0.023,-0.585 0.055,-0.87l-0.565,-0.499c-4.531,0.725 -7.997,4.64 -7.997,9.375A9.508,9.508 0,0 0,12 21.443c5.146,0 9.327,-4.09 9.492,-9.198l-0.63,-0.556ZM5,12.75v-1.5l0.5,-0.5L7,10.75l0.5,0.5v1.5l-0.5,0.5L5.5,13.25l-0.5,-0.5ZM9.5,18l-0.5,0.5L7.5,18.5L7,18v-1.5l0.5,-0.5L9,16l0.5,0.5L9.5,18ZM9.5,7.5L9,8L7.5,8L7,7.5L7,6l0.5,-0.5L9,5.5l0.5,0.5v1.5ZM13.25,12.75 L12.75,13.25h-1.5l-0.5,-0.5v-1.5l0.5,-0.5h1.5l0.5,0.5v1.5ZM17,18l-0.5,0.5L15,18.5l-0.5,-0.5v-1.5l0.5,-0.5h1.5l0.5,0.5L17,18Z" />
|
||||
</vector>
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?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/. -->
|
||||
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/enabled"
|
||||
android:drawable="@drawable/ic_tracking_protection_enabled"
|
||||
android:state_checked="true" />
|
||||
<item
|
||||
android:id="@+id/disabled"
|
||||
android:drawable="@drawable/ic_tracking_protection_disabled" />
|
||||
|
||||
</animated-selector>
|
|
@ -0,0 +1,67 @@
|
|||
<?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="?attr/layer1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/navigate_back"
|
||||
android:layout_width="@dimen/tracking_protection_item_height"
|
||||
android:layout_height="@dimen/tracking_protection_item_height"
|
||||
android:contentDescription="@string/etp_back_button_content_description"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/mozac_ic_back"
|
||||
app:tint="?attr/textPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/navigate_back"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Turn off Cookize Banner Reduction for [domain.com]? " />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textColor="?attr/textSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/navigate_back"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
tools:text="Firefox will clear this site’s cookies and refresh the page. Clearing all cookies may sign you out or empty shopping carts." />
|
||||
|
||||
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
|
||||
android:id="@+id/cookieBannerSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/tracking_protection_item_height"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="23dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/navigate_back"
|
||||
app:layout_constraintTop_toBottomOf="@id/details"
|
||||
app:switchDescriptionOff="@string/reduce_cookie_banner_off_for_site"
|
||||
app:switchDescriptionOn="@string/reduce_cookie_banner_on_for_site"
|
||||
app:switchShowIcon="false"
|
||||
app:switchTitle="@string/preferences_cookie_banner_reduction" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -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/. -->
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/quick_settings_sheet"
|
||||
android:fillViewport="true">
|
||||
<FrameLayout
|
||||
android:id="@+id/cookieBannerDetailsInfoLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -1,24 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
<?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"
|
||||
<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">
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/cookieBannerItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/tracking_protection_item_height"
|
||||
app:layout_constraintBottom_toTopOf="@id/trackingProtectionSwitch"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
|
||||
android:id="@+id/trackingProtectionSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:minHeight="@dimen/tracking_protection_item_height"
|
||||
android:text="@string/preference_enhanced_tracking_protection"
|
||||
app:layout_constraintBottom_toTopOf="@id/trackingProtectionDetails"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:switchDescription="@string/etp_panel_on"
|
||||
app:switchIcon="@drawable/ic_tracking_protection"
|
||||
app:layout_constraintTop_toBottomOf="@id/cookieBannerItem"
|
||||
app:switchDescriptionOff="@string/etp_panel_off"
|
||||
app:switchDescriptionOn="@string/etp_panel_on"
|
||||
app:switchIconOff="@drawable/ic_tracking_protection_disabled"
|
||||
app:switchIconOn="@drawable/ic_tracking_protection_enabled"
|
||||
app:switchTitle="@string/preference_enhanced_tracking_protection" />
|
||||
|
||||
<TextView
|
||||
|
@ -26,8 +35,8 @@
|
|||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
android:gravity="end|center_vertical"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:gravity="end|center_vertical"
|
||||
android:text="@string/enhanced_tracking_protection_details"
|
||||
android:visibility="gone"
|
||||
app:drawableEndCompat="@drawable/ic_arrowhead_right"
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<TextView
|
||||
android:layout_marginTop="4dp"
|
||||
android:id="@+id/trackingProtectionCategoryTitle"
|
||||
android:id="@+id/switch_with_description_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="48dp"
|
||||
|
@ -20,7 +20,7 @@
|
|||
android:importantForAccessibility="no"
|
||||
android:textAppearance="@style/ListItemTextStyle"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/trackingProtectionCategoryItemDescription"
|
||||
app:layout_constraintBottom_toTopOf="@id/switch_with_description_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@ -29,7 +29,7 @@
|
|||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trackingProtectionCategoryItemDescription"
|
||||
android:id="@+id/switch_with_description_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
|
@ -38,10 +38,10 @@
|
|||
android:textColor="?attr/textSecondary"
|
||||
android:layout_marginBottom="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/trackingProtectionCategoryTitle"
|
||||
app:layout_constraintEnd_toEndOf="@id/switch_with_description_title"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@id/trackingProtectionCategoryTitle"
|
||||
app:layout_constraintTop_toBottomOf="@id/trackingProtectionCategoryTitle"
|
||||
app:layout_constraintStart_toStartOf="@id/switch_with_description_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/switch_with_description_title"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
|
@ -51,10 +51,7 @@
|
|||
android:minHeight="@dimen/tracking_protection_item_height"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textOff="@string/etp_panel_off"
|
||||
android:textOn="@string/etp_panel_on"
|
||||
app:drawableStartCompat="@drawable/ic_tracking_protection"
|
||||
app:layout_constraintBottom_toBottomOf="@id/trackingProtectionCategoryItemDescription"
|
||||
app:layout_constraintBottom_toBottomOf="@id/switch_with_description_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -130,6 +130,9 @@
|
|||
<action
|
||||
android:id="@+id/action_global_trackingProtectionPanelDialogFragment"
|
||||
app:destination="@id/trackingProtectionPanelDialogFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_cookieBannerProtectionPanelDialogFragment"
|
||||
app:destination="@id/cookieBannerPanelDialogFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_quickSettingsSheetDialogFragment"
|
||||
app:destination="@id/quickSettingsSheetDialogFragment" />
|
||||
|
@ -953,6 +956,9 @@
|
|||
<argument
|
||||
android:name="isTrackingProtectionEnabled"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isCookieHandlingEnabled"
|
||||
app:argType="boolean" />
|
||||
</dialog>
|
||||
<fragment
|
||||
android:id="@+id/accountProblemFragment"
|
||||
|
@ -978,6 +984,9 @@
|
|||
<argument
|
||||
android:name="trackingProtectionEnabled"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="cookieBannerHandlingEnabled"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="gravity"
|
||||
android:defaultValue="80"
|
||||
|
@ -1016,6 +1025,33 @@
|
|||
android:defaultValue="80"
|
||||
app:argType="integer" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/cookieBannerPanelDialogFragment"
|
||||
android:name="org.mozilla.fenix.settings.quicksettings.protections.cookiebanners.CookieBannerPanelDialogFragment"
|
||||
tools:layout="@layout/quicksettings_website_info">
|
||||
<argument
|
||||
android:name="sessionId"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="url"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="trackingProtectionEnabled"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="cookieBannerHandlingEnabled"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="gravity"
|
||||
android:defaultValue="80"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="sitePermissions"
|
||||
app:argType="mozilla.components.concept.engine.permission.SitePermissions"
|
||||
app:nullable="true" />
|
||||
|
||||
</dialog>
|
||||
<fragment
|
||||
android:id="@+id/trackingProtectionBlockingFragment"
|
||||
android:name="org.mozilla.fenix.trackingprotection.TrackingProtectionBlockingFragment"
|
||||
|
|
|
@ -90,8 +90,11 @@
|
|||
|
||||
<declare-styleable name="SwitchWithDescription">
|
||||
<attr name="switchTitle" format="reference" />
|
||||
<attr name="switchDescription" format="reference" />
|
||||
<attr name="switchIcon" format="reference" />
|
||||
<attr name="switchDescriptionOn" format="reference" />
|
||||
<attr name="switchDescriptionOff" format="reference" />
|
||||
<attr name="switchIconOn" format="reference" />
|
||||
<attr name="switchIconOff" format="reference" />
|
||||
<attr name="switchShowIcon" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="DeleteBrowsingDataItem">
|
||||
|
|
|
@ -329,6 +329,18 @@
|
|||
<string name="reduce_cookie_banner_option">Reduce cookie banners</string>
|
||||
<!-- Summary for the preference for rejecting all cookies whenever possible. -->
|
||||
<string name="reduce_cookie_banner_summary">Firefox automatically tries to reject cookie requests on cookie banners. If a reject option isn’t available, Firefox may accept all cookies to dismiss the banner.</string>
|
||||
<!-- Text for indicating cookie banner handling is off this site, this is shown as part of the protections panel with the tracking protection toggle -->
|
||||
<string name="reduce_cookie_banner_off_for_site">Off for this site</string>
|
||||
<!-- Text for indicating cookie banner handling is on this site, this is shown as part of the protections panel with the tracking protection toggle -->
|
||||
<string name="reduce_cookie_banner_on_for_site">On for this site</string>
|
||||
<!-- Title text for a detail explanation indicating cookie banner handling is on this site, this is shown as part of the cookie banner panel in the toolbar. The first parameter is a shortened URL of the current site-->
|
||||
<string name="reduce_cookie_banner_details_panel_title_on_for_site">Turn on Cookie Banner Reduction for %1$s?</string>
|
||||
<!-- Title text for a detail explanation indicating cookie banner handling is off this site, this is shown as part of the cookie banner panel in the toolbar. The first parameter is a shortened URL of the current site-->
|
||||
<string name="reduce_cookie_banner_details_panel_title_off_for_site">Turn off Cookie Banner Reduction for %1$s?</string>
|
||||
<!-- Long text for a detail explanation indicating what will happen if cookie banner handling is off for a site, this is shown as part of the cookie banner panel in the toolbar. The first parameter is the application name -->
|
||||
<string name="reduce_cookie_banner_details_panel_description_off_for_site">%1$s will clear this site’s cookies and refresh the page. Clearing all cookies may sign you out or empty shopping carts.</string>
|
||||
<!-- Long text for a detail explanation indicating what will happen if cookie banner handling is on for a site, this is shown as part of the cookie banner panel in the toolbar -->
|
||||
<string name="reduce_cookie_banner_details_panel_description_on_for_site">Firefox can try to automatically reject cookie requests. If a reject option isn’t available, Firefox may accept all cookies to dismiss the banner.</string>
|
||||
|
||||
<!-- Description of the preference to enable "HTTPS-Only" mode. -->
|
||||
<string name="preferences_https_only_summary">Automatically attempts to connect to sites using HTTPS encryption protocol for increased security.</string>
|
||||
|
|
|
@ -17,10 +17,13 @@ import io.mockk.spyk
|
|||
import io.mockk.verify
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import mozilla.components.feature.session.TrackingProtectionUseCases
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
@ -40,12 +43,19 @@ class DefaultConnectionDetailsControllerTest {
|
|||
@MockK(relaxed = true)
|
||||
private lateinit var sitePermissions: SitePermissions
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var cookieBannersStorage: CookieBannersStorage
|
||||
|
||||
private lateinit var controller: DefaultConnectionDetailsController
|
||||
|
||||
private lateinit var tab: TabSessionState
|
||||
|
||||
private var gravity = 54
|
||||
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule()
|
||||
private val scope = coroutinesTestRule.scope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
|
@ -55,6 +65,8 @@ class DefaultConnectionDetailsControllerTest {
|
|||
controller = DefaultConnectionDetailsController(
|
||||
fragment = fragment,
|
||||
context = context,
|
||||
ioScope = scope,
|
||||
cookieBannersStorage = cookieBannersStorage,
|
||||
navController = { navController },
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.CookieBanners
|
||||
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
||||
import org.mozilla.fenix.components.PermissionStorage
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
@ -303,6 +304,23 @@ class DefaultQuickSettingsControllerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleCookieBannerHandlingDetailsClicked should call popBackStack and navigate to details page`() {
|
||||
every { context.components.core.store } returns browserStore
|
||||
every { store.state.protectionsState } returns mockk(relaxed = true)
|
||||
every { context.components.settings } returns appSettings
|
||||
every { context.components.settings.toolbarPosition.androidGravity } returns mockk(relaxed = true)
|
||||
|
||||
controller.handleCookieBannerHandlingDetailsClicked()
|
||||
|
||||
verify {
|
||||
navController.popBackStack()
|
||||
|
||||
navController.navigate(any<NavDirections>())
|
||||
}
|
||||
assertNotNull(CookieBanners.visitedPanel.testGetValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleTrackingProtectionToggled should call the right use cases`() = runTestOnMain {
|
||||
val trackingProtectionUseCases: TrackingProtectionUseCases = mockk(relaxed = true)
|
||||
|
@ -348,11 +366,12 @@ class DefaultQuickSettingsControllerTest {
|
|||
websiteUrl = tab.content.url,
|
||||
sessionId = tab.id,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = isTrackingProtectionEnabled,
|
||||
)
|
||||
|
||||
every { store.state.trackingProtectionState } returns state
|
||||
every { store.state.protectionsState } returns state
|
||||
|
||||
controller.handleDetailsClicked()
|
||||
controller.handleTrackingProtectionDetailsClicked()
|
||||
|
||||
verify {
|
||||
navController.popBackStack()
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/* 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.quicksettings
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.databinding.QuicksettingsProtectionsPanelBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.settings.quicksettings.protections.ProtectionsInteractor
|
||||
import org.mozilla.fenix.settings.quicksettings.protections.ProtectionsView
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class ProtectionsViewTest {
|
||||
|
||||
private lateinit var view: ProtectionsView
|
||||
private lateinit var binding: QuicksettingsProtectionsPanelBinding
|
||||
private lateinit var interactor: ProtectionsInteractor
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var settings: Settings
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
interactor = mockk(relaxed = true)
|
||||
view = spyk(ProtectionsView(FrameLayout(testContext), interactor, settings))
|
||||
binding = view.binding
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN updating THEN bind checkbox`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = ProtectionsState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldUseTrackingProtection } returns true
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertTrue(binding.root.isVisible)
|
||||
assertTrue(binding.trackingProtectionSwitch.isChecked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN TP is globally off WHEN updating THEN hide the TP section`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = ProtectionsState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldUseTrackingProtection } returns false
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertFalse(binding.trackingProtectionSwitch.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banners handling is globally off WHEN updating THEN hide the cookie banner section`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = ProtectionsState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldShowCookieBannerUI } returns true
|
||||
every { settings.shouldUseCookieBanner } returns false
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertFalse(binding.cookieBannerItem.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banners handling UI feature flag is off WHEN updating THEN hide the cookie banner section`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = ProtectionsState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldShowCookieBannerUI } returns false
|
||||
every { settings.shouldUseCookieBanner } returns false
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertFalse(binding.cookieBannerItem.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN updateDetailsSection is called THEN update the visibility of the section`() {
|
||||
every { settings.shouldUseTrackingProtection } returns false
|
||||
|
||||
view.updateDetailsSection(false)
|
||||
|
||||
assertFalse(binding.trackingProtectionDetails.isVisible)
|
||||
|
||||
view.updateDetailsSection(true)
|
||||
|
||||
assertTrue(binding.trackingProtectionDetails.isVisible)
|
||||
}
|
||||
}
|
|
@ -12,8 +12,8 @@ import org.junit.Assert.assertNotSame
|
|||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState.Mode.Normal
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState.Mode.Normal
|
||||
|
||||
class QuickSettingsFragmentReducerTest {
|
||||
|
||||
|
@ -30,13 +30,14 @@ class QuickSettingsFragmentReducerTest {
|
|||
val map =
|
||||
mapOf<PhoneFeature, WebsitePermission>(PhoneFeature.CAMERA to toggleablePermission)
|
||||
val infoState = WebsiteInfoState("", "", WebsiteSecurityUiValues.SECURE, "")
|
||||
val tpState = TrackingProtectionState(
|
||||
val tpState = ProtectionsState(
|
||||
null,
|
||||
"",
|
||||
false,
|
||||
emptyList(),
|
||||
Normal,
|
||||
"",
|
||||
isTrackingProtectionEnabled = false,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = emptyList(),
|
||||
mode = Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
val state = QuickSettingsFragmentState(infoState, map, tpState)
|
||||
val newState = quickSettingsFragmentReducer(
|
||||
|
@ -67,13 +68,14 @@ class QuickSettingsFragmentReducerTest {
|
|||
val map =
|
||||
mapOf<PhoneFeature, WebsitePermission>(PhoneFeature.AUTOPLAY to permissionPermission)
|
||||
val infoState = WebsiteInfoState("", "", WebsiteSecurityUiValues.SECURE, "")
|
||||
val tpState = TrackingProtectionState(
|
||||
val tpState = ProtectionsState(
|
||||
null,
|
||||
"",
|
||||
false,
|
||||
emptyList(),
|
||||
Normal,
|
||||
"",
|
||||
isTrackingProtectionEnabled = false,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = emptyList(),
|
||||
mode = Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
val state = QuickSettingsFragmentState(infoState, map, tpState)
|
||||
val autoplayValue = AutoplayValue.AllowAll(
|
||||
|
@ -92,14 +94,15 @@ class QuickSettingsFragmentReducerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `TrackingProtectionAction - ToggleTrackingProtectionEnabled`() = runTest {
|
||||
fun `ProtectionsAction - ToggleTrackingProtectionEnabled`() = runTest {
|
||||
val state = QuickSettingsFragmentState(
|
||||
webInfoState = WebsiteInfoState("", "", WebsiteSecurityUiValues.SECURE, ""),
|
||||
websitePermissionsState = emptyMap(),
|
||||
trackingProtectionState = TrackingProtectionState(
|
||||
protectionsState = ProtectionsState(
|
||||
tab = null,
|
||||
url = "https://www.firefox.com",
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = Normal,
|
||||
lastAccessedCategory = "",
|
||||
|
@ -112,7 +115,7 @@ class QuickSettingsFragmentReducerTest {
|
|||
)
|
||||
|
||||
assertNotSame(state, newState)
|
||||
assertFalse(newState.trackingProtectionState.isTrackingProtectionEnabled)
|
||||
assertFalse(newState.protectionsState.isTrackingProtectionEnabled)
|
||||
}
|
||||
|
||||
private fun createTestRule() = SitePermissionsRules(
|
||||
|
|
|
@ -39,7 +39,7 @@ import org.mozilla.fenix.settings.quicksettings.WebsiteInfoState.Companion.creat
|
|||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
|
@ -83,13 +83,14 @@ class QuickSettingsFragmentStoreTest {
|
|||
settings = appSettings,
|
||||
sessionId = tab.id,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieHandlingEnabled = true,
|
||||
)
|
||||
|
||||
assertNotNull(store)
|
||||
assertNotNull(store.state)
|
||||
assertNotNull(store.state.webInfoState)
|
||||
assertNotNull(store.state.websitePermissionsState)
|
||||
assertNotNull(store.state.trackingProtectionState)
|
||||
assertNotNull(store.state.protectionsState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -286,7 +287,7 @@ class QuickSettingsFragmentStoreTest {
|
|||
val initialState = QuickSettingsFragmentState(
|
||||
webInfoState = websiteInfoState,
|
||||
websitePermissionsState = initialWebsitePermissionsState,
|
||||
trackingProtectionState = mockk(),
|
||||
protectionsState = mockk(),
|
||||
)
|
||||
val store = QuickSettingsFragmentStore(initialState)
|
||||
|
||||
|
@ -340,6 +341,7 @@ class QuickSettingsFragmentStoreTest {
|
|||
val tab = createTab("https://www.firefox.com")
|
||||
val browserStore = BrowserStore(BrowserState(tabs = listOf(tab)))
|
||||
val isTrackingProtectionEnabled = true
|
||||
val isCookieHandlingEnabled = true
|
||||
|
||||
every { context.components.core.store } returns browserStore
|
||||
|
||||
|
@ -348,14 +350,16 @@ class QuickSettingsFragmentStoreTest {
|
|||
websiteUrl = tab.content.url,
|
||||
sessionId = tab.id,
|
||||
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
|
||||
isCookieHandlingEnabled = isCookieHandlingEnabled,
|
||||
)
|
||||
|
||||
assertNotNull(state)
|
||||
assertEquals(tab, state.tab)
|
||||
assertEquals(tab.content.url, state.url)
|
||||
assertEquals(isTrackingProtectionEnabled, state.isTrackingProtectionEnabled)
|
||||
assertEquals(isCookieHandlingEnabled, state.isCookieBannerHandlingEnabled)
|
||||
assertEquals(0, state.listTrackers.size)
|
||||
assertEquals(TrackingProtectionState.Mode.Normal, state.mode)
|
||||
assertEquals(ProtectionsState.Mode.Normal, state.mode)
|
||||
assertEquals("", state.lastAccessedCategory)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,20 @@ class QuickSettingsInteractorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `onBlockedItemsClicked should delegate the controller`() {
|
||||
interactor.onDetailsClicked()
|
||||
fun `onCookieBannerHandlingClicked should delegate the controller`() {
|
||||
interactor.onCookieBannerHandlingDetailsClicked()
|
||||
|
||||
verify {
|
||||
controller.handleDetailsClicked()
|
||||
controller.handleCookieBannerHandlingDetailsClicked()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onBlockedItemsClicked should delegate the controller`() {
|
||||
interactor.onTrackingProtectionDetailsClicked()
|
||||
|
||||
verify {
|
||||
controller.handleTrackingProtectionDetailsClicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.junit.Rule
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.settings.quicksettings.protections.ProtectionsView
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class QuickSettingsSheetDialogFragmentTest {
|
||||
|
@ -107,11 +108,11 @@ class QuickSettingsSheetDialogFragmentTest {
|
|||
fun `GIVEN no trackers WHEN calling updateTrackers THEN hide the details section`() {
|
||||
val tab = createTab("mozilla.org")
|
||||
val trackingProtectionUseCases: TrackingProtectionUseCases = mockk(relaxed = true)
|
||||
val trackingProtectionView: TrackingProtectionView = mockk(relaxed = true)
|
||||
val protectionsView: ProtectionsView = mockk(relaxed = true)
|
||||
|
||||
val onComplete = slot<(List<TrackerLog>) -> Unit>()
|
||||
|
||||
every { fragment.trackingProtectionView } returns trackingProtectionView
|
||||
every { fragment.protectionsView } returns protectionsView
|
||||
|
||||
every {
|
||||
trackingProtectionUseCases.fetchTrackingLogs.invoke(
|
||||
|
@ -126,7 +127,7 @@ class QuickSettingsSheetDialogFragmentTest {
|
|||
fragment.updateTrackers(tab)
|
||||
|
||||
verify {
|
||||
trackingProtectionView.updateDetailsSection(false)
|
||||
protectionsView.updateDetailsSection(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,11 +135,11 @@ class QuickSettingsSheetDialogFragmentTest {
|
|||
fun `GIVEN trackers WHEN calling updateTrackers THEN show the details section`() {
|
||||
val tab = createTab("mozilla.org")
|
||||
val trackingProtectionUseCases: TrackingProtectionUseCases = mockk(relaxed = true)
|
||||
val trackingProtectionView: TrackingProtectionView = mockk(relaxed = true)
|
||||
val protectionsView: ProtectionsView = mockk(relaxed = true)
|
||||
|
||||
val onComplete = slot<(List<TrackerLog>) -> Unit>()
|
||||
|
||||
every { fragment.trackingProtectionView } returns trackingProtectionView
|
||||
every { fragment.protectionsView } returns protectionsView
|
||||
|
||||
every {
|
||||
trackingProtectionUseCases.fetchTrackingLogs.invoke(
|
||||
|
@ -153,7 +154,7 @@ class QuickSettingsSheetDialogFragmentTest {
|
|||
fragment.updateTrackers(tab)
|
||||
|
||||
verify {
|
||||
trackingProtectionView.updateDetailsSection(true)
|
||||
protectionsView.updateDetailsSection(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.quicksettings
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.databinding.QuicksettingsTrackingProtectionBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class TrackingProtectionViewTest {
|
||||
|
||||
private lateinit var view: TrackingProtectionView
|
||||
private lateinit var binding: QuicksettingsTrackingProtectionBinding
|
||||
private lateinit var interactor: TrackingProtectionInteractor
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var settings: Settings
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
interactor = mockk(relaxed = true)
|
||||
view = spyk(TrackingProtectionView(FrameLayout(testContext), interactor, settings))
|
||||
binding = view.binding
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN updating THEN bind checkbox`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = TrackingProtectionState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldUseTrackingProtection } returns true
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertTrue(binding.root.isVisible)
|
||||
assertTrue(binding.trackingProtectionSwitch.switchWidget.isChecked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN TP is globally off WHEN updating THEN hide the TP section`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = TrackingProtectionState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
every { settings.shouldUseTrackingProtection } returns false
|
||||
|
||||
view.update(state)
|
||||
|
||||
assertFalse(binding.root.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN updateDetailsSection is called THEN update the visibility of the section`() {
|
||||
every { settings.shouldUseTrackingProtection } returns false
|
||||
|
||||
view.updateDetailsSection(false)
|
||||
|
||||
assertFalse(binding.trackingProtectionDetails.isVisible)
|
||||
|
||||
view.updateDetailsSection(true)
|
||||
|
||||
assertTrue(binding.trackingProtectionDetails.isVisible)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
import mozilla.components.support.ktx.kotlin.toShortUrl
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.ComponentCookieBannerDetailsPanelBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsState
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class CookieBannerHandlingDetailsViewTest {
|
||||
|
||||
private lateinit var view: CookieBannerHandlingDetailsView
|
||||
private lateinit var binding: ComponentCookieBannerDetailsPanelBinding
|
||||
private lateinit var interactor: CookieBannerDetailsInteractor
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var publicSuffixList: PublicSuffixList
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
interactor = mockk(relaxed = true)
|
||||
view = spyk(
|
||||
CookieBannerHandlingDetailsView(
|
||||
container = FrameLayout(testContext),
|
||||
context = testContext,
|
||||
publicSuffixList = publicSuffixList,
|
||||
interactor = interactor,
|
||||
),
|
||||
)
|
||||
binding = view.binding
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN updating THEN bind title,back button, description and switch`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
val state = ProtectionsState(
|
||||
tab = createTab(url = websiteUrl),
|
||||
url = websiteUrl,
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
view.update(state)
|
||||
|
||||
verify {
|
||||
view.bindTitle(state.url, state.isCookieBannerHandlingEnabled)
|
||||
view.bindBackButtonListener()
|
||||
view.bindDescription(state.isCookieBannerHandlingEnabled)
|
||||
view.bindSwitch(state.isCookieBannerHandlingEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is enabled WHEN biding title THEN title view must have the expected string`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
|
||||
view.bindTitle(url = websiteUrl, isCookieBannerHandlingEnabled = true)
|
||||
|
||||
val expectedText =
|
||||
testContext.getString(
|
||||
R.string.reduce_cookie_banner_details_panel_title_off_for_site,
|
||||
websiteUrl.toShortUrl(publicSuffixList),
|
||||
)
|
||||
|
||||
assertEquals(expectedText, view.binding.title.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is disabled WHEN biding title THEN title view must have the expected string`() {
|
||||
val websiteUrl = "https://mozilla.org"
|
||||
|
||||
view.bindTitle(url = websiteUrl, isCookieBannerHandlingEnabled = false)
|
||||
|
||||
val expectedText =
|
||||
testContext.getString(
|
||||
R.string.reduce_cookie_banner_details_panel_title_on_for_site,
|
||||
websiteUrl.toShortUrl(publicSuffixList),
|
||||
)
|
||||
|
||||
assertEquals(expectedText, view.binding.title.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN clicking the back button THEN view must delegate to the interactor#onBackPressed()`() {
|
||||
view.bindBackButtonListener()
|
||||
|
||||
view.binding.navigateBack.performClick()
|
||||
|
||||
verify {
|
||||
interactor.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is enabled WHEN biding description THEN description view must have the expected string`() {
|
||||
view.bindDescription(isCookieBannerHandlingEnabled = true)
|
||||
|
||||
val expectedText =
|
||||
testContext.getString(
|
||||
R.string.reduce_cookie_banner_details_panel_description_off_for_site,
|
||||
testContext.getString(R.string.app_name),
|
||||
)
|
||||
|
||||
assertEquals(expectedText, view.binding.details.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is disabled WHEN biding description THEN description view must have the expected string`() {
|
||||
view.bindDescription(isCookieBannerHandlingEnabled = false)
|
||||
|
||||
val expectedText =
|
||||
testContext.getString(
|
||||
R.string.reduce_cookie_banner_details_panel_description_on_for_site,
|
||||
)
|
||||
|
||||
assertEquals(expectedText, view.binding.details.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is disabled WHEN biding switch THEN switch view must have the expected isChecked status`() {
|
||||
view.bindSwitch(isCookieBannerHandlingEnabled = false)
|
||||
|
||||
assertFalse(view.binding.cookieBannerSwitch.isChecked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner handling is enabled WHEN biding switch THEN switch view must have the expected isChecked status`() {
|
||||
view.bindSwitch(isCookieBannerHandlingEnabled = true)
|
||||
|
||||
assertTrue(view.binding.cookieBannerSwitch.isChecked)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import mozilla.components.feature.session.TrackingProtectionUseCases
|
||||
import mozilla.components.service.glean.testing.GleanTestRule
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import mozilla.components.support.test.rule.runTestOnMain
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.CookieBanners
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsAction
|
||||
import org.mozilla.fenix.trackingprotection.ProtectionsStore
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
internal class DefaultCookieBannerDetailsControllerTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var navController: NavController
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var fragment: Fragment
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var sitePermissions: SitePermissions
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var cookieBannersStorage: CookieBannersStorage
|
||||
|
||||
private lateinit var controller: DefaultCookieBannerDetailsController
|
||||
|
||||
private lateinit var tab: TabSessionState
|
||||
|
||||
private lateinit var browserStore: BrowserStore
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var protectionsStore: ProtectionsStore
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var reload: SessionUseCases.ReloadUrlUseCase
|
||||
|
||||
private var gravity = 54
|
||||
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule()
|
||||
private val scope = coroutinesTestRule.scope
|
||||
|
||||
@get:Rule
|
||||
val gleanRule = GleanTestRule(testContext)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
val trackingProtectionUseCases: TrackingProtectionUseCases = mockk(relaxed = true)
|
||||
context = spyk(testContext)
|
||||
tab = createTab("https://mozilla.org")
|
||||
browserStore = BrowserStore(BrowserState(tabs = listOf(tab)))
|
||||
controller = DefaultCookieBannerDetailsController(
|
||||
fragment = fragment,
|
||||
context = context,
|
||||
ioScope = scope,
|
||||
cookieBannersStorage = cookieBannersStorage,
|
||||
navController = { navController },
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = gravity,
|
||||
getCurrentTab = { tab },
|
||||
sessionId = tab.id,
|
||||
browserStore = browserStore,
|
||||
protectionsStore = protectionsStore,
|
||||
reload = reload,
|
||||
)
|
||||
|
||||
every { fragment.context } returns context
|
||||
every { context.components.useCases.trackingProtectionUseCases } returns trackingProtectionUseCases
|
||||
|
||||
val onComplete = slot<(Boolean) -> Unit>()
|
||||
every {
|
||||
trackingProtectionUseCases.containsException.invoke(
|
||||
any(),
|
||||
capture(onComplete),
|
||||
)
|
||||
}.answers { onComplete.captured.invoke(true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN handleBackPressed is called THEN should call popBackStack and navigate`() {
|
||||
controller.handleBackPressed()
|
||||
|
||||
verify {
|
||||
navController.popBackStack()
|
||||
|
||||
navController.navigate(any<NavDirections>())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner is enabled WHEN handleTogglePressed THEN remove from the storage, send telemetry and reload the tab`() =
|
||||
runTestOnMain {
|
||||
val isEnabled = true
|
||||
|
||||
assertNull(CookieBanners.exceptionRemoved.testGetValue())
|
||||
every { protectionsStore.dispatch(any()) } returns mockk()
|
||||
|
||||
controller.handleTogglePressed(isEnabled)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerifyOrder {
|
||||
cookieBannersStorage.removeException(
|
||||
uri = tab.content.url,
|
||||
privateBrowsing = tab.content.private,
|
||||
)
|
||||
protectionsStore.dispatch(
|
||||
ProtectionsAction.ToggleCookieBannerHandlingProtectionEnabled(
|
||||
isEnabled,
|
||||
),
|
||||
)
|
||||
reload(tab.id)
|
||||
}
|
||||
|
||||
assertNotNull(CookieBanners.exceptionRemoved.testGetValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cookie banner is disabled WHEN handleTogglePressed THEN remove from the storage, send telemetry and reload the tab`() =
|
||||
runTestOnMain {
|
||||
val isEnabled = false
|
||||
|
||||
assertNull(CookieBanners.exceptionRemoved.testGetValue())
|
||||
every { protectionsStore.dispatch(any()) } returns mockk()
|
||||
|
||||
controller.handleTogglePressed(isEnabled)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerifyOrder {
|
||||
cookieBannersStorage.addException(
|
||||
uri = tab.content.url,
|
||||
privateBrowsing = tab.content.private,
|
||||
)
|
||||
protectionsStore.dispatch(
|
||||
ProtectionsAction.ToggleCookieBannerHandlingProtectionEnabled(
|
||||
isEnabled,
|
||||
),
|
||||
)
|
||||
reload(tab.id)
|
||||
}
|
||||
|
||||
assertNotNull(CookieBanners.exceptionAdded.testGetValue())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* 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.quicksettings.protections.cookiebanners
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class DefaultCookieBannerDetailsInteractorTest {
|
||||
private lateinit var controller: CookieBannerDetailsController
|
||||
private lateinit var interactor: DefaultCookieBannerDetailsInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
controller = mockk(relaxed = true)
|
||||
interactor = DefaultCookieBannerDetailsInteractor(controller)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onBackPressed is called THEN delegate the controller`() {
|
||||
interactor.onBackPressed()
|
||||
|
||||
verify {
|
||||
controller.handleBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onTogglePressed is called THEN delegate the controller`() {
|
||||
interactor.onTogglePressed(true)
|
||||
|
||||
verify {
|
||||
controller.handleTogglePressed(true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,19 +10,20 @@ import mozilla.components.browser.state.state.SessionState
|
|||
import mozilla.components.concept.engine.content.blocking.TrackerLog
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class TrackingProtectionStoreTest {
|
||||
class ProtectionsStoreTest {
|
||||
|
||||
val tab: SessionState = mockk(relaxed = true)
|
||||
|
||||
@Test
|
||||
fun enterDetailsMode() = runTest {
|
||||
val initialState = defaultState()
|
||||
val store = TrackingProtectionStore(initialState)
|
||||
val store = ProtectionsStore(initialState)
|
||||
|
||||
store.dispatch(
|
||||
TrackingProtectionAction.EnterDetailsMode(
|
||||
ProtectionsAction.EnterDetailsMode(
|
||||
TrackingProtectionCategory.FINGERPRINTERS,
|
||||
true,
|
||||
),
|
||||
|
@ -31,7 +32,7 @@ class TrackingProtectionStoreTest {
|
|||
assertNotSame(initialState, store.state)
|
||||
assertEquals(
|
||||
store.state.mode,
|
||||
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
|
||||
ProtectionsState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
|
||||
)
|
||||
assertEquals(store.state.lastAccessedCategory, TrackingProtectionCategory.FINGERPRINTERS.name)
|
||||
}
|
||||
|
@ -39,13 +40,13 @@ class TrackingProtectionStoreTest {
|
|||
@Test
|
||||
fun exitDetailsMode() = runTest {
|
||||
val initialState = detailsState()
|
||||
val store = TrackingProtectionStore(initialState)
|
||||
val store = ProtectionsStore(initialState)
|
||||
|
||||
store.dispatch(TrackingProtectionAction.ExitDetailsMode).join()
|
||||
store.dispatch(ProtectionsAction.ExitDetailsMode).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(
|
||||
store.state.mode,
|
||||
TrackingProtectionState.Mode.Normal,
|
||||
ProtectionsState.Mode.Normal,
|
||||
)
|
||||
assertEquals(store.state.lastAccessedCategory, initialState.lastAccessedCategory)
|
||||
}
|
||||
|
@ -53,10 +54,10 @@ class TrackingProtectionStoreTest {
|
|||
@Test
|
||||
fun trackerListChanged() = runTest {
|
||||
val initialState = defaultState()
|
||||
val store = TrackingProtectionStore(initialState)
|
||||
val store = ProtectionsStore(initialState)
|
||||
val tracker = TrackerLog("url", listOf())
|
||||
|
||||
store.dispatch(TrackingProtectionAction.TrackerLogChange(listOf(tracker))).join()
|
||||
store.dispatch(ProtectionsAction.TrackerLogChange(listOf(tracker))).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(
|
||||
listOf(tracker),
|
||||
|
@ -67,9 +68,9 @@ class TrackingProtectionStoreTest {
|
|||
@Test
|
||||
fun urlChanged() = runTest {
|
||||
val initialState = defaultState()
|
||||
val store = TrackingProtectionStore(initialState)
|
||||
val store = ProtectionsStore(initialState)
|
||||
|
||||
store.dispatch(TrackingProtectionAction.UrlChange("newURL")).join()
|
||||
store.dispatch(ProtectionsAction.UrlChange("newURL")).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(
|
||||
"newURL",
|
||||
|
@ -80,15 +81,16 @@ class TrackingProtectionStoreTest {
|
|||
@Test
|
||||
fun onChange() = runTest {
|
||||
val initialState = defaultState()
|
||||
val store = TrackingProtectionStore(initialState)
|
||||
val store = ProtectionsStore(initialState)
|
||||
val tracker = TrackerLog("url", listOf(), listOf(), cookiesHasBeenBlocked = false)
|
||||
|
||||
store.dispatch(
|
||||
TrackingProtectionAction.Change(
|
||||
ProtectionsAction.Change(
|
||||
"newURL",
|
||||
false,
|
||||
listOf(tracker),
|
||||
TrackingProtectionState.Mode.Details(
|
||||
isTrackingProtectionEnabled = false,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = listOf(tracker),
|
||||
mode = ProtectionsState.Mode.Details(
|
||||
TrackingProtectionCategory.FINGERPRINTERS,
|
||||
true,
|
||||
),
|
||||
|
@ -103,31 +105,51 @@ class TrackingProtectionStoreTest {
|
|||
false,
|
||||
store.state.isTrackingProtectionEnabled,
|
||||
)
|
||||
assertEquals(
|
||||
false,
|
||||
store.state.isCookieBannerHandlingEnabled,
|
||||
)
|
||||
assertEquals(
|
||||
listOf(tracker),
|
||||
store.state.listTrackers,
|
||||
)
|
||||
assertEquals(
|
||||
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
|
||||
ProtectionsState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
|
||||
store.state.mode,
|
||||
)
|
||||
}
|
||||
|
||||
private fun defaultState(): TrackingProtectionState = TrackingProtectionState(
|
||||
@Test
|
||||
fun `ProtectionsAction - ToggleCookieBannerHandlingProtectionEnabled`() = runTest {
|
||||
val initialState = defaultState()
|
||||
val store = ProtectionsStore(initialState)
|
||||
|
||||
store.dispatch(
|
||||
ProtectionsAction.ToggleCookieBannerHandlingProtectionEnabled(
|
||||
isEnabled = true,
|
||||
),
|
||||
).join()
|
||||
|
||||
assertTrue(store.state.isCookieBannerHandlingEnabled)
|
||||
}
|
||||
|
||||
private fun defaultState(): ProtectionsState = ProtectionsState(
|
||||
tab = tab,
|
||||
url = "www.mozilla.org",
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
private fun detailsState(): TrackingProtectionState = TrackingProtectionState(
|
||||
private fun detailsState(): ProtectionsState = ProtectionsState(
|
||||
tab = tab,
|
||||
url = "www.mozilla.org",
|
||||
isTrackingProtectionEnabled = true,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true),
|
||||
mode = ProtectionsState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true),
|
||||
lastAccessedCategory = TrackingProtectionCategory.CRYPTOMINERS.name,
|
||||
)
|
||||
}
|
|
@ -50,32 +50,32 @@ class TrackingProtectionPanelDialogFragmentTest {
|
|||
|
||||
@Test
|
||||
fun `WHEN the url is updated THEN the url view is updated`() {
|
||||
val trackingProtectionStore: TrackingProtectionStore = mockk(relaxed = true)
|
||||
val protectionsStore: ProtectionsStore = mockk(relaxed = true)
|
||||
val tab = createTab("mozilla.org")
|
||||
|
||||
every { fragment.trackingProtectionStore } returns trackingProtectionStore
|
||||
every { fragment.protectionsStore } returns protectionsStore
|
||||
every { fragment.provideCurrentTabId() } returns tab.id
|
||||
|
||||
fragment.observeUrlChange(store)
|
||||
addAndSelectTab(tab)
|
||||
|
||||
verify(exactly = 1) {
|
||||
trackingProtectionStore.dispatch(TrackingProtectionAction.UrlChange("mozilla.org"))
|
||||
protectionsStore.dispatch(ProtectionsAction.UrlChange("mozilla.org"))
|
||||
}
|
||||
|
||||
store.dispatch(ContentAction.UpdateUrlAction(tab.id, "wikipedia.org")).joinBlocking()
|
||||
|
||||
verify(exactly = 1) {
|
||||
trackingProtectionStore.dispatch(TrackingProtectionAction.UrlChange("wikipedia.org"))
|
||||
protectionsStore.dispatch(ProtectionsAction.UrlChange("wikipedia.org"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a tracker is loaded THEN trackers view is updated`() {
|
||||
val trackingProtectionStore: TrackingProtectionStore = mockk(relaxed = true)
|
||||
val protectionsStore: ProtectionsStore = mockk(relaxed = true)
|
||||
val tab = createTab("mozilla.org")
|
||||
|
||||
every { fragment.trackingProtectionStore } returns trackingProtectionStore
|
||||
every { fragment.protectionsStore } returns protectionsStore
|
||||
every { fragment.provideCurrentTabId() } returns tab.id
|
||||
every { fragment.updateTrackers(any()) } returns Unit
|
||||
|
||||
|
@ -99,10 +99,10 @@ class TrackingProtectionPanelDialogFragmentTest {
|
|||
|
||||
@Test
|
||||
fun `WHEN a tracker is blocked THEN trackers view is updated`() {
|
||||
val trackingProtectionStore: TrackingProtectionStore = mockk(relaxed = true)
|
||||
val protectionsStore: ProtectionsStore = mockk(relaxed = true)
|
||||
val tab = createTab("mozilla.org")
|
||||
|
||||
every { fragment.trackingProtectionStore } returns trackingProtectionStore
|
||||
every { fragment.protectionsStore } returns protectionsStore
|
||||
every { fragment.provideCurrentTabId() } returns tab.id
|
||||
every { fragment.updateTrackers(any()) } returns Unit
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
|
@ -17,11 +18,15 @@ import io.mockk.spyk
|
|||
import io.mockk.verify
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
|
||||
import mozilla.components.concept.engine.permission.SitePermissions
|
||||
import mozilla.components.feature.session.TrackingProtectionUseCases
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import mozilla.components.support.test.rule.runTestOnMain
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
@ -42,12 +47,16 @@ class TrackingProtectionPanelInteractorTest {
|
|||
private lateinit var sitePermissions: SitePermissions
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var store: TrackingProtectionStore
|
||||
private lateinit var store: ProtectionsStore
|
||||
|
||||
private lateinit var interactor: TrackingProtectionPanelInteractor
|
||||
|
||||
private lateinit var tab: TabSessionState
|
||||
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule()
|
||||
private val scope = coroutinesTestRule.scope
|
||||
|
||||
private var learnMoreClicked = false
|
||||
private var openSettings = false
|
||||
private var gravity = 54
|
||||
|
@ -59,11 +68,14 @@ class TrackingProtectionPanelInteractorTest {
|
|||
|
||||
context = spyk(testContext)
|
||||
tab = createTab("https://mozilla.org")
|
||||
val cookieBannersStorage: CookieBannersStorage = mockk(relaxed = true)
|
||||
|
||||
interactor = TrackingProtectionPanelInteractor(
|
||||
context = context,
|
||||
fragment = fragment,
|
||||
store = store,
|
||||
ioScope = scope,
|
||||
cookieBannersStorage = cookieBannersStorage,
|
||||
navController = { navController },
|
||||
openTrackingProtectionSettings = { openSettings = true },
|
||||
openLearnMoreLink = { learnMoreClicked = true },
|
||||
|
@ -92,7 +104,7 @@ class TrackingProtectionPanelInteractorTest {
|
|||
|
||||
verify {
|
||||
store.dispatch(
|
||||
TrackingProtectionAction.EnterDetailsMode(
|
||||
ProtectionsAction.EnterDetailsMode(
|
||||
TrackingProtectionCategory.FINGERPRINTERS,
|
||||
true,
|
||||
),
|
||||
|
@ -103,7 +115,7 @@ class TrackingProtectionPanelInteractorTest {
|
|||
|
||||
verify {
|
||||
store.dispatch(
|
||||
TrackingProtectionAction.EnterDetailsMode(
|
||||
ProtectionsAction.EnterDetailsMode(
|
||||
TrackingProtectionCategory.REDIRECT_TRACKERS,
|
||||
true,
|
||||
),
|
||||
|
@ -126,10 +138,10 @@ class TrackingProtectionPanelInteractorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onBackPressed is called THEN call popBackStack and navigate`() {
|
||||
fun `WHEN onBackPressed is called THEN call popBackStack and navigate`() = runTestOnMain {
|
||||
interactor.onBackPressed()
|
||||
|
||||
verify {
|
||||
coVerify {
|
||||
navController.popBackStack()
|
||||
|
||||
navController.navigate(any<NavDirections>())
|
||||
|
@ -140,6 +152,6 @@ class TrackingProtectionPanelInteractorTest {
|
|||
fun `WHEN onExitDetailMode is called THEN store should dispatch ExitDetailsMode action`() {
|
||||
interactor.onExitDetailMode()
|
||||
|
||||
verify { store.dispatch(TrackingProtectionAction.ExitDetailsMode) }
|
||||
verify { store.dispatch(ProtectionsAction.ExitDetailsMode) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,12 +37,13 @@ class TrackingProtectionPanelViewTest {
|
|||
private lateinit var container: ViewGroup
|
||||
private lateinit var interactor: TrackingProtectionPanelInteractor
|
||||
private lateinit var view: TrackingProtectionPanelView
|
||||
private val baseState = TrackingProtectionState(
|
||||
private val baseState = ProtectionsState(
|
||||
tab = null,
|
||||
url = "",
|
||||
isTrackingProtectionEnabled = false,
|
||||
isCookieBannerHandlingEnabled = false,
|
||||
listTrackers = emptyList(),
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
mode = ProtectionsState.Mode.Normal,
|
||||
lastAccessedCategory = "",
|
||||
)
|
||||
|
||||
|
@ -61,7 +62,7 @@ class TrackingProtectionPanelViewTest {
|
|||
mockkStatic("org.mozilla.fenix.ext.ContextKt") {
|
||||
every { any<Context>().settings() } returns mockk(relaxed = true)
|
||||
|
||||
view.update(baseState.copy(mode = TrackingProtectionState.Mode.Normal))
|
||||
view.update(baseState.copy(mode = ProtectionsState.Mode.Normal))
|
||||
assertFalse(view.binding.detailsMode.isVisible)
|
||||
assertTrue(view.binding.normalMode.isVisible)
|
||||
assertTrue(view.binding.protectionSettings.isVisible)
|
||||
|
@ -78,7 +79,7 @@ class TrackingProtectionPanelViewTest {
|
|||
}
|
||||
val expectedTitle = testContext.getString(R.string.etp_cookies_title_2)
|
||||
|
||||
view.update(baseState.copy(mode = TrackingProtectionState.Mode.Normal))
|
||||
view.update(baseState.copy(mode = ProtectionsState.Mode.Normal))
|
||||
|
||||
assertEquals(expectedTitle, view.binding.crossSiteTracking.text)
|
||||
assertEquals(expectedTitle, view.binding.crossSiteTrackingLoaded.text)
|
||||
|
@ -93,7 +94,7 @@ class TrackingProtectionPanelViewTest {
|
|||
}
|
||||
val expectedTitle = testContext.getString(R.string.etp_cookies_title)
|
||||
|
||||
view.update(baseState.copy(mode = TrackingProtectionState.Mode.Normal))
|
||||
view.update(baseState.copy(mode = ProtectionsState.Mode.Normal))
|
||||
|
||||
assertEquals(expectedTitle, view.binding.crossSiteTracking.text)
|
||||
assertEquals(expectedTitle, view.binding.crossSiteTrackingLoaded.text)
|
||||
|
@ -104,7 +105,7 @@ class TrackingProtectionPanelViewTest {
|
|||
fun testPrivateModeUi() {
|
||||
view.update(
|
||||
baseState.copy(
|
||||
mode = TrackingProtectionState.Mode.Details(
|
||||
mode = ProtectionsState.Mode.Details(
|
||||
selectedCategory = TrackingProtectionCategory.TRACKING_CONTENT,
|
||||
categoryBlocked = false,
|
||||
),
|
||||
|
@ -137,7 +138,7 @@ class TrackingProtectionPanelViewTest {
|
|||
|
||||
view.update(
|
||||
baseState.copy(
|
||||
mode = TrackingProtectionState.Mode.Details(
|
||||
mode = ProtectionsState.Mode.Details(
|
||||
selectedCategory = CROSS_SITE_TRACKING_COOKIES,
|
||||
categoryBlocked = false,
|
||||
),
|
||||
|
@ -160,7 +161,7 @@ class TrackingProtectionPanelViewTest {
|
|||
|
||||
view.update(
|
||||
baseState.copy(
|
||||
mode = TrackingProtectionState.Mode.Details(
|
||||
mode = ProtectionsState.Mode.Details(
|
||||
selectedCategory = CROSS_SITE_TRACKING_COOKIES,
|
||||
categoryBlocked = false,
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue