Bug 1797593 - Add re-engagement dialog for cookie banners.
parent
614ba71842
commit
804d286650
@ -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.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import mozilla.components.concept.engine.EngineSession.CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL
|
||||
import mozilla.components.concept.engine.Settings
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.CookieBanners
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getRootView
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* Displays a cookie banner dialog fragment that contains the dialog compose and his logic.
|
||||
*/
|
||||
class CookieBannerReEngagementDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View = ComposeView(requireContext()).apply {
|
||||
CookieBanners.visitedReEngagementDialog.record(NoExtras())
|
||||
|
||||
setContent {
|
||||
FirefoxTheme {
|
||||
val cookieBannerDialogSelectedVariant =
|
||||
CookieBannerReEngagementDialogUtils.getCookieBannerDialogVariants(requireContext())
|
||||
CookieBannerReEngagementDialogCompose(
|
||||
dialogTitle = cookieBannerDialogSelectedVariant.title,
|
||||
dialogText = cookieBannerDialogSelectedVariant.message,
|
||||
allowButtonText = cookieBannerDialogSelectedVariant.positiveTextButton,
|
||||
declineButtonText = getString(R.string.reduce_cookie_banner_dialog_not_now_button),
|
||||
onAllowButtonClicked = {
|
||||
CookieBanners.allowReEngagementDialog.record(NoExtras())
|
||||
requireContext().settings().shouldUseCookieBanner = true
|
||||
getEngineSettings().cookieBannerHandlingModePrivateBrowsing = REJECT_OR_ACCEPT_ALL
|
||||
getEngineSettings().cookieBannerHandlingMode = REJECT_OR_ACCEPT_ALL
|
||||
reload()
|
||||
requireContext().getRootView()?.let {
|
||||
FenixSnackbar.make(
|
||||
view = it,
|
||||
duration = FenixSnackbar.LENGTH_LONG,
|
||||
isDisplayedWithBrowserToolbar = true,
|
||||
)
|
||||
.setText(getString(R.string.reduce_cookie_banner_dialog_snackbar_text))
|
||||
.show()
|
||||
}
|
||||
dismiss()
|
||||
},
|
||||
onNotNowButtonClicked = {
|
||||
CookieBanners.notNowReEngagementDialog.record(NoExtras())
|
||||
dismiss()
|
||||
},
|
||||
onCloseButtonClicked = {
|
||||
requireContext().settings().userOptOutOfReEngageCookieBannerDialog = true
|
||||
CookieBanners.optOutReEngagementDialog.record(NoExtras())
|
||||
dismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEngineSettings(): Settings {
|
||||
return requireContext().components.core.engine.settings
|
||||
}
|
||||
|
||||
private fun reload() {
|
||||
return requireContext().components.useCases.sessionUseCases.reload()
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/* 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.dialog
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.theme.defaultTypography
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
private fun CookieBannerReEngagementDialogComposePreview() {
|
||||
FirefoxTheme {
|
||||
CookieBannerReEngagementDialogCompose(
|
||||
dialogTitle = "Cookie banners begone!",
|
||||
dialogText =
|
||||
"Automatically reject cookie requests, when possible. Otherwise, " +
|
||||
"accept all cookies to dismiss cookie banners.",
|
||||
onAllowButtonClicked = {},
|
||||
onNotNowButtonClicked = {},
|
||||
onCloseButtonClicked = {},
|
||||
allowButtonText = "Dismiss banners",
|
||||
declineButtonText = "NOT NOW",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the cookie banner reducer dialog
|
||||
*/
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
fun CookieBannerReEngagementDialogCompose(
|
||||
dialogTitle: String,
|
||||
dialogText: String,
|
||||
allowButtonText: String,
|
||||
declineButtonText: String,
|
||||
onCloseButtonClicked: () -> Unit,
|
||||
onAllowButtonClicked: () -> Unit,
|
||||
onNotNowButtonClicked: () -> Unit,
|
||||
) {
|
||||
Dialog(
|
||||
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = false),
|
||||
onDismissRequest = onNotNowButtonClicked,
|
||||
) {
|
||||
Surface(
|
||||
color = Color.Transparent,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(color = FirefoxTheme.colors.layer1),
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
top = 24.dp,
|
||||
start = 24.dp,
|
||||
end = 24.dp,
|
||||
bottom = 8.dp,
|
||||
),
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
text = dialogTitle,
|
||||
style = defaultTypography.headline7,
|
||||
)
|
||||
IconButton(
|
||||
modifier = Modifier
|
||||
.size(48.dp),
|
||||
onClick = onCloseButtonClicked,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.mozac_ic_close),
|
||||
contentDescription = stringResource(R.string.content_description_close_button),
|
||||
tint = FirefoxTheme.colors.iconPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
fontSize = 16.sp,
|
||||
text = dialogText,
|
||||
style = defaultTypography.body1,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 24.dp, bottom = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 8.dp,
|
||||
alignment = Alignment.End,
|
||||
),
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onNotNowButtonClicked,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
) {
|
||||
Text(
|
||||
text = declineButtonText.uppercase(),
|
||||
fontSize = 14.sp,
|
||||
style = MaterialTheme.typography.button,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
onClick = onAllowButtonClicked,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
) {
|
||||
Text(
|
||||
text = allowButtonText.uppercase(),
|
||||
fontSize = 14.sp,
|
||||
style = MaterialTheme.typography.button,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/* 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.dialog
|
||||
|
||||
import android.content.Context
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.concept.engine.EngineSession.CookieBannerHandlingStatus
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.nimbus.CookieBannersSection
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
private const val CONTROL_VARIANT = 0
|
||||
private const val VARIANT_ONE = 1
|
||||
private const val VARIANT_TWO = 2
|
||||
|
||||
/**
|
||||
* An utility object for interacting with the re-engagement cookie banner dialog.
|
||||
*/
|
||||
object CookieBannerReEngagementDialogUtils {
|
||||
/**
|
||||
* Returns a the current [CookieBannerDialogVariant] to the given nimbus experiment.
|
||||
*/
|
||||
fun getCookieBannerDialogVariants(context: Context): CookieBannerDialogVariant {
|
||||
val textVariant =
|
||||
FxNimbus.features.cookieBanners.value().sectionsEnabled[CookieBannersSection.DIALOG_TEXT_VARIANT]
|
||||
return when (textVariant) {
|
||||
CONTROL_VARIANT -> CookieBannerDialogVariant(
|
||||
title = context.getString(R.string.reduce_cookie_banner_control_experiment_dialog_title),
|
||||
message = context.getString(
|
||||
R.string.reduce_cookie_banner_control_experiment_dialog_body,
|
||||
),
|
||||
positiveTextButton = context.getString(
|
||||
R.string.reduce_cookie_banner_control_experiment_dialog_change_setting_button,
|
||||
),
|
||||
)
|
||||
VARIANT_ONE -> CookieBannerDialogVariant(
|
||||
title = context.getString(R.string.reduce_cookie_banner_variant_1_experiment_dialog_title),
|
||||
message = context.getString(
|
||||
R.string.reduce_cookie_banner_variant_1_experiment_dialog_body,
|
||||
context.getString(R.string.app_name),
|
||||
),
|
||||
positiveTextButton = context.getString(
|
||||
R.string.reduce_cookie_banner_variant_1_experiment_dialog_change_setting_button,
|
||||
),
|
||||
)
|
||||
VARIANT_TWO -> CookieBannerDialogVariant(
|
||||
title = context.getString(R.string.reduce_cookie_banner_variant_2_experiment_dialog_title),
|
||||
message = context.getString(
|
||||
R.string.reduce_cookie_banner_variant_2_experiment_dialog_body,
|
||||
context.getString(R.string.app_name),
|
||||
),
|
||||
positiveTextButton = context.getString(
|
||||
R.string.reduce_cookie_banner_variant_2_experiment_dialog_change_setting_button,
|
||||
),
|
||||
)
|
||||
else -> {
|
||||
CookieBannerDialogVariant(
|
||||
title = context.getString(R.string.reduce_cookie_banner_control_experiment_dialog_title),
|
||||
message = context.getString(
|
||||
R.string.reduce_cookie_banner_control_experiment_dialog_body,
|
||||
),
|
||||
positiveTextButton = context.getString(
|
||||
R.string.reduce_cookie_banner_control_experiment_dialog_change_setting_button,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to show the re-engagement cookie banner dialog, when the right conditions are met, o
|
||||
* otherwise the dialog won't show.
|
||||
*/
|
||||
fun tryToShowReEngagementDialog(
|
||||
settings: Settings,
|
||||
status: CookieBannerHandlingStatus,
|
||||
navController: NavController,
|
||||
) {
|
||||
if (status == CookieBannerHandlingStatus.DETECTED &&
|
||||
settings.shouldCookieBannerReEngagementDialog()
|
||||
) {
|
||||
settings.cookieBannerDetectedPreviously = true
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionBrowserFragmentToCookieBannerDialogFragment()
|
||||
navController.nav(R.id.browserFragment, directions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for cookie banner dialog variant
|
||||
* @property title of the dialog
|
||||
* @property message of the dialog
|
||||
* @property positiveTextButton indicates the text of the positive button of the dialog
|
||||
*/
|
||||
data class CookieBannerDialogVariant(
|
||||
val title: String,
|
||||
val message: String,
|
||||
val positiveTextButton: String,
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue