[fenix] Closes https://github.com/mozilla-mobile/fenix/issues/1079: Managing site permissions exceptions
parent
62a755f0d3
commit
44e5c9518d
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
|
@ -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>
|
||||||
|
|
@ -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>
|
@ -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>
|
@ -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"/>
|
@ -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"/>
|
@ -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>
|
Loading…
Reference in New Issue