diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 0bb5c9331..7e6a4dd0e 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -321,18 +321,22 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { } override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { - 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 - ) - nav(R.id.browserFragment, directions) + requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> + 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 + ) + nav(R.id.browserFragment, directions) + } } private val collectionStorageObserver = object : TabCollectionStorage.Observer { diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index 0bd6203b5..6bbe13e68 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -177,18 +177,21 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler } override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { - 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 - ) - nav(R.id.externalAppBrowserFragment, directions) + requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> + 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) + } } override fun getContextMenuCandidates( diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt index 48ea3d23c..0d7cdfe40 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt @@ -11,12 +11,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase import mozilla.components.concept.engine.permission.SitePermissions +import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase import mozilla.components.feature.tabs.TabsUseCases.AddNewTabUseCase import mozilla.components.support.base.feature.OnNeedToRequestPermissions import mozilla.components.support.ktx.kotlin.getOrigin +import org.mozilla.fenix.R import org.mozilla.fenix.components.PermissionStorage +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.nav import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.toggle @@ -54,6 +57,16 @@ interface QuickSettingsController { * feature [PhoneFeature] which the user granted Android permission(s) for. */ fun handleAndroidPermissionGranted(feature: PhoneFeature) + + /** + * @see [TrackingProtectionInteractor.onTrackingProtectionToggled] + */ + fun handleTrackingProtectionToggled(isEnabled: Boolean) + + /** + * @see [TrackingProtectionInteractor.onBlockedItemsClicked] + */ + fun handleBlockedItemsClicked() } /** @@ -155,6 +168,24 @@ class DefaultQuickSettingsController( ) } + override fun handleTrackingProtectionToggled(isEnabled: Boolean) { + TODO("Not yet implemented") + } + + override fun handleBlockedItemsClicked() { + dismiss.invoke() + + val state = quickSettingsStore.state.trackingProtectionState + val directions = QuickSettingsSheetDialogFragmentDirections + .actionGlobalTrackingProtectionPanelDialogFragment( + sessionId = sessionId, + url = state.url, + trackingProtectionEnabled = state.isTrackingProtectionEnabled, + gravity = context.components.settings.toolbarPosition.androidGravity + ) + navController.nav(R.id.quickSettingsSheetDialogFragment, directions) + } + /** * Request a certain set of runtime Android permissions. * diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt index 0d9e8c75d..bcf6ce193 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt @@ -15,6 +15,7 @@ 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.utils.Settings /** @@ -24,7 +25,8 @@ import org.mozilla.fenix.utils.Settings */ data class QuickSettingsFragmentState( val webInfoState: WebsiteInfoState, - val websitePermissionsState: WebsitePermissionsState + val websitePermissionsState: WebsitePermissionsState, + val trackingProtectionState: TrackingProtectionState ) : State /** diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt index cbf36b871..45218a763 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt @@ -6,16 +6,19 @@ package org.mozilla.fenix.settings.quicksettings import android.content.Context import androidx.annotation.VisibleForTesting +import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.state.content.PermissionHighlightsState import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.lib.state.Action import mozilla.components.lib.state.Reducer import mozilla.components.lib.state.State import mozilla.components.lib.state.Store +import org.mozilla.fenix.ext.components import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.createStore 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.utils.Settings import java.util.EnumMap @@ -48,7 +51,10 @@ class QuickSettingsFragmentStore( * @param isSecured [Boolean] whether the connection is secured (TLS) or not. * @param permissions [SitePermissions]? list of website permissions and their status. * @param settings [Settings] application settings. - * @param certificateName [String] the certificate name of the current web page. + * @param certificateName [String] the certificate name of the current web page. + * @param sessionId [String] TODO + * @param isTrackingProtectionEnabled [Boolean] Current status of tracking protection + * for this session. */ @Suppress("LongParameterList") fun createStore( @@ -59,7 +65,9 @@ class QuickSettingsFragmentStore( isSecured: Boolean, permissions: SitePermissions?, permissionHighlights: PermissionHighlightsState, - settings: Settings + settings: Settings, + sessionId: String, + isTrackingProtectionEnabled: Boolean ) = QuickSettingsFragmentStore( QuickSettingsFragmentState( webInfoState = createWebsiteInfoState( @@ -73,6 +81,12 @@ class QuickSettingsFragmentStore( permissions, permissionHighlights, settings + ), + trackingProtectionState = createTrackingProtectionState( + context, + sessionId, + websiteUrl, + isTrackingProtectionEnabled ) ) ) @@ -127,6 +141,33 @@ class QuickSettingsFragmentStore( return state } + /** + * Construct an initial [TrackingProtectionState] to be rendered by + * [TrackingProtectionView]. + * + * @param context [Context] used for various Android interactions. + * @param sessionId [String] TODO + * @param websiteUrl [String] the URL of the current web page. + * @param isTrackingProtectionEnabled [Boolean] Current status of tracking protection + * for this session. + */ + @VisibleForTesting + fun createTrackingProtectionState( + context: Context, + sessionId: String, + websiteUrl: String, + isTrackingProtectionEnabled: Boolean + ): TrackingProtectionState { + return TrackingProtectionState( + tab = context.components.core.store.state.findTabOrCustomTab(sessionId), + url = websiteUrl, + isTrackingProtectionEnabled = isTrackingProtectionEnabled, + listTrackers = listOf(), + mode = TrackingProtectionState.Mode.Normal, + lastAccessedCategory = "" + ) + } + /** * [PhoneFeature] to a [WebsitePermission] mapper. */ diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt index 0b5ea9086..08e3e0217 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt @@ -15,7 +15,7 @@ package org.mozilla.fenix.settings.quicksettings */ class QuickSettingsInteractor( private val controller: QuickSettingsController -) : WebsitePermissionInteractor { +) : WebsitePermissionInteractor, TrackingProtectionInteractor { override fun onPermissionsShown() { controller.handlePermissionsShown() } @@ -27,4 +27,12 @@ class QuickSettingsInteractor( override fun onAutoplayChanged(value: AutoplayValue) { controller.handleAutoplayChanged(value) } + + override fun onTrackingProtectionToggled(isEnabled: Boolean) { + controller.handleTrackingProtectionToggled(isEnabled) + } + + override fun onBlockedItemsClicked() { + controller.handleBlockedItemsClicked() + } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt index eb933655c..9b3c12c20 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt @@ -50,7 +50,9 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { private lateinit var quickSettingsController: QuickSettingsController private lateinit var websiteInfoView: WebsiteInfoView private lateinit var websitePermissionsView: WebsitePermissionsView + private lateinit var trackingProtectionView: TrackingProtectionView private lateinit var interactor: QuickSettingsInteractor + private var tryToRequestPermissions: Boolean = false private val args by navArgs() @@ -77,7 +79,9 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { permissions = args.sitePermissions, settings = components.settings, certificateName = args.certificateName, - permissionHighlights = args.permissionHighlights + permissionHighlights = args.permissionHighlights, + sessionId = args.sessionId, + isTrackingProtectionEnabled = args.isTrackingProtectionEnabled ) quickSettingsController = DefaultQuickSettingsController( @@ -105,6 +109,8 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { websiteInfoView = WebsiteInfoView(binding.websiteInfoLayout) websitePermissionsView = WebsitePermissionsView(binding.websitePermissionsLayout, interactor) + trackingProtectionView = + TrackingProtectionView(rootView.trackingProtectionLayout, interactor) return rootView } @@ -143,6 +149,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { consumeFrom(quickSettingsStore) { websiteInfoView.update(it.webInfoState) websitePermissionsView.update(it.websitePermissionsState) + trackingProtectionView.update(it.trackingProtectionState) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt new file mode 100644 index 000000000..158e9701a --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt @@ -0,0 +1,67 @@ +/* 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.View +import android.view.ViewGroup +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.component_tracking_protection_panel.trackingProtectionSwitch +import kotlinx.android.synthetic.main.quicksettings_tracking_protection.* +import kotlinx.android.synthetic.main.switch_with_description.view.* +import org.mozilla.fenix.R +import org.mozilla.fenix.trackingprotection.TrackingProtectionState + +/** + * Contract declaring all possible user interactions with [TrackingProtectionView] + */ +interface TrackingProtectionInteractor { + + /** + * Called whenever the tracking protection toggle for this site is toggled + * + * @param isEnabled new status of session tracking protection + */ + fun onTrackingProtectionToggled(isEnabled: Boolean) + + /** + * Navigates to the tracking protection preferences. Called when a user clicks on the + * "Blocked items" button. + */ + fun onBlockedItemsClicked() +} + +/** + * TODO + * + * @param containerView [ViewGroup] in which this View will inflate itself. + * @param interactor [TrackingProtectionInteractor] which will have delegated to all user interactions. + */ +class TrackingProtectionView( + override val containerView: ViewGroup, + val interactor: TrackingProtectionInteractor +) : LayoutContainer { + + private val context = containerView.context + val view: View = LayoutInflater.from(context) + .inflate(R.layout.quicksettings_tracking_protection, containerView, true) + + fun update(state: TrackingProtectionState) { + bindTrackingProtectionInfo(state.isTrackingProtectionEnabled) + + trackingProtectionBlockedItems.setOnClickListener { + interactor.onBlockedItemsClicked() + } + } + + private fun bindTrackingProtectionInfo(isTrackingProtectionEnabled: Boolean) { + trackingProtectionSwitch.switch_widget.isChecked = isTrackingProtectionEnabled + trackingProtectionSwitch.switch_widget.jumpDrawablesToCurrentState() + + trackingProtectionSwitch.switch_widget.setOnCheckedChangeListener { _, isChecked -> + interactor.onTrackingProtectionToggled(isChecked) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt index 4a50c3783..c7c50fabe 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt @@ -35,26 +35,13 @@ class WebsiteInfoView( */ fun update(state: WebsiteInfoState) { bindUrl(state.websiteUrl) - bindTitle(state.websiteTitle) bindSecurityInfo(state.websiteSecurityUiValues) - bindCertificateName(state.certificateName) } private fun bindUrl(websiteUrl: String) { binding.url.text = websiteUrl } - private fun bindTitle(websiteTitle: String) { - binding.title.text = websiteTitle - } - - private fun bindCertificateName(cert: String) { - val certificateLabel = - binding.root.context.getString(R.string.certificate_info_verified_by, cert) - binding.certificateInfo.text = certificateLabel - binding.certificateInfo.isVisible = cert.isNotEmpty() - } - private fun bindSecurityInfo(uiValues: WebsiteSecurityUiValues) { val tint = getColor(binding.root.context, uiValues.iconTintRes) binding.securityInfo.setText(uiValues.securityInfoRes) diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt index 3e03dc54d..85ef24084 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt @@ -148,7 +148,7 @@ class TrackingProtectionOverlay( val toolbar = getToolbar() val trackingProtectionIcon: View = - toolbar.findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) + toolbar.findViewById(R.id.mozac_browser_toolbar_security_indicator) val xOffset = triangleMarginStartPx + triangleWidthPx / 2 @@ -172,12 +172,10 @@ class TrackingProtectionOverlay( it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } - val etpShield = - getToolbar().findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) trackingOnboardingDialog.message.setOnClickListener { metrics.track(Event.ContextualHintETPInsideTap) trackingOnboardingDialog.dismiss() - etpShield.performClick() + trackingProtectionIcon.performClick() } metrics.track(Event.ContextualHintETPDisplayed) diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt index ec7ad0cdc..445c8e4cb 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -87,9 +87,9 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt trackingProtectionStore = StoreProvider.get(this) { TrackingProtectionStore( TrackingProtectionState( - tab, - args.url, - args.trackingProtectionEnabled, + tab = tab, + url = args.url, + isTrackingProtectionEnabled = args.trackingProtectionEnabled, listTrackers = listOf(), mode = TrackingProtectionState.Mode.Normal, lastAccessedCategory = "" diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt index 394587b9f..087b7392f 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt @@ -47,12 +47,14 @@ sealed class TrackingProtectionAction : Action { /** * The state for the Tracking Protection Panel + * @property tab TODO * @property url Current URL to display - * @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception) + * @property isTrackingProtectionEnabled Current status of tracking 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_moode + * accessibly focus after returning from details_mode */ data class TrackingProtectionState( val tab: SessionState?, diff --git a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml index f5d321de7..0a785afb1 100644 --- a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml +++ b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml @@ -26,7 +26,7 @@ android:id="@+id/websitePermissionsLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" /> + app:layout_constraintBottom_toTopOf="@id/trackingProtectionDivider" /> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/quicksettings_website_info.xml b/app/src/main/res/layout/quicksettings_website_info.xml index 594138d94..815e0c0ac 100644 --- a/app/src/main/res/layout/quicksettings_website_info.xml +++ b/app/src/main/res/layout/quicksettings_website_info.xml @@ -16,16 +16,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:minHeight="@dimen/tracking_protection_item_height"> - - + android:minHeight="@dimen/tracking_protection_item_height" + android:paddingTop="8dp" + android:paddingBottom="8dp"> - - + tools:text="Connection is secure" /> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index d780417aa..941860b0d 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -780,6 +780,9 @@ + Cryptominers Fingerprinters + + Blocked items + Blocked Allowed