diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 679f065f4..223052c0b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -517,10 +517,14 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope { val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance( url = session.url, isSecured = session.securityInfo.secure, + isTrackingProtectionOn = Settings.getInstance(context!!).shouldUseTrackingProtection, sitePermissions = sitePermissions ) quickSettingsSheet.sitePermissions = sitePermissions - quickSettingsSheet.show(requireFragmentManager(), QuickSettingsSheetDialogFragment.FRAGMENT_TAG) + quickSettingsSheet.show( + requireFragmentManager(), + QuickSettingsSheetDialogFragment.FRAGMENT_TAG + ) } } } @@ -572,6 +576,6 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope { private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2 private const val REQUEST_CODE_APP_PERMISSIONS = 3 private const val TOOLBAR_HEIGHT = 56f - private const val REPORT_SITE_ISSUE_URL = "https://webcompat.com/issues/new?url=%s&label=browser-fenix" + const val REPORT_SITE_ISSUE_URL = "https://webcompat.com/issues/new?url=%s&label=browser-fenix" } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index e281337cc..a4a9aa815 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -80,6 +80,16 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse requireComponents.search.searchEngineManager.getDefaultSearchEngine(it).name } + val trackingProtectionPreference = + findPreference(getString(R.string.pref_key_tracking_protection_settings)) + trackingProtectionPreference?.summary = context?.let { + if (org.mozilla.fenix.utils.Settings.getInstance(it).shouldUseTrackingProtection) { + getString(R.string.tracking_protection_on) + } else { + getString(R.string.tracking_protection_off) + } + } + val themesPreference = findPreference(getString(R.string.pref_key_theme)) themesPreference?.summary = context?.let { 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 3662957ea..b689df046 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt @@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.requireComponents @@ -40,6 +41,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() { sessions.forEach { getEngineSession(it)?.enableTrackingProtection(policy) } } } + requireContext().components.useCases.sessionUseCases.reload.invoke() true } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsComponent.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsComponent.kt index 7b026717d..b3a6b48a3 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsComponent.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsComponent.kt @@ -35,6 +35,7 @@ class QuickSettingsComponent( mode = QuickSettingsState.Mode.Normal( change.url, change.isSecured, + change.isTrackingProtectionOn, change.sitePermissions ) ) @@ -97,15 +98,27 @@ class QuickSettingsComponent( data class QuickSettingsState(val mode: Mode) : ViewState { sealed class Mode { - data class Normal(val url: String, val isSecured: Boolean, val sitePermissions: SitePermissions?) : Mode() - data class ActionLabelUpdated(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) : + data class Normal( + val url: String, + val isSecured: Boolean, + val isTrackingProtectionOn: Boolean, + val sitePermissions: SitePermissions? + ) : Mode() + + data class ActionLabelUpdated( + val phoneFeature: PhoneFeature, + val sitePermissions: SitePermissions? + ) : Mode() - data class CheckPendingFeatureBlockedByAndroid(val sitePermissions: SitePermissions?) : Mode() + data class CheckPendingFeatureBlockedByAndroid(val sitePermissions: SitePermissions?) : + Mode() } } sealed class QuickSettingsAction : Action { + data class SelectReportProblem(val url: String) : QuickSettingsAction() + data class ToggleTrackingProtection(val trackingProtection: Boolean) : QuickSettingsAction() data class SelectBlockedByAndroid(val permissions: Array) : QuickSettingsAction() data class TogglePermission(val featurePhone: PhoneFeature) : QuickSettingsAction() } @@ -114,6 +127,7 @@ sealed class QuickSettingsChange : Change { data class Change( val url: String, val isSecured: Boolean, + val isTrackingProtectionOn: Boolean, val sitePermissions: SitePermissions? ) : QuickSettingsChange() diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt index 28a9620d5..dc0534d3d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt @@ -10,6 +10,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout +import androidx.preference.PreferenceManager import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,7 +18,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import mozilla.components.feature.sitepermissions.SitePermissions import org.mozilla.fenix.R +import org.mozilla.fenix.browser.BrowserFragment 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 @@ -27,6 +30,7 @@ import kotlin.coroutines.CoroutineContext private const val KEY_URL = "KEY_URL" private const val KEY_IS_SECURED = "KEY_IS_SECURED" private const val KEY_SITE_PERMISSIONS = "KEY_SITE_PERMISSIONS" +private const val KEY_IS_TP_ON = "KEY_IS_TP_ON" private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4 @SuppressWarnings("TooManyFunctions") @@ -34,6 +38,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS private val safeArguments get() = requireNotNull(arguments) private val url: String by lazy { safeArguments.getString(KEY_URL) } private val isSecured: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SECURED) } + private val isTrackingProtectionOn: Boolean by lazy { safeArguments.getBoolean(KEY_IS_TP_ON) } private lateinit var quickSettingsComponent: QuickSettingsComponent private lateinit var job: Job @@ -59,7 +64,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS quickSettingsComponent = QuickSettingsComponent( rootView as ConstraintLayout, ActionBusFactory.get(this), QuickSettingsState( - QuickSettingsState.Mode.Normal(url, isSecured, sitePermissions) + QuickSettingsState.Mode.Normal(url, isSecured, isTrackingProtectionOn, sitePermissions) ) ) } @@ -70,6 +75,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS fun newInstance( url: String, isSecured: Boolean, + isTrackingProtectionOn: Boolean, sitePermissions: SitePermissions? ): QuickSettingsSheetDialogFragment { @@ -79,6 +85,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS with(arguments) { putString(KEY_URL, url) putBoolean(KEY_IS_SECURED, isSecured) + putBoolean(KEY_IS_TP_ON, isTrackingProtectionOn) putParcelable(KEY_SITE_PERMISSIONS, sitePermissions) } fragment.arguments = arguments @@ -110,6 +117,37 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineS is QuickSettingsAction.SelectBlockedByAndroid -> { requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS) } + is QuickSettingsAction.SelectReportProblem -> { + launch(Dispatchers.Main) { + val reportUrl = + String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, it.url) + requireComponents.useCases.sessionUseCases.loadUrl.invoke(reportUrl) + } + } + is QuickSettingsAction.ToggleTrackingProtection -> { + val trackingEnabled = it.trackingProtection + with(requireComponents.core) { + val policy = + createTrackingProtectionPolicy(trackingEnabled) + PreferenceManager.getDefaultSharedPreferences(context).edit() + .putBoolean( + context!!.getString(R.string.pref_key_tracking_protection), + trackingEnabled + ).apply() + engine.settings.trackingProtectionPolicy = policy + + with(sessionManager) { + sessions.forEach { + getEngineSession(it)?.enableTrackingProtection( + policy + ) + } + } + } + launch(Dispatchers.Main) { + requireContext().components.useCases.sessionUseCases.reload.invoke() + } + } is QuickSettingsAction.TogglePermission -> { launch { diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsUIView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsUIView.kt index 7ba7a9ab1..d6963d9f3 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsUIView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsUIView.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.widget.Switch import android.widget.TextView import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.AppCompatTextView @@ -19,6 +20,7 @@ import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes import mozilla.components.support.ktx.kotlin.toUri +import org.mozilla.fenix.DefaultThemeManager import org.mozilla.fenix.R import org.mozilla.fenix.mvi.UIView import org.mozilla.fenix.settings.PhoneFeature @@ -28,6 +30,7 @@ import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION import org.mozilla.fenix.utils.Settings +@Suppress("TooManyFunctions") class QuickSettingsUIView( container: ViewGroup, actionEmitter: Observer, @@ -38,6 +41,8 @@ class QuickSettingsUIView( ) { private val securityInfoLabel: TextView private val urlLabel: TextView + private val trackingProtectionSwitch: Switch + private val reportProblemAction: TextView private val cameraActionLabel: TextView private val cameraLabel: TextView private val microphoneActionLabel: TextView @@ -53,6 +58,8 @@ class QuickSettingsUIView( init { urlLabel = view.findViewById(R.id.url) securityInfoLabel = view.findViewById(R.id.security_info) + trackingProtectionSwitch = view.findViewById(R.id.tracking_protection) + reportProblemAction = view.findViewById(R.id.report_problem) cameraActionLabel = view.findViewById(R.id.camera_action_label) cameraLabel = view.findViewById(R.id.camera_icon) microphoneActionLabel = view.findViewById(R.id.microphone_action_label) @@ -68,6 +75,8 @@ class QuickSettingsUIView( is QuickSettingsState.Mode.Normal -> { bindUrl(state.mode.url) bindSecurityInfo(state.mode.isSecured) + bindReportProblemAction(state.mode.url) + bindTrackingProtectionInfo(state.mode.isTrackingProtectionOn) bindPhoneFeatureItem(cameraActionLabel, CAMERA, state.mode.sitePermissions) bindPhoneFeatureItem(microphoneActionLabel, MICROPHONE, state.mode.sitePermissions) bindPhoneFeatureItem(notificationActionLabel, NOTIFICATION, state.mode.sitePermissions) @@ -90,6 +99,38 @@ class QuickSettingsUIView( urlLabel.text = url.toUri().hostWithoutCommonPrefixes } + private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) { + val drawableId = + if (isTrackingProtectionOn) R.drawable.ic_tracking_protection else + R.drawable.ic_tracking_protection_disabled + val drawableTint = if (isTrackingProtectionOn) DefaultThemeManager.resolveAttribute( + R.attr.primaryText, + context + ) else DefaultThemeManager.resolveAttribute(R.attr.neutral, context) + val icon = AppCompatResources.getDrawable(context, drawableId) + val resolvedColor = ContextCompat.getColor(context, drawableTint) + icon?.setTint(resolvedColor) + trackingProtectionSwitch.setTextColor(resolvedColor) + trackingProtectionSwitch.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null) + trackingProtectionSwitch.isChecked = isTrackingProtectionOn + + trackingProtectionSwitch.setOnCheckedChangeListener { _, isChecked -> + actionEmitter.onNext( + QuickSettingsAction.ToggleTrackingProtection( + isChecked + ) + ) + } + } + + private fun bindReportProblemAction(url: String) { + reportProblemAction.setOnClickListener { + actionEmitter.onNext( + QuickSettingsAction.SelectReportProblem(url) + ) + } + } + private fun bindSecurityInfo(isSecured: Boolean) { val stringId: Int val drawableId: Int diff --git a/app/src/main/res/drawable/ic_tracking_protection_disabled.xml b/app/src/main/res/drawable/ic_tracking_protection_disabled.xml new file mode 100644 index 000000000..f621408ea --- /dev/null +++ b/app/src/main/res/drawable/ic_tracking_protection_disabled.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml index 7c9fd5671..a5639e13b 100644 --- a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml +++ b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml @@ -35,14 +35,48 @@ app:layout_constraintTop_toBottomOf="@id/url"/> + android:id="@+id/line_divider_security" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + android:background="?neutral" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/security_info" /> + + + + + + Blocked by Android Exceptions + + Report a problem + + On + + Off