parent
5d8a9bb4be
commit
35a132d7ff
|
@ -3,6 +3,7 @@ package org.mozilla.fenix.ui
|
|||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
|
@ -36,6 +37,7 @@ class LibraryMenuTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Intermittently failing. See https://github.com/mozilla-mobile/fenix/issues/9287")
|
||||
fun libraryMenuItemsTest() {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
|
@ -50,6 +52,7 @@ class LibraryMenuTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Intermittent failure. See https://github.com/mozilla-mobile/fenix/issues/9232")
|
||||
fun backButtonTest() {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
|
|
|
@ -391,6 +391,13 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
view = view
|
||||
)
|
||||
|
||||
context.settings().setSitePermissionSettingListener(viewLifecycleOwner) {
|
||||
// If the user connects to WIFI while on the BrowserFragment, this will update the
|
||||
// SitePermissionsRules (specifically autoplay) accordingly
|
||||
this.context?.let { assignSitePermissionsRules(it) }
|
||||
}
|
||||
assignSitePermissionsRules(context)
|
||||
|
||||
fullScreenFeature.set(
|
||||
feature = FullScreenFeature(
|
||||
sessionManager,
|
||||
|
@ -541,8 +548,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
components.useCases.sessionUseCases.reload()
|
||||
}
|
||||
hideToolbar()
|
||||
|
||||
assignSitePermissionsRules()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
|
@ -673,8 +678,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
/**
|
||||
* Updates the site permissions rules based on user settings.
|
||||
*/
|
||||
private fun assignSitePermissionsRules() {
|
||||
val settings = requireContext().settings()
|
||||
private fun assignSitePermissionsRules(context: Context) {
|
||||
val settings = context.settings()
|
||||
|
||||
val rules: SitePermissionsRules = settings.getSitePermissionsCustomSettingsRules()
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.net.toUri
|
||||
|
@ -17,8 +18,11 @@ import mozilla.components.feature.tabs.TabsUseCases
|
|||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
import org.mozilla.fenix.utils.ClipboardHandler
|
||||
import org.mozilla.fenix.wifi.WifiConnectionMonitor
|
||||
import org.mozilla.fenix.wifi.WifiIntegration
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val DAY_IN_MINUTES = 24 * 60L
|
||||
|
@ -96,4 +100,12 @@ class Components(private val context: Context) {
|
|||
val migrationStore by lazy { MigrationStore() }
|
||||
val performance by lazy { PerformanceComponent() }
|
||||
val push by lazy { Push(context, analytics.crashReporter) }
|
||||
val wifiIntegration by lazy {
|
||||
WifiIntegration(
|
||||
settings = context.settings(),
|
||||
wifiConnectionMonitor = WifiConnectionMonitor(
|
||||
context as Application
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
|||
import mozilla.components.support.ktx.android.content.isPermissionGranted
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.getPreferenceKey
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_ALLOW_ALL
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_ALLOW_ON_WIFI
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_AUDIBLE
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import android.Manifest.permission.CAMERA as CAMERA_PERMISSION
|
||||
|
||||
|
@ -49,11 +53,13 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>)
|
|||
when (isAndroidPermissionGranted(context)) {
|
||||
false -> R.string.phone_feature_blocked_by_android
|
||||
else -> when (this) {
|
||||
AUTOPLAY_AUDIBLE, AUTOPLAY_INAUDIBLE -> {
|
||||
when (getStatus(sitePermissions, settings)) {
|
||||
SitePermissions.Status.BLOCKED -> R.string.preference_option_autoplay_blocked
|
||||
SitePermissions.Status.ALLOWED -> R.string.preference_option_autoplay_allowed
|
||||
else -> R.string.preference_option_autoplay_allowed
|
||||
AUTOPLAY_AUDIBLE -> {
|
||||
when (settings?.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) ?: AUTOPLAY_BLOCK_ALL) {
|
||||
AUTOPLAY_ALLOW_ALL -> R.string.preference_option_autoplay_allowed2
|
||||
AUTOPLAY_ALLOW_ON_WIFI -> R.string.preference_option_autoplay_allowed_wifi_only2
|
||||
AUTOPLAY_BLOCK_AUDIBLE -> R.string.preference_option_autoplay_block_audio2
|
||||
AUTOPLAY_BLOCK_ALL -> R.string.preference_option_autoplay_blocked3
|
||||
else -> R.string.preference_option_autoplay_blocked3
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
|
|
@ -61,10 +61,17 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
|
|||
val settings = context.settings()
|
||||
|
||||
val summary = phoneFeature.getActionLabel(context, settings = settings)
|
||||
// Remove autoplaySummary after https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed
|
||||
val autoplaySummary =
|
||||
if (summary == context.getString(R.string.preference_option_autoplay_allowed2)) {
|
||||
context.getString(R.string.preference_option_autoplay_allowed_wifi_only2)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val preferenceKey = phoneFeature.getPreferenceKey(context)
|
||||
|
||||
val cameraPhoneFeatures: Preference = requireNotNull(findPreference(preferenceKey))
|
||||
cameraPhoneFeatures.summary = summary
|
||||
cameraPhoneFeatures.summary = autoplaySummary ?: summary
|
||||
|
||||
cameraPhoneFeatures.onPreferenceClickListener = OnPreferenceClickListener {
|
||||
navigateToPhoneFeature(phoneFeature)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.settings.sitepermissions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
|
@ -20,18 +21,30 @@ import android.view.ViewGroup
|
|||
import android.widget.Button
|
||||
import android.widget.RadioButton
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.ask_to_allow_radio
|
||||
import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.block_radio
|
||||
import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.fourth_radio
|
||||
import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.third_radio
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ALLOWED
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ASK_TO_ALLOW
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.BLOCKED
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_AUDIBLE
|
||||
import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_INAUDIBLE
|
||||
import org.mozilla.fenix.settings.initBlockedByAndroidView
|
||||
import org.mozilla.fenix.settings.setStartCheckedIndicator
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
const val AUTOPLAY_BLOCK_ALL = 0
|
||||
const val AUTOPLAY_BLOCK_AUDIBLE = 1
|
||||
const val AUTOPLAY_ALLOW_ON_WIFI = 2
|
||||
const val AUTOPLAY_ALLOW_ALL = 3
|
||||
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
class SitePermissionsManagePhoneFeatureFragment : Fragment() {
|
||||
private lateinit var phoneFeature: PhoneFeature
|
||||
|
@ -62,6 +75,8 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() {
|
|||
|
||||
initFirstRadio(rootView)
|
||||
initSecondRadio(rootView)
|
||||
initThirdRadio(rootView)
|
||||
initFourthRadio(rootView)
|
||||
bindBlockedByAndroidContainer(rootView)
|
||||
|
||||
return rootView
|
||||
|
@ -73,66 +88,133 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() {
|
|||
}
|
||||
|
||||
private fun initFirstRadio(rootView: View) {
|
||||
val radio = rootView.findViewById<RadioButton>(R.id.ask_to_allow_radio)
|
||||
val askToAllowText = when (phoneFeature) {
|
||||
PhoneFeature.AUTOPLAY_AUDIBLE ->
|
||||
getString(R.string.preference_option_autoplay_blocked)
|
||||
else -> getString(R.string.preference_option_phone_feature_ask_to_allow)
|
||||
with(rootView.ask_to_allow_radio) {
|
||||
if (phoneFeature == AUTOPLAY_AUDIBLE) {
|
||||
// Disabled because GV does not allow this setting. TODO Reenable after
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed
|
||||
// text = getString(R.string.preference_option_autoplay_allowed2)
|
||||
// setOnClickListener {
|
||||
// saveActionInSettings(it.context, AUTOPLAY_ALLOW_ALL)
|
||||
// }
|
||||
// restoreState(AUTOPLAY_ALLOW_ALL)
|
||||
visibility = View.GONE
|
||||
} else {
|
||||
text = getCombinedLabel(
|
||||
getString(R.string.preference_option_phone_feature_ask_to_allow),
|
||||
getString(R.string.phone_feature_recommended)
|
||||
)
|
||||
setOnClickListener {
|
||||
saveActionInSettings(ASK_TO_ALLOW)
|
||||
}
|
||||
restoreState(ASK_TO_ALLOW)
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
val recommendedText = getString(R.string.phone_feature_recommended)
|
||||
val recommendedTextSize =
|
||||
resources.getDimensionPixelSize(R.dimen.phone_feature_label_recommended_text_size)
|
||||
val recommendedSpannable = SpannableString(recommendedText)
|
||||
|
||||
recommendedSpannable.setSpan(
|
||||
ForegroundColorSpan(Color.GRAY),
|
||||
0,
|
||||
recommendedSpannable.length,
|
||||
SPAN_EXCLUSIVE_INCLUSIVE
|
||||
)
|
||||
|
||||
recommendedSpannable.setSpan(
|
||||
AbsoluteSizeSpan(recommendedTextSize), 0,
|
||||
recommendedSpannable.length,
|
||||
SPAN_EXCLUSIVE_INCLUSIVE
|
||||
)
|
||||
|
||||
radio.text = with(SpannableStringBuilder()) {
|
||||
append(askToAllowText)
|
||||
append("\n")
|
||||
append(recommendedSpannable)
|
||||
this
|
||||
}
|
||||
val expectedAction = if (phoneFeature == PhoneFeature.AUTOPLAY_AUDIBLE) BLOCKED else ASK_TO_ALLOW
|
||||
radio.setOnClickListener {
|
||||
saveActionInSettings(expectedAction)
|
||||
}
|
||||
radio.restoreState(expectedAction)
|
||||
}
|
||||
|
||||
private fun RadioButton.restoreState(action: SitePermissionsRules.Action) {
|
||||
if (phoneFeature.getAction(settings) == action) {
|
||||
private fun initSecondRadio(rootView: View) {
|
||||
with(rootView.block_radio) {
|
||||
if (phoneFeature == AUTOPLAY_AUDIBLE) {
|
||||
text = getCombinedLabel(
|
||||
getString(R.string.preference_option_autoplay_allowed_wifi_only2),
|
||||
getString(R.string.preference_option_autoplay_allowed_wifi_subtext)
|
||||
)
|
||||
setOnClickListener {
|
||||
// TODO replace with AUTOPLAY_ALLOW_ON_WIFI when
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed. This GV bug
|
||||
// makes ALLOW_ALL behave as ALLOW_ON_WIFI
|
||||
saveActionInSettings(it.context, AUTOPLAY_ALLOW_ALL)
|
||||
}
|
||||
// TODO replace with AUTOPLAY_ALLOW_ON_WIFI when
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed. This GV bug
|
||||
// makes ALLOW_ALL behave as ALLOW_ON_WIFI
|
||||
restoreState(AUTOPLAY_ALLOW_ALL)
|
||||
} else {
|
||||
text = getString(R.string.preference_option_phone_feature_blocked)
|
||||
setOnClickListener {
|
||||
saveActionInSettings(BLOCKED)
|
||||
}
|
||||
restoreState(BLOCKED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initThirdRadio(rootView: View) {
|
||||
with(rootView.third_radio) {
|
||||
if (phoneFeature == AUTOPLAY_AUDIBLE) {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.preference_option_autoplay_block_audio2)
|
||||
setOnClickListener {
|
||||
saveActionInSettings(it.context, AUTOPLAY_BLOCK_AUDIBLE)
|
||||
}
|
||||
restoreState(AUTOPLAY_BLOCK_AUDIBLE)
|
||||
} else {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFourthRadio(rootView: View) {
|
||||
with(rootView.fourth_radio) {
|
||||
if (phoneFeature == AUTOPLAY_AUDIBLE) {
|
||||
visibility = View.VISIBLE
|
||||
text = getCombinedLabel(
|
||||
getString(R.string.preference_option_autoplay_blocked3),
|
||||
getString(R.string.phone_feature_recommended)
|
||||
)
|
||||
setOnClickListener {
|
||||
saveActionInSettings(it.context, AUTOPLAY_BLOCK_ALL)
|
||||
}
|
||||
restoreState(AUTOPLAY_BLOCK_ALL)
|
||||
} else {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RadioButton.restoreState(buttonAction: SitePermissionsRules.Action) {
|
||||
if (phoneFeature.getAction(settings) == buttonAction) {
|
||||
this.isChecked = true
|
||||
this.setStartCheckedIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSecondRadio(rootView: View) {
|
||||
val radio = rootView.findViewById<RadioButton>(R.id.block_radio)
|
||||
radio.text = when (phoneFeature) {
|
||||
PhoneFeature.AUTOPLAY_AUDIBLE, PhoneFeature.AUTOPLAY_INAUDIBLE ->
|
||||
getString(R.string.preference_option_autoplay_allowed)
|
||||
else -> getString(R.string.preference_option_phone_feature_blocked)
|
||||
private fun RadioButton.restoreState(buttonAutoplaySetting: Int) {
|
||||
if (settings.getAutoplayUserSetting(AUTOPLAY_BLOCK_ALL) == buttonAutoplaySetting) {
|
||||
this.isChecked = true
|
||||
this.setStartCheckedIndicator()
|
||||
}
|
||||
val expectedAction = if (phoneFeature == PhoneFeature.AUTOPLAY_AUDIBLE) ALLOWED else BLOCKED
|
||||
radio.setOnClickListener {
|
||||
saveActionInSettings(expectedAction)
|
||||
}
|
||||
|
||||
private fun saveActionInSettings(action: SitePermissionsRules.Action) {
|
||||
settings.setSitePermissionsPhoneFeatureAction(phoneFeature, action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the user selected autoplay setting.
|
||||
*
|
||||
* See [Settings.setAutoplayUserSetting] kdoc for an explanation of why this cannot follow the
|
||||
* same code path as other permissions.
|
||||
*/
|
||||
private fun saveActionInSettings(context: Context, autoplaySetting: Int) {
|
||||
settings.setAutoplayUserSetting(autoplaySetting)
|
||||
val (audible, inaudible) = when (autoplaySetting) {
|
||||
AUTOPLAY_ALLOW_ALL -> ALLOWED to ALLOWED
|
||||
AUTOPLAY_ALLOW_ON_WIFI -> {
|
||||
context.components.wifiIntegration.addWifiConnectedListener()
|
||||
return
|
||||
}
|
||||
AUTOPLAY_BLOCK_AUDIBLE -> BLOCKED to ALLOWED
|
||||
AUTOPLAY_BLOCK_ALL -> BLOCKED to BLOCKED
|
||||
else -> return
|
||||
}
|
||||
radio.restoreState(expectedAction)
|
||||
context.components.wifiIntegration.removeWifiConnectedListener()
|
||||
settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_AUDIBLE, audible)
|
||||
settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_INAUDIBLE, inaudible)
|
||||
}
|
||||
|
||||
private fun bindBlockedByAndroidContainer(rootView: View) {
|
||||
blockedByAndroidView = rootView.findViewById<View>(R.id.permissions_blocked_container)
|
||||
blockedByAndroidView = rootView.findViewById(R.id.permissions_blocked_container)
|
||||
initSettingsButton(blockedByAndroidView)
|
||||
}
|
||||
|
||||
|
@ -158,7 +240,32 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() {
|
|||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun saveActionInSettings(action: SitePermissionsRules.Action) {
|
||||
settings.setSitePermissionsPhoneFeatureAction(phoneFeature, action)
|
||||
/**
|
||||
* Returns a [CharSequence] that arranges and styles [mainText], a line break, and then [subText]
|
||||
*/
|
||||
private fun getCombinedLabel(mainText: CharSequence, subText: CharSequence): CharSequence {
|
||||
val subTextSize =
|
||||
resources.getDimensionPixelSize(R.dimen.phone_feature_label_recommended_text_size)
|
||||
val recommendedSpannable = SpannableString(subText)
|
||||
|
||||
recommendedSpannable.setSpan(
|
||||
ForegroundColorSpan(Color.GRAY),
|
||||
0,
|
||||
recommendedSpannable.length,
|
||||
SPAN_EXCLUSIVE_INCLUSIVE
|
||||
)
|
||||
|
||||
recommendedSpannable.setSpan(
|
||||
AbsoluteSizeSpan(subTextSize), 0,
|
||||
recommendedSpannable.length,
|
||||
SPAN_EXCLUSIVE_INCLUSIVE
|
||||
)
|
||||
|
||||
return with(SpannableStringBuilder()) {
|
||||
append(mainText)
|
||||
append("\n")
|
||||
append(recommendedSpannable)
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package org.mozilla.fenix.utils;
|
||||
|
||||
/**
|
||||
* Functional interface for listening to when wifi is/is not connected.
|
||||
*
|
||||
* This is not a () -> Boolean so that method parameters can be more clearly typed.
|
||||
*
|
||||
* This file is in Java because of the SAM conversion problem in Kotlin.
|
||||
* See https://youtrack.jetbrains.com/issue/KT-7770.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface OnWifiChanged {
|
||||
void invoke(boolean Connected);
|
||||
}
|
|
@ -12,6 +12,7 @@ import android.content.SharedPreferences
|
|||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayAction
|
||||
|
@ -29,8 +30,11 @@ import org.mozilla.fenix.components.metrics.MozillaProductDetector
|
|||
import org.mozilla.fenix.ext.getPreferenceKey
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType
|
||||
import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING"
|
||||
|
||||
/**
|
||||
* A simple wrapper for SharedPreferences that makes reading preference a little bit easier.
|
||||
*/
|
||||
|
@ -175,10 +179,6 @@ class Settings private constructor(
|
|||
default = true
|
||||
)
|
||||
|
||||
val isAutoPlayEnabled = getSitePermissionsPhoneFeatureAction(
|
||||
PhoneFeature.AUTOPLAY_AUDIBLE, Action.BLOCKED
|
||||
) != Action.BLOCKED
|
||||
|
||||
private var trackingProtectionOnboardingShownThisSession = false
|
||||
var isOverrideTPPopupsForPerformanceTest = false
|
||||
|
||||
|
@ -452,6 +452,34 @@ class Settings private constructor(
|
|||
) =
|
||||
preferences.getInt(feature.getPreferenceKey(appContext), default.toInt()).toAction()
|
||||
|
||||
/**
|
||||
* Saves the user selected autoplay setting.
|
||||
*
|
||||
* Under the hood, autoplay is represented by two settings, [AUTOPLAY_AUDIBLE] and
|
||||
* [AUTOPLAY_INAUDIBLE]. The user selection cannot be inferred from the combination of these
|
||||
* settings because, while on [AUTOPLAY_ALLOW_ON_WIFI], they will be indistinguishable from
|
||||
* either [AUTOPLAY_ALLOW_ALL] or [AUTOPLAY_BLOCK_ALL]. Because of this, we are forced to save
|
||||
* the user selected setting as well.
|
||||
*/
|
||||
fun setAutoplayUserSetting(
|
||||
autoplaySetting: Int
|
||||
) {
|
||||
preferences.edit().putInt(AUTOPLAY_USER_SETTING, autoplaySetting).apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user selected autoplay setting.
|
||||
*
|
||||
* Under the hood, autoplay is represented by two settings, [AUTOPLAY_AUDIBLE] and
|
||||
* [AUTOPLAY_INAUDIBLE]. The user selection cannot be inferred from the combination of these
|
||||
* settings because, while on [AUTOPLAY_ALLOW_ON_WIFI], they will be indistinguishable from
|
||||
* either [AUTOPLAY_ALLOW_ALL] or [AUTOPLAY_BLOCK_ALL]. Because of this, we are forced to save
|
||||
* the user selected setting as well.
|
||||
*/
|
||||
fun getAutoplayUserSetting(
|
||||
default: Int
|
||||
) = preferences.getInt(AUTOPLAY_USER_SETTING, default)
|
||||
|
||||
fun getSitePermissionsPhoneFeatureAutoplayAction(
|
||||
feature: PhoneFeature,
|
||||
default: AutoplayAction = AutoplayAction.BLOCKED
|
||||
|
@ -471,11 +499,25 @@ class Settings private constructor(
|
|||
location = getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION),
|
||||
camera = getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA),
|
||||
autoplayAudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_AUDIBLE),
|
||||
// TODO autoplayInaudible will be hardcoded until additional options are added in #8017
|
||||
autoplayInaudible = AutoplayAction.ALLOWED
|
||||
autoplayInaudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_INAUDIBLE)
|
||||
)
|
||||
}
|
||||
|
||||
fun setSitePermissionSettingListener(lifecycleOwner: LifecycleOwner, listener: () -> Unit) {
|
||||
val sitePermissionKeys = listOf(
|
||||
PhoneFeature.NOTIFICATION,
|
||||
PhoneFeature.MICROPHONE,
|
||||
PhoneFeature.LOCATION,
|
||||
PhoneFeature.CAMERA,
|
||||
PhoneFeature.AUTOPLAY_AUDIBLE,
|
||||
PhoneFeature.AUTOPLAY_INAUDIBLE
|
||||
).map { it.getPreferenceKey(appContext) }
|
||||
|
||||
preferences.registerOnSharedPreferenceChangeListener(lifecycleOwner) { _, key ->
|
||||
if (key in sitePermissionKeys) listener.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
var shouldPromptToSaveLogins by booleanPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_save_logins),
|
||||
default = true
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* 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.wifi
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
|
||||
/**
|
||||
* Attaches itself to the [Application] and listens for WIFI available/not available events. This
|
||||
* allows calling code to set simpler listeners.
|
||||
*
|
||||
* Example:
|
||||
* ```kotlin
|
||||
* app.components.wifiConnectionListener.addOnWifiConnectedChangedListener { isConnected ->
|
||||
* if (isConnected) {
|
||||
* downloadThing()
|
||||
* }
|
||||
* }
|
||||
* app.components.wifiConnectionListener.start()
|
||||
* ```
|
||||
*/
|
||||
class WifiConnectionMonitor(app: Application) {
|
||||
private val callbacks = mutableSetOf<(Boolean) -> Unit>()
|
||||
private val connectivityManager = app.getSystemService(Context.CONNECTIVITY_SERVICE) as
|
||||
ConnectivityManager
|
||||
|
||||
private var lastKnownStateWasAvailable: Boolean? = null
|
||||
private var isRegistered = false
|
||||
|
||||
private val frameworkListener = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onLost(network: Network?) {
|
||||
callbacks.forEach { it(false) }
|
||||
lastKnownStateWasAvailable = false
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network?) {
|
||||
callbacks.forEach { it(true) }
|
||||
lastKnownStateWasAvailable = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the [WifiConnectionMonitor] to the application. After this has been called, callbacks
|
||||
* added via [addOnWifiConnectedChangedListener] will be called until either the app exits, or
|
||||
* [stop] is called.
|
||||
*
|
||||
* Any existing callbacks will be called with the current state when this is called.
|
||||
*/
|
||||
fun start() {
|
||||
// Framework code throws if a listener is registered twice without unregistering.
|
||||
if (isRegistered) return
|
||||
val request = NetworkRequest.Builder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.build()
|
||||
|
||||
// AFAICT, the framework does not send an event when a new NetworkCallback is registered
|
||||
// while the WIFI is not connected, so we push this manually. If the WIFI is on, it will send
|
||||
// a follow up event shortly
|
||||
val noCallbacksReceivedYet = lastKnownStateWasAvailable == null
|
||||
if (noCallbacksReceivedYet) {
|
||||
lastKnownStateWasAvailable = false
|
||||
callbacks.forEach { it(false) }
|
||||
}
|
||||
|
||||
connectivityManager.registerNetworkCallback(request, frameworkListener)
|
||||
isRegistered = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Detatches the [WifiConnectionMonitor] from the app. No callbacks added via
|
||||
* [addOnWifiConnectedChangedListener] will be called after this has been called.
|
||||
*/
|
||||
fun stop() {
|
||||
// Framework code will throw if an unregistered listener attempts to unregister.
|
||||
if (!isRegistered) return
|
||||
connectivityManager.unregisterNetworkCallback(frameworkListener)
|
||||
isRegistered = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds [onWifiChanged] to a list of listeners that will be called whenever WIFI connects or
|
||||
* disconnects.
|
||||
*
|
||||
* If [onWifiChanged] is successfully added (i.e., it is a new listener), it will be immediately
|
||||
* called with the last known state.
|
||||
*/
|
||||
fun addOnWifiConnectedChangedListener(onWifiChanged: (Boolean) -> Unit) {
|
||||
val lastKnownState = lastKnownStateWasAvailable
|
||||
if (callbacks.add(onWifiChanged) && lastKnownState != null) {
|
||||
onWifiChanged(lastKnownState)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes [onWifiChanged] from the list of listeners to be called whenever WIFI connects or
|
||||
* disconnects.
|
||||
*/
|
||||
fun removeOnWifiConnectedChangedListener(onWifiChanged: (Boolean) -> Unit) {
|
||||
callbacks.remove(onWifiChanged)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* 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.wifi
|
||||
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_ALLOW_ON_WIFI
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* Handles implementation details of only setting up a WIFI connectivity listener if the current
|
||||
* user settings require it.
|
||||
*/
|
||||
class WifiIntegration(private val settings: Settings, private val wifiConnectionMonitor: WifiConnectionMonitor) {
|
||||
|
||||
/**
|
||||
* Adds listener for autplay setting [AUTOPLAY_ALLOW_ON_WIFI]. Sets all autoplay to allowed when
|
||||
* WIFI is connected, blocked otherwise.
|
||||
*/
|
||||
private val wifiConnectedListener: ((Boolean) -> Unit) by lazy {
|
||||
{ connected: Boolean ->
|
||||
val setting =
|
||||
if (connected) SitePermissionsRules.Action.ALLOWED else SitePermissionsRules.Action.BLOCKED
|
||||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_AUDIBLE, setting)
|
||||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, setting)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If autoplay is only enabled on WIFI, sets a WIFI listener to set them accordingly. Otherwise
|
||||
* noop.
|
||||
*/
|
||||
fun maybeAddWifiConnectedListener() {
|
||||
if (settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) == AUTOPLAY_ALLOW_ON_WIFI) {
|
||||
addWifiConnectedListener()
|
||||
}
|
||||
}
|
||||
|
||||
fun addWifiConnectedListener() {
|
||||
wifiConnectionMonitor.addOnWifiConnectedChangedListener(wifiConnectedListener)
|
||||
}
|
||||
|
||||
fun removeWifiConnectedListener() {
|
||||
wifiConnectionMonitor.removeOnWifiConnectedChangedListener(wifiConnectedListener)
|
||||
}
|
||||
|
||||
// Until https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed, AUTOPLAY_ALLOW_ALL
|
||||
// only works while WIFI is active, so we are not using AUTOPLAY_ALLOW_ON_WIFI (or this class).
|
||||
// Once that is fixed, [start] and [maybeAddWifiConnectedListener] will need to be called on
|
||||
// activity startup.
|
||||
fun start() { wifiConnectionMonitor.start() }
|
||||
|
||||
fun stop() { wifiConnectionMonitor.stop() }
|
||||
}
|
|
@ -3,11 +3,11 @@
|
|||
- 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:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
@ -21,31 +21,59 @@
|
|||
android:id="@+id/ask_to_allow_radio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preference_option_phone_feature_ask_to_allow"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:button="@null"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle"
|
||||
android:drawablePadding="@dimen/radio_button_preference_drawable_padding"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingStart="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"/>
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle"
|
||||
tools:text="@string/preference_option_phone_feature_ask_to_allow" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/block_radio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/radio_button_preference_height"
|
||||
android:text="@string/preference_option_phone_feature_blocked"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:button="@null"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle"
|
||||
android:drawablePadding="@dimen/radio_button_preference_drawable_padding"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingStart="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"/>
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle"
|
||||
tools:text="@string/preference_option_phone_feature_blocked" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/third_radio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:button="@null"
|
||||
android:drawablePadding="@dimen/radio_button_preference_drawable_padding"
|
||||
android:paddingStart="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/fourth_radio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:button="@null"
|
||||
android:drawablePadding="@dimen/radio_button_preference_drawable_padding"
|
||||
android:paddingStart="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingTop="@dimen/radio_button_preference_vertical"
|
||||
android:paddingEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingBottom="@dimen/radio_button_preference_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle" />
|
||||
</RadioGroup>
|
||||
|
||||
<include layout="@layout/component_permissions_blocked_by_android"/>
|
||||
|
|
|
@ -596,18 +596,16 @@
|
|||
<string name="tracking_protection_on">On</string>
|
||||
<!-- Summary of tracking protection preference if tracking protection is set to off -->
|
||||
<string name="tracking_protection_off">Off</string>
|
||||
<!-- DEPRECATED: Label that indicates video and audio autoplay is blocked -->
|
||||
<string name="preference_option_autoplay_blocked">Video and audio blocked</string>
|
||||
<!-- DEPRECATED: Label that indicates video and audio autoplay is allowed -->
|
||||
<string name="preference_option_autoplay_allowed">Video and audio allowed</string>
|
||||
<!-- Label that indicates that all video and audio autoplay is allowed -->
|
||||
<string name="preference_option_autoplay_allowed2">Allow audio and video</string>
|
||||
<!-- Label that indicates that video and audio autoplay is only allowed over WIFI -->
|
||||
<string name="preference_option_autoplay_allowed_wifi_only">Allow audio and video on Wi-Fi only</string>
|
||||
<!-- Label that indicates that video and audio autoplay is only allowed over Wi-Fi -->
|
||||
<string name="preference_option_autoplay_allowed_wifi_only2">Block audio and video on cellular data only</string>
|
||||
<!-- Subtext that explains 'autoplay on Wi-Fi only' option -->
|
||||
<string name="preference_option_autoplay_allowed_wifi_subtext">Audio and video will play on Wi-Fi</string>
|
||||
<!-- Label that indicates that video autoplay is allowed, but audio autoplay is blocked -->
|
||||
<string name="preference_option_autoplay_block_audio">Block audio</string>
|
||||
<string name="preference_option_autoplay_block_audio2">Block audio only</string>
|
||||
<!-- Label that indicates that all video and audio autoplay is blocked -->
|
||||
<string name="preference_option_autoplay_blocked2">Block video and audio</string>
|
||||
<string name="preference_option_autoplay_blocked3">Block audio and video</string>
|
||||
<!-- Summary of delete browsing data on quit preference if it is set to on -->
|
||||
<string name="delete_browsing_data_quit_on">On</string>
|
||||
<!-- Summary of delete browsing data on quit preference if it is set to off -->
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
android:icon="@drawable/ic_autoplay_enabled"
|
||||
android:key="@string/pref_key_browser_feature_autoplay_audible"
|
||||
android:title="@string/preference_browser_feature_autoplay"
|
||||
android:summary="@string/preference_option_autoplay_blocked"/>
|
||||
android:summary="@string/preference_option_autoplay_blocked3"/>
|
||||
|
||||
<androidx.preference.Preference
|
||||
android:icon="@drawable/ic_camera_enabled"
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.TestApplication
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.getInsecureWebsiteUiValues
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.getPermissionStatus
|
||||
|
@ -33,6 +34,7 @@ import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Compa
|
|||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.toWebsitePermission
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
@ -120,6 +122,7 @@ class QuickSettingsFragmentStoreTest {
|
|||
every { permissions.location } returns SitePermissions.Status.ALLOWED
|
||||
every { permissions.autoplayAudible } returns SitePermissions.Status.BLOCKED
|
||||
every { permissions.autoplayInaudible } returns SitePermissions.Status.BLOCKED
|
||||
every { appSettings.getAutoplayUserSetting(any()) } returns AUTOPLAY_BLOCK_ALL
|
||||
|
||||
val state = QuickSettingsFragmentStore.createWebsitePermissionState(
|
||||
context, permissions, appSettings
|
||||
|
|
|
@ -497,14 +497,7 @@ class SettingsTest {
|
|||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, ALLOWED)
|
||||
|
||||
assertEquals(
|
||||
defaultPermissions(),
|
||||
settings.getSitePermissionsCustomSettingsRules()
|
||||
)
|
||||
|
||||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, BLOCKED)
|
||||
|
||||
assertEquals(
|
||||
defaultPermissions(),
|
||||
defaultPermissions().copy(autoplayInaudible = ALLOWED),
|
||||
settings.getSitePermissionsCustomSettingsRules()
|
||||
)
|
||||
}
|
||||
|
@ -524,5 +517,5 @@ private fun defaultPermissions() = SitePermissionsRules(
|
|||
microphone = ASK_TO_ALLOW,
|
||||
notification = ASK_TO_ALLOW,
|
||||
autoplayAudible = AutoplayAction.BLOCKED,
|
||||
autoplayInaudible = AutoplayAction.ALLOWED
|
||||
autoplayInaudible = AutoplayAction.BLOCKED
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue