mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-09 19:10:42 +00:00
For #3360 - Adds empty state for TP Exceptions
This commit is contained in:
parent
7588251f8b
commit
9b5baa2358
@ -35,6 +35,7 @@ import mozilla.components.support.utils.SafeIntent
|
|||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
import org.mozilla.fenix.components.isSentryEnabled
|
import org.mozilla.fenix.components.isSentryEnabled
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
|
import org.mozilla.fenix.exceptions.ExceptionsFragmentDirections
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.getRootView
|
import org.mozilla.fenix.ext.getRootView
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
@ -230,15 +231,21 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
|||||||
}
|
}
|
||||||
BrowserDirection.FromSearch -> {
|
BrowserDirection.FromSearch -> {
|
||||||
fragmentId = R.id.searchFragment
|
fragmentId = R.id.searchFragment
|
||||||
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(customTabSessionId)
|
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(
|
||||||
|
customTabSessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
BrowserDirection.FromSettings -> {
|
BrowserDirection.FromSettings -> {
|
||||||
fragmentId = R.id.settingsFragment
|
fragmentId = R.id.settingsFragment
|
||||||
SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(customTabSessionId)
|
SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(
|
||||||
|
customTabSessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
BrowserDirection.FromBookmarks -> {
|
BrowserDirection.FromBookmarks -> {
|
||||||
fragmentId = R.id.bookmarkFragment
|
fragmentId = R.id.bookmarkFragment
|
||||||
BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(customTabSessionId)
|
BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(
|
||||||
|
customTabSessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
BrowserDirection.FromBookmarksFolderSelect -> {
|
BrowserDirection.FromBookmarksFolderSelect -> {
|
||||||
fragmentId = R.id.bookmarkSelectFolderFragment
|
fragmentId = R.id.bookmarkSelectFolderFragment
|
||||||
@ -247,7 +254,9 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
|||||||
}
|
}
|
||||||
BrowserDirection.FromHistory -> {
|
BrowserDirection.FromHistory -> {
|
||||||
fragmentId = R.id.historyFragment
|
fragmentId = R.id.historyFragment
|
||||||
HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(customTabSessionId)
|
HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(
|
||||||
|
customTabSessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -259,7 +268,12 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load(searchTermOrURL: String, newTab: Boolean, engine: SearchEngine?, forceSearch: Boolean) {
|
private fun load(
|
||||||
|
searchTermOrURL: String,
|
||||||
|
newTab: Boolean,
|
||||||
|
engine: SearchEngine?,
|
||||||
|
forceSearch: Boolean
|
||||||
|
) {
|
||||||
val isPrivate = this.browsingModeManager.isPrivate
|
val isPrivate = this.browsingModeManager.isPrivate
|
||||||
|
|
||||||
val loadUrlUseCase = if (newTab) {
|
val loadUrlUseCase = if (newTab) {
|
||||||
@ -273,7 +287,13 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
|||||||
val searchUseCase: (String) -> Unit = { searchTerms ->
|
val searchUseCase: (String) -> Unit = { searchTerms ->
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
components.useCases.searchUseCases.newTabSearch
|
components.useCases.searchUseCases.newTabSearch
|
||||||
.invoke(searchTerms, Session.Source.USER_ENTERED, true, isPrivate, searchEngine = engine)
|
.invoke(
|
||||||
|
searchTerms,
|
||||||
|
Session.Source.USER_ENTERED,
|
||||||
|
true,
|
||||||
|
isPrivate,
|
||||||
|
searchEngine = engine
|
||||||
|
)
|
||||||
} else components.useCases.searchUseCases.defaultSearch.invoke(searchTerms, engine)
|
} else components.useCases.searchUseCases.defaultSearch.invoke(searchTerms, engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,5 +397,5 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
|||||||
|
|
||||||
enum class BrowserDirection {
|
enum class BrowserDirection {
|
||||||
FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks,
|
FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks,
|
||||||
FromBookmarksFolderSelect, FromHistory
|
FromBookmarksFolderSelect, FromHistory, FromExceptions
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
package org.mozilla.fenix.exceptions
|
package org.mozilla.fenix.exceptions
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.mozilla.fenix.mvi.ViewState
|
|
||||||
import org.mozilla.fenix.mvi.Change
|
|
||||||
import org.mozilla.fenix.mvi.Action
|
import org.mozilla.fenix.mvi.Action
|
||||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||||
|
import org.mozilla.fenix.mvi.Change
|
||||||
import org.mozilla.fenix.mvi.UIComponent
|
import org.mozilla.fenix.mvi.UIComponent
|
||||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||||
|
import org.mozilla.fenix.mvi.ViewState
|
||||||
import org.mozilla.fenix.test.Mockable
|
import org.mozilla.fenix.test.Mockable
|
||||||
|
|
||||||
data class ExceptionsItem(val url: String)
|
data class ExceptionsItem(val url: String)
|
||||||
@ -38,6 +38,7 @@ class ExceptionsComponent(
|
|||||||
data class ExceptionsState(val items: List<ExceptionsItem>) : ViewState
|
data class ExceptionsState(val items: List<ExceptionsItem>) : ViewState
|
||||||
|
|
||||||
sealed class ExceptionsAction : Action {
|
sealed class ExceptionsAction : Action {
|
||||||
|
object LearnMore : ExceptionsAction()
|
||||||
sealed class Delete : ExceptionsAction() {
|
sealed class Delete : ExceptionsAction() {
|
||||||
object All : Delete()
|
object All : Delete()
|
||||||
data class One(val item: ExceptionsItem) : Delete()
|
data class One(val item: ExceptionsItem) : Delete()
|
||||||
|
@ -17,11 +17,14 @@ import kotlinx.coroutines.Dispatchers.IO
|
|||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.FenixViewModelProvider
|
import org.mozilla.fenix.FenixViewModelProvider
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||||
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
|
|
||||||
class ExceptionsFragment : Fragment() {
|
class ExceptionsFragment : Fragment() {
|
||||||
private lateinit var exceptionsComponent: ExceptionsComponent
|
private lateinit var exceptionsComponent: ExceptionsComponent
|
||||||
@ -56,6 +59,14 @@ class ExceptionsFragment : Fragment() {
|
|||||||
getAutoDisposeObservable<ExceptionsAction>()
|
getAutoDisposeObservable<ExceptionsAction>()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
is ExceptionsAction.LearnMore -> {
|
||||||
|
(activity as HomeActivity).openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
|
||||||
|
(SupportUtils.SumoTopic.TRACKING_PROTECTION),
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromExceptions
|
||||||
|
)
|
||||||
|
}
|
||||||
is ExceptionsAction.Delete.All -> viewLifecycleOwner.lifecycleScope.launch(IO) {
|
is ExceptionsAction.Delete.All -> viewLifecycleOwner.lifecycleScope.launch(IO) {
|
||||||
val domains = ExceptionDomains.load(context!!)
|
val domains = ExceptionDomains.load(context!!)
|
||||||
ExceptionDomains.remove(context!!, domains)
|
ExceptionDomains.remove(context!!, domains)
|
||||||
|
@ -4,10 +4,15 @@
|
|||||||
|
|
||||||
package org.mozilla.fenix.exceptions
|
package org.mozilla.fenix.exceptions
|
||||||
|
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.functions.Consumer
|
import io.reactivex.functions.Consumer
|
||||||
@ -20,20 +25,45 @@ class ExceptionsUIView(
|
|||||||
actionEmitter: Observer<ExceptionsAction>,
|
actionEmitter: Observer<ExceptionsAction>,
|
||||||
changesObservable: Observable<ExceptionsChange>
|
changesObservable: Observable<ExceptionsChange>
|
||||||
) :
|
) :
|
||||||
UIView<ExceptionsState, ExceptionsAction, ExceptionsChange>(container, actionEmitter, changesObservable) {
|
UIView<ExceptionsState, ExceptionsAction, ExceptionsChange>(
|
||||||
|
container,
|
||||||
|
actionEmitter,
|
||||||
|
changesObservable
|
||||||
|
) {
|
||||||
|
|
||||||
override val view: RecyclerView = LayoutInflater.from(container.context)
|
override val view: FrameLayout = LayoutInflater.from(container.context)
|
||||||
.inflate(R.layout.component_exceptions, container, true)
|
.inflate(R.layout.component_exceptions, container, true)
|
||||||
.findViewById(R.id.exceptions_list)
|
.findViewById(R.id.exceptions_wrapper)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
view.exceptions_list.apply {
|
view.exceptions_list.apply {
|
||||||
adapter = ExceptionsAdapter(actionEmitter)
|
adapter = ExceptionsAdapter(actionEmitter)
|
||||||
layoutManager = LinearLayoutManager(container.context)
|
layoutManager = LinearLayoutManager(container.context)
|
||||||
}
|
}
|
||||||
|
val descriptionText = String
|
||||||
|
.format(
|
||||||
|
view.exceptions_empty_view.text.toString(),
|
||||||
|
System.getProperty("line.separator")
|
||||||
|
)
|
||||||
|
val linkStartIndex = descriptionText.indexOf("\n\n") + 2
|
||||||
|
val linkAction = object : ClickableSpan() {
|
||||||
|
override fun onClick(widget: View?) {
|
||||||
|
actionEmitter.onNext(ExceptionsAction.LearnMore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val textWithLink = SpannableString(descriptionText).apply {
|
||||||
|
setSpan(linkAction, linkStartIndex, descriptionText.length, 0)
|
||||||
|
val colorSpan = ForegroundColorSpan(view.exceptions_empty_view.currentTextColor)
|
||||||
|
setSpan(colorSpan, linkStartIndex, descriptionText.length, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
view.exceptions_empty_view.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
view.exceptions_empty_view.text = textWithLink
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateView() = Consumer<ExceptionsState> {
|
override fun updateView() = Consumer<ExceptionsState> {
|
||||||
|
view.exceptions_empty_view.visibility = if (it.items.isEmpty()) View.VISIBLE else View.GONE
|
||||||
|
view.exceptions_list.visibility = if (it.items.isEmpty()) View.GONE else View.VISIBLE
|
||||||
(view.exceptions_list.adapter as ExceptionsAdapter).updateData(it.items)
|
(view.exceptions_list.adapter as ExceptionsAdapter).updateData(it.items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ object SupportUtils {
|
|||||||
) {
|
) {
|
||||||
HELP("faq-android"),
|
HELP("faq-android"),
|
||||||
PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"),
|
PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"),
|
||||||
YOUR_RIGHTS("your-rights")
|
YOUR_RIGHTS("your-rights"),
|
||||||
|
TRACKING_PROTECTION("tracking-protection-firefox-preview")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSumoURLForTopic(context: Context, topic: SumoTopic): String {
|
fun getSumoURLForTopic(context: Context, topic: SumoTopic): String {
|
||||||
|
@ -11,7 +11,6 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
|
||||||
import org.mozilla.fenix.ext.getPreferenceKey
|
import org.mozilla.fenix.ext.getPreferenceKey
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.utils.Settings
|
import org.mozilla.fenix.utils.Settings
|
||||||
@ -34,7 +33,8 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||||||
preferenceTP?.isChecked = Settings.getInstance(context!!).shouldUseTrackingProtection
|
preferenceTP?.isChecked = Settings.getInstance(context!!).shouldUseTrackingProtection
|
||||||
preferenceTP?.onPreferenceChangeListener =
|
preferenceTP?.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
Settings.getInstance(requireContext()).setTrackingProtection(newValue = newValue as Boolean)
|
Settings.getInstance(requireContext())
|
||||||
|
.setTrackingProtection(newValue = newValue as Boolean)
|
||||||
with(requireComponents) {
|
with(requireComponents) {
|
||||||
val policy = core.createTrackingProtectionPolicy(newValue)
|
val policy = core.createTrackingProtectionPolicy(newValue)
|
||||||
useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
|
useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
|
||||||
@ -43,20 +43,16 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.let {
|
|
||||||
val exceptionsEmpty = ExceptionDomains.load(it).isEmpty()
|
|
||||||
val exceptions =
|
val exceptions =
|
||||||
it.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
|
context!!.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions)
|
||||||
val preferenceExceptions = findPreference<Preference>(exceptions)
|
val preferenceExceptions = findPreference<Preference>(exceptions)
|
||||||
preferenceExceptions?.shouldDisableView = true
|
|
||||||
preferenceExceptions?.isEnabled = !exceptionsEmpty
|
|
||||||
preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions()
|
preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClickListenerForExceptions(): Preference.OnPreferenceClickListener {
|
private fun getClickListenerForExceptions(): Preference.OnPreferenceClickListener {
|
||||||
return Preference.OnPreferenceClickListener {
|
return Preference.OnPreferenceClickListener {
|
||||||
val directions = TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
|
val directions =
|
||||||
|
TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment()
|
||||||
Navigation.findNavController(view!!).navigate(directions)
|
Navigation.findNavController(view!!).navigate(directions)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,27 @@
|
|||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
<!-- 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
|
- 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/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/exceptions_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:visibility="gone"
|
||||||
android:id="@+id/exceptions_list"
|
android:id="@+id/exceptions_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:listitem="@layout/exception_item" />
|
tools:listitem="@layout/exception_item" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/exceptions_empty_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:text="@string/exceptions_empty_message"
|
||||||
|
android:textColor="?secondaryText"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
@ -379,7 +379,13 @@
|
|||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/exceptionsFragment"
|
android:id="@+id/exceptionsFragment"
|
||||||
android:name="org.mozilla.fenix.exceptions.ExceptionsFragment"
|
android:name="org.mozilla.fenix.exceptions.ExceptionsFragment"
|
||||||
android:label="@string/preference_exceptions" />
|
android:label="@string/preference_exceptions">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_exceptionsFragment_to_browserFragment"
|
||||||
|
app:destination="@id/browserFragment"
|
||||||
|
app:popUpTo="@id/settingsFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
</fragment>
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/createCollectionFragment"
|
android:id="@+id/createCollectionFragment"
|
||||||
android:name="org.mozilla.fenix.collections.CreateCollectionFragment"
|
android:name="org.mozilla.fenix.collections.CreateCollectionFragment"
|
||||||
|
@ -197,6 +197,8 @@
|
|||||||
<string name="preferences_tracking_protection_exceptions_description">Tracking Protection is off for these websites</string>
|
<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) -->
|
<!-- 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>
|
<string name="preferences_tracking_protection_exceptions_turn_on_for_all">Turn on for all sites</string>
|
||||||
|
<!-- Text displayed when there are no exceptions, includes a learn more link that brings users to a tracking protection SUMO page -->
|
||||||
|
<string name="exceptions_empty_message">Exceptions let you disable tracking protection for selected sites.\n\nLearn more</string>
|
||||||
<!-- Description in Quick Settings that tells user tracking protection is off globally for all sites, and links to Settings to turn it on -->
|
<!-- Description in Quick Settings that tells user tracking protection is off globally for all sites, and links to Settings to turn it on -->
|
||||||
<string name="preferences_tracking_protection_turned_off_globally">Turned off globally, go to Settings to turn it on.</string>
|
<string name="preferences_tracking_protection_turned_off_globally">Turned off globally, go to Settings to turn it on.</string>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user