You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
6.0 KiB
Kotlin
156 lines
6.0 KiB
Kotlin
/* 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.components.toolbar
|
|
|
|
import android.content.Context
|
|
import androidx.annotation.VisibleForTesting
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.material.Text
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.text.style.TextDecoration
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.navigation.findNavController
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.cancel
|
|
import kotlinx.coroutines.flow.filter
|
|
import kotlinx.coroutines.flow.mapNotNull
|
|
import kotlinx.coroutines.flow.transformWhile
|
|
import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab
|
|
import mozilla.components.browser.state.selector.selectedTab
|
|
import mozilla.components.browser.state.store.BrowserStore
|
|
import mozilla.components.browser.toolbar.BrowserToolbar
|
|
import mozilla.components.lib.state.ext.flowScoped
|
|
import mozilla.components.service.glean.private.NoExtras
|
|
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
|
import org.mozilla.fenix.R
|
|
import org.mozilla.fenix.compose.cfr.CFRPopup
|
|
import org.mozilla.fenix.compose.cfr.CFRPopup.PopupAlignment.INDICATOR_CENTERED_IN_ANCHOR
|
|
import org.mozilla.fenix.compose.cfr.CFRPopupProperties
|
|
import org.mozilla.fenix.ext.components
|
|
import org.mozilla.fenix.settings.SupportUtils
|
|
import org.mozilla.fenix.settings.SupportUtils.SumoTopic.TOTAL_COOKIE_PROTECTION
|
|
import org.mozilla.fenix.settings.quicksettings.protections.cookiebanners.dialog.CookieBannerReEngagementDialogUtils
|
|
import org.mozilla.fenix.theme.FirefoxTheme
|
|
import org.mozilla.fenix.utils.Settings
|
|
|
|
/**
|
|
* Vertical padding needed to improve the visual alignment of the popup and respect the UX design.
|
|
*/
|
|
private const val CFR_TO_ANCHOR_VERTICAL_PADDING = -6
|
|
|
|
/**
|
|
* Delegate for handling all the business logic for showing CFRs in the toolbar.
|
|
*
|
|
* @param context used for various Android interactions.
|
|
* @param browserStore will be observed for tabs updates
|
|
* @param settings used to read and write persistent user settings
|
|
* @param toolbar will serve as anchor for the CFRs
|
|
* @param sessionId optional custom tab id used to identify the custom tab in which to show a CFR.
|
|
*/
|
|
class BrowserToolbarCFRPresenter(
|
|
private val context: Context,
|
|
private val browserStore: BrowserStore,
|
|
private val settings: Settings,
|
|
private val toolbar: BrowserToolbar,
|
|
private val sessionId: String? = null,
|
|
) {
|
|
@VisibleForTesting
|
|
internal var tcpCfrScope: CoroutineScope? = null
|
|
|
|
@VisibleForTesting
|
|
internal var tcpCfrPopup: CFRPopup? = null
|
|
|
|
/**
|
|
* Start observing [browserStore] for updates which may trigger showing a CFR.
|
|
*/
|
|
@Suppress("MagicNumber")
|
|
fun start() {
|
|
if (settings.shouldShowTotalCookieProtectionCFR) {
|
|
tcpCfrScope = browserStore.flowScoped { flow ->
|
|
flow
|
|
.mapNotNull { it.findCustomTabOrSelectedTab(sessionId)?.content?.progress }
|
|
// The "transformWhile" below ensures that the 100% progress is only collected once.
|
|
.transformWhile { progress ->
|
|
emit(progress)
|
|
progress != 100
|
|
}
|
|
.filter { it == 100 }
|
|
.collect {
|
|
tcpCfrScope?.cancel()
|
|
showTcpCfr()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop listening for [browserStore] updates.
|
|
* CFRs already shown are not automatically dismissed.
|
|
*/
|
|
fun stop() {
|
|
tcpCfrScope?.cancel()
|
|
}
|
|
|
|
@VisibleForTesting
|
|
internal fun showTcpCfr() {
|
|
CFRPopup(
|
|
text = context.getString(R.string.tcp_cfr_message),
|
|
anchor = toolbar.findViewById(
|
|
R.id.mozac_browser_toolbar_security_indicator,
|
|
),
|
|
properties = CFRPopupProperties(
|
|
popupAlignment = INDICATOR_CENTERED_IN_ANCHOR,
|
|
indicatorDirection = if (settings.toolbarPosition == ToolbarPosition.TOP) {
|
|
CFRPopup.IndicatorDirection.UP
|
|
} else {
|
|
CFRPopup.IndicatorDirection.DOWN
|
|
},
|
|
popupVerticalOffset = CFR_TO_ANCHOR_VERTICAL_PADDING.dp,
|
|
),
|
|
onDismiss = {
|
|
when (it) {
|
|
true -> TrackingProtection.tcpCfrExplicitDismissal.record(NoExtras())
|
|
false -> TrackingProtection.tcpCfrImplicitDismissal.record(NoExtras())
|
|
}
|
|
tryToShowCookieBannerDialogIfNeeded()
|
|
},
|
|
) {
|
|
Text(
|
|
text = context.getString(R.string.tcp_cfr_learn_more),
|
|
color = FirefoxTheme.colors.textOnColorPrimary,
|
|
modifier = Modifier.clickable {
|
|
context.components.useCases.tabsUseCases.selectOrAddTab.invoke(
|
|
SupportUtils.getSumoURLForTopic(
|
|
context,
|
|
TOTAL_COOKIE_PROTECTION,
|
|
),
|
|
)
|
|
TrackingProtection.tcpSumoLinkClicked.record(NoExtras())
|
|
tcpCfrPopup?.dismiss()
|
|
},
|
|
style = FirefoxTheme.typography.body2.copy(
|
|
textDecoration = TextDecoration.Underline,
|
|
),
|
|
)
|
|
}.run {
|
|
settings.shouldShowTotalCookieProtectionCFR = false
|
|
tcpCfrPopup = this
|
|
show()
|
|
TrackingProtection.tcpCfrShown.record(NoExtras())
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
internal fun tryToShowCookieBannerDialogIfNeeded() {
|
|
browserStore.state.selectedTab?.let { tab ->
|
|
CookieBannerReEngagementDialogUtils.tryToShowReEngagementDialog(
|
|
settings = settings,
|
|
status = tab.cookieBanner,
|
|
navController = toolbar.findNavController(),
|
|
)
|
|
}
|
|
}
|
|
}
|