1
0
Fork 0

8017 advanced autoplay controls. (#8978)

* For #8017: add advanced autoplay controls
master
Severin Rudie 2020-03-20 12:10:42 -07:00 committed by GitHub
parent 5d8a9bb4be
commit 35a132d7ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 480 additions and 98 deletions

View File

@ -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) {

View File

@ -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()

View File

@ -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
)
)
}
}

View File

@ -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 -> {

View File

@ -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)

View File

@ -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
}
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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() }
}

View File

@ -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"/>

View File

@ -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 -->

View File

@ -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"

View File

@ -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

View File

@ -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
)