1
0
Fork 0

Closes #1170: Allow user to add a new site exception to site permissions

master
Arturo Mejia 2019-04-07 20:24:22 -04:00 committed by Colin Lee
parent 36e9939d9e
commit 22eba72f8f
13 changed files with 342 additions and 85 deletions

View File

@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1238 - Added the ability to edit bookmark folders
- #1239 - Added the ability to move bookmark folders
- #1068 - Adds the ability to quickly copy the URL by long clicking the URLBar
- #1170: Allow user to add a new site exception to site permissions
### Changed
- #1429 - Updated site permissions ui for MVP
### Removed

View File

@ -25,8 +25,10 @@ import kotlinx.android.synthetic.main.component_search.*
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.android.synthetic.main.fragment_search.*
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.appservices.places.BookmarkRoot
import mozilla.components.browser.session.Session
@ -39,12 +41,14 @@ import mozilla.components.feature.session.FullScreenFeature
import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.ThumbnailsFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
import mozilla.components.feature.sitepermissions.SitePermissionsRules
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.android.view.enterToImmersiveMode
import mozilla.components.support.ktx.android.view.exitImmersiveModeIfNeeded
import mozilla.components.support.ktx.kotlin.toUri
import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.HomeActivity
@ -73,9 +77,10 @@ import org.mozilla.fenix.quickactionsheet.QuickActionComponent
import org.mozilla.fenix.settings.quicksettings.QuickSettingsSheetDialogFragment
import org.mozilla.fenix.utils.ItsNotBrokenSnack
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions", "LargeClass")
class BrowserFragment : Fragment(), BackHandler {
class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
private lateinit var toolbarComponent: ToolbarComponent
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
@ -88,9 +93,17 @@ class BrowserFragment : Fragment(), BackHandler {
private val fullScreenFeature = ViewBoundFeatureWrapper<FullScreenFeature>()
private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
private lateinit var job: Job
var sessionId: String? = null
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -263,13 +276,7 @@ class BrowserFragment : Fragment(), BackHandler {
)
}
toolbarComponent.getView().setOnSiteSecurityClickedListener {
val session = getSessionByIdOrUseSelectedSession()
val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance(
url = session.url,
isSecured = session.securityInfo.secure,
isSiteInExceptionList = false
)
quickSettingsSheet.show(requireFragmentManager(), QuickSettingsSheetDialogFragment.FRAGMENT_TAG)
showQuickSettingsDialog()
}
}
@ -388,6 +395,11 @@ class BrowserFragment : Fragment(), BackHandler {
promptsFeature.withFeature { it.onActivityResult(requestCode, resultCode, data) }
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
// This method triggers the complexity warning. However it's actually not that hard to understand.
@SuppressWarnings("ComplexMethod")
private fun trackToolbarItemInteraction(action: SearchAction.ToolbarMenuItemTapped) {
@ -472,6 +484,26 @@ class BrowserFragment : Fragment(), BackHandler {
}
}
private fun showQuickSettingsDialog() {
val session = getSessionByIdOrUseSelectedSession()
val host = requireNotNull(session.url.toUri().host)
launch {
val storage = requireContext().components.storage
val sitePermissions: SitePermissions? = storage.findSitePermissionsBy(host)
launch(Main) {
val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance(
url = session.url,
isSecured = session.securityInfo.secure,
sitePermissions = sitePermissions
)
quickSettingsSheet.sitePermissions = sitePermissions
quickSettingsSheet.show(requireFragmentManager(), QuickSettingsSheetDialogFragment.FRAGMENT_TAG)
}
}
}
private fun getSessionByIdOrUseSelectedSession(): Session {
return if (sessionId != null) {
requireNotNull(requireContext().components.core.sessionManager.findSessionById(requireNotNull(sessionId)))

View File

@ -17,4 +17,5 @@ class Components(private val context: Context) {
val useCases by lazy { UseCases(context, core.sessionManager, search.searchEngineManager) }
val utils by lazy { Utilities(context, core.sessionManager, useCases.sessionUseCases, useCases.searchUseCases) }
val analytics by lazy { Analytics(context) }
val storage by lazy { Storage(context) }
}

View File

@ -0,0 +1,44 @@
/* 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.components
import android.content.Context
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status
import mozilla.components.feature.sitepermissions.SitePermissionsStorage
class Storage(private val context: Context) {
private val permissionsStorage by lazy {
SitePermissionsStorage(context)
}
fun addSitePermissionException(
origin: String,
location: Status,
notification: Status,
microphone: Status,
camera: Status
): SitePermissions {
val sitePermissions = SitePermissions(
origin = origin,
location = location,
camera = camera,
microphone = microphone,
notification = notification,
savedAt = System.currentTimeMillis()
)
permissionsStorage.save(sitePermissions)
return sitePermissions
}
fun findSitePermissionsBy(origin: String): SitePermissions? {
return permissionsStorage.findSitePermissionsBy(origin)
}
fun updateSitePermissions(sitePermissions: SitePermissions) {
permissionsStorage.update(sitePermissions)
}
}

View File

@ -15,7 +15,7 @@ internal fun SitePermissionsRules.Action.toString(context: Context): String {
context.getString(R.string.preference_option_phone_feature_ask_to_allow)
}
SitePermissionsRules.Action.BLOCKED -> {
context.getString(R.string.preference_option_phone_feature_block)
context.getString(R.string.preference_option_phone_feature_blocked)
}
}
}
@ -23,13 +23,53 @@ internal fun SitePermissionsRules.Action.toString(context: Context): String {
internal fun SitePermissions.Status.toString(context: Context): String {
return when (this) {
SitePermissions.Status.BLOCKED -> {
context.getString(R.string.preference_option_phone_feature_block)
context.getString(R.string.preference_option_phone_feature_blocked)
}
SitePermissions.Status.NO_DECISION -> {
context.getString(R.string.preference_option_phone_feature_ask_to_allow)
}
SitePermissions.Status.ALLOWED -> {
context.getString(R.string.phone_feature_no_decision)
context.getString(R.string.preference_option_phone_feature_allowed)
}
}
}
fun SitePermissionsRules.Action.toStatus(): SitePermissions.Status {
return when (this) {
SitePermissionsRules.Action.BLOCKED -> SitePermissions.Status.BLOCKED
SitePermissionsRules.Action.ASK_TO_ALLOW -> SitePermissions.Status.NO_DECISION
}
}
fun SitePermissions.Status.toggle(): SitePermissions.Status {
return when (this) {
SitePermissions.Status.BLOCKED -> SitePermissions.Status.ALLOWED
SitePermissions.Status.NO_DECISION -> SitePermissions.Status.ALLOWED
SitePermissions.Status.ALLOWED -> SitePermissions.Status.BLOCKED
}
}
fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions {
return when (featurePhone) {
PhoneFeature.CAMERA -> {
copy(
camera = camera.toggle()
)
}
PhoneFeature.LOCATION -> {
copy(
location = location.toggle()
)
}
PhoneFeature.MICROPHONE -> {
copy(
microphone = microphone.toggle()
)
}
PhoneFeature.NOTIFICATION -> {
copy(
notification = notification.toggle()
)
}
}
}

View File

@ -58,6 +58,31 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>)
}
}
fun getStatus(sitePermissions: SitePermissions? = null, settings: Settings): SitePermissions.Status {
return when (this) {
CAMERA -> {
sitePermissions?.camera ?: settings
.getSitePermissionsPhoneFeatureCameraAction()
.toStatus()
}
LOCATION -> {
sitePermissions?.location ?: settings
.getSitePermissionsPhoneFeatureLocation()
.toStatus()
}
MICROPHONE -> {
sitePermissions?.microphone ?: settings
.getSitePermissionsPhoneFeatureMicrophoneAction()
.toStatus()
}
NOTIFICATION -> {
sitePermissions?.notification ?: settings
.getSitePermissionsPhoneFeatureNotificationAction()
.toStatus()
}
}
}
companion object {
fun findFeatureBy(permissions: Array<out String>): PhoneFeature? {
return PhoneFeature.values().find { feature ->

View File

@ -4,7 +4,11 @@
package org.mozilla.fenix.settings.quicksettings
import android.content.Context
import android.view.ViewGroup
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.support.ktx.kotlin.toUri
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change
@ -12,6 +16,9 @@ import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.UIView
import org.mozilla.fenix.mvi.ViewState
import org.mozilla.fenix.settings.PhoneFeature
import org.mozilla.fenix.settings.toStatus
import org.mozilla.fenix.settings.toggle
import org.mozilla.fenix.utils.Settings
class QuickSettingsComponent(
private val container: ViewGroup,
@ -28,18 +35,23 @@ class QuickSettingsComponent(
mode = QuickSettingsState.Mode.Normal(
change.url,
change.isSecured,
change.isSiteInExceptionList
change.sitePermissions
)
)
}
is QuickSettingsChange.PermissionGranted -> {
state.copy(
mode = QuickSettingsState.Mode.ActionLabelUpdated(change.phoneFeature)
mode = QuickSettingsState.Mode.ActionLabelUpdated(change.phoneFeature, change.sitePermissions)
)
}
QuickSettingsChange.PromptRestarted -> {
is QuickSettingsChange.PromptRestarted -> {
state.copy(
mode = QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid
mode = QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid(change.sitePermissions)
)
}
is QuickSettingsChange.Stored -> {
state.copy(
mode = QuickSettingsState.Mode.ActionLabelUpdated(change.phoneFeature, change.sitePermissions)
)
}
}
@ -52,28 +64,62 @@ class QuickSettingsComponent(
init {
render(reducer)
}
fun toggleSitePermission(
context: Context,
featurePhone: PhoneFeature,
url: String,
sitePermissions: SitePermissions?
): SitePermissions {
return if (sitePermissions == null) {
val settings = Settings.getInstance(context)
val origin = requireNotNull(url.toUri().host)
var location = settings.getSitePermissionsPhoneFeatureLocation().toStatus()
var camera = settings.getSitePermissionsPhoneFeatureCameraAction().toStatus()
var microphone = settings.getSitePermissionsPhoneFeatureMicrophoneAction().toStatus()
var notification = settings.getSitePermissionsPhoneFeatureNotificationAction().toStatus()
when (featurePhone) {
PhoneFeature.CAMERA -> camera = camera.toggle()
PhoneFeature.LOCATION -> location = location.toggle()
PhoneFeature.MICROPHONE -> microphone = microphone.toggle()
PhoneFeature.NOTIFICATION -> notification = notification.toggle()
}
context.components.storage.addSitePermissionException(origin, location, camera, microphone, notification)
} else {
val updatedSitePermissions = sitePermissions.toggle(featurePhone)
context.components.storage.updateSitePermissions(updatedSitePermissions)
updatedSitePermissions
}
}
}
data class QuickSettingsState(val mode: Mode) : ViewState {
sealed class Mode {
data class Normal(val url: String, val isSecured: Boolean, val isSiteInExceptionList: Boolean) : Mode()
data class ActionLabelUpdated(val phoneFeature: PhoneFeature) : Mode()
object CheckPendingFeatureBlockedByAndroid : Mode()
data class Normal(val url: String, val isSecured: Boolean, val sitePermissions: SitePermissions?) : Mode()
data class ActionLabelUpdated(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) :
Mode()
data class CheckPendingFeatureBlockedByAndroid(val sitePermissions: SitePermissions?) : Mode()
}
}
sealed class QuickSettingsAction : Action {
data class SelectBlockedByAndroid(val permissions: Array<String>) : QuickSettingsAction()
object DismissDialog : QuickSettingsAction()
data class TogglePermission(val featurePhone: PhoneFeature) : QuickSettingsAction()
}
sealed class QuickSettingsChange : Change {
data class Change(
val url: String,
val isSecured: Boolean,
val isSiteInExceptionList: Boolean
val sitePermissions: SitePermissions?
) : QuickSettingsChange()
data class PermissionGranted(val phoneFeature: PhoneFeature) : QuickSettingsChange()
object PromptRestarted : QuickSettingsChange()
data class PermissionGranted(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) :
QuickSettingsChange()
data class PromptRestarted(val sitePermissions: SitePermissions?) : QuickSettingsChange()
data class Stored(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) : QuickSettingsChange()
}

View File

@ -11,24 +11,44 @@ import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.settings.PhoneFeature
import kotlin.coroutines.CoroutineContext
private const val KEY_URL = "KEY_URL"
private const val KEY_IS_SECURED = "KEY_IS_SECURED"
private const val KEY_IS_SITE_IN_EXCEPTION_LIST = "KEY_IS_SITE_IN_EXCEPTION_LIST"
private const val KEY_SITE_PERMISSIONS = "KEY_SITE_PERMISSIONS"
private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4
@SuppressWarnings("TooManyFunctions")
class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment(), CoroutineScope {
private val safeArguments get() = requireNotNull(arguments)
private val url: String by lazy { safeArguments.getString(KEY_URL) }
private val isSecured: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SECURED) }
private val isSiteInExceptionList: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SITE_IN_EXCEPTION_LIST) }
private lateinit var quickSettingsComponent: QuickSettingsComponent
private lateinit var job: Job
var sitePermissions: SitePermissions?
get() = safeArguments.getParcelable(KEY_SITE_PERMISSIONS)
set(value) {
safeArguments.putParcelable(KEY_SITE_PERMISSIONS, value)
}
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_quick_settings_dialog_sheet, container, false)
@ -39,7 +59,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
quickSettingsComponent = QuickSettingsComponent(
rootView as ConstraintLayout, ActionBusFactory.get(this),
QuickSettingsState(
QuickSettingsState.Mode.Normal(url, isSecured, isSiteInExceptionList)
QuickSettingsState.Mode.Normal(url, isSecured, sitePermissions)
)
)
}
@ -50,7 +70,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
fun newInstance(
url: String,
isSecured: Boolean,
isSiteInExceptionList: Boolean
sitePermissions: SitePermissions?
): QuickSettingsSheetDialogFragment {
val fragment = QuickSettingsSheetDialogFragment()
@ -59,7 +79,7 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
with(arguments) {
putString(KEY_URL, url)
putBoolean(KEY_IS_SECURED, isSecured)
putBoolean(KEY_IS_SITE_IN_EXCEPTION_LIST, isSiteInExceptionList)
putParcelable(KEY_SITE_PERMISSIONS, sitePermissions)
}
fragment.arguments = arguments
return fragment
@ -70,10 +90,15 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
if (arePermissionsGranted(requestCode, grantResults)) {
val feature = requireNotNull(PhoneFeature.findFeatureBy(permissions))
getManagedEmitter<QuickSettingsChange>()
.onNext(QuickSettingsChange.PermissionGranted(feature))
.onNext(QuickSettingsChange.PermissionGranted(feature, sitePermissions))
}
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private fun arePermissionsGranted(requestCode: Int, grantResults: IntArray) =
requestCode == REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS && grantResults.all { it == PERMISSION_GRANTED }
@ -85,13 +110,30 @@ class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
is QuickSettingsAction.SelectBlockedByAndroid -> {
requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS)
}
is QuickSettingsAction.DismissDialog -> dismiss()
is QuickSettingsAction.TogglePermission -> {
launch {
sitePermissions = quickSettingsComponent.toggleSitePermission(
context = requireContext(),
featurePhone = it.featurePhone,
url = url,
sitePermissions = sitePermissions
)
launch(Dispatchers.Main) {
getManagedEmitter<QuickSettingsChange>()
.onNext(QuickSettingsChange.Stored(it.featurePhone, sitePermissions))
requireContext().components.useCases.sessionUseCases.reload.invoke()
}
}
}
}
}
if (isVisible) {
getManagedEmitter<QuickSettingsChange>()
.onNext(QuickSettingsChange.PromptRestarted)
.onNext(QuickSettingsChange.PromptRestarted(sitePermissions))
}
}
}

View File

@ -4,8 +4,9 @@
package org.mozilla.fenix.settings.quicksettings
import android.util.TypedValue
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
@ -14,9 +15,10 @@ import androidx.core.content.ContextCompat
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.functions.Consumer
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
import mozilla.components.support.ktx.kotlin.toUri
import org.jetbrains.anko.textColorResource
import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.UIView
import org.mozilla.fenix.settings.PhoneFeature
@ -24,7 +26,6 @@ import org.mozilla.fenix.settings.PhoneFeature.CAMERA
import org.mozilla.fenix.settings.PhoneFeature.LOCATION
import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION
import org.mozilla.fenix.utils.ItsNotBrokenSnack
import org.mozilla.fenix.utils.Settings
class QuickSettingsUIView(
@ -38,26 +39,28 @@ class QuickSettingsUIView(
private val securityInfoLabel: TextView
private val urlLabel: TextView
private val cameraActionLabel: TextView
private val cameraLabel: TextView
private val microphoneActionLabel: TextView
private val microphoneLabel: TextView
private val locationActionLabel: TextView
private val locationLabel: TextView
private val notificationActionLabel: TextView
private val notificationLabel: TextView
private val blockedByAndroidPhoneFeatures = mutableListOf<PhoneFeature>()
private val context get() = view.context
private val settings: Settings = Settings.getInstance(context)
private val toolbarTextColorId by lazy {
val typedValue = TypedValue()
context.theme.resolveAttribute(R.attr.toolbarTextColor, typedValue, true)
typedValue.resourceId
}
init {
urlLabel = view.findViewById<AppCompatTextView>(R.id.url)
securityInfoLabel = view.findViewById<AppCompatTextView>(R.id.security_info)
cameraActionLabel = view.findViewById<AppCompatTextView>(R.id.camera_action_label)
cameraLabel = view.findViewById<AppCompatTextView>(R.id.camera_icon)
microphoneActionLabel = view.findViewById<AppCompatTextView>(R.id.microphone_action_label)
microphoneLabel = view.findViewById<AppCompatTextView>(R.id.microphone_icon)
locationLabel = view.findViewById<AppCompatTextView>(R.id.location_icon)
locationActionLabel = view.findViewById<AppCompatTextView>(R.id.location_action_label)
notificationActionLabel = view.findViewById<AppCompatTextView>(R.id.notification_action_label)
notificationLabel = view.findViewById<AppCompatTextView>(R.id.notification_icon)
}
override fun updateView() = Consumer<QuickSettingsState> { state ->
@ -65,20 +68,20 @@ class QuickSettingsUIView(
is QuickSettingsState.Mode.Normal -> {
bindUrl(state.mode.url)
bindSecurityInfo(state.mode.isSecured)
bindPhoneFeatureItem(cameraActionLabel, CAMERA)
bindPhoneFeatureItem(microphoneActionLabel, MICROPHONE)
bindPhoneFeatureItem(notificationActionLabel, NOTIFICATION)
bindPhoneFeatureItem(locationActionLabel, LOCATION)
bindManagePermissionsButton()
bindPhoneFeatureItem(cameraActionLabel, CAMERA, state.mode.sitePermissions)
bindPhoneFeatureItem(microphoneActionLabel, MICROPHONE, state.mode.sitePermissions)
bindPhoneFeatureItem(notificationActionLabel, NOTIFICATION, state.mode.sitePermissions)
bindPhoneFeatureItem(locationActionLabel, LOCATION, state.mode.sitePermissions)
}
is QuickSettingsState.Mode.ActionLabelUpdated -> {
bindPhoneFeatureItem(
state.mode.phoneFeature.actionLabel,
state.mode.phoneFeature
state.mode.phoneFeature.labelAndAction.second,
state.mode.phoneFeature,
state.mode.sitePermissions
)
}
is QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid -> {
checkFeaturesBlockedByAndroid()
checkFeaturesBlockedByAndroid(state.mode.sitePermissions)
}
}
}
@ -108,17 +111,41 @@ class QuickSettingsUIView(
securityInfoLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
}
private fun bindPhoneFeatureItem(actionLabel: TextView, phoneFeature: PhoneFeature) {
private fun bindPhoneFeatureItem(
actionLabel: TextView,
phoneFeature: PhoneFeature,
sitePermissions: SitePermissions? = null
) {
if (phoneFeature.shouldBeHidden(sitePermissions)) {
hide(phoneFeature)
return
}
show(phoneFeature)
if (!phoneFeature.isAndroidPermissionGranted(context)) {
handleBlockedByAndroidAction(actionLabel, phoneFeature)
} else {
bindPhoneAction(actionLabel, phoneFeature)
bindPhoneAction(actionLabel, phoneFeature, sitePermissions)
}
}
private fun show(phoneFeature: PhoneFeature) {
val (label, action) = phoneFeature.labelAndAction
label.visibility = VISIBLE
action.visibility = VISIBLE
}
private fun hide(phoneFeature: PhoneFeature) {
val (label, action) = phoneFeature.labelAndAction
label.visibility = GONE
action.visibility = GONE
}
private fun PhoneFeature.shouldBeHidden(sitePermissions: SitePermissions?): Boolean {
return getStatus(sitePermissions, settings) == NO_DECISION
}
private fun handleBlockedByAndroidAction(actionLabel: TextView, phoneFeature: PhoneFeature) {
actionLabel.setText(R.string.phone_feature_blocked_by_android)
actionLabel.setTextColor(ContextCompat.getColor(context, R.color.photonBlue50))
actionLabel.tag = phoneFeature
actionLabel.setOnClickListener {
val feature = it.tag as PhoneFeature
@ -131,38 +158,44 @@ class QuickSettingsUIView(
blockedByAndroidPhoneFeatures.add(phoneFeature)
}
private fun bindPhoneAction(actionLabel: TextView, phoneFeature: PhoneFeature) {
actionLabel.text = phoneFeature.getActionLabel(context = context, settings = settings)
actionLabel.textColorResource = toolbarTextColorId
actionLabel.isEnabled = false
private fun bindPhoneAction(
actionLabel: TextView,
phoneFeature: PhoneFeature,
sitePermissions: SitePermissions? = null
) {
actionLabel.text = phoneFeature.getActionLabel(
context = context,
sitePermissions = sitePermissions,
settings = settings
)
actionLabel.tag = phoneFeature
actionLabel.setOnClickListener {
val feature = it.tag as PhoneFeature
actionEmitter.onNext(
QuickSettingsAction.TogglePermission(feature)
)
}
blockedByAndroidPhoneFeatures.remove(phoneFeature)
}
private fun bindManagePermissionsButton() {
val urlLabel = view.findViewById<TextView>(R.id.manage_site_permissions)
urlLabel.setOnClickListener {
actionEmitter.onNext(QuickSettingsAction.DismissDialog)
ItsNotBrokenSnack(context).showSnackbar(issueNumber = "1170")
}
}
private fun checkFeaturesBlockedByAndroid() {
private fun checkFeaturesBlockedByAndroid(sitePermissions: SitePermissions?) {
val clonedList = blockedByAndroidPhoneFeatures.toTypedArray()
clonedList.forEach { phoneFeature ->
if (phoneFeature.isAndroidPermissionGranted(context)) {
val actionLabel = phoneFeature.actionLabel
bindPhoneAction(actionLabel, phoneFeature)
val actionLabel = phoneFeature.labelAndAction.second
bindPhoneAction(actionLabel, phoneFeature, sitePermissions)
}
}
}
private val PhoneFeature.actionLabel
get(): TextView {
private val PhoneFeature.labelAndAction
get(): Pair<TextView, TextView> {
return when (this) {
CAMERA -> cameraActionLabel
LOCATION -> locationActionLabel
MICROPHONE -> microphoneActionLabel
NOTIFICATION -> notificationActionLabel
CAMERA -> cameraLabel to cameraActionLabel
LOCATION -> locationLabel to locationActionLabel
MICROPHONE -> microphoneLabel to microphoneActionLabel
NOTIFICATION -> notificationLabel to notificationActionLabel
}
}
}

View File

@ -34,7 +34,7 @@
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_block"
android:text="@string/preference_option_phone_feature_blocked"
android:textAppearance="?android:attr/textAppearanceListItem"
android:background="?android:attr/selectableItemBackground"
android:button="@null"

View File

@ -121,16 +121,5 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/notification_action_label"/>
<TextView
android:id="@+id/manage_site_permissions"
style="@style/QuickSettingsText"
android:layout_width="wrap_content"
android:layout_height="@dimen/quicksettings_item_height"
android:textColor="@color/photonBlue50"
android:background="?android:attr/selectableItemBackground"
android:text="@string/quick_settings_sheet_manage_site_permissions"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/location_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -36,7 +36,10 @@
<!-- 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_block">Block</string>
<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-->

View File

@ -252,5 +252,6 @@
<item name="android:paddingEnd">24dp</item>
<item name="android:gravity">end|center_vertical</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:textColor">@color/photonBlue50</item>
</style>
</resources>