Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

Commit

Permalink
For #1797578 - Create toggle option in the toolbar that allow a site …
Browse files Browse the repository at this point in the history
…to be excluded from cookie banner handling in Focus
  • Loading branch information
iorgamgabriel authored and mergify[bot] committed Dec 12, 2022
1 parent 3efe3ea commit a9a09b1
Show file tree
Hide file tree
Showing 19 changed files with 709 additions and 8 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ dependencies {
implementation "org.mozilla.components:lib-state:${AndroidComponents.VERSION}"
implementation "org.mozilla.components:feature-media:${AndroidComponents.VERSION}"
implementation "org.mozilla.components:lib-auth:${AndroidComponents.VERSION}"
implementation "org.mozilla.components:lib-publicsuffixlist:${AndroidComponents.VERSION}"

implementation "org.mozilla.components:service-glean:${AndroidComponents.VERSION}", {
exclude group: 'org.mozilla.telemetry', module: 'glean-native'
Expand Down
52 changes: 52 additions & 0 deletions app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2289,3 +2289,55 @@ cookie_banner:
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=1797578
data_reviews:
- https://github.com/mozilla-mobile/focus-android/pull/8124#issuecomment-1344449866
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
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=1797578
data_reviews:
- https://github.com/mozilla-mobile/focus-android/pull/8124#issuecomment-1344449866
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
metadata:
tags:
- Privacy&Security

visited_panel:
type: event
description: A user visited the cookie banner exception panel
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797578
data_reviews:
- https://github.com/mozilla-mobile/focus-android/pull/8124#issuecomment-1344449866
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
metadata:
tags:
- Privacy&Security
6 changes: 6 additions & 0 deletions app/src/main/java/org/mozilla/focus/Components.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.content.Intent
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import mozilla.components.browser.engine.gecko.cookiebanners.GeckoCookieBannersStorage
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.state.engine.EngineMiddleware
import mozilla.components.browser.state.store.BrowserStore
Expand Down Expand Up @@ -45,6 +46,7 @@ import mozilla.components.lib.crash.sentry.SentryService
import mozilla.components.lib.crash.service.CrashReporterService
import mozilla.components.lib.crash.service.GleanCrashReporterService
import mozilla.components.lib.crash.service.MozillaSocorroService
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.service.location.LocationService
import mozilla.components.service.location.MozillaLocationService
import mozilla.components.service.nimbus.NimbusApi
Expand Down Expand Up @@ -182,6 +184,10 @@ class Components(

val tabsUseCases: TabsUseCases by lazy { TabsUseCases(store) }

val cookieBannerStorage: GeckoCookieBannersStorage by lazy { EngineProvider.createCookieBannerStorage(context) }

val publicSuffixList by lazy { PublicSuffixList(context) }

val searchUseCases: SearchUseCases by lazy {
SearchUseCases(store, tabsUseCases, sessionUseCases)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package org.mozilla.focus.components

import android.content.Context
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.concept.engine.DefaultSettings
import mozilla.components.concept.engine.Engine
Expand Down Expand Up @@ -40,6 +41,12 @@ object EngineProvider {
return GeckoEngine(context, defaultSettings, runtime)
}

fun createCookieBannerStorage(context: Context): GeckoCookieBannersStorage {
val runtime = getOrCreateRuntime(context)

return GeckoCookieBannersStorage(runtime)
}

fun createClient(context: Context): Client {
val runtime = getOrCreateRuntime(context)
return GeckoViewFetchClient(context, runtime)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* 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.focus.cookiebannerexception

import android.content.Context
import android.view.View
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import mozilla.components.support.ktx.kotlin.toShortUrl
import org.mozilla.focus.R
import org.mozilla.focus.databinding.CookieBannerExceptionDetailsBinding
import org.mozilla.focus.ext.components

class CookieBannerExceptionDetailsPanel(
context: Context,
cookieBannerExceptionStore: CookieBannerExceptionStore,
private val tabUrl: String,
private val goBack: () -> Unit,
private val defaultCookieBannerInteractor: DefaultCookieBannerExceptionInteractor,
) : BottomSheetDialog(context) {

private var binding: CookieBannerExceptionDetailsBinding =
CookieBannerExceptionDetailsBinding.inflate(layoutInflater, null, false)

init {
setContentView(binding.root)
expandBottomSheet()
setListeners()
updateViews(cookieBannerExceptionStore.state.hasException)
bindSwitchItem(cookieBannerExceptionStore.state.hasException)
updateSwitchItem()
}

private fun updateViews(hasException: Boolean) {
bindTitle(hasException)
bindDescription(hasException)
bindSwitchItemDescription(hasException)
}

private fun expandBottomSheet() {
val bottomSheet =
findViewById<View>(R.id.design_bottom_sheet) as FrameLayout
BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
}

private fun updateSwitchItem() {
binding.cookieBannerExceptionDetailsSwitch.binding.switchWidget.setOnClickListener {
val isChecked = binding.cookieBannerExceptionDetailsSwitch.binding.switchWidget.isChecked
defaultCookieBannerInteractor.handleToggleCookieBannerException(isChecked)
updateViews(!isChecked)
}
}

private fun bindSwitchItem(hasException: Boolean) {
binding.cookieBannerExceptionDetailsSwitch.binding.switchWidget.isChecked = !hasException
}

private fun bindSwitchItemDescription(hasException: Boolean) {
val stringID =
if (hasException) {
R.string.cookie_banner_exception_panel_switch_state_off
} else {
R.string.cookie_banner_exception_panel_switch_state_on
}
binding.cookieBannerExceptionDetailsSwitch.binding.description.text = context.getString(stringID)
}

private fun bindTitle(hasException: Boolean) {
val stringID =
if (hasException) {
R.string.cookie_banner_exception_panel_title_state_on_for_site
} else {
R.string.cookie_banner_exception_panel_title_state_off_for_site
}
val shortUrl = tabUrl.toShortUrl(context.components.publicSuffixList)
binding.title.text = context.getString(stringID, shortUrl)
}

private fun bindDescription(hasException: Boolean) {
val stringID =
if (hasException) {
R.string.cookie_banner_exception_panel_description_state_off_for_site
} else {
R.string.cookie_banner_exception_panel_description_state_on_for_site
}
binding.details.text = context.getString(stringID, context.getString(R.string.app_name))
}

private fun setListeners() {
binding.detailsBack.setOnClickListener {
goBack.invoke()
dismiss()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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.focus.cookiebannerexception

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import org.mozilla.focus.R
import org.mozilla.focus.databinding.SwitchWithDescriptionBinding

class CookieBannerExceptionDetailsSwitch @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : ConstraintLayout(context, attrs, defStyleAttr) {

internal var binding: SwitchWithDescriptionBinding

init {
val view =
LayoutInflater.from(context).inflate(R.layout.switch_with_description, this, true)
binding = SwitchWithDescriptionBinding.bind(view)
setTitle()
}

private fun setTitle() {
binding.title.text = context.getString(R.string.cookie_banner_exception_panel_switch_title)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* 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.focus.cookiebannerexception

import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.RectangleShape
import androidx.compose.ui.res.colorResource
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 org.mozilla.focus.R
import org.mozilla.focus.ui.theme.FocusTheme
import org.mozilla.focus.ui.theme.focusColors

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun CookieBannerExceptionItemPreviewHasException() {
FocusTheme {
CookieBannerExceptionItem(true) {}
}
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun CookieBannerExceptionItemPreviewHasNotException() {
FocusTheme {
CookieBannerExceptionItem(false) {}
}
}

/**
* Displays the cookie banner exception item from Tracking Protection panel.
*
* @param hasException if the site hasException
* @param preferenceOnClickListener Callback that will redirect the user to cookie banner item details.
*/
@Composable
fun CookieBannerExceptionItem(hasException: Boolean, preferenceOnClickListener: () -> Unit) {
Row(
modifier = Modifier
.clickable { preferenceOnClickListener() }
.defaultMinSize(minHeight = 48.dp)
.background(
colorResource(R.color.settings_background),
shape = RectangleShape,
),
verticalAlignment = Alignment.CenterVertically,
) {
val painter = if (hasException) {
painterResource(id = R.drawable.ic_cookies_disable)
} else {
painterResource(id = R.drawable.mozac_ic_cookies)
}
Icon(
painter = painter,
contentDescription = null,
tint = focusColors.onPrimary,
modifier = Modifier.padding(end = 20.dp),
)

Column(
modifier = Modifier.weight(1f),
) {
Text(
text = stringResource(R.string.cookie_banner_exception_item_title),
maxLines = 1,
color = focusColors.settingsTextColor,
fontSize = 14.sp,
lineHeight = 20.sp,
)
val summary = if (hasException) {
stringResource(id = R.string.cookie_banner_exception_item_description_state_off)
} else {
stringResource(id = R.string.cookie_banner_exception_item_description_state_on)
}
Text(
text = summary,
maxLines = 1,
color = colorResource(R.color.disabled),
fontSize = 12.sp,
lineHeight = 16.sp,
)
}
Icon(
modifier = Modifier
.padding(end = 0.dp)
.size(24.dp),
tint = focusColors.onPrimary,
painter = painterResource(id = R.drawable.mozac_ic_arrowhead_right),
contentDescription = null,
)
}
}
Loading

0 comments on commit a9a09b1

Please sign in to comment.