diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 8339fc237..b4ad5ee49 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -35,6 +35,7 @@ import mozilla.components.support.utils.SafeIntent import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.isSentryEnabled import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.exceptions.ExceptionsFragmentDirections import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getRootView import org.mozilla.fenix.ext.nav @@ -230,15 +231,21 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback } BrowserDirection.FromSearch -> { fragmentId = R.id.searchFragment - SearchFragmentDirections.actionSearchFragmentToBrowserFragment(customTabSessionId) + SearchFragmentDirections.actionSearchFragmentToBrowserFragment( + customTabSessionId + ) } BrowserDirection.FromSettings -> { fragmentId = R.id.settingsFragment - SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(customTabSessionId) + SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment( + customTabSessionId + ) } BrowserDirection.FromBookmarks -> { fragmentId = R.id.bookmarkFragment - BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(customTabSessionId) + BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment( + customTabSessionId + ) } BrowserDirection.FromBookmarksFolderSelect -> { fragmentId = R.id.bookmarkSelectFolderFragment @@ -247,7 +254,9 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback } BrowserDirection.FromHistory -> { fragmentId = R.id.historyFragment - HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(customTabSessionId) + HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment( + customTabSessionId + ) } } } 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 loadUrlUseCase = if (newTab) { @@ -273,7 +287,13 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback val searchUseCase: (String) -> Unit = { searchTerms -> if (newTab) { 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) } @@ -377,5 +397,5 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback enum class BrowserDirection { FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks, - FromBookmarksFolderSelect, FromHistory + FromBookmarksFolderSelect, FromHistory, FromExceptions } diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsComponent.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsComponent.kt index c485eb49c..87fe487fc 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsComponent.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsComponent.kt @@ -5,13 +5,13 @@ package org.mozilla.fenix.exceptions 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.ActionBusFactory +import org.mozilla.fenix.mvi.Change import org.mozilla.fenix.mvi.UIComponent import org.mozilla.fenix.mvi.UIComponentViewModelBase import org.mozilla.fenix.mvi.UIComponentViewModelProvider +import org.mozilla.fenix.mvi.ViewState import org.mozilla.fenix.test.Mockable data class ExceptionsItem(val url: String) @@ -38,6 +38,7 @@ class ExceptionsComponent( data class ExceptionsState(val items: List) : ViewState sealed class ExceptionsAction : Action { + object LearnMore : ExceptionsAction() sealed class Delete : ExceptionsAction() { object All : Delete() data class One(val item: ExceptionsItem) : Delete() diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt index b0fb136dc..76c8dbe18 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt @@ -17,11 +17,14 @@ import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.FenixViewModelProvider +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.getAutoDisposeObservable import org.mozilla.fenix.mvi.getManagedEmitter +import org.mozilla.fenix.settings.SupportUtils class ExceptionsFragment : Fragment() { private lateinit var exceptionsComponent: ExceptionsComponent @@ -56,6 +59,14 @@ class ExceptionsFragment : Fragment() { getAutoDisposeObservable() .subscribe { 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) { val domains = ExceptionDomains.load(context!!) ExceptionDomains.remove(context!!, domains) diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsUIView.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsUIView.kt index 97019b1ca..557022414 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsUIView.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsUIView.kt @@ -4,10 +4,15 @@ 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.View import android.view.ViewGroup +import android.widget.FrameLayout import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import io.reactivex.Observable import io.reactivex.Observer import io.reactivex.functions.Consumer @@ -20,20 +25,45 @@ class ExceptionsUIView( actionEmitter: Observer, changesObservable: Observable ) : - UIView(container, actionEmitter, changesObservable) { + UIView( + 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) - .findViewById(R.id.exceptions_list) + .findViewById(R.id.exceptions_wrapper) init { view.exceptions_list.apply { adapter = ExceptionsAdapter(actionEmitter) 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 { + 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) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt index c61c5db8e..ad5f3a268 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt @@ -28,7 +28,8 @@ object SupportUtils { ) { HELP("faq-android"), 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 { diff --git a/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt index bc38fb7bb..964d14bc8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt @@ -11,7 +11,6 @@ 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.getPreferenceKey import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.utils.Settings @@ -34,7 +33,8 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() { preferenceTP?.isChecked = Settings.getInstance(context!!).shouldUseTrackingProtection preferenceTP?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - Settings.getInstance(requireContext()).setTrackingProtection(newValue = newValue as Boolean) + Settings.getInstance(requireContext()) + .setTrackingProtection(newValue = newValue as Boolean) with(requireComponents) { val policy = core.createTrackingProtectionPolicy(newValue) useCases.settingsUseCases.updateTrackingProtection.invoke(policy) @@ -43,20 +43,16 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() { true } - context?.let { - val exceptionsEmpty = ExceptionDomains.load(it).isEmpty() - val exceptions = - it.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions) - val preferenceExceptions = findPreference(exceptions) - preferenceExceptions?.shouldDisableView = true - preferenceExceptions?.isEnabled = !exceptionsEmpty - preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions() - } + val exceptions = + context!!.getPreferenceKey(R.string.pref_key_tracking_protection_exceptions) + val preferenceExceptions = findPreference(exceptions) + preferenceExceptions?.onPreferenceClickListener = getClickListenerForExceptions() } private fun getClickListenerForExceptions(): Preference.OnPreferenceClickListener { return Preference.OnPreferenceClickListener { - val directions = TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment() + val directions = + TrackingProtectionFragmentDirections.actionTrackingProtectionFragmentToExceptionsFragment() Navigation.findNavController(view!!).navigate(directions) true } diff --git a/app/src/main/res/layout/component_exceptions.xml b/app/src/main/res/layout/component_exceptions.xml index 750a61958..8570ae2bb 100644 --- a/app/src/main/res/layout/component_exceptions.xml +++ b/app/src/main/res/layout/component_exceptions.xml @@ -2,10 +2,27 @@ - + + + tools:listitem="@layout/exception_item" /> + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 87c5ef0d4..0695259f9 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -375,11 +375,17 @@ + android:label="@string/preferences_delete_browsing_data" /> + android:label="@string/preference_exceptions"> + + Tracking Protection is off for these websites Turn on for all sites + + Exceptions let you disable tracking protection for selected sites.\n\nLearn more Turned off globally, go to Settings to turn it on.