Add Tracking Protection Info Panel
parent
9e83edcac5
commit
4485b7f647
@ -0,0 +1,73 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.core.content.res.TypedArrayUtils
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import androidx.preference.PreferenceViewHolder
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.ext.increaseTapArea
|
||||||
|
|
||||||
|
class RadioButtonInfoPreference @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null
|
||||||
|
) : RadioButtonPreference(context, attrs) {
|
||||||
|
private var infoClickListener: (() -> Unit)? = null
|
||||||
|
private var infoView: ImageView? = null
|
||||||
|
|
||||||
|
fun onInfoClickListener(listener: (() -> Unit)) {
|
||||||
|
infoClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setEnabled(enabled: Boolean) {
|
||||||
|
super.setEnabled(enabled)
|
||||||
|
infoView?.alpha = if (enabled) FULL_ALPHA else HALF_ALPHA
|
||||||
|
infoView?.isEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
layoutResource = R.layout.preference_widget_radiobutton_with_info
|
||||||
|
|
||||||
|
context.withStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
androidx.preference.R.styleable.Preference,
|
||||||
|
TypedArrayUtils.getAttr(
|
||||||
|
context,
|
||||||
|
androidx.preference.R.attr.preferenceStyle,
|
||||||
|
android.R.attr.preferenceStyle
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) {
|
||||||
|
val defaultValue = when {
|
||||||
|
hasValue(androidx.preference.R.styleable.Preference_defaultValue) ->
|
||||||
|
getBoolean(androidx.preference.R.styleable.Preference_defaultValue, false)
|
||||||
|
hasValue(androidx.preference.R.styleable.Preference_android_defaultValue) ->
|
||||||
|
getBoolean(
|
||||||
|
androidx.preference.R.styleable.Preference_android_defaultValue,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
setDefaultValue(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||||
|
super.onBindViewHolder(holder)
|
||||||
|
infoView = holder.findViewById(R.id.info_button) as ImageView
|
||||||
|
infoView?.increaseTapArea(EXTRA_TAP_AREA)
|
||||||
|
infoView?.setOnClickListener {
|
||||||
|
infoClickListener?.invoke()
|
||||||
|
}
|
||||||
|
infoView?.alpha = if (isEnabled) FULL_ALPHA else HALF_ALPHA
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_TAP_AREA = 22
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import kotlinx.android.synthetic.main.switch_with_description.view.*
|
||||||
|
import kotlinx.android.synthetic.main.tracking_protection_category.view.switchItemDescription
|
||||||
|
import kotlinx.android.synthetic.main.tracking_protection_category.view.switchItemTitle
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class SwitchWithDescription @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : ConstraintLayout(context, attrs) {
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
switch_widget?.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
resources.getDrawable(
|
||||||
|
id,
|
||||||
|
context.theme
|
||||||
|
), null, null, null
|
||||||
|
)
|
||||||
|
switchItemTitle?.text = resources.getString(
|
||||||
|
getResourceId(
|
||||||
|
R.styleable.SwitchWithDescription_switchTitle,
|
||||||
|
R.string.preference_enhanced_tracking_protection
|
||||||
|
)
|
||||||
|
)
|
||||||
|
switchItemDescription?.text = resources.getString(
|
||||||
|
getResourceId(
|
||||||
|
R.styleable.SwitchWithDescription_switchDescription,
|
||||||
|
R.string.preference_enhanced_tracking_protection_explanation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlinx.android.synthetic.main.fragment_tracking_protection_blocking.*
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class TrackingProtectionBlockingFragment : Fragment() {
|
||||||
|
private val safeArguments get() = requireNotNull(arguments)
|
||||||
|
|
||||||
|
private val isStrict: Boolean by lazy {
|
||||||
|
TrackingProtectionBlockingFragmentArgs.fromBundle(safeArguments).strictMode
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_tracking_protection_blocking, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
if (isStrict) {
|
||||||
|
category_tracking_content.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
category_tracking_content.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
(activity as AppCompatActivity).title =
|
||||||
|
getString(
|
||||||
|
if (isStrict) R.string.preference_enhanced_tracking_protection_strict else
|
||||||
|
R.string.preference_enhanced_tracking_protection_standard
|
||||||
|
)
|
||||||
|
(activity as AppCompatActivity).supportActionBar?.show()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import kotlinx.android.synthetic.main.tracking_protection_category.view.*
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class TrackingProtectionCategoryItem @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||||
|
init {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.tracking_protection_category, this, true)
|
||||||
|
|
||||||
|
context.withStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
R.styleable.TrackingProtectionCategory,
|
||||||
|
defStyleAttr,
|
||||||
|
0
|
||||||
|
) {
|
||||||
|
val id = getResourceId(
|
||||||
|
R.styleable.TrackingProtectionCategory_categoryItemIcon,
|
||||||
|
R.drawable.ic_cryptominers
|
||||||
|
)
|
||||||
|
switchIcon?.background = resources.getDrawable(id, context.theme)
|
||||||
|
switchItemTitle?.text = resources.getString(
|
||||||
|
getResourceId(
|
||||||
|
R.styleable.TrackingProtectionCategory_categoryItemTitle,
|
||||||
|
R.string.etp_cookies_title
|
||||||
|
)
|
||||||
|
)
|
||||||
|
switchItemDescription?.text = resources.getString(
|
||||||
|
getResourceId(
|
||||||
|
R.styleable.TrackingProtectionCategory_categoryItemDescription,
|
||||||
|
R.string.etp_cookies_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.whenStarted
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import kotlinx.android.synthetic.main.fragment_tracking_protection.view.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.concept.engine.content.blocking.Tracker
|
||||||
|
import mozilla.components.lib.state.ext.observe
|
||||||
|
import mozilla.components.support.base.feature.BackHandler
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.components.StoreProvider
|
||||||
|
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import org.mozilla.fenix.ext.nav
|
||||||
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
|
import java.net.MalformedURLException
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHandler {
|
||||||
|
|
||||||
|
private val safeArguments get() = requireNotNull(arguments)
|
||||||
|
|
||||||
|
private val sessionId: String by lazy {
|
||||||
|
TrackingProtectionPanelDialogFragmentArgs.fromBundle(
|
||||||
|
safeArguments
|
||||||
|
).sessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
private val url: String by lazy {
|
||||||
|
TrackingProtectionPanelDialogFragmentArgs.fromBundle(safeArguments).url
|
||||||
|
}
|
||||||
|
|
||||||
|
private val trackingProtectionEnabled: Boolean by lazy {
|
||||||
|
TrackingProtectionPanelDialogFragmentArgs.fromBundle(safeArguments)
|
||||||
|
.trackingProtectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
private val promptGravity: Int by lazy {
|
||||||
|
TrackingProtectionPanelDialogFragmentArgs.fromBundle(
|
||||||
|
safeArguments
|
||||||
|
).gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inflateRootView(container: ViewGroup? = null): View {
|
||||||
|
val contextThemeWrapper = ContextThemeWrapper(
|
||||||
|
activity,
|
||||||
|
(activity as HomeActivity).themeManager.currentThemeResource
|
||||||
|
)
|
||||||
|
return LayoutInflater.from(contextThemeWrapper).inflate(
|
||||||
|
R.layout.fragment_tracking_protection,
|
||||||
|
container,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var trackingProtectionStore: TrackingProtectionStore
|
||||||
|
private lateinit var trackingProtectionView: TrackingProtectionPanelView
|
||||||
|
private lateinit var trackingProtectionInteractor: TrackingProtectionPanelInteractor
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val view = inflateRootView(container)
|
||||||
|
val session = requireComponents.core.sessionManager.findSessionById(sessionId)
|
||||||
|
session?.register(sessionObserver, view = view)
|
||||||
|
trackingProtectionStore = StoreProvider.get(this) {
|
||||||
|
TrackingProtectionStore(
|
||||||
|
TrackingProtectionState(
|
||||||
|
url,
|
||||||
|
trackingProtectionEnabled,
|
||||||
|
session?.trackersBlocked ?: listOf(),
|
||||||
|
session?.trackersLoaded ?: listOf(),
|
||||||
|
TrackingProtectionState.Mode.Normal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
trackingProtectionInteractor = TrackingProtectionPanelInteractor(
|
||||||
|
trackingProtectionStore,
|
||||||
|
::toggleTrackingProtection,
|
||||||
|
::openTrackingProtectionSettings
|
||||||
|
)
|
||||||
|
trackingProtectionView =
|
||||||
|
TrackingProtectionPanelView(view.fragment_tp, trackingProtectionInteractor)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sessionObserver = object : Session.Observer {
|
||||||
|
override fun onUrlChanged(session: Session, url: String) {
|
||||||
|
trackingProtectionStore.dispatch(
|
||||||
|
TrackingProtectionAction.UrlChange(url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTrackerBlocked(session: Session, tracker: Tracker, all: List<Tracker>) {
|
||||||
|
trackingProtectionStore.dispatch(
|
||||||
|
TrackingProtectionAction.TrackerListChange(all)
|
||||||
|
)
|
||||||
|
trackingProtectionStore.dispatch(
|
||||||
|
TrackingProtectionAction.TrackerLoadedListChange(session.trackersLoaded)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTrackerLoaded(session: Session, tracker: Tracker, all: List<Tracker>) {
|
||||||
|
trackingProtectionStore.dispatch(
|
||||||
|
TrackingProtectionAction.TrackerListChange(session.trackersBlocked)
|
||||||
|
)
|
||||||
|
trackingProtectionStore.dispatch(
|
||||||
|
TrackingProtectionAction.TrackerLoadedListChange(all)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
trackingProtectionStore.observe(view) {
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
whenStarted {
|
||||||
|
trackingProtectionView.update(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openTrackingProtectionSettings() {
|
||||||
|
nav(
|
||||||
|
R.id.trackingProtectionPanelDialogFragment,
|
||||||
|
TrackingProtectionPanelDialogFragmentDirections
|
||||||
|
.actionTrackingProtectionPanelDialogFragmentToTrackingProtectionFragment()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleTrackingProtection(isEnabled: Boolean) {
|
||||||
|
context?.let {
|
||||||
|
val host = try {
|
||||||
|
URL(url).host
|
||||||
|
} catch (e: MalformedURLException) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (!ExceptionDomains.load(it).contains(host)) {
|
||||||
|
ExceptionDomains.add(it, host)
|
||||||
|
} else {
|
||||||
|
ExceptionDomains.remove(it, listOf(host))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.components.useCases.sessionUseCases.reload.invoke()
|
||||||
|
}
|
||||||
|
trackingProtectionStore.dispatch(TrackingProtectionAction.TrackerBlockingChanged(isEnabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return if (promptGravity == Gravity.BOTTOM) {
|
||||||
|
object : BottomSheetDialog(requireContext(), this.theme) {
|
||||||
|
override fun onBackPressed() {
|
||||||
|
this@TrackingProtectionPanelDialogFragment.onBackPressed()
|
||||||
|
}
|
||||||
|
}.apply {
|
||||||
|
setOnShowListener {
|
||||||
|
val bottomSheet =
|
||||||
|
findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as? FrameLayout
|
||||||
|
val behavior = BottomSheetBehavior.from(bottomSheet)
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
object : Dialog(requireContext()) {
|
||||||
|
override fun onBackPressed() {
|
||||||
|
this@TrackingProtectionPanelDialogFragment.onBackPressed()
|
||||||
|
}
|
||||||
|
}.applyCustomizationsForTopDialog(inflateRootView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Dialog.applyCustomizationsForTopDialog(rootView: View): Dialog {
|
||||||
|
addContentView(
|
||||||
|
rootView,
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
window?.apply {
|
||||||
|
setGravity(promptGravity)
|
||||||
|
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
// This must be called after addContentView, or it won't fully fill to the edge.
|
||||||
|
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean = trackingProtectionView.onBackPressed()
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactor for the tracking protection panel
|
||||||
|
* Provides implementations for the TrackingProtectionPanelViewInteractor
|
||||||
|
*/
|
||||||
|
class TrackingProtectionPanelInteractor(
|
||||||
|
private val store: TrackingProtectionStore,
|
||||||
|
private val toggleTrackingProtection: (Boolean) -> Unit,
|
||||||
|
private val openTrackingProtectionSettings: () -> Unit
|
||||||
|
) : TrackingProtectionPanelViewInteractor {
|
||||||
|
override fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean) {
|
||||||
|
store.dispatch(TrackingProtectionAction.EnterDetailsMode(category, categoryBlocked))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun selectTrackingProtectionSettings() {
|
||||||
|
openTrackingProtectionSettings.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trackingProtectionToggled(isEnabled: Boolean) {
|
||||||
|
toggleTrackingProtection.invoke(isEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
store.dispatch(TrackingProtectionAction.ExitDetailsMode)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,265 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import kotlinx.android.synthetic.main.component_tracking_protection_panel.*
|
||||||
|
import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.url
|
||||||
|
import kotlinx.android.synthetic.main.switch_with_description.view.*
|
||||||
|
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING
|
||||||
|
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING
|
||||||
|
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL
|
||||||
|
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.AD
|
||||||
|
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS
|
||||||
|
import mozilla.components.concept.engine.content.blocking.Tracker
|
||||||
|
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.ext.getHostFromUrl
|
||||||
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CROSS_SITE_TRACKING_COOKIES
|
||||||
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CRYPTOMINERS
|
||||||
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.FINGERPRINTERS
|
||||||
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.SOCIAL_MEDIA_TRACKERS
|
||||||
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.TRACKING_CONTENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the TrackingProtectionPanelViewInteractor. This interface is implemented by objects that want
|
||||||
|
* to respond to user interaction on the TrackingProtectionPanelView
|
||||||
|
*/
|
||||||
|
interface TrackingProtectionPanelViewInteractor {
|
||||||
|
/**
|
||||||
|
* Called whenever the settings option is tapped
|
||||||
|
*/
|
||||||
|
fun selectTrackingProtectionSettings()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever the tracking protection toggle for this site is toggled
|
||||||
|
* @param isEnabled new status of session tracking protection
|
||||||
|
*/
|
||||||
|
fun trackingProtectionToggled(isEnabled: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever back is pressed
|
||||||
|
*/
|
||||||
|
fun onBackPressed()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever an active tracking protection category is tapped
|
||||||
|
* @param category The Tracking Protection Category to view details about
|
||||||
|
* @param categoryBlocked The trackers from this category were blocked
|
||||||
|
*/
|
||||||
|
fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View that contains and configures the Tracking Protection Panel
|
||||||
|
*/
|
||||||
|
class TrackingProtectionPanelView(
|
||||||
|
override val containerView: ViewGroup,
|
||||||
|
val interactor: TrackingProtectionPanelInteractor
|
||||||
|
) : LayoutContainer {
|
||||||
|
val view: ConstraintLayout = LayoutInflater.from(containerView.context)
|
||||||
|
.inflate(R.layout.component_tracking_protection_panel, containerView, true)
|
||||||
|
.findViewById(R.id.panel_wrapper)
|
||||||
|
|
||||||
|
private val context get() = view.context
|
||||||
|
|
||||||
|
var mode: TrackingProtectionState.Mode = TrackingProtectionState.Mode.Normal
|
||||||
|
private set
|
||||||
|
|
||||||
|
var trackers: List<Tracker> = listOf()
|
||||||
|
private set
|
||||||
|
|
||||||
|
var bucketedTrackers: HashMap<TrackingProtectionCategory, List<String>> = HashMap()
|
||||||
|
|
||||||
|
var loadedTrackers: List<Tracker> = listOf()
|
||||||
|
private set
|
||||||
|
|
||||||
|
var bucketedLoadedTrackers: HashMap<TrackingProtectionCategory, List<String>> = HashMap()
|
||||||
|
|
||||||
|
fun update(state: TrackingProtectionState) {
|
||||||
|
if (state.mode != mode) {
|
||||||
|
mode = state.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.listTrackers != trackers) {
|
||||||
|
trackers = state.listTrackers
|
||||||
|
bucketedTrackers = getHashMapOfTrackersForCategory(state.listTrackers)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.listTrackersLoaded != loadedTrackers) {
|
||||||
|
loadedTrackers = state.listTrackersLoaded
|
||||||
|
bucketedLoadedTrackers = getHashMapOfTrackersForCategory(state.listTrackersLoaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (val mode = state.mode) {
|
||||||
|
is TrackingProtectionState.Mode.Normal -> setUIForNormalMode(state)
|
||||||
|
is TrackingProtectionState.Mode.Details -> setUIForDetailsMode(
|
||||||
|
mode.selectedCategory,
|
||||||
|
mode.categoryBlocked
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUIForNormalMode(state: TrackingProtectionState) {
|
||||||
|
details_mode.visibility = View.GONE
|
||||||
|
normal_mode.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
not_blocking_header.visibility =
|
||||||
|
if (bucketedLoadedTrackers.size == 0) View.GONE else View.VISIBLE
|
||||||
|
bindUrl(state.url)
|
||||||
|
bindTrackingProtectionInfo(state.isTrackingProtectionEnabled)
|
||||||
|
protection_settings.setOnClickListener {
|
||||||
|
interactor.selectTrackingProtectionSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
blocking_header.visibility =
|
||||||
|
if (bucketedTrackers.size == 0) View.GONE else View.VISIBLE
|
||||||
|
updateCategoryVisibility()
|
||||||
|
setCategoryClickListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ComplexMethod")
|
||||||
|
private fun updateCategoryVisibility() {
|
||||||
|
cross_site_tracking.visibility = bucketedTrackers.getVisibility(CROSS_SITE_TRACKING_COOKIES)
|
||||||
|
social_media_trackers.visibility = bucketedTrackers.getVisibility(SOCIAL_MEDIA_TRACKERS)
|
||||||
|
fingerprinters.visibility = bucketedTrackers.getVisibility(FINGERPRINTERS)
|
||||||
|
tracking_content.visibility = bucketedTrackers.getVisibility(TRACKING_CONTENT)
|
||||||
|
cryptominers.visibility = bucketedTrackers.getVisibility(CRYPTOMINERS)
|
||||||
|
|
||||||
|
cross_site_tracking_loaded.visibility =
|
||||||
|
bucketedLoadedTrackers.getVisibility(CROSS_SITE_TRACKING_COOKIES)
|
||||||
|
social_media_trackers_loaded.visibility =
|
||||||
|
bucketedLoadedTrackers.getVisibility(SOCIAL_MEDIA_TRACKERS)
|
||||||
|
fingerprinters_loaded.visibility = bucketedLoadedTrackers.getVisibility(FINGERPRINTERS)
|
||||||
|
tracking_content_loaded.visibility = bucketedLoadedTrackers.getVisibility(TRACKING_CONTENT)
|
||||||
|
cryptominers_loaded.visibility = bucketedLoadedTrackers.getVisibility(CRYPTOMINERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun HashMap<TrackingProtectionCategory, List<String>>.getVisibility(
|
||||||
|
category: TrackingProtectionCategory
|
||||||
|
): Int = if (this[category]?.isNotEmpty() == true) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
private fun setCategoryClickListeners() {
|
||||||
|
social_media_trackers.setOnClickListener {
|
||||||
|
interactor.openDetails(SOCIAL_MEDIA_TRACKERS, categoryBlocked = true)
|
||||||
|
}
|
||||||
|
fingerprinters.setOnClickListener {
|
||||||
|
interactor.openDetails(FINGERPRINTERS, categoryBlocked = true)
|
||||||
|
}
|
||||||
|
cross_site_tracking.setOnClickListener {
|
||||||
|
interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = true)
|
||||||
|
}
|
||||||
|
tracking_content.setOnClickListener {
|
||||||
|
interactor.openDetails(TRACKING_CONTENT, categoryBlocked = true)
|
||||||
|
}
|
||||||
|
cryptominers.setOnClickListener {
|
||||||
|
interactor.openDetails(CRYPTOMINERS, categoryBlocked = true)
|
||||||
|
}
|
||||||
|
social_media_trackers_loaded.setOnClickListener {
|
||||||
|
interactor.openDetails(SOCIAL_MEDIA_TRACKERS, categoryBlocked = false)
|
||||||
|
}
|
||||||
|
fingerprinters_loaded.setOnClickListener {
|
||||||
|
interactor.openDetails(FINGERPRINTERS, categoryBlocked = false)
|
||||||
|
}
|
||||||
|
cross_site_tracking_loaded.setOnClickListener {
|
||||||
|
interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = false)
|
||||||
|
}
|
||||||
|
tracking_content_loaded.setOnClickListener {
|
||||||
|
interactor.openDetails(TRACKING_CONTENT, categoryBlocked = false)
|
||||||
|
}
|
||||||
|
cryptominers_loaded.setOnClickListener {
|
||||||
|
interactor.openDetails(CRYPTOMINERS, categoryBlocked = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUIForDetailsMode(
|
||||||
|
category: TrackingProtectionCategory,
|
||||||
|
categoryBlocked: Boolean
|
||||||
|
) {
|
||||||
|
normal_mode.visibility = View.GONE
|
||||||
|
details_mode.visibility = View.VISIBLE
|
||||||
|
category_title.text = context.getString(category.title)
|
||||||
|
val stringList = bucketedTrackers[category]?.joinToString("\n")
|
||||||
|
blocking_text_list.text = stringList
|
||||||
|
category_description.text = context.getString(category.description)
|
||||||
|
details_blocking_header.text =
|
||||||
|
context.getString(
|
||||||
|
if (categoryBlocked) R.string.enhanced_tracking_protection_blocked else
|
||||||
|
R.string.enhanced_tracking_protection_allowed
|
||||||
|
)
|
||||||
|
details_back.setOnClickListener {
|
||||||
|
interactor.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHashMapOfTrackersForCategory(
|
||||||
|
list: List<Tracker>
|
||||||
|
): HashMap<TrackingProtectionCategory, List<String>> {
|
||||||
|
val hashMap = HashMap<TrackingProtectionCategory, List<String>>()
|
||||||
|
items@ for (item in list) {
|
||||||
|
when {
|
||||||
|
item.trackingCategories.contains(CRYPTOMINING) -> {
|
||||||
|
hashMap[CRYPTOMINERS] =
|
||||||
|
(hashMap[CRYPTOMINERS]
|
||||||
|
?: listOf()).plus(item.url.getHostFromUrl() ?: item.url)
|
||||||
|
continue@items
|
||||||
|
}
|
||||||
|
item.trackingCategories.contains(FINGERPRINTING) -> {
|
||||||
|
hashMap[FINGERPRINTERS] =
|
||||||
|
(hashMap[FINGERPRINTERS]
|
||||||
|
?: listOf()).plus(item.url.getHostFromUrl() ?: item.url)
|
||||||
|
continue@items
|
||||||
|
}
|
||||||
|
item.trackingCategories.contains(SOCIAL) -> {
|
||||||
|
hashMap[SOCIAL_MEDIA_TRACKERS] =
|
||||||
|
(hashMap[SOCIAL_MEDIA_TRACKERS] ?: listOf()).plus(
|
||||||
|
item.url.getHostFromUrl() ?: item.url
|
||||||
|
)
|
||||||
|
continue@items
|
||||||
|
}
|
||||||
|
item.trackingCategories.contains(AD) ||
|
||||||
|
item.trackingCategories.contains(SOCIAL) ||
|
||||||
|
item.trackingCategories.contains(ANALYTICS) -> {
|
||||||
|
hashMap[TRACKING_CONTENT] =
|
||||||
|
(hashMap[TRACKING_CONTENT] ?: listOf()).plus(
|
||||||
|
item.url.getHostFromUrl() ?: item.url
|
||||||
|
)
|
||||||
|
continue@items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hashMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindUrl(url: String) {
|
||||||
|
this.url.text = url.toUri().hostWithoutCommonPrefixes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) {
|
||||||
|
tracking_protection.switchItemDescription.text =
|
||||||
|
context.getString(if (isTrackingProtectionOn) R.string.etp_panel_on else R.string.etp_panel_off)
|
||||||
|
tracking_protection.switch_widget.isChecked = isTrackingProtectionOn
|
||||||
|
|
||||||
|
tracking_protection.switch_widget.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
interactor.trackingProtectionToggled(isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBackPressed(): Boolean {
|
||||||
|
return when (mode) {
|
||||||
|
is TrackingProtectionState.Mode.Details -> {
|
||||||
|
mode = TrackingProtectionState.Mode.Normal
|
||||||
|
interactor.onBackPressed()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import mozilla.components.concept.engine.content.blocking.Tracker
|
||||||
|
import mozilla.components.lib.state.Action
|
||||||
|
import mozilla.components.lib.state.State
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Store] for holding the [TrackingProtectionState] and applying [TrackingProtectionAction]s.
|
||||||
|
*/
|
||||||
|
class TrackingProtectionStore(initialState: TrackingProtectionState) :
|
||||||
|
Store<TrackingProtectionState, TrackingProtectionAction>(
|
||||||
|
initialState,
|
||||||
|
::trackingProtectionStateReducer
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions to dispatch through the `TrackingProtectionStore` to modify `TrackingProtectionState` through the reducer.
|
||||||
|
*/
|
||||||
|
sealed class TrackingProtectionAction : Action {
|
||||||
|
data class Change(
|
||||||
|
val url: String,
|
||||||
|
val isTrackingProtectionEnabled: Boolean,
|
||||||
|
val listTrackers: List<Tracker>,
|
||||||
|
val listTrackersLoaded: List<Tracker>,
|
||||||
|
val mode: TrackingProtectionState.Mode
|
||||||
|
) : TrackingProtectionAction()
|
||||||
|
|
||||||
|
data class UrlChange(val url: String) : TrackingProtectionAction()
|
||||||
|
data class TrackerListChange(val listTrackers: List<Tracker>) : TrackingProtectionAction()
|
||||||
|
data class TrackerLoadedListChange(val listTrackersLoaded: List<Tracker>) :
|
||||||
|
TrackingProtectionAction()
|
||||||
|
|
||||||
|
data class TrackerBlockingChanged(val isTrackingProtectionEnabled: Boolean) :
|
||||||
|
TrackingProtectionAction()
|
||||||
|
|
||||||
|
object ExitDetailsMode : TrackingProtectionAction()
|
||||||
|
data class EnterDetailsMode(
|
||||||
|
val category: TrackingProtectionCategory,
|
||||||
|
val categoryBlocked: Boolean
|
||||||
|
) :
|
||||||
|
TrackingProtectionAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state for the Tracking Protection Panel
|
||||||
|
* @property url Current URL to display
|
||||||
|
* @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception)
|
||||||
|
* @property listTrackers List of currently blocked Trackers
|
||||||
|
* @property listTrackersLoaded List of currently not blocked Trackers
|
||||||
|
* @property mode Current Mode of TrackingProtection
|
||||||
|
*/
|
||||||
|
data class TrackingProtectionState(
|
||||||
|
val url: String,
|
||||||
|
val isTrackingProtectionEnabled: Boolean,
|
||||||
|
val listTrackers: List<Tracker>,
|
||||||
|
val listTrackersLoaded: List<Tracker>,
|
||||||
|
val mode: Mode
|
||||||
|
) : State {
|
||||||
|
sealed class Mode {
|
||||||
|
object Normal : Mode()
|
||||||
|
data class Details(
|
||||||
|
val selectedCategory: TrackingProtectionCategory,
|
||||||
|
val categoryBlocked: Boolean
|
||||||
|
) : Mode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 5 categories of Tracking Protection to display
|
||||||
|
*/
|
||||||
|
enum class TrackingProtectionCategory(val title: Int, val description: Int) {
|
||||||
|
SOCIAL_MEDIA_TRACKERS(
|
||||||
|
R.string.etp_social_media_trackers_title,
|
||||||
|
R.string.etp_social_media_trackers_description
|
||||||
|
),
|
||||||
|
CROSS_SITE_TRACKING_COOKIES(
|
||||||
|
R.string.etp_cookies_title,
|
||||||
|
R.string.etp_cookies_description
|
||||||
|
),
|
||||||
|
CRYPTOMINERS(
|
||||||
|
R.string.etp_cryptominers_title,
|
||||||
|
R.string.etp_cryptominers_description
|
||||||
|
),
|
||||||
|
FINGERPRINTERS(R.string.etp_fingerprinters_title, R.string.etp_fingerprinters_description),
|
||||||
|
TRACKING_CONTENT(R.string.etp_tracking_content_title, R.string.etp_tracking_content_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TrackingProtectionState Reducer.
|
||||||
|
*/
|
||||||
|
fun trackingProtectionStateReducer(
|
||||||
|
state: TrackingProtectionState,
|
||||||
|
action: TrackingProtectionAction
|
||||||
|
): TrackingProtectionState {
|
||||||
|
return when (action) {
|
||||||
|
is TrackingProtectionAction.Change -> state.copy(
|
||||||
|
url = action.url,
|
||||||
|
isTrackingProtectionEnabled = action.isTrackingProtectionEnabled,
|
||||||
|
listTrackers = action.listTrackers,
|
||||||
|
mode = action.mode
|
||||||
|
)
|
||||||
|
is TrackingProtectionAction.UrlChange -> state.copy(
|
||||||
|
url = action.url
|
||||||
|
)
|
||||||
|
is TrackingProtectionAction.TrackerListChange -> state.copy(
|
||||||
|
listTrackers = action.listTrackers
|
||||||
|
)
|
||||||
|
is TrackingProtectionAction.TrackerLoadedListChange -> state.copy(
|
||||||
|
listTrackersLoaded = action.listTrackersLoaded
|
||||||
|
)
|
||||||
|
TrackingProtectionAction.ExitDetailsMode -> state.copy(
|
||||||
|
mode = TrackingProtectionState.Mode.Normal
|
||||||
|
)
|
||||||
|
is TrackingProtectionAction.EnterDetailsMode -> state.copy(
|
||||||
|
mode = TrackingProtectionState.Mode.Details(
|
||||||
|
action.category,
|
||||||
|
action.categoryBlocked
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is TrackingProtectionAction.TrackerBlockingChanged ->
|
||||||
|
state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="7"
|
||||||
|
android:viewportHeight="13">
|
||||||
|
<path
|
||||||
|
android:fillColor="?primaryText"
|
||||||
|
android:pathData="M0.9998,12.4877C0.5954,12.4877 0.2309,12.244 0.0761,11.8704C-0.0786,11.4967 0.0069,11.0667 0.2928,10.7807L4.5858,6.4877L0.2928,2.1947C-0.0862,1.8023 -0.0807,1.1786 0.305,0.7929C0.6907,0.4072 1.3144,0.4017 1.7068,0.7807L6.7068,5.7807C7.0972,6.1712 7.0972,6.8042 6.7068,7.1947L1.7068,12.1947C1.5193,12.3823 1.265,12.4877 0.9998,12.4877Z" />
|
||||||
|
</vector>
|
@ -0,0 +1,13 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?primaryText"
|
||||||
|
android:pathData="M21.3,12c-0.8,0 -1.6,-0.1 -2.4,-0.5l0.1,0.5c0,0.5 -0.5,1 -1,1a1,1 0,0 1,-1 -1c0,-0.5 0.3,-0.7 0.4,-0.8l0.3,-0.2C14,10 12,5 12,2.7c0,-1.4 -4,-0.6 -3.6,0.7 -0.5,-1.3 -4,1 -3,2 -1,-1 -3.3,2.5 -2,3 -1.3,-0.5 -2,3.6 -0.7,3.6 -1.4,0 -0.6,4 0.7,3.6 -1.3,0.5 1,4 2,3 -1,1 2.5,3.3 3,2 -0.5,1.3 3.6,2 3.6,0.7 0,1.4 4,0.6 3.5,-0.7 0.6,1.3 4,-1 3,-2 1,1 3.4,-2.5 2,-3 1.4,0.5 2.2,-3.6 0.8,-3.6zM7,12c0,0.5 -0.5,1 -1,1a1,1 0,0 1,-1 -1c0,-0.5 0.5,-1 1,-1s1,0.5 1,1zM9,18a1,1 0,0 1,-1 -1c0,-0.5 0.5,-1 1,-1s1,0.5 1,1 -0.5,1 -1,1zM9,8a1,1 0,0 1,-1 -1c0,-0.6 0.4,-1 1,-1s1,0.4 1,1 -0.4,1 -1,1zM12,13a1,1 0,0 1,-1 -1c0,-0.5 0.5,-1 1,-1s1,0.5 1,1 -0.5,1 -1,1zM15,18a1,1 0,0 1,-1 -1c0,-0.5 0.5,-1 1,-1s1,0.5 1,1 -0.5,1 -1,1z" />
|
||||||
|
</vector>
|
@ -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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?primaryText"
|
||||||
|
android:pathData="M11,4V3a1,1 0,0 1,2 0v1h6v5h-6v12a1,1 0,0 1,-2 0V9H5.4C4.2,9 3.6,7.8 4.3,7l1.6,-2c0.5,-0.6 1.3,-1 2.2,-1H11z"
|
||||||
|
android:strokeLineJoin="round" />
|
||||||
|
</vector>
|
@ -0,0 +1,163 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="1889dp"
|
||||||
|
android:height="1051dp"
|
||||||
|
android:viewportWidth="1889"
|
||||||
|
android:viewportHeight="1051">
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M0,0h1889.2v1054.8h-1889.2z M 0,0" />
|
||||||
|
<path android:pathData="M1958.6,1531.8H-396.8c-1.1,0 -2,-0.7 -2,-1.6v-1748c0,-0.9 0.9,-1.6 2,-1.6h2355.3c1.1,0 2.1,0.7 2.1,1.6v1748C1960.6,1531 1959.7,1531.8 1958.6,1531.8z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="1808.0254"
|
||||||
|
android:endY="-370.9758"
|
||||||
|
android:startX="-246.2341"
|
||||||
|
android:startY="1683.2838"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#FF0250BB"
|
||||||
|
android:offset="0.6075" />
|
||||||
|
<item
|
||||||
|
android:color="#FF1F52C9"
|
||||||
|
android:offset="0.6818" />
|
||||||
|
<item
|
||||||
|
android:color="#FF7057F0"
|
||||||
|
android:offset="0.9" />
|
||||||
|
<item
|
||||||
|
android:color="#FF9059FF"
|
||||||
|
android:offset="1" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:pathData="M1255.1,437.8c0,35.9 -29.1,65.4 -65.1,65.4H928.4c-27.4,0 -49.7,-22.6 -49.7,-50c0,-27.4 22.2,-50 49.7,-50c4.1,0 8.1,0.6 12,1.5c-0.9,-3.7 -1.4,-7.6 -1.4,-11.6c0,-28.5 23.1,-51.5 51.5,-51.5c10.8,0 20.8,3.3 29.1,9c9.7,-37.3 43.5,-64.8 83.8,-64.8c47.8,0 86.6,38.8 86.6,86.6C1226,372.4 1255.1,401.9 1255.1,437.8zM2046.6,286.4c-6.2,0 -12.2,0.8 -18,2.2c1.3,-5.6 2,-11.4 2,-17.4c0,-42.9 -34.8,-77.7 -77.7,-77.7c-16.3,0 -31.4,5 -43.9,13.6c-14.6,-56.1 -65.6,-97.6 -126.3,-97.6c-72.1,0 -130.5,58.4 -130.5,130.5c-54.2,0 -98.1,44 -98.1,98.2s43.9,98.2 98.1,98.2h394.3c41.3,0 74.9,-33.6 74.9,-75C2121.5,320 2087.9,286.4 2046.6,286.4z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="1450.4999"
|
||||||
|
android:endY="440.826"
|
||||||
|
android:startX="702.678"
|
||||||
|
android:startY="981.0675"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#FF0250BB"
|
||||||
|
android:offset="0" />
|
||||||
|
<item
|
||||||
|
android:color="#FF0850BE"
|
||||||
|
android:offset="0.137" />
|
||||||
|
<item
|
||||||
|
android:color="#FF1851C6"
|
||||||
|
android:offset="0.3157" />
|
||||||
|
<item
|
||||||
|
android:color="#FF3353D2"
|
||||||
|
android:offset="0.5175" />
|
||||||
|
<item
|
||||||
|
android:color="#FF5955E4"
|
||||||
|
android:offset="0.7363" />
|
||||||
|
<item
|
||||||
|
android:color="#FF8859FB"
|
||||||
|
android:offset="0.9663" />
|
||||||
|
<item
|
||||||
|
android:color="#FF9059FF"
|
||||||
|
android:offset="1" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:pathData="M1510.9,464.7m-512.3,0a512.3,512.3 0,1 1,1024.6 0a512.3,512.3 0,1 1,-1024.6 0">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="1889.9442"
|
||||||
|
android:endY="85.6326"
|
||||||
|
android:startX="731.7512"
|
||||||
|
android:startY="1243.8254"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#FF0250BB"
|
||||||
|
android:offset="0" />
|
||||||
|
<item
|
||||||
|
android:color="#FF0850BE"
|
||||||
|
android:offset="0.137" />
|
||||||
|
<item
|
||||||
|
android:color="#FF1851C6"
|
||||||
|
android:offset="0.3157" />
|
||||||
|
<item
|
||||||
|
android:color="#FF3353D2"
|
||||||
|
android:offset="0.5175" />
|
||||||
|
<item
|
||||||
|
android:color="#FF5955E4"
|
||||||
|
android:offset="0.7363" />
|
||||||
|
<item
|
||||||
|
android:color="#FF8859FB"
|
||||||
|
android:offset="0.9663" />
|
||||||
|
<item
|
||||||
|
android:color="#FF9059FF"
|
||||||
|
android:offset="1" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M1510.9,464.7m-512.3,0a512.3,512.3 0,1 1,1024.6 0a512.3,512.3 0,1 1,-1024.6 0 M 0,0" />
|
||||||
|
<path android:pathData="M1256.1,440.2c0,35.9 -29.1,65.4 -65.1,65.4H929.4c-27.4,0 -49.7,-22.6 -49.7,-50s22.2,-50 49.7,-50c4.1,0 8.1,0.6 12,1.5c-0.9,-3.7 -1.4,-7.6 -1.4,-11.6c0,-28.5 23.1,-51.5 51.5,-51.5c10.8,0 20.8,3.3 29.1,9c9.7,-37.3 43.5,-64.8 83.8,-64.8c47.8,0 86.6,38.8 86.6,86.6C1227,374.8 1256.1,404.3 1256.1,440.2zM2047.6,288.8c-6.2,0 -12.2,0.8 -18,2.2c1.3,-5.6 2,-11.4 2,-17.4c0,-42.9 -34.8,-77.7 -77.7,-77.7c-16.3,0 -31.4,5 -43.9,13.6c-14.6,-56.1 -65.6,-97.6 -126.3,-97.6c-72.1,0 -130.5,58.4 -130.5,130.5c-54.2,0 -98.1,44 -98.1,98.2s43.9,98.2 98.1,98.2h394.3c41.3,0 74.9,-33.6 74.9,-75C2122.5,322.4 2089,288.8 2047.6,288.8z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="1920.3588"
|
||||||
|
android:endY="-4.6193"
|
||||||
|
android:startX="748.0001"
|
||||||
|
android:startY="1167.739"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#FF0090ED"
|
||||||
|
android:offset="0" />
|
||||||
|
<item
|
||||||
|
android:color="#FF2183F1"
|
||||||
|
android:offset="0.1231" />
|
||||||
|
<item
|
||||||
|
android:color="#FF4974F6"
|
||||||
|
android:offset="0.2957" />
|
||||||
|
<item
|
||||||
|
android:color="#FF6868FA"
|
||||||
|
android:offset="0.466" />
|
||||||
|
<item
|
||||||
|
android:color="#FF7E60FD"
|
||||||
|
android:offset="0.631" />
|
||||||
|
<item
|
||||||
|
android:color="#FF8B5BFE"
|
||||||
|
android:offset="0.7884" />
|
||||||
|
<item
|
||||||
|
android:color="#FF9059FF"
|
||||||
|
android:offset="0.9298" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</group>
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1386.8,915.2c1.2,-1.5 2,-2.9 2.3,-4.3c2.2,-5.8 1.7,-13.1 -3.1,-18.4c-18.7,-20.5 -38.6,-49.6 -57.6,-80.7c8,1.9 15.9,3.3 23.3,4.2c11.1,1.4 17.6,-14.4 9.4,-23.4c-20.8,-22.9 -43.5,-58.5 -63.6,-93.8c11.2,4 22.6,6.3 32.9,7.6c8.8,1.1 13.9,-11.4 7.4,-18.5c-27.6,-30.3 -59.3,-89 -78.7,-127.4c-0.9,-3 -2.9,-5.1 -5.2,-6.2c-1.1,-0.5 -2.3,-0.8 -3.4,-0.8c-1.2,0 -2.3,0.2 -3.4,0.8c-2.3,1.1 -4.3,3.2 -5.2,6.2c-19.3,38.4 -51,97.1 -78.7,127.4c-6.4,7.1 -1.3,19.6 7.4,18.5c10.3,-1.3 21.8,-3.6 32.9,-7.6c-20.1,35.3 -42.8,70.9 -63.6,93.8c-8.1,8.9 -1.7,24.8 9.4,23.4c7.4,-0.9 15.3,-2.3 23.3,-4.2c-19,31.1 -38.9,60.2 -57.6,80.7c-7.4,8.2 -4.7,21.2 2.9,26.2c32.8,34.2 74.7,36.9 133.2,36.9c60.1,0 107.8,-9.9 133.8,-38.4C1385.6,916.7 1386.2,916 1386.8,915.2z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1977.1,936.2c1.2,-1.5 2,-2.9 2.3,-4.3c2.2,-5.8 1.7,-13.1 -3.1,-18.4c-18.7,-20.5 -38.6,-49.6 -57.6,-80.7c8,1.9 15.9,3.3 23.3,4.2c11.1,1.4 17.6,-14.4 9.4,-23.4c-20.8,-22.9 -43.5,-58.5 -63.6,-93.8c11.1,4 22.6,6.3 32.9,7.6c8.8,1.1 13.9,-11.4 7.4,-18.5c-27.6,-30.3 -59.3,-89 -78.6,-127.4c-0.9,-3 -2.9,-5.1 -5.2,-6.2c-1.1,-0.5 -2.3,-0.8 -3.4,-0.8c-1.2,0 -2.3,0.2 -3.4,0.8c-2.3,1.1 -4.3,3.2 -5.2,6.2c-19.3,38.4 -51,97.1 -78.6,127.4c-6.4,7.1 -1.3,19.6 7.4,18.5c10.3,-1.3 21.8,-3.6 32.9,-7.6c-20.1,35.3 -42.8,70.9 -63.6,93.8c-8.1,8.9 -1.7,24.8 9.4,23.4c7.4,-0.9 15.3,-2.3 23.3,-4.2c-19,31.1 -38.9,60.2 -57.6,80.7c-7.4,8.2 -4.7,21.2 2.9,26.2c32.8,34.2 74.7,36.9 133.2,36.9c60.1,0 107.8,-9.9 133.8,-38.4C1976,937.6 1976.6,936.9 1977.1,936.2z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M2080.8,1359.7c1.8,-2.1 2.9,-4.1 3.3,-6.1c3.2,-8.3 2.4,-18.7 -4.5,-26.2c-26.6,-29.2 -55,-70.7 -82.1,-115c11.4,2.7 22.6,4.7 33.2,6c15.8,2 25,-20.6 13.4,-33.3c-29.7,-32.6 -61.9,-83.3 -90.6,-133.6c15.9,5.7 32.2,9 46.9,10.9c12.5,1.6 19.8,-16.2 10.6,-26.3c-39.4,-43.2 -84.5,-126.8 -112,-181.5c-1.3,-4.2 -4.1,-7.3 -7.4,-8.8c-1.6,-0.7 -3.2,-1.1 -4.9,-1.1c-1.6,0 -3.3,0.3 -4.9,1.1c-3.3,1.5 -6.1,4.6 -7.4,8.8c-27.5,54.8 -72.7,138.3 -112,181.5c-9.2,10.1 -1.9,27.9 10.6,26.3c14.7,-1.9 31,-5.2 46.9,-10.9c-28.6,50.3 -60.9,101.1 -90.6,133.6c-11.6,12.7 -2.4,35.3 13.4,33.3c10.6,-1.3 21.8,-3.3 33.2,-6c-27,44.3 -55.4,85.7 -82.1,115c-10.6,11.7 -6.7,30.1 4.1,37.3c46.7,48.8 106.4,52.5 189.7,52.5c85.5,0 153.6,-14.1 190.5,-54.6C2079.2,1361.7 2080,1360.7 2080.8,1359.7z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1653,1019.3c1.5,-1.8 2.5,-3.6 2.9,-5.3c2.8,-7.2 2.1,-16.2 -3.9,-22.7c-23.1,-25.3 -47.7,-61.2 -71.1,-99.6c9.9,2.4 19.6,4 28.8,5.2c13.7,1.7 21.7,-17.8 11.6,-28.8c-25.7,-28.2 -53.7,-72.2 -78.5,-115.8c13.8,4.9 27.9,7.8 40.7,9.4c10.8,1.4 17.1,-14.1 9.2,-22.8c-34.1,-37.4 -73.2,-109.8 -97.1,-157.3c-1.1,-3.7 -3.6,-6.3 -6.4,-7.6c-1.4,-0.7 -2.8,-1 -4.2,-1c-1.4,0 -2.9,0.3 -4.2,1c-2.9,1.3 -5.3,4 -6.4,7.6c-23.8,47.4 -63,119.9 -97.1,157.3c-7.9,8.7 -1.6,24.2 9.2,22.8c12.8,-1.6 26.9,-4.5 40.7,-9.4c-24.8,43.6 -52.8,87.5 -78.5,115.8c-10.1,11 -2.1,30.6 11.6,28.8c9.2,-1.2 18.9,-2.8 28.8,-5.2c-23.4,38.4 -48,74.3 -71.1,99.6c-9.2,10.1 -5.8,26.1 3.5,32.3c40.5,42.3 92.2,45.5 164.3,45.5c74.1,0 133,-12.2 165.1,-47.3C1651.6,1021 1652.3,1020.2 1653,1019.3z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1841.2,1095.2c1.2,-1.5 2,-2.9 2.3,-4.3c2.2,-5.8 1.7,-13.1 -3.1,-18.4c-18.7,-20.5 -38.6,-49.6 -57.6,-80.7c8,1.9 15.9,3.3 23.3,4.2c11.1,1.4 17.6,-14.4 9.4,-23.4c-20.8,-22.9 -43.5,-58.5 -63.6,-93.8c11.1,4 22.6,6.3 32.9,7.6c8.8,1.1 13.9,-11.4 7.4,-18.5c-27.6,-30.3 -59.3,-89 -78.6,-127.4c-0.9,-3 -2.9,-5.1 -5.2,-6.2c-1.1,-0.5 -2.3,-0.8 -3.4,-0.8c-1.2,0 -2.3,0.2 -3.4,0.8c-2.3,1.1 -4.3,3.2 -5.2,6.2c-19.3,38.4 -51,97.1 -78.7,127.4c-6.4,7.1 -1.3,19.6 7.4,18.5c10.3,-1.3 21.8,-3.6 33,-7.6c-20.1,35.3 -42.8,70.9 -63.6,93.8c-8.1,8.9 -1.7,24.8 9.4,23.4c7.4,-0.9 15.3,-2.3 23.3,-4.2c-19,31.1 -38.9,60.2 -57.6,80.7c-7.4,8.2 -4.7,21.2 2.9,26.2c32.8,34.2 74.7,36.9 133.2,36.9c60.1,0 107.8,-9.9 133.8,-38.4C1840,1096.7 1840.6,1096 1841.2,1095.2z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1700.2,990.1c0.7,-0.8 1.2,-1.7 1.4,-2.5c1.3,-3.4 1,-7.6 -1.8,-10.6c-10.8,-11.9 -22.4,-28.7 -33.3,-46.7c4.6,1.1 9.2,1.9 13.5,2.4c6.4,0.8 10.2,-8.3 5.4,-13.5c-12.1,-13.2 -25.2,-33.8 -36.8,-54.3c6.5,2.3 13.1,3.7 19.1,4.4c5.1,0.6 8,-6.6 4.3,-10.7c-16,-17.5 -34.3,-51.5 -45.5,-73.8c-0.5,-1.7 -1.7,-3 -3,-3.6c-0.6,-0.3 -1.3,-0.5 -2,-0.4c-0.7,0 -1.3,0.1 -2,0.4c-1.3,0.6 -2.5,1.8 -3,3.6c-11.2,22.2 -29.5,56.2 -45.5,73.8c-3.7,4.1 -0.8,11.3 4.3,10.7c6,-0.8 12.6,-2.1 19.1,-4.4c-11.6,20.5 -24.8,41.1 -36.8,54.3c-4.7,5.2 -1,14.3 5.4,13.5c4.3,-0.5 8.9,-1.3 13.5,-2.4c-11,18 -22.5,34.8 -33.3,46.7c-4.3,4.7 -2.7,12.2 1.7,15.1c19,19.8 43.2,21.3 77.1,21.3c34.8,0 62.4,-5.7 77.4,-22.2C1699.5,990.9 1699.9,990.5 1700.2,990.1z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#0250BB"
|
||||||
|
android:pathData="M1179.6,815c0.5,-0.6 0.8,-1.2 0.9,-1.7c0.9,-2.3 0.7,-5.3 -1.3,-7.4c-7.5,-8.2 -15.5,-19.9 -23.1,-32.4c3.2,0.8 6.4,1.3 9.4,1.7c4.4,0.6 7.1,-5.8 3.8,-9.4c-8.4,-9.2 -17.5,-23.5 -25.5,-37.7c4.5,1.6 9.1,2.5 13.2,3.1c3.5,0.5 5.6,-4.6 3,-7.4c-11.1,-12.2 -23.8,-35.7 -31.6,-51.2c-0.4,-1.2 -1.2,-2 -2.1,-2.5c-0.4,-0.2 -0.9,-0.3 -1.4,-0.3s-0.9,0.1 -1.4,0.3c-0.9,0.4 -1.7,1.3 -2.1,2.5c-7.8,15.4 -20.5,39 -31.6,51.2c-2.6,2.8 -0.5,7.9 3,7.4c4.2,-0.5 8.8,-1.5 13.2,-3.1c-8.1,14.2 -17.2,28.5 -25.5,37.7c-3.3,3.6 -0.7,10 3.8,9.4c3,-0.4 6.1,-0.9 9.4,-1.7c-7.6,12.5 -15.6,24.2 -23.1,32.4c-3,3.3 -1.9,8.5 1.2,10.5c13.2,13.8 30,14.8 53.5,14.8c24.1,0 43.3,-4 53.7,-15.4C1179.2,815.6 1179.4,815.3 1179.6,815z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M1675.2,266.4c0,-15.9 -11.2,-29.2 -26.4,-31.9l-163.2,-28l-163.2,28c-15.2,2.6 -26.4,16 -26.4,31.9c-0.1,38.4 0.1,106.9 2.9,137c8,87.7 23.9,131.1 60.9,181.4c50.2,68.3 125.8,76.8 125.8,76.8s75.6,-8.5 125.8,-76.8c37,-50.3 52.9,-93.7 60.9,-181.4C1675,373.3 1675.3,304.8 1675.2,266.4zM1634.5,400c-7.9,86.8 -23.6,121.5 -53.7,162.4c-33.5,45.5 -80.8,58 -95.2,60.9c-14.3,-2.9 -62.1,-15.6 -95.4,-60.9c-30.1,-40.8 -45.7,-75.6 -53.7,-162.4c-1.9,-20.4 -2.8,-67.2 -2.7,-128.9l151.7,-26l151.7,26C1637.4,332.7 1636.4,379.5 1634.5,400z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M1393.3,394.8c7.3,80.5 21.1,104.3 42.8,133.9c15.7,21.3 36.1,31.2 49.4,35.8c0,0 0,0 0,0V302.8l-94.6,16.2C1391.3,354.7 1392.1,381.8 1393.3,394.8z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -0,0 +1,13 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?primaryText"
|
||||||
|
android:pathData="M12.4,2c5.1,0 9.3,4.2 9.3,9.3 0,3.5 -1,5.5 -1.8,7 -0.5,1 -0.8,1.6 -0.8,2.4a1.3,1.3 0,0 1,-2.7 0c0,-1.5 0.6,-2.6 1.1,-3.7a11,11 0,0 0,1.6 -5.7,6.7 6.7,0 0,0 -13.4,0 1.3,1.3 0,1 1,-2.6 0C3,6.2 7.2,2 12.4,2zM12.4,6c3,0 5.3,2.4 5.3,5.3 0,4.3 -1.6,6.4 -2.8,8 -0.5,0.7 -1,1.3 -1.3,2a1.3,1.3 0,0 1,-2.4 -1.2c0.4,-1 1,-1.7 1.5,-2.4 1.2,-1.5 2.4,-3 2.4,-6.4a2.7,2.7 0,0 0,-5.4 0c0,2.6 -2,5.4 -5.3,5.4a1.3,1.3 0,1 1,0 -2.7c2,0 2.7,-1.8 2.7,-2.7C7,8.3 9.5,6 12.4,6zM12.4,10c0.7,0 1.3,0.6 1.3,1.3 0,7.6 -8.5,10.5 -8.9,10.6l-0.4,0.1a1.3,1.3 0,0 1,-0.4 -2.6s7,-2.5 7,-8c0,-0.8 0.7,-1.4 1.4,-1.4z" />
|
||||||
|
</vector>
|
@ -0,0 +1,13 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/primaryText"
|
||||||
|
android:pathData="M20.9,8.4c0.6,-1.3 0.1,-2.4 -0.8,-3.3 0.3,-1.8 -0.9,-2.5 -2.8,-3 -0.5,-0.2 -2.5,0 -3.3,0l-6,0.3C6,2.4 3.8,2 1.7,2v8.6h4.3c0.3,0.1 1,0.8 1.1,1 0.2,0.1 0.2,0.3 0.2,0.3s0.4,1 0.8,1.3c1.6,1.6 2.9,4.5 3.5,6.8 0.5,0.7 -1,1.8 0.9,2 1.7,0.2 2.4,-3 1.8,-4.3 -0.6,-1.3 -1.2,-3.8 0,-3.5l1,0.2c1.6,0.5 4,0.7 5.3,-0.1 1.2,-0.9 0.6,-1.6 0.4,-2.6 1,-1 0.8,-2.2 0,-3.3" />
|
||||||
|
</vector>
|
@ -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/. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/primaryText"
|
||||||
|
android:pathData="M19,21L5,21a3,3 0,0 1,-3 -3L2,6a3,3 0,0 1,3 -3h14a3,3 0,0 1,3 3v12a3,3 0,0 1,-3 3zM19,5L5,5a1,1 0,0 0,-1 1v12c0,0.6 0.4,1 1,1h14c0.6,0 1,-0.4 1,-1L20,6c0,-0.6 -0.4,-1 -1,-1zM18,17L6,17v-2c0,-0.2 0,-0.4 0.2,-0.5l2.5,-2.3c0.2,-0.2 0.4,-0.2 0.6,-0.1l2.3,1c0.2,0.2 0.5,0 0.7,-0.1l2.2,-2.8a0.6,0.6 0,0 1,1 0l2.4,3.4 0.1,0.4v3zM8.5,8a1.5,1.5 0,1 1,0 3,1.5 1.5,0 0,1 0,-3z"
|
||||||
|
android:strokeLineJoin="round" />
|
||||||
|
</vector>
|
@ -0,0 +1,20 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<rotate
|
||||||
|
android:fromDegrees="45"
|
||||||
|
android:pivotX="-40%"
|
||||||
|
android:pivotY="87%"
|
||||||
|
android:toDegrees="45">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:width="10dp"
|
||||||
|
android:color="#0250BB" />
|
||||||
|
<solid android:color="#0250BB" />
|
||||||
|
</shape>
|
||||||
|
</rotate>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
@ -0,0 +1,19 @@
|
|||||||
|
<?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/. --><shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="225"
|
||||||
|
android:endColor="#0250BB"
|
||||||
|
android:startColor="#9059FF"
|
||||||
|
android:type="linear" />
|
||||||
|
<size
|
||||||
|
android:width="256dp"
|
||||||
|
android:height="152dp" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="8dp"
|
||||||
|
android:bottomRightRadius="8dp"
|
||||||
|
android:topLeftRadius="8dp"
|
||||||
|
android:topRightRadius="8dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,259 @@
|
|||||||
|
<?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="?foundation">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/normal_mode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/url"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="https://wikipedia.org" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
|
||||||
|
android:id="@+id/tracking_protection"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:text="@string/preference_enhanced_tracking_protection"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/url"
|
||||||
|
app:switchDescription="@string/etp_panel_on"
|
||||||
|
app:switchIcon="@drawable/ic_tracking_protection"
|
||||||
|
app:switchTitle="@string/preference_enhanced_tracking_protection" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/blocking_header"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:text="@string/enhanced_tracking_protection_blocked"
|
||||||
|
android:textColor="?attr/primaryText"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cross_site_tracking"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_cookies"
|
||||||
|
android:text="@string/etp_cookies_title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/blocking_header" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fingerprinters"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_fingerprinters"
|
||||||
|
android:text="@string/etp_fingerprinters_title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cross_site_tracking" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cryptominers"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_cryptominers"
|
||||||
|
android:text="@string/etp_cryptominers_title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/fingerprinters" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/social_media_trackers"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_social_media_trackers"
|
||||||
|
android:text="@string/etp_social_media_trackers_title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cryptominers" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tracking_content"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_tracking_content"
|
||||||
|
android:text="@string/etp_tracking_content_title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/social_media_trackers" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/not_blocking_header"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:text="@string/enhanced_tracking_protection_allowed"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tracking_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cross_site_tracking_loaded"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_cookies"
|
||||||
|
android:text="@string/etp_cookies_title"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/not_blocking_header" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fingerprinters_loaded"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_fingerprinters"
|
||||||
|
android:text="@string/etp_fingerprinters_title"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cross_site_tracking_loaded" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cryptominers_loaded"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_cryptominers"
|
||||||
|
android:text="@string/etp_cryptominers_title"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/fingerprinters_loaded" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/social_media_trackers_loaded"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_social_media_trackers"
|
||||||
|
android:text="@string/etp_social_media_trackers_title"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cryptominers_loaded" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tracking_content_loaded"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_tracking_content"
|
||||||
|
android:text="@string/etp_tracking_content_title"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/social_media_trackers_loaded" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/line_divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?neutralFaded"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tracking_content_loaded" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/protection_settings"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_settings"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:text="@string/etp_settings"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/line_divider" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/details_mode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/details_back"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:src="@drawable/mozac_ic_back"
|
||||||
|
android:tint="?attr/primaryText"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/category_description"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/category_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/category_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="52dp"
|
||||||
|
android:layout_marginTop="11dp"
|
||||||
|
android:layout_marginEnd="19dp"
|
||||||
|
android:textColor="?attr/primaryText"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/category_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="52dp"
|
||||||
|
android:layout_marginEnd="19dp"
|
||||||
|
android:textColor="?secondaryText"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/category_title"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/line_divider_details"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="?neutralFaded"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/category_description" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/details_blocking_header"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:layout_marginStart="52dp"
|
||||||
|
android:layout_marginEnd="26dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:text="@string/enhanced_tracking_protection_blocked"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/line_divider_details" />
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/blocking_scrollview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/details_blocking_header"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/details_blocking_header">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/blocking_text_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="52dp"
|
||||||
|
android:layout_marginEnd="26dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?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
|
||||||
|
android:id="@+id/fragment_tp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"/>
|
@ -0,0 +1,80 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/details_blocking_header"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/tracking_protection_item_height"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:text="@string/enhanced_tracking_protection_blocked"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
|
||||||
|
android:id="@+id/category_social_media"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:categoryItemDescription="@string/etp_social_media_trackers_description"
|
||||||
|
app:categoryItemIcon="@drawable/ic_social_media_trackers"
|
||||||
|
app:categoryItemTitle="@string/etp_social_media_trackers_title" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
|
||||||
|
android:id="@+id/category_cookies"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:categoryItemDescription="@string/etp_cookies_description"
|
||||||
|
app:categoryItemIcon="@drawable/ic_cookies"
|
||||||
|
app:categoryItemTitle="@string/etp_cookies_title" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
|
||||||
|
android:id="@+id/category_cryptominers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:categoryItemDescription="@string/etp_cryptominers_description"
|
||||||
|
app:categoryItemIcon="@drawable/ic_cryptominers"
|
||||||
|
app:categoryItemTitle="@string/etp_cryptominers_title" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
|
||||||
|
android:id="@+id/category_fingerprinters"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:categoryItemDescription="@string/etp_fingerprinters_description"
|
||||||
|
app:categoryItemIcon="@drawable/ic_fingerprinters"
|
||||||
|
app:categoryItemTitle="@string/etp_fingerprinters_title" />
|
||||||
|
|
||||||
|
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
|
||||||
|
android:id="@+id/category_tracking_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:categoryItemDescription="@string/etp_tracking_content_description"
|
||||||
|
app:categoryItemIcon="@drawable/ic_tracking_content"
|
||||||
|
app:categoryItemTitle="@string/etp_tracking_content_title" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
@ -0,0 +1,76 @@
|
|||||||
|
<?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/constraintLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||||
|
android:paddingBottom="@dimen/radio_button_preference_vertical">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:layout_marginStart="42dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:button="@null"
|
||||||
|
android:clickable="false"
|
||||||
|
android:drawablePadding="6dp"
|
||||||
|
android:focusable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:drawableStart="?android:attr/listChoiceIndicatorSingle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:gravity="center|start"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/radio_button"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/vertical_divider"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/radio_button"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/radio_button"
|
||||||
|
tools:text="Use recommended settings" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_summary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/vertical_divider"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/vertical_divider"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?neutralFaded"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/info_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/title"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/info_button"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginStart="18dp"
|
||||||
|
android:layout_marginEnd="18dp"
|
||||||
|
android:src="@drawable/ic_info"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/vertical_divider"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,53 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<merge 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:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/switchItemTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="48dp"
|
||||||
|
android:layout_marginEnd="64dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textAppearance="@style/ListItemTextStyle"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/switchItemDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/switchItemDescription"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textColor="?attr/secondaryText"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/switchItemTitle"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/switchItemTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/switchItemTitle"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/switch_widget"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:drawableStart="@drawable/ic_tracking_protection"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/switchItemDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/switchItemTitle" />
|
||||||
|
</merge>
|
@ -0,0 +1,57 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<merge 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/switchIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginTop="@dimen/library_item_icon_margin_vertical"
|
||||||
|
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginBottom="@dimen/library_item_icon_margin_vertical"
|
||||||
|
android:background="@drawable/ic_cryptominers"
|
||||||
|
android:clickable="false"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/switchItemTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textAppearance="@style/ListItemTextStyle"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/switchItemDescription"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/switchIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/switchItemDescription"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||||
|
android:layout_marginBottom="18dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textColor="?attr/secondaryText"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/switchIcon"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/switchItemTitle"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
</merge>
|
@ -0,0 +1,84 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/ic_etp_artwork"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:letterSpacing="0.05"
|
||||||
|
android:scrollHorizontally="false"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="@string/preference_enhanced_tracking_protection_explanation_title"
|
||||||
|
android:textAppearance="@style/Header16TextStyle"
|
||||||
|
android:textColor="@color/primary_text_dark_theme"
|
||||||
|
app:layout_constraintBottom_toTopOf="@android:id/summary"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/summary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:scrollHorizontally="false"
|
||||||
|
android:text="@string/preference_enhanced_tracking_protection_explanation"
|
||||||
|
android:textColor="@color/primary_text_dark_theme"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="@android:id/title"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/learn_more"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:text="@string/preference_enhanced_tracking_protection_explanation_learn_more"
|
||||||
|
android:textColor="@color/primary_text_dark_theme"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="@android:id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@android:id/summary" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="8dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/ic_arrowhead_right"
|
||||||
|
android:tint="@color/primary_text_dark_theme"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/learn_more"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/learn_more"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/learn_more" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_begin="210dp" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,57 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<LinearLayout 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="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/onboarding_popup_background"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/onboarding_message"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:lineSpacingExtra="2dp"
|
||||||
|
android:text="@string/etp_onboarding_message"
|
||||||
|
android:textColor="@color/primary_text_dark_theme"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/close_onboarding"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/close_onboarding"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:tint="@color/primary_text_dark_theme"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:rotation="180"
|
||||||
|
android:src="@drawable/ic_triangle"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/message" />
|
||||||
|
</LinearLayout>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
|
||||||
|
class TrackingProtectionPanelInteractorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun openDetails() {
|
||||||
|
val store: TrackingProtectionStore = mockk(relaxed = true)
|
||||||
|
val interactor =
|
||||||
|
TrackingProtectionPanelInteractor(store, mockk(), mockk())
|
||||||
|
interactor.openDetails(TrackingProtectionCategory.FINGERPRINTERS, true)
|
||||||
|
verify {
|
||||||
|
store.dispatch(
|
||||||
|
TrackingProtectionAction.EnterDetailsMode(
|
||||||
|
TrackingProtectionCategory.FINGERPRINTERS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun selectTrackingProtectionSettings() {
|
||||||
|
var openSettings = false
|
||||||
|
val interactor = TrackingProtectionPanelInteractor(
|
||||||
|
mockk(),
|
||||||
|
mockk(),
|
||||||
|
{ openSettings = true }
|
||||||
|
)
|
||||||
|
interactor.selectTrackingProtectionSettings()
|
||||||
|
assertEquals(true, openSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun trackingProtectionToggled() {
|
||||||
|
var trackingProtectionNewValue: Boolean? = null
|
||||||
|
val interactor = TrackingProtectionPanelInteractor(
|
||||||
|
mockk(),
|
||||||
|
{ trackingProtectionNewValue = it },
|
||||||
|
mockk()
|
||||||
|
)
|
||||||
|
interactor.trackingProtectionToggled(true)
|
||||||
|
assertEquals(true, trackingProtectionNewValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onBackPressed() {
|
||||||
|
val store: TrackingProtectionStore = mockk(relaxed = true)
|
||||||
|
val interactor =
|
||||||
|
TrackingProtectionPanelInteractor(store, mockk(), mockk())
|
||||||
|
interactor.onBackPressed()
|
||||||
|
verify { store.dispatch(TrackingProtectionAction.ExitDetailsMode) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/* 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.trackingprotection
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotSame
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import mozilla.components.concept.engine.content.blocking.Tracker
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class TrackingProtectionStoreTest {
|
||||||
|
@Test
|
||||||
|
fun enterDetailsMode() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
TrackingProtectionAction.EnterDetailsMode(
|
||||||
|
TrackingProtectionCategory.FINGERPRINTERS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
store.state.mode,
|
||||||
|
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun exitDetailsMode() = runBlocking {
|
||||||
|
val initialState = detailsState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
|
||||||
|
store.dispatch(TrackingProtectionAction.ExitDetailsMode).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
store.state.mode,
|
||||||
|
TrackingProtectionState.Mode.Normal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun trackerBlockingChanged() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
|
||||||
|
store.dispatch(TrackingProtectionAction.TrackerBlockingChanged(false)).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
store.state.mode,
|
||||||
|
TrackingProtectionState.Mode.Normal
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
false,
|
||||||
|
store.state.isTrackingProtectionEnabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun trackerListChanged() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
val tracker = Tracker("url", listOf())
|
||||||
|
|
||||||
|
store.dispatch(TrackingProtectionAction.TrackerListChange(listOf(tracker))).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
listOf(tracker),
|
||||||
|
store.state.listTrackers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun trackerLoadedListChanged() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
val tracker = Tracker("url", listOf())
|
||||||
|
|
||||||
|
store.dispatch(TrackingProtectionAction.TrackerLoadedListChange(listOf(tracker))).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
listOf(tracker),
|
||||||
|
store.state.listTrackersLoaded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun urlChanged() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
|
||||||
|
store.dispatch(TrackingProtectionAction.UrlChange("newURL")).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
"newURL",
|
||||||
|
store.state.url
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onChange() = runBlocking {
|
||||||
|
val initialState = defaultState()
|
||||||
|
val store = TrackingProtectionStore(initialState)
|
||||||
|
val tracker = Tracker("url", listOf())
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
TrackingProtectionAction.Change(
|
||||||
|
"newURL",
|
||||||
|
false,
|
||||||
|
listOf(tracker),
|
||||||
|
listOf(),
|
||||||
|
TrackingProtectionState.Mode.Details(
|
||||||
|
TrackingProtectionCategory.FINGERPRINTERS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).join()
|
||||||
|
assertNotSame(initialState, store.state)
|
||||||
|
assertEquals(
|
||||||
|
"newURL",
|
||||||
|
store.state.url
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
false,
|
||||||
|
store.state.isTrackingProtectionEnabled
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf<Tracker>(),
|
||||||
|
store.state.listTrackersLoaded
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf(tracker),
|
||||||
|
store.state.listTrackers
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true),
|
||||||
|
store.state.mode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultState(): TrackingProtectionState = TrackingProtectionState(
|
||||||
|
url = "www.mozilla.org",
|
||||||
|
isTrackingProtectionEnabled = true,
|
||||||
|
listTrackers = listOf(),
|
||||||
|
listTrackersLoaded = listOf(),
|
||||||
|
mode = TrackingProtectionState.Mode.Normal
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun detailsState(): TrackingProtectionState = TrackingProtectionState(
|
||||||
|
url = "www.mozilla.org",
|
||||||
|
isTrackingProtectionEnabled = true,
|
||||||
|
listTrackers = listOf(),
|
||||||
|
listTrackersLoaded = listOf(),
|
||||||
|
mode = TrackingProtectionState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true)
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue