1
0
Fork 0

Closes #1079: Managing site permissions exceptions

master
Arturo Mejia 2019-04-11 16:04:51 -04:00
parent 5ffaa94150
commit c3e31a15db
24 changed files with 915 additions and 179 deletions

View File

@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #176 - Added a swipe to delete gesture on home screen
- #1539 - Added bookmarks multi-select related features
- #1603 - Remove deprecated success path for Firefox Accounts login
- #1079 - Managing site permissions exceptions
### Changed
- #1429 - Updated site permissions ui for MVP

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix.components
import android.content.Context
import androidx.paging.DataSource
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status
import mozilla.components.feature.sitepermissions.SitePermissionsStorage
@ -41,4 +42,16 @@ class Storage(private val context: Context) {
fun updateSitePermissions(sitePermissions: SitePermissions) {
permissionsStorage.update(sitePermissions)
}
fun getSitePermissionsPaged(): DataSource.Factory<Int, SitePermissions> {
return permissionsStorage.getSitePermissionsPaged()
}
fun deleteSitePermissions(sitePermissions: SitePermissions) {
permissionsStorage.remove(sitePermissions)
}
fun deleteAllSitePermissions() {
permissionsStorage.removeAll()
}
}

View File

@ -5,6 +5,9 @@
package org.mozilla.fenix.settings
import android.content.Context
import android.view.View
import android.widget.TextView
import androidx.core.text.HtmlCompat
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import org.mozilla.fenix.R
@ -73,3 +76,37 @@ fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions {
}
}
}
fun PhoneFeature.getLabel(context: Context): String {
return when (this) {
PhoneFeature.CAMERA -> context.getString(R.string.preference_phone_feature_camera)
PhoneFeature.LOCATION -> context.getString(R.string.preference_phone_feature_location)
PhoneFeature.MICROPHONE -> context.getString(R.string.preference_phone_feature_microphone)
PhoneFeature.NOTIFICATION -> context.getString(R.string.preference_phone_feature_notification)
}
}
fun PhoneFeature.getPreferenceKey(context: Context): String {
return when (this) {
PhoneFeature.CAMERA -> context.getString(R.string.pref_key_phone_feature_camera)
PhoneFeature.LOCATION -> context.getString(R.string.pref_key_phone_feature_location)
PhoneFeature.MICROPHONE -> context.getString(R.string.pref_key_phone_feature_microphone)
PhoneFeature.NOTIFICATION -> context.getString(R.string.pref_key_phone_feature_notification)
}
}
fun initBlockedByAndroidView(phoneFeature: PhoneFeature, blockedByAndroidView: View) {
val context = blockedByAndroidView.context
if (!phoneFeature.isAndroidPermissionGranted(context)) {
blockedByAndroidView.visibility = View.VISIBLE
val descriptionLabel = blockedByAndroidView.findViewById<TextView>(R.id.blocked_by_android_explanation_label)
val text = context.getString(
R.string.phone_feature_blocked_by_android_explanation,
phoneFeature.getLabel(context)
)
descriptionLabel.text = HtmlCompat.fromHtml(text, HtmlCompat.FROM_HTML_MODE_COMPACT)
} else {
blockedByAndroidView.visibility = View.GONE
}
}

View File

@ -4,7 +4,6 @@
package org.mozilla.fenix.settings
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.Manifest.permission.RECORD_AUDIO
@ -13,16 +12,17 @@ import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.support.ktx.android.content.isPermissionGranted
import org.mozilla.fenix.utils.Settings
const val ID_CAMERA_PERMISSION = 0
const val ID_LOCATION_PERMISSION = 1
const val ID_MICROPHONE_PERMISSION = 2
const val ID_NOTIFICATION_PERMISSION = 3
private const val CAMERA_PERMISSION = android.Manifest.permission.CAMERA
enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>) {
CAMERA(SitePermissionsManagePhoneFeature.CAMERA_PERMISSION, arrayOf(Manifest.permission.CAMERA)),
LOCATION(
SitePermissionsManagePhoneFeature.LOCATION_PERMISSION, arrayOf(
ACCESS_COARSE_LOCATION,
ACCESS_FINE_LOCATION
)
),
MICROPHONE(SitePermissionsManagePhoneFeature.MICROPHONE_PERMISSION, arrayOf(RECORD_AUDIO)),
NOTIFICATION(SitePermissionsManagePhoneFeature.NOTIFICATION_PERMISSION, emptyArray());
CAMERA(ID_CAMERA_PERMISSION, arrayOf(CAMERA_PERMISSION)),
LOCATION(ID_LOCATION_PERMISSION, arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION)),
MICROPHONE(ID_MICROPHONE_PERMISSION, arrayOf(RECORD_AUDIO)),
NOTIFICATION(ID_NOTIFICATION_PERMISSION, emptyArray());
@Suppress("SpreadOperator")
fun isAndroidPermissionGranted(context: Context): Boolean {
@ -33,54 +33,56 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>)
return context.isPermissionGranted(*permissions)
}
fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings): String {
return when (this) {
fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings? = null): String {
val label = when (this) {
CAMERA -> {
sitePermissions?.camera?.toString(context) ?: settings
.getSitePermissionsPhoneFeatureCameraAction()
.toString(context)
?.getSitePermissionsPhoneFeatureCameraAction()
?.toString(context)
}
LOCATION -> {
sitePermissions?.location?.toString(context) ?: settings
.getSitePermissionsPhoneFeatureLocation()
.toString(context)
?.getSitePermissionsPhoneFeatureLocation()
?.toString(context)
}
MICROPHONE -> {
sitePermissions?.microphone?.toString(context) ?: settings
.getSitePermissionsPhoneFeatureMicrophoneAction()
.toString(context)
?.getSitePermissionsPhoneFeatureMicrophoneAction()
?.toString(context)
}
NOTIFICATION -> {
sitePermissions?.notification?.toString(context) ?: settings
.getSitePermissionsPhoneFeatureNotificationAction()
.toString(context)
?.getSitePermissionsPhoneFeatureNotificationAction()
?.toString(context)
}
}
return requireNotNull(label)
}
fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings): SitePermissions.Status {
return when (this) {
fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings? = null): SitePermissions.Status {
val status = when (this) {
CAMERA -> {
sitePermissions?.camera ?: settings
.getSitePermissionsPhoneFeatureCameraAction()
.toStatus()
?.getSitePermissionsPhoneFeatureCameraAction()
?.toStatus()
}
LOCATION -> {
sitePermissions?.location ?: settings
.getSitePermissionsPhoneFeatureLocation()
.toStatus()
?.getSitePermissionsPhoneFeatureLocation()
?.toStatus()
}
MICROPHONE -> {
sitePermissions?.microphone ?: settings
.getSitePermissionsPhoneFeatureMicrophoneAction()
.toStatus()
?.getSitePermissionsPhoneFeatureMicrophoneAction()
?.toStatus()
}
NOTIFICATION -> {
sitePermissions?.notification ?: settings
.getSitePermissionsPhoneFeatureNotificationAction()
.toStatus()
?.getSitePermissionsPhoneFeatureNotificationAction()
?.toStatus()
}
}
return requireNotNull(status)
}
companion object {

View File

@ -0,0 +1,130 @@
/* 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.settings
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
import org.jetbrains.anko.alert
import org.jetbrains.anko.noButton
import org.jetbrains.anko.yesButton
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.settings.PhoneFeature.CAMERA
import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
import org.mozilla.fenix.settings.PhoneFeature.LOCATION
import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION
import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions")
class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), CoroutineScope {
private lateinit var job: Job
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
private lateinit var sitePermissions: SitePermissions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activity as AppCompatActivity).supportActionBar?.show()
sitePermissions = SitePermissionsDetailsExceptionsFragmentArgs
.fromBundle(requireArguments())
.sitePermissions
(activity as AppCompatActivity).title = sitePermissions.origin
job = Job()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.site_permissions_details_exceptions_preferences, rootKey)
}
override fun onResume() {
super.onResume()
launch(IO) {
val context = requireContext()
sitePermissions = requireNotNull(context.components.storage.findSitePermissionsBy(sitePermissions.origin))
launch(Main) {
bindCategoryPhoneFeatures()
}
}
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private fun bindCategoryPhoneFeatures() {
val context = requireContext()
val cameraAction = CAMERA.getActionLabel(context, sitePermissions)
val locationAction = LOCATION.getActionLabel(context, sitePermissions)
val microphoneAction = MICROPHONE.getActionLabel(context, sitePermissions)
val notificationAction = NOTIFICATION.getActionLabel(context, sitePermissions)
initPhoneFeature(CAMERA, cameraAction)
initPhoneFeature(LOCATION, locationAction)
initPhoneFeature(MICROPHONE, microphoneAction)
initPhoneFeature(NOTIFICATION, notificationAction)
bindClearPermissionsButton()
}
private fun initPhoneFeature(phoneFeature: PhoneFeature, summary: String) {
val keyPreference = phoneFeature.getPreferenceKey(requireContext())
val cameraPhoneFeatures: Preference = requireNotNull(findPreference(keyPreference))
cameraPhoneFeatures.summary = summary
cameraPhoneFeatures.onPreferenceClickListener = Preference.OnPreferenceClickListener {
navigateToPhoneFeature(phoneFeature)
true
}
}
private fun bindClearPermissionsButton() {
val keyPreference = getString(R.string.pref_key_exceptions_clear_site_permissions)
val button: Preference = requireNotNull(findPreference(keyPreference))
button.onPreferenceClickListener = Preference.OnPreferenceClickListener {
requireContext().alert(
R.string.confirm_clear_permissions_site,
R.string.clear_permissions
) {
yesButton {
clearSitePermissions()
}
noButton { }
}.show()
true
}
}
private fun clearSitePermissions() {
launch(IO) {
requireContext().components.storage.deleteSitePermissions(sitePermissions)
launch(Main) {
Navigation.findNavController(requireNotNull(view)).popBackStack()
}
}
}
private fun navigateToPhoneFeature(phoneFeature: PhoneFeature) {
val directions =
SitePermissionsDetailsExceptionsFragmentDirections.actionSitePermissionsToExceptionsToManagePhoneFeature(
phoneFeatureId = phoneFeature.id,
sitePermissions = sitePermissions
)
Navigation.findNavController(view!!).navigate(directions)
}
}

View File

@ -0,0 +1,194 @@
/* 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.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.navigation.Navigation
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.feature.sitepermissions.SitePermissions
import org.jetbrains.anko.alert
import org.jetbrains.anko.noButton
import org.jetbrains.anko.yesButton
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import kotlin.coroutines.CoroutineContext
import android.graphics.drawable.BitmapDrawable
private const val MAX_ITEMS_PER_PAGE = 50
@SuppressWarnings("TooManyFunctions")
class SitePermissionsExceptionsFragment : Fragment(), View.OnClickListener, CoroutineScope {
private lateinit var emptyContainerMessage: View
private lateinit var recyclerView: RecyclerView
private lateinit var clearButton: Button
private lateinit var job: Job
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activity as AppCompatActivity).supportActionBar?.show()
job = Job()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_site_permissions_exceptions, container, false)
}
override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) {
super.onViewCreated(rootView, savedInstanceState)
bindEmptyContainerMess(rootView)
bindClearButton(rootView)
bindRecyclerView(rootView)
}
private fun bindRecyclerView(rootView: View) {
recyclerView = rootView.findViewById(R.id.exceptions)
recyclerView.layoutManager = LinearLayoutManager(requireContext())
val sitePermissionsPaged = requireContext().components.storage.getSitePermissionsPaged()
val adapter = ExceptionsAdapter(this)
val liveData = LivePagedListBuilder(sitePermissionsPaged, MAX_ITEMS_PER_PAGE).build()
liveData.observe(this, Observer<PagedList<SitePermissions>> {
if (it.isEmpty()) {
showEmptyListMessage()
} else {
hideEmptyListMessage()
adapter.submitList(it)
recyclerView.adapter = adapter
}
})
}
private fun hideEmptyListMessage() {
emptyContainerMessage.visibility = GONE
recyclerView.visibility = VISIBLE
clearButton.visibility = VISIBLE
}
private fun showEmptyListMessage() {
emptyContainerMessage.visibility = VISIBLE
recyclerView.visibility = GONE
clearButton.visibility = GONE
}
private fun bindEmptyContainerMess(rootView: View) {
emptyContainerMessage = rootView.findViewById<View>(R.id.empty_exception_container)
}
private fun bindClearButton(rootView: View) {
clearButton = rootView.findViewById(R.id.delete_all_site_permissions_button)
clearButton.setOnClickListener {
requireContext().alert(
R.string.confirm_clear_permissions_on_all_sites,
R.string.clear_permissions
) {
yesButton {
deleteAllSitePermissions()
}
noButton { }
}.show()
}
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private fun deleteAllSitePermissions() {
launch(IO) {
requireContext().components.storage.deleteAllSitePermissions()
launch(Main) {
showEmptyListMessage()
}
}
}
override fun onClick(view: View?) {
val sitePermissions = view?.tag as SitePermissions
val directions = SitePermissionsExceptionsFragmentDirections
.actionSitePermissionsToExceptionsToSitePermissionsDetails(sitePermissions)
Navigation.findNavController(requireNotNull(view)).navigate(directions)
}
}
class SitePermissionsViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
class ExceptionsAdapter(private val clickListener: View.OnClickListener) :
PagedListAdapter<SitePermissions, SitePermissionsViewHolder>(diffCallback), CoroutineScope {
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
job = Job()
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
job.cancel()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SitePermissionsViewHolder {
val context = parent.context
val inflater = LayoutInflater.from(context)
val textView = inflater.inflate(R.layout.fragment_site_permissions_exceptions_item, parent, false) as TextView
return SitePermissionsViewHolder(textView)
}
override fun onBindViewHolder(holder: SitePermissionsViewHolder, position: Int) {
val sitePermissions = requireNotNull(getItem(position))
val context = holder.textView.context
val client = context.components.core.client
launch(IO) {
val bitmap = BrowserIcons(context, client)
.loadIcon(IconRequest(sitePermissions.origin)).await().bitmap
launch(Main) {
val drawable = BitmapDrawable(context.resources, bitmap)
holder.textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
}
}
holder.textView.text = sitePermissions.origin
holder.textView.tag = sitePermissions
holder.textView.setOnClickListener(clickListener)
}
companion object {
private val diffCallback = object :
DiffUtil.ItemCallback<SitePermissions>() {
override fun areItemsTheSame(old: SitePermissions, new: SitePermissions) = old.origin == new.origin
override fun areContentsTheSame(old: SitePermissions, new: SitePermissions) = old == new
}
}
}

View File

@ -38,6 +38,18 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
private fun setupPreferences() {
bindCategoryPhoneFeatures()
bindExceptions()
}
private fun bindExceptions() {
val keyExceptions = getString(R.string.pref_key_show_site_exceptions)
val exceptionsCategory = requireNotNull<Preference>(findPreference(keyExceptions))
exceptionsCategory.onPreferenceClickListener = OnPreferenceClickListener {
val directions = SitePermissionsFragmentDirections.actionSitePermissionsToExceptions()
Navigation.findNavController(view!!).navigate(directions)
true
}
}
private fun bindCategoryPhoneFeatures() {
@ -66,7 +78,7 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
}
private fun initPhoneFeature(phoneFeature: PhoneFeature, summary: String) {
val keyPreference = getPreferenceKeyBy(phoneFeature)
val keyPreference = phoneFeature.getPreferenceKey(requireContext())
val cameraPhoneFeatures: Preference = requireNotNull(findPreference(keyPreference))
cameraPhoneFeatures.summary = summary
@ -76,15 +88,6 @@ class SitePermissionsFragment : PreferenceFragmentCompat() {
}
}
private fun getPreferenceKeyBy(phoneFeature: PhoneFeature): String {
return when (phoneFeature) {
CAMERA -> getString(R.string.pref_key_phone_feature_camera)
LOCATION -> getString(R.string.pref_key_phone_feature_location)
MICROPHONE -> getString(R.string.pref_key_phone_feature_microphone)
NOTIFICATION -> getString(R.string.pref_key_phone_feature_notification)
}
}
private fun navigateToPhoneFeature(phoneFeature: PhoneFeature) {
val directions = SitePermissionsFragmentDirections.actionSitePermissionsToManagePhoneFeatures(phoneFeature.id)
Navigation.findNavController(view!!).navigate(directions)

View File

@ -0,0 +1,177 @@
/* 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.settings
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.RadioButton
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status.ALLOWED
import mozilla.components.feature.sitepermissions.SitePermissions.Status.BLOCKED
import org.jetbrains.anko.alert
import org.jetbrains.anko.noButton
import org.jetbrains.anko.yesButton
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions")
class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment(), CoroutineScope {
private lateinit var phoneFeature: PhoneFeature
private lateinit var sitePermissions: SitePermissions
private lateinit var radioAllow: RadioButton
private lateinit var radioBlock: RadioButton
private lateinit var blockedByAndroidView: View
private lateinit var job: Job
val settings by lazy { Settings.getInstance(requireContext()) }
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
phoneFeature = SitePermissionsManageExceptionsPhoneFeatureFragmentArgs
.fromBundle(requireArguments())
.phoneFeatureId.toPhoneFeature()
sitePermissions = SitePermissionsManageExceptionsPhoneFeatureFragmentArgs
.fromBundle(requireArguments())
.sitePermissions
(activity as AppCompatActivity).title = phoneFeature.getLabel(requireContext())
(activity as AppCompatActivity).supportActionBar?.show()
job = Job()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView =
inflater.inflate(R.layout.fragment_manage_site_permissions_exceptions_feature_phone, container, false)
initAskToAllowRadio(rootView)
initBlockRadio(rootView)
bindBlockedByAndroidContainer(rootView)
initClearPermissionsButton(rootView)
return rootView
}
override fun onResume() {
super.onResume()
initBlockedByAndroidView(phoneFeature, blockedByAndroidView)
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private fun initAskToAllowRadio(rootView: View) {
radioAllow = rootView.findViewById(R.id.ask_to_allow_radio)
val askToAllowText = getString(R.string.preference_option_phone_feature_allowed)
radioAllow.text = askToAllowText
radioAllow.setOnClickListener {
updatedSitePermissions(ALLOWED)
}
radioAllow.restoreState(ALLOWED)
}
private fun RadioButton.restoreState(status: SitePermissions.Status) {
if (phoneFeature.getStatus(sitePermissions) == status) {
this.isChecked = true
}
}
private fun initBlockRadio(rootView: View) {
radioBlock = rootView.findViewById(R.id.block_radio)
radioBlock.setOnClickListener {
updatedSitePermissions(BLOCKED)
}
radioBlock.restoreState(BLOCKED)
}
private fun initClearPermissionsButton(rootView: View) {
val button = rootView.findViewById<Button>(R.id.reset_permission)
button.setText(R.string.clear_permission)
button.setOnClickListener {
requireContext().alert(
R.string.confirm_clear_permission_site,
R.string.clear_permission
) {
yesButton {
val defaultStatus = phoneFeature.getStatus(settings = settings)
updatedSitePermissions(defaultStatus)
resetRadioButtonsStatus(defaultStatus)
}
noButton { }
}.show()
}
}
private fun resetRadioButtonsStatus(defaultStatus: SitePermissions.Status) {
radioAllow.isChecked = false
radioBlock.isChecked = false
radioAllow.restoreState(defaultStatus)
radioBlock.restoreState(defaultStatus)
}
private fun bindBlockedByAndroidContainer(rootView: View) {
blockedByAndroidView = rootView.findViewById<View>(R.id.permissions_blocked_container)
initSettingsButton(blockedByAndroidView)
}
private fun initSettingsButton(rootView: View) {
val button = rootView.findViewById<Button>(R.id.settings_button)
button.setOnClickListener {
openSettings()
}
}
private fun openSettings() {
val intent = Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", requireContext().packageName, null)
intent.data = uri
startActivity(intent)
}
private fun Int.toPhoneFeature(): PhoneFeature {
return requireNotNull(PhoneFeature.values().find { feature ->
this == feature.id
}) {
"$this is a invalid PhoneFeature"
}
}
private fun updatedSitePermissions(status: SitePermissions.Status) {
val updatedSitePermissions = when (phoneFeature) {
PhoneFeature.CAMERA -> sitePermissions.copy(camera = status)
PhoneFeature.LOCATION -> sitePermissions.copy(location = status)
PhoneFeature.MICROPHONE -> sitePermissions.copy(microphone = status)
PhoneFeature.NOTIFICATION -> sitePermissions.copy(notification = status)
}
launch(IO) {
requireContext().components.storage.updateSitePermissions(updatedSitePermissions)
}
}
}

View File

@ -9,7 +9,6 @@ import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import org.mozilla.fenix.utils.Settings
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE
@ -17,50 +16,48 @@ import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.Button
import android.widget.RadioButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT
import androidx.fragment.app.Fragment
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.BLOCKED
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.utils.Settings
class SitePermissionsManagePhoneFeature : Fragment() {
@SuppressWarnings("TooManyFunctions")
class SitePermissionsManagePhoneFeatureFragment : Fragment() {
private lateinit var phoneFeature: PhoneFeature
private lateinit var settings: Settings
private lateinit var blockedByAndroidView: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
phoneFeature = SitePermissionsManagePhoneFeatureArgs
phoneFeature = SitePermissionsManagePhoneFeatureFragmentArgs
.fromBundle(requireArguments())
.permission.toPhoneFeature()
(activity as AppCompatActivity).title = phoneFeature.label
(activity as AppCompatActivity).title = phoneFeature.getLabel(requireContext())
(activity as AppCompatActivity).supportActionBar?.show()
settings = Settings.getInstance(requireContext())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment_manage_site_permissions_feature_phone, container, false)
initAskToAllowRadio(rootView)
initBlockRadio(rootView)
initBockedByAndroidContainer(rootView)
bindBlockedByAndroidContainer(rootView)
return rootView
}
override fun onResume() {
super.onResume()
initBlockedByAndroidView(phoneFeature, blockedByAndroidView)
}
private fun initAskToAllowRadio(rootView: View) {
val radio = rootView.findViewById<RadioButton>(R.id.ask_to_allow_radio)
@ -108,34 +105,11 @@ class SitePermissionsManagePhoneFeature : Fragment() {
radio.restoreState(BLOCKED)
}
private fun initBockedByAndroidContainer(rootView: View) {
if (!phoneFeature.isAndroidPermissionGranted) {
val containerView = rootView.findViewById<View>(R.id.permissions_blocked_container)
containerView.visibility = VISIBLE
val descriptionLabel = rootView.findViewById<TextView>(R.id.blocked_by_android_explanation_label)
val text = getString(R.string.phone_feature_blocked_by_android_explanation, phoneFeature.label)
descriptionLabel.text = HtmlCompat.fromHtml(text, FROM_HTML_MODE_COMPACT)
initSettingsButton(rootView)
}
private fun bindBlockedByAndroidContainer(rootView: View) {
blockedByAndroidView = rootView.findViewById<View>(R.id.permissions_blocked_container)
initSettingsButton(blockedByAndroidView)
}
private val PhoneFeature.label: String
get() {
return when (this) {
PhoneFeature.CAMERA -> getString(R.string.preference_phone_feature_camera)
PhoneFeature.LOCATION -> getString(R.string.preference_phone_feature_location)
PhoneFeature.MICROPHONE -> getString(R.string.preference_phone_feature_microphone)
PhoneFeature.NOTIFICATION -> getString(R.string.preference_phone_feature_notification)
}
}
private val PhoneFeature.isAndroidPermissionGranted: Boolean
get() {
return this.isAndroidPermissionGranted(requireContext())
}
private fun Int.toPhoneFeature(): PhoneFeature {
return requireNotNull(PhoneFeature.values().find { feature ->
this == feature.id
@ -176,11 +150,4 @@ class SitePermissionsManagePhoneFeature : Fragment() {
PhoneFeature.NOTIFICATION -> settings.setSitePermissionsPhoneFeatureNotificationAction(action)
}
}
companion object {
const val CAMERA_PERMISSION = 0
const val LOCATION_PERMISSION = 1
const val MICROPHONE_PERMISSION = 2
const val NOTIFICATION_PERMISSION = 3
}
}

View File

@ -86,7 +86,7 @@ class QuickSettingsComponent(
PhoneFeature.MICROPHONE -> microphone = microphone.toggle()
PhoneFeature.NOTIFICATION -> notification = notification.toggle()
}
context.components.storage.addSitePermissionException(origin, location, camera, microphone, notification)
context.components.storage.addSitePermissionException(origin, location, notification, microphone, camera)
} else {
val updatedSitePermissions = sitePermissions.toggle(featurePhone)
context.components.storage.updateSitePermissions(updatedSitePermissions)

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp"/>
<solid android:color="@color/photonBlue80"/>
</shape>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/permissions_blocked_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="32dp"
android:paddingStart="@dimen/radio_button_preference_horizontal"
android:paddingEnd="@dimen/radio_button_preference_horizontal"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:text="@string/phone_feature_blocked_by_android"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/blocked_by_android_explanation_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:text="@string/phone_feature_blocked_by_android_explanation"/>
<Button
android:id="@+id/settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/SitePermissionButton"
android:text="@string/phone_feature_go_to_settings"
android:layout_marginTop="24dp"/>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginTop="@dimen/radio_button_preference_vertical">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
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"
android:drawableStart="?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:paddingEnd="@dimen/radio_button_preference_horizontal"
android:paddingBottom="@dimen/radio_button_preference_vertical"/>
<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:background="?android:attr/selectableItemBackground"
android:button="@null"
android:drawableStart="?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:paddingEnd="@dimen/radio_button_preference_horizontal"
android:paddingBottom="@dimen/radio_button_preference_vertical"/>
</RadioGroup>
<include layout="@layout/layout_clear_permission_button"/>
<include layout="@layout/component_permissions_blocked_by_android"/>
</LinearLayout>

View File

@ -5,7 +5,6 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -46,39 +45,7 @@
android:paddingBottom="@dimen/radio_button_preference_vertical"/>
</RadioGroup>
<LinearLayout
android:id="@+id/permissions_blocked_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="32dp"
android:paddingStart="@dimen/radio_button_preference_horizontal"
android:paddingEnd="@dimen/radio_button_preference_horizontal"
android:visibility="gone"
tools:visibility="visible">
<include layout="@layout/component_permissions_blocked_by_android"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:text="@string/phone_feature_blocked_by_android"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/blocked_by_android_explanation_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:text="@string/phone_feature_blocked_by_android_explanation"/>
<Button
android:id="@+id/settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/SitePermissionButton"
android:text="@string/phone_feature_go_to_settings"
android:layout_marginTop="24dp"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/exceptions"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/delete_all_site_permissions_button"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clear_permissions_on_all_sites"
app:layout_constraintTop_toBottomOf="@id/exceptions"
app:layout_constraintStart_toStartOf="parent"
android:layout_margin="16dp"
style="@style/SitePermissionButton"/>
<TextView
android:id="@+id/empty_exception_container"
tools:visibility="visible"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/toolbarTextColor"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/no_site_exceptions"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/exception_item"
android:layout_width="match_parent"
android:layout_height="@dimen/site_permissions_exceptions_item_height"
android:textSize="@dimen/site_permissions_exceptions_item_text_size"
android:drawablePadding="16dp"
android:drawableStart="@drawable/ic_internet"
android:textColor="?attr/toolbarTextColor"
android:gravity="start|center_vertical"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:background="?android:attr/selectableItemBackground"/>

View File

@ -0,0 +1,11 @@
<!-- 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/. -->
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/reset_permission"
android:text="@string/clear_permissions"
android:layout_margin="16dp"
style="@style/SitePermissionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

View File

@ -50,13 +50,47 @@
<fragment
android:id="@+id/SitePermissionsManagePhoneFeature"
android:name="org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature"
android:name="org.mozilla.fenix.settings.SitePermissionsManagePhoneFeatureFragment"
tools:layout="@layout/fragment_manage_site_permissions_feature_phone">
<argument
android:name="permission"
app:argType="integer"/>
</fragment>
<fragment
android:id="@+id/sitePermissionsExceptionsFragment"
android:name="org.mozilla.fenix.settings.SitePermissionsExceptionsFragment"
android:label="@string/preference_exceptions"
tools:layout="@layout/fragment_site_permissions_exceptions">
<action
android:id="@+id/action_site_permissions_to_exceptions_to_site_permissions_details"
app:destination="@id/sitePermissionsDetailsExceptionsFragment"
app:popUpTo="@+id/sitePermissionsExceptionsFragment"/>
</fragment>
<fragment
android:id="@+id/sitePermissionsDetailsExceptionsFragment"
android:name="org.mozilla.fenix.settings.SitePermissionsDetailsExceptionsFragment"
tools:layout="@xml/site_permissions_details_exceptions_preferences">
<action
android:id="@+id/action_site_permissions_to_exceptions_to_manage_phone_feature"
app:destination="@id/sitePermissionsManageExceptionsPhoneFeatureFragment"
app:popUpTo="@+id/sitePermissionsDetailsExceptionsFragment"/>
<argument android:name="sitePermissions"
app:argType="mozilla.components.feature.sitepermissions.SitePermissions"/>
</fragment>
<fragment
android:id="@+id/sitePermissionsManageExceptionsPhoneFeatureFragment"
android:name="org.mozilla.fenix.settings.SitePermissionsManageExceptionsPhoneFeatureFragment"
tools:layout="@layout/fragment_manage_site_permissions_feature_phone">
<argument
android:name="phoneFeatureId"
app:argType="integer"/>
<argument android:name="sitePermissions"
app:argType="mozilla.components.feature.sitepermissions.SitePermissions"/>
</fragment>
<fragment
android:id="@+id/browserFragment"
android:name="org.mozilla.fenix.browser.BrowserFragment"
@ -202,14 +236,17 @@
<fragment android:id="@+id/dataChoicesFragment" android:name="org.mozilla.fenix.settings.DataChoicesFragment"
android:label="DataChoicesFragment"/>
<fragment
android:id="@+id/sitePermissionsFragment"
android:name="org.mozilla.fenix.settings.SitePermissionsFragment"
android:label="@string/preferences_site_permissions">
android:id="@+id/sitePermissionsFragment"
android:name="org.mozilla.fenix.settings.SitePermissionsFragment"
android:label="@string/preferences_site_permissions">
<action
android:id="@+id/action_site_permissions_to_manage_phone_features"
app:destination="@id/SitePermissionsManagePhoneFeature"
app:popUpTo="@id/sitePermissionsFragment"/>
<action
android:id="@+id/action_site_permissions_to_exceptions"
app:destination="@id/sitePermissionsExceptionsFragment"
app:popUpTo="@id/sitePermissionsFragment"/>
</fragment>
<fragment android:id="@+id/accessibilityFragment" android:name="org.mozilla.fenix.settings.AccessibilityFragment"

View File

@ -27,6 +27,8 @@
<dimen name="radio_button_preference_drawable_padding">16dp</dimen>
<dimen name="radio_button_preference_vertical">12dp</dimen>
<dimen name="phone_feature_label_recommended_text_size">14sp</dimen>
<dimen name="site_permissions_exceptions_item_text_size">18sp</dimen>
<dimen name="site_permissions_exceptions_item_height">56dp</dimen>
<!--Quick Settings-->
<dimen name="quicksettings_item_height">46dp</dimen>

View File

@ -8,8 +8,6 @@
<string name="preference_optimize">Optimize</string>
<!-- Label summary to explain how the optimize preference works -->
<string name="preference_optimize_summary">Lower image quality, throttle streaming bandwidth, and platform-level optimizations</string>
<!-- Preference for showing a list of websites that the default configurations won't apply to them -->
<string name="preference_exceptions">Exceptions</string>
<!-- Preference for applying recommend rules to all sites -->
<string name="preference_recommended_settings">Use recommended settings</string>
<!-- Label summary to explain how the recommended settings work -->
@ -25,43 +23,23 @@
<string name="preference_custom_settings">Use custom settings</string>
<!-- Preference category for feature phone permissions likes Camera,Microphone, Location ... etc -->
<string name="preference_category_phone_feature">Phone Feature</string>
<!-- Preference for altering the camera access for all websites -->
<string name="preference_phone_feature_camera">Camera</string>
<!-- Preference for altering the microphone access for all websites -->
<string name="preference_phone_feature_microphone">Microphone</string>
<!-- Preference for altering the location access for all websites -->
<string name="preference_phone_feature_location">Location</string>
<!-- Preference for altering the notification access for all websites -->
<string name="preference_phone_feature_notification">Notification</string>
<!-- Label that indicates that a permission must be asked always -->
<string name="preference_option_phone_feature_ask_to_allow">Ask to allow</string>
<!-- Label that indicates that a permission must be blocked -->
<string name="preference_option_phone_feature_blocked">Blocked</string>
<!-- Label that indicates that a permission must be allowed -->
<string name="preference_option_phone_feature_allowed">Allowed</string>
<!--Label that indicates a permission is by the Android OS-->
<string name="phone_feature_blocked_by_android">Blocked by Android</string>
<!--Label that indicates that a user hasn't select a value for a site permission-->
<string name="phone_feature_no_decision">No Decision</string>
<!-- Alternative explanation label that is shown when a permissions like (camera,location and microphone) is required, this indicate to the user how to enable the permission via Android settings %1$s indicate the name of the permission (camera,location and microphone) -->
<string name="phone_feature_blocked_by_android_explanation"><![CDATA[
To allow it: <br/><br/> 1. Go to Android Settings <br/><br/>2. Tap <b>Permissions</b> <br/><br/> 3. Toggle <b>%1$s</b> to ON
]]></string>
<!-- Label that indicates that this option it the recommended one -->
<string name="phone_feature_recommended">Recommended</string>
<!-- Button label that take the user to the Android App setting -->
<string name="phone_feature_go_to_settings">Go to Settings</string>
<!-- Content description (not visible, for screen readers etc.): Quick settings sheet
to give users access to site specific information / settings. For example:
Secure settings status and a button to modify site permissions -->
<string name="quick_settings_sheet">Quick settings sheet</string>
<!-- Label that indicates a site is using a secure connection -->
<string name="quick_settings_sheet_secure_connection">Secure Connection</string>
<!-- Label that indicates a site is using a insecure connection -->
<string name="quick_settings_sheet_insecure_connection">Insecure Connection</string>
<!-- button that allows editing site permissions settings -->
<string name="quick_settings_sheet_manage_site_permissions">Manage site permissions</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for all sites-->
<string name="confirm_clear_permissions_on_all_sites">Are you sure that you want to clear all the permissions on all sites?</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for a site-->
<string name="confirm_clear_permissions_site">Are you sure that you want to clear all the permissions for this site?</string>
<!-- Confirmation message for a dialog confirming if the user wants to set default value a permission for a site-->
<string name="confirm_clear_permission_site">Are you sure that you want to clear this permission for this site?</string>
<!-- label shown when there are not site exceptions to show in the site exception settings -->
<string name="no_site_exceptions">No site exceptions</string>
</resources>

View File

@ -49,6 +49,7 @@
<string name="pref_key_phone_feature_microphone" translatable="false">pref_key_phone_feature_microphone</string>
<string name="pref_key_phone_feature_notification" translatable="false">pref_key_phone_feature_notification</string>
<string name="pref_key_category_phone_feature" translatable="false">pref_key_category_phone_feature</string>
<string name="pref_key_exceptions_clear_site_permissions" translatable="false">pref_key_exceptions_clear_site_permissions</string>
<!-- Theme Settings -->
<string name="pref_key_light_theme" translatable="false">pref_key_light_theme</string>

View File

@ -336,4 +336,42 @@
<!-- Message for copying the URL via long press on the toolbar -->
<string name="url_copied">URL copied</string>
<!-- Site Permissions -->
<!-- Button label that take the user to the Android App setting -->
<string name="phone_feature_go_to_settings">Go to Settings</string>
<!-- Content description (not visible, for screen readers etc.): Quick settings sheet
to give users access to site specific information / settings. For example:
Secure settings status and a button to modify site permissions -->
<string name="quick_settings_sheet">Quick settings sheet</string>
<!-- Label that indicates that this option it the recommended one -->
<string name="phone_feature_recommended">Recommended</string>
<!-- button that allows editing site permissions settings -->
<string name="quick_settings_sheet_manage_site_permissions">Manage site permissions</string>
<!-- Button label for clear all the information of a site permissions-->
<string name="clear_permissions">Clear permissions</string>
<!-- Button label for clear a site permissions-->
<string name="clear_permission">Clear permission</string>
<!-- Button label for clear all the information fall all sites-->
<string name="clear_permissions_on_all_sites">Clear permissions on all sites</string>
<!-- Preference for altering the camera access for all websites -->
<string name="preference_phone_feature_camera">Camera</string>
<!-- Preference for altering the microphone access for all websites -->
<string name="preference_phone_feature_microphone">Microphone</string>
<!-- Preference for altering the location access for all websites -->
<string name="preference_phone_feature_location">Location</string>
<!-- Preference for altering the notification access for all websites -->
<string name="preference_phone_feature_notification">Notification</string>
<!-- Label that indicates that a permission must be asked always -->
<string name="preference_option_phone_feature_ask_to_allow">Ask to allow</string>
<!-- Label that indicates that a permission must be blocked -->
<string name="preference_option_phone_feature_blocked">Blocked</string>
<!-- Label that indicates that a permission must be allowed -->
<string name="preference_option_phone_feature_allowed">Allowed</string>
<!--Label that indicates a permission is by the Android OS-->
<string name="phone_feature_blocked_by_android">Blocked by Android</string>
<!-- Preference for showing a list of websites that the default configurations won't apply to them -->
<string name="preference_exceptions">Exceptions</string>
</resources>

View File

@ -186,20 +186,11 @@
<item name="android:background">@drawable/search_pill_drawable_button_background</item>
</style>
<style name="SitePermissionButton" parent="Widget.AppCompat.Button.Borderless">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<style name="SitePermissionButton" parent="Widget.AppCompat.Button">
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/photonWhite</item>
<item name="android:gravity">center_vertical</item>
<item name="android:singleLine">true</item>
<item name="android:padding">8dp</item>
<item name="android:textColor">?attr/toolbarTextColor</item>
<item name="android:paddingStart">24dp</item>
<item name="android:paddingEnd">24dp</item>
<item name="android:foreground">?android:attr/selectableItemBackground</item>
<item name="android:background">@drawable/site_permissions_button_background</item>
</style>
<style name="CurrentSessionBottomSheetDialogTheme" parent="Theme.MaterialComponents.Light.BottomSheetDialog">

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.Preference
android:icon="@drawable/ic_camera"
android:key="@string/pref_key_phone_feature_camera"
android:title="@string/preference_phone_feature_camera"
android:summary="@string/preference_option_phone_feature_ask_to_allow"/>
<androidx.preference.Preference
android:icon="@drawable/ic_location"
android:key="@string/pref_key_phone_feature_location"
android:title="@string/preference_phone_feature_location"
android:summary="@string/preference_option_phone_feature_ask_to_allow"/>
<androidx.preference.Preference
android:icon="@drawable/ic_microphone"
android:key="@string/pref_key_phone_feature_microphone"
android:title="@string/preference_phone_feature_microphone"
android:summary="@string/preference_option_phone_feature_ask_to_allow"/>
<androidx.preference.Preference
android:icon="@drawable/ic_notification"
android:key="@string/pref_key_phone_feature_notification"
android:title="@string/preference_phone_feature_notification"
android:summary="@string/preference_option_phone_feature_ask_to_allow"/>
<androidx.preference.Preference
android:key="@string/pref_key_exceptions_clear_site_permissions"
android:layout="@layout/layout_clear_permission_button"/>
</androidx.preference.PreferenceScreen>