parent
79fcb97ed2
commit
84a7430e57
|
@ -9,14 +9,45 @@ import mozilla.components.browser.errorpages.ErrorPages
|
|||
import mozilla.components.browser.errorpages.ErrorType
|
||||
import mozilla.components.concept.engine.EngineSession
|
||||
import mozilla.components.concept.engine.request.RequestInterceptor
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
|
||||
override fun onLoadRequest(session: EngineSession, uri: String): RequestInterceptor.InterceptionResponse? {
|
||||
adjustTrackingProtection(uri, context)
|
||||
// Accounts uses interception to check for a "success URL" in the sign-in flow to finalize authentication.
|
||||
return context.components.services.accountsAuthFeature.interceptor.onLoadRequest(session, uri)
|
||||
}
|
||||
|
||||
private fun adjustTrackingProtection(url: String, context: Context) {
|
||||
val host = try {
|
||||
URL(url).host
|
||||
} catch (e: MalformedURLException) {
|
||||
url
|
||||
}
|
||||
val trackingProtectionException = ExceptionDomains.load(context).contains(host)
|
||||
val trackingProtectionEnabled = Settings.getInstance(context).shouldUseTrackingProtection
|
||||
val core = context.components.core
|
||||
if (trackingProtectionException || !trackingProtectionEnabled) {
|
||||
with(core.sessionManager) {
|
||||
selectedSession?.let {
|
||||
getEngineSession(it)?.disableTrackingProtection()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val policy = core.createTrackingProtectionPolicy(normalMode = true)
|
||||
core.engine.settings.trackingProtectionPolicy = policy
|
||||
with(core.sessionManager) {
|
||||
selectedSession?.let {
|
||||
getEngineSession(it)?.enableTrackingProtection(policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onErrorRequest(
|
||||
session: EngineSession,
|
||||
errorType: ErrorType,
|
||||
|
@ -27,8 +58,10 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
|
|||
val htmlResource = getPageForRiskLevel(riskLevel)
|
||||
val cssResource = getStyleForRiskLevel(riskLevel)
|
||||
|
||||
return RequestInterceptor.ErrorResponse(ErrorPages
|
||||
.createErrorPage(context, errorType, uri = uri, htmlResource = htmlResource, cssResource = cssResource))
|
||||
return RequestInterceptor.ErrorResponse(
|
||||
ErrorPages
|
||||
.createErrorPage(context, errorType, uri = uri, htmlResource = htmlResource, cssResource = cssResource)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getPageForRiskLevel(riskLevel: RiskLevel): Int {
|
||||
|
|
|
@ -667,7 +667,7 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope,
|
|||
val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance(
|
||||
url = session.url,
|
||||
isSecured = session.securityInfo.secure,
|
||||
isTrackingProtectionOn = Settings.getInstance(context!!).shouldUseTrackingProtection,
|
||||
isTrackingProtectionOn = session.trackerBlockingEnabled,
|
||||
sitePermissions = sitePermissions,
|
||||
gravity = getAppropriateLayoutGravity()
|
||||
)
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package org.mozilla.fenix.exceptions
|
||||
|
||||
/* 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/. */
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
/**
|
||||
* Contains functionality to manage custom domains for allow-list.
|
||||
*/
|
||||
object ExceptionDomains {
|
||||
private const val PREFERENCE_NAME = "exceptions"
|
||||
private const val KEY_DOMAINS = "exceptions_domains"
|
||||
private const val SEPARATOR = "@<;>@"
|
||||
|
||||
private var exceptions: List<String>? = null
|
||||
|
||||
/**
|
||||
* Loads the previously added/saved custom domains from preferences.
|
||||
*
|
||||
* @param context the application context
|
||||
* @return list of custom domains
|
||||
*/
|
||||
fun load(context: Context): List<String> {
|
||||
if (exceptions == null) {
|
||||
exceptions = (preferences(context)
|
||||
.getString(KEY_DOMAINS, "") ?: "")
|
||||
.split(SEPARATOR)
|
||||
.filter { !it.isEmpty() }
|
||||
}
|
||||
|
||||
return exceptions ?: listOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the provided domains to preferences.
|
||||
*
|
||||
* @param context the application context
|
||||
* @param domains list of domains
|
||||
*/
|
||||
fun save(context: Context, domains: List<String>) {
|
||||
exceptions = domains
|
||||
|
||||
preferences(context)
|
||||
.edit()
|
||||
.putString(KEY_DOMAINS, domains.joinToString(separator = SEPARATOR))
|
||||
.apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided domain to preferences.
|
||||
*
|
||||
* @param context the application context
|
||||
* @param domain the domain to add
|
||||
*/
|
||||
fun add(context: Context, domain: String) {
|
||||
val domains = mutableListOf<String>()
|
||||
domains.addAll(load(context))
|
||||
domains.add(domain)
|
||||
|
||||
save(context, domains)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the provided domain from preferences.
|
||||
*
|
||||
* @param context the application context
|
||||
* @param domains the domain to remove
|
||||
*/
|
||||
fun remove(context: Context, domains: List<String>) {
|
||||
save(context, load(context) - domains)
|
||||
}
|
||||
|
||||
private fun preferences(context: Context): SharedPreferences =
|
||||
context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* 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.exceptions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.coroutines.Job
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
|
||||
|
||||
private sealed class AdapterItem {
|
||||
object DeleteButton : AdapterItem()
|
||||
object Header : AdapterItem()
|
||||
data class Item(val item: ExceptionsItem) : AdapterItem()
|
||||
}
|
||||
|
||||
private class ExceptionsList(val exceptions: List<ExceptionsItem>) {
|
||||
val items: List<AdapterItem>
|
||||
init {
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
items.add(AdapterItem.Header)
|
||||
for (exception in exceptions) {
|
||||
items.add(AdapterItem.Item(exception))
|
||||
}
|
||||
items.add(AdapterItem.DeleteButton)
|
||||
this.items = items
|
||||
}
|
||||
}
|
||||
|
||||
class ExceptionsAdapter(
|
||||
private val actionEmitter: Observer<ExceptionsAction>
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
private var exceptionsList: ExceptionsList = ExceptionsList(emptyList())
|
||||
private lateinit var job: Job
|
||||
|
||||
fun updateData(items: List<ExceptionsItem>) {
|
||||
this.exceptionsList = ExceptionsList(items)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = exceptionsList.items.size
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (exceptionsList.items[position]) {
|
||||
is AdapterItem.DeleteButton -> ExceptionsDeleteButtonViewHolder.LAYOUT_ID
|
||||
is AdapterItem.Header -> ExceptionsHeaderViewHolder.LAYOUT_ID
|
||||
is AdapterItem.Item -> ExceptionsListItemViewHolder.LAYOUT_ID
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
|
||||
return when (viewType) {
|
||||
ExceptionsDeleteButtonViewHolder.LAYOUT_ID -> ExceptionsDeleteButtonViewHolder(view, actionEmitter)
|
||||
ExceptionsHeaderViewHolder.LAYOUT_ID -> ExceptionsHeaderViewHolder(view)
|
||||
ExceptionsListItemViewHolder.LAYOUT_ID -> ExceptionsListItemViewHolder(view, actionEmitter, job)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is ExceptionsListItemViewHolder -> (exceptionsList.items[position] as AdapterItem.Item).also {
|
||||
holder.bind(it.item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
job = Job()
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
job.cancel()
|
||||
}
|
||||
}
|
|
@ -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.exceptions
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
import org.mozilla.fenix.mvi.UIComponent
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
|
||||
data class ExceptionsItem(val url: String)
|
||||
|
||||
@Mockable
|
||||
class ExceptionsComponent(
|
||||
private val container: ViewGroup,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: ExceptionsState = ExceptionsState(emptyList())
|
||||
) :
|
||||
UIComponent<ExceptionsState, ExceptionsAction, ExceptionsChange>(
|
||||
bus.getManagedEmitter(ExceptionsAction::class.java),
|
||||
bus.getSafeManagedObservable(ExceptionsChange::class.java)
|
||||
) {
|
||||
|
||||
override val reducer: (ExceptionsState, ExceptionsChange) -> ExceptionsState = { state, change ->
|
||||
when (change) {
|
||||
is ExceptionsChange.Change -> state.copy(items = change.list)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() = ExceptionsUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
init {
|
||||
render(reducer)
|
||||
}
|
||||
}
|
||||
|
||||
data class ExceptionsState(val items: List<ExceptionsItem>) : ViewState
|
||||
|
||||
sealed class ExceptionsAction : Action {
|
||||
sealed class Delete : ExceptionsAction() {
|
||||
object All : Delete()
|
||||
data class One(val item: ExceptionsItem) : Delete()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ExceptionsChange : Change {
|
||||
data class Change(val list: List<ExceptionsItem>) : ExceptionsChange()
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package org.mozilla.fenix.exceptions
|
||||
|
||||
/* 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/. */
|
||||
|
||||
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 androidx.navigation.Navigation
|
||||
import kotlinx.android.synthetic.main.fragment_exceptions.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ExceptionsFragment : Fragment(), CoroutineScope {
|
||||
private var job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
private lateinit var exceptionsComponent: ExceptionsComponent
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
job = Job()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activity?.title = getString(R.string.preference_exceptions)
|
||||
(activity as AppCompatActivity).supportActionBar?.show()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_exceptions, container, false)
|
||||
exceptionsComponent = ExceptionsComponent(
|
||||
view.exceptions_layout,
|
||||
ActionBusFactory.get(this),
|
||||
ExceptionsState(loadAndMapExceptions())
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
getAutoDisposeObservable<ExceptionsAction>()
|
||||
.subscribe {
|
||||
when (it) {
|
||||
is ExceptionsAction.Delete.All -> launch(Dispatchers.IO) {
|
||||
val domains = ExceptionDomains.load(context!!)
|
||||
ExceptionDomains.remove(context!!, domains)
|
||||
launch(Dispatchers.Main) {
|
||||
view?.let { view: View -> Navigation.findNavController(view).navigateUp() }
|
||||
}
|
||||
}
|
||||
is ExceptionsAction.Delete.One -> launch(Dispatchers.IO) {
|
||||
ExceptionDomains.remove(context!!, listOf(it.item.url))
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadAndMapExceptions(): List<ExceptionsItem> {
|
||||
return ExceptionDomains.load(context!!)
|
||||
.map { item ->
|
||||
ExceptionsItem(
|
||||
item
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun reloadData() {
|
||||
val items = loadAndMapExceptions()
|
||||
|
||||
coroutineScope {
|
||||
launch(Dispatchers.Main) {
|
||||
if (items.isEmpty()) {
|
||||
view?.let { view: View -> Navigation.findNavController(view).navigateUp() }
|
||||
return@launch
|
||||
}
|
||||
getManagedEmitter<ExceptionsChange>().onNext(ExceptionsChange.Change(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* 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.exceptions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.component_exceptions.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
|
||||
class ExceptionsUIView(
|
||||
container: ViewGroup,
|
||||
actionEmitter: Observer<ExceptionsAction>,
|
||||
changesObservable: Observable<ExceptionsChange>
|
||||
) :
|
||||
UIView<ExceptionsState, ExceptionsAction, ExceptionsChange>(container, actionEmitter, changesObservable) {
|
||||
|
||||
override val view: LinearLayout = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_exceptions, container, true)
|
||||
.findViewById(R.id.exceptions_wrapper)
|
||||
|
||||
init {
|
||||
view.exceptions_list.apply {
|
||||
adapter = ExceptionsAdapter(actionEmitter)
|
||||
layoutManager = LinearLayoutManager(container.context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateView() = Consumer<ExceptionsState> {
|
||||
(view.exceptions_list.adapter as ExceptionsAdapter).updateData(it.items)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* 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.exceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.synthetic.main.delete_exceptions_button.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionsAction
|
||||
|
||||
class ExceptionsDeleteButtonViewHolder(
|
||||
view: View,
|
||||
private val actionEmitter: Observer<ExceptionsAction>
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
private val deleteButton = view.removeAllExceptions
|
||||
|
||||
init {
|
||||
deleteButton.setOnClickListener {
|
||||
actionEmitter.onNext(ExceptionsAction.Delete.All)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.delete_exceptions_button
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* 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.exceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class ExceptionsHeaderViewHolder(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.exceptions_description
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* 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.exceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.synthetic.main.exception_item.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.icons.IconRequest
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionsAction
|
||||
import org.mozilla.fenix.exceptions.ExceptionsItem
|
||||
import org.mozilla.fenix.ext.components
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ExceptionsListItemViewHolder(
|
||||
view: View,
|
||||
private val actionEmitter: Observer<ExceptionsAction>,
|
||||
val job: Job
|
||||
) : RecyclerView.ViewHolder(view), CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.IO + job
|
||||
|
||||
private val favicon = view.favicon_image
|
||||
private val url = view.domainView
|
||||
private val deleteButton = view.delete_exception
|
||||
|
||||
private var item: ExceptionsItem? = null
|
||||
|
||||
init {
|
||||
deleteButton.setOnClickListener {
|
||||
item?.let {
|
||||
actionEmitter.onNext(ExceptionsAction.Delete.One(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: ExceptionsItem) {
|
||||
this.item = item
|
||||
url.text = item.url
|
||||
updateFavIcon(item.url)
|
||||
}
|
||||
|
||||
private fun updateFavIcon(url: String) {
|
||||
launch(Dispatchers.IO) {
|
||||
val bitmap = favicon.context.components.utils.icons
|
||||
.loadIcon(IconRequest(url)).await().bitmap
|
||||
launch(Dispatchers.Main) {
|
||||
favicon.setImageBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.exception_item
|
||||
}
|
||||
}
|
|
@ -6,43 +6,66 @@ package org.mozilla.fenix.settings
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getPreferenceKey
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
(activity as AppCompatActivity).title = getString(R.string.preferences_tracking_protection)
|
||||
(activity as AppCompatActivity).supportActionBar?.show()
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.tracking_protection_preferences, rootKey)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activity?.title = getString(R.string.preferences_tracking_protection)
|
||||
(activity as AppCompatActivity).supportActionBar?.show()
|
||||
|
||||
// Tracking Protection Switch
|
||||
val trackingProtectionKey =
|
||||
context!!.getPreferenceKey(R.string.pref_key_tracking_protection)
|
||||
val preferenceTP = findPreference<Preference>(trackingProtectionKey)
|
||||
val preferenceTP = findPreference<SwitchPreference>(trackingProtectionKey)
|
||||
preferenceTP?.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
Settings.getInstance(context!!).setTrackingProtection(newValue = newValue as Boolean)
|
||||
with(requireComponents.core) {
|
||||
val policy =
|
||||
createTrackingProtectionPolicy(newValue as Boolean)
|
||||
createTrackingProtectionPolicy(newValue)
|
||||
engine.settings.trackingProtectionPolicy = policy
|
||||
|
||||
with(sessionManager) {
|
||||
sessions.forEach { getEngineSession(it)?.enableTrackingProtection(policy) }
|
||||
sessions.forEach {
|
||||
if (newValue)
|
||||
getEngineSession(it)?.enableTrackingProtection(policy) else
|
||||
getEngineSession(it)?.disableTrackingProtection()
|
||||
}
|
||||
}
|
||||
}
|
||||
requireContext().components.useCases.sessionUseCases.reload.invoke()
|
||||
true
|
||||
}
|
||||
|
||||
context?.let {
|
||||
val exceptionsEmpty = ExceptionDomains.load(it).isEmpty()
|
||||
val exceptions =
|
||||
it.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
|
||||
val preferenceExceptions = findPreference<Preference>(exceptions)
|
||||
preferenceExceptions.shouldDisableView = true
|
||||
preferenceExceptions.isEnabled = !exceptionsEmpty
|
||||
preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getClickListenerForExceptions(): Preference.OnPreferenceClickListener {
|
||||
return Preference.OnPreferenceClickListener {
|
||||
val directions = TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
|
||||
Navigation.findNavController(view!!).navigate(directions)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,15 @@ import mozilla.components.feature.sitepermissions.SitePermissions
|
|||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserFragment
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
private const val KEY_URL = "KEY_URL"
|
||||
|
@ -174,19 +176,17 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment(), CoroutineSco
|
|||
private fun arePermissionsGranted(requestCode: Int, grantResults: IntArray) =
|
||||
requestCode == REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS && grantResults.all { it == PERMISSION_GRANTED }
|
||||
|
||||
private fun toggleTrackingProtection(trackingEnabled: Boolean, context: Context) {
|
||||
with(requireComponents.core) {
|
||||
val policy =
|
||||
createTrackingProtectionPolicy(trackingEnabled)
|
||||
Settings.getInstance(context).setTrackingProtection(trackingEnabled)
|
||||
engine.settings.trackingProtectionPolicy = policy
|
||||
|
||||
with(sessionManager) {
|
||||
sessions.forEach {
|
||||
getEngineSession(it)?.enableTrackingProtection(
|
||||
policy
|
||||
)
|
||||
}
|
||||
private fun toggleTrackingProtection(context: Context, url: String) {
|
||||
val host = try {
|
||||
URL(url).host
|
||||
} catch (e: MalformedURLException) {
|
||||
url
|
||||
}
|
||||
launch {
|
||||
if (!ExceptionDomains.load(context).contains(host)) {
|
||||
ExceptionDomains.add(context, host)
|
||||
} else {
|
||||
ExceptionDomains.remove(context, listOf(host))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment(), CoroutineSco
|
|||
}
|
||||
is QuickSettingsAction.ToggleTrackingProtection -> {
|
||||
val trackingEnabled = it.trackingProtection
|
||||
context?.let { toggleTrackingProtection(trackingEnabled, it) }
|
||||
context?.let { context: Context -> toggleTrackingProtection(context, url) }
|
||||
launch(Dispatchers.Main) {
|
||||
getManagedEmitter<QuickSettingsChange>().onNext(
|
||||
QuickSettingsChange.Change(
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?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"
|
||||
android:id="@+id/exceptions_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/exceptions_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,18 @@
|
|||
<?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/. -->
|
||||
<Button android:id="@+id/removeAllExceptions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_margin="12dp"
|
||||
android:backgroundTint="?attr/neutral"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/preferences_tracking_protection_exceptions_turn_on_for_all"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?attr/accentHighContrast"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@ -0,0 +1,60 @@
|
|||
<?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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/favicon_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/domainView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="middle"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?primaryText"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/delete_exception"
|
||||
app:layout_constraintStart_toEndOf="@+id/favicon_image"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="mozilla.org" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/delete_exception"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_close"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -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/. -->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/exceptions_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@string/preferences_tracking_protection_exceptions_description"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="16sp" />
|
|
@ -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/. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/exceptions_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="org.mozilla.fenix.exceptions.ExceptionsFragment">
|
||||
</LinearLayout>
|
|
@ -290,5 +290,13 @@
|
|||
<fragment
|
||||
android:id="@+id/trackingProtectionFragment"
|
||||
android:name="org.mozilla.fenix.settings.TrackingProtectionFragment"
|
||||
android:label="TrackingProtectionFragment" />
|
||||
android:label="TrackingProtectionFragment" >
|
||||
<action
|
||||
android:id="@+id/action_trackingProtectionFragment_to_exceptionsFragment"
|
||||
app:destination="@id/exceptionsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/exceptionsFragment"
|
||||
android:name="org.mozilla.fenix.exceptions.ExceptionsFragment"
|
||||
android:label="ExceptionsFragment" />
|
||||
</navigation>
|
|
@ -167,6 +167,10 @@
|
|||
<string name="preferences_tracking_protection_description">Block content and scripts that track you online</string>
|
||||
<!-- Preference for tracking protection exceptions -->
|
||||
<string name="preferences_tracking_protection_exceptions">Exceptions</string>
|
||||
<!-- Preference description for tracking protection exceptions -->
|
||||
<string name="preferences_tracking_protection_exceptions_description">Tracking Protection is off for these websites</string>
|
||||
<!-- Button in Exceptions Preference to turn on tracking protection for all sites (remove all exceptions) -->
|
||||
<string name="preferences_tracking_protection_exceptions_turn_on_for_all">Turn on for all sites</string>
|
||||
|
||||
<!-- Preference switch for Telemetry -->
|
||||
<string name="preferences_telemetry">Telemetry</string>
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
<!-- 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.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:icon="@drawable/ic_tracking_protection"
|
||||
|
@ -11,7 +10,6 @@
|
|||
android:summary="@string/preferences_tracking_protection_description"
|
||||
android:title="@string/preferences_tracking_protection" />
|
||||
<Preference
|
||||
app:isPreferenceVisible="false"
|
||||
android:icon="@drawable/ic_internet"
|
||||
android:key="@string/pref_key_tracking_protection_exceptions"
|
||||
android:title="@string/preferences_tracking_protection_exceptions" />
|
||||
|
|
Loading…
Reference in New Issue