Closes #1165: Added Doorhanger to the toolbar.
parent
2670f0e1d7
commit
4489edd97b
|
@ -12,6 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- #957 - Adds telemetry for the context menu
|
||||
- #1036 - Adds telemetry for Find in Page
|
||||
- #1049 - Add style for progress bar with gradient drawable
|
||||
|
||||
- #1165 - Added doorhanger to the toolbar
|
||||
### Changed
|
||||
### Removed
|
|
@ -67,6 +67,7 @@ import org.mozilla.fenix.quickactionsheet.QuickActionAction
|
|||
import org.mozilla.fenix.quickactionsheet.QuickActionComponent
|
||||
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsSheetDialogFragment
|
||||
|
||||
class BrowserFragment : Fragment(), BackHandler {
|
||||
private lateinit var toolbarComponent: ToolbarComponent
|
||||
|
@ -255,6 +256,17 @@ class BrowserFragment : Fragment(), BackHandler {
|
|||
view = view
|
||||
)
|
||||
}
|
||||
toolbarComponent.getView().setOnSiteSecurityClickedListener {
|
||||
sessionId?.run {
|
||||
val session = requireNotNull(requireContext().components.core.sessionManager.findSessionById(this))
|
||||
val quickSettingsSheet = QuickSettingsSheetDialogFragment.newInstance(
|
||||
url = session.url,
|
||||
isSecured = session.securityInfo.secure,
|
||||
isSiteInExceptionList = false
|
||||
)
|
||||
quickSettingsSheet.show(requireFragmentManager(), QuickSettingsSheetDialogFragment.FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package org.mozilla.fenix.settings
|
||||
|
||||
import android.content.Context
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
|
@ -18,3 +19,17 @@ 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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* 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.Manifest
|
||||
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
import android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||
import android.Manifest.permission.RECORD_AUDIO
|
||||
import android.content.Context
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.support.ktx.android.content.isPermissionGranted
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
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());
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
fun isAndroidPermissionGranted(context: Context): Boolean {
|
||||
val permissions = when (this) {
|
||||
CAMERA, LOCATION, MICROPHONE -> androidPermissionsList
|
||||
NOTIFICATION -> return true
|
||||
}
|
||||
return context.isPermissionGranted(*permissions)
|
||||
}
|
||||
|
||||
fun getActionLabel(context: Context, sitePermissions: SitePermissions? = null, settings: Settings): String {
|
||||
return when (this) {
|
||||
CAMERA -> {
|
||||
sitePermissions?.cameraBack?.toString(context) ?: settings
|
||||
.getSitePermissionsPhoneFeatureCameraAction()
|
||||
.toString(context)
|
||||
}
|
||||
LOCATION -> {
|
||||
sitePermissions?.location?.toString(context) ?: settings
|
||||
.getSitePermissionsPhoneFeatureLocation()
|
||||
.toString(context)
|
||||
}
|
||||
MICROPHONE -> {
|
||||
sitePermissions?.microphone?.toString(context) ?: settings
|
||||
.getSitePermissionsPhoneFeatureMicrophoneAction()
|
||||
.toString(context)
|
||||
}
|
||||
NOTIFICATION -> {
|
||||
sitePermissions?.notification?.toString(context) ?: settings
|
||||
.getSitePermissionsPhoneFeatureNotificationAction()
|
||||
.toString(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun findFeatureBy(permissions: Array<out String>): PhoneFeature? {
|
||||
return PhoneFeature.values().find { feature ->
|
||||
feature.androidPermissionsList.any { permission ->
|
||||
permission == permissions.first()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,10 @@ import androidx.preference.Preference
|
|||
import androidx.preference.Preference.OnPreferenceClickListener
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature
|
||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.NOTIFICATION
|
||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.LOCATION
|
||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.CAMERA
|
||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.MICROPHONE
|
||||
import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION
|
||||
import org.mozilla.fenix.settings.PhoneFeature.LOCATION
|
||||
import org.mozilla.fenix.settings.PhoneFeature.CAMERA
|
||||
import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
|
||||
package org.mozilla.fenix.settings
|
||||
|
||||
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
import android.Manifest.permission.CAMERA
|
||||
import android.Manifest.permission.RECORD_AUDIO
|
||||
import android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
|
@ -33,7 +29,6 @@ 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.support.ktx.android.content.isPermissionGranted
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class SitePermissionsManagePhoneFeature : Fragment() {
|
||||
|
@ -126,13 +121,6 @@ class SitePermissionsManagePhoneFeature : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
enum class PhoneFeature(val id: Int) {
|
||||
CAMERA(CAMERA_PERMISSION),
|
||||
LOCATION(LOCATION_PERMISSION),
|
||||
MICROPHONE(MICROPHONE_PERMISSION),
|
||||
NOTIFICATION(NOTIFICATION_PERMISSION)
|
||||
}
|
||||
|
||||
private val PhoneFeature.label: String
|
||||
get() {
|
||||
return when (this) {
|
||||
|
@ -143,18 +131,9 @@ class SitePermissionsManagePhoneFeature : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
private val PhoneFeature.isAndroidPermissionGranted: Boolean
|
||||
get() {
|
||||
val permissions = when (this) {
|
||||
PhoneFeature.CAMERA -> arrayOf(CAMERA)
|
||||
PhoneFeature.LOCATION -> arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION)
|
||||
PhoneFeature.MICROPHONE -> arrayOf(RECORD_AUDIO)
|
||||
PhoneFeature.NOTIFICATION -> {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return requireContext().isPermissionGranted(*permissions)
|
||||
return this.isAndroidPermissionGranted(requireContext())
|
||||
}
|
||||
|
||||
private fun Int.toPhoneFeature(): PhoneFeature {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* 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.quicksettings
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
import org.mozilla.fenix.mvi.UIComponent
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
|
||||
class QuickSettingsComponent(
|
||||
private val container: ViewGroup,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: QuickSettingsState
|
||||
) : UIComponent<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
||||
bus.getManagedEmitter(QuickSettingsAction::class.java),
|
||||
bus.getSafeManagedObservable(QuickSettingsChange::class.java)
|
||||
) {
|
||||
override val reducer: (QuickSettingsState, QuickSettingsChange) -> QuickSettingsState = { state, change ->
|
||||
when (change) {
|
||||
is QuickSettingsChange.Change -> {
|
||||
state.copy(
|
||||
mode = QuickSettingsState.Mode.Normal(
|
||||
change.url,
|
||||
change.isSecured,
|
||||
change.isSiteInExceptionList
|
||||
)
|
||||
)
|
||||
}
|
||||
is QuickSettingsChange.PermissionGranted -> {
|
||||
state.copy(
|
||||
mode = QuickSettingsState.Mode.ActionLabelUpdated(change.phoneFeature)
|
||||
)
|
||||
}
|
||||
QuickSettingsChange.PromptRestarted -> {
|
||||
state.copy(
|
||||
mode = QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView(): UIView<QuickSettingsState, QuickSettingsAction, QuickSettingsChange> {
|
||||
return QuickSettingsUIView(container, actionEmitter, changesObservable, container)
|
||||
}
|
||||
|
||||
init {
|
||||
render(reducer)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class QuickSettingsAction : Action {
|
||||
data class SelectBlockedByAndroid(val permissions: Array<String>) : QuickSettingsAction()
|
||||
object DismissDialog : QuickSettingsAction()
|
||||
}
|
||||
|
||||
sealed class QuickSettingsChange : Change {
|
||||
data class Change(
|
||||
val url: String,
|
||||
val isSecured: Boolean,
|
||||
val isSiteInExceptionList: Boolean
|
||||
) : QuickSettingsChange()
|
||||
|
||||
data class PermissionGranted(val phoneFeature: PhoneFeature) : QuickSettingsChange()
|
||||
object PromptRestarted : QuickSettingsChange()
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/* 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.quicksettings
|
||||
|
||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
|
||||
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 REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4
|
||||
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
class QuickSettingsSheetDialogFragment : BottomSheetDialogFragment() {
|
||||
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
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_quick_settings_dialog_sheet, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(rootView, savedInstanceState)
|
||||
quickSettingsComponent = QuickSettingsComponent(
|
||||
rootView as ConstraintLayout, ActionBusFactory.get(this),
|
||||
QuickSettingsState(
|
||||
QuickSettingsState.Mode.Normal(url, isSecured, isSiteInExceptionList)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FRAGMENT_TAG = "QUICK_SETTINGS_FRAGMENT_TAG"
|
||||
|
||||
fun newInstance(
|
||||
url: String,
|
||||
isSecured: Boolean,
|
||||
isSiteInExceptionList: Boolean
|
||||
): QuickSettingsSheetDialogFragment {
|
||||
|
||||
val fragment = QuickSettingsSheetDialogFragment()
|
||||
val arguments = fragment.arguments ?: Bundle()
|
||||
|
||||
with(arguments) {
|
||||
putString(KEY_URL, url)
|
||||
putBoolean(KEY_IS_SECURED, isSecured)
|
||||
putBoolean(KEY_IS_SITE_IN_EXCEPTION_LIST, isSiteInExceptionList)
|
||||
}
|
||||
fragment.arguments = arguments
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
if (arePermissionsGranted(requestCode, grantResults)) {
|
||||
val feature = requireNotNull(PhoneFeature.findFeatureBy(permissions))
|
||||
getManagedEmitter<QuickSettingsChange>()
|
||||
.onNext(QuickSettingsChange.PermissionGranted(feature))
|
||||
}
|
||||
}
|
||||
|
||||
private fun arePermissionsGranted(requestCode: Int, grantResults: IntArray) =
|
||||
requestCode == REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS && grantResults.all { it == PERMISSION_GRANTED }
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
getAutoDisposeObservable<QuickSettingsAction>()
|
||||
.subscribe {
|
||||
when (it) {
|
||||
is QuickSettingsAction.SelectBlockedByAndroid -> {
|
||||
requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS)
|
||||
}
|
||||
is QuickSettingsAction.DismissDialog -> dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
if (isVisible) {
|
||||
getManagedEmitter<QuickSettingsChange>()
|
||||
.onNext(QuickSettingsChange.PromptRestarted)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/* 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.quicksettings
|
||||
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.functions.Consumer
|
||||
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
|
||||
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(
|
||||
container: ViewGroup,
|
||||
actionEmitter: Observer<QuickSettingsAction>,
|
||||
changesObservable: Observable<QuickSettingsChange>,
|
||||
override val view: View
|
||||
) : UIView<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
||||
container, actionEmitter, changesObservable
|
||||
) {
|
||||
private val securityInfoLabel: TextView
|
||||
private val urlLabel: TextView
|
||||
private val cameraActionLabel: TextView
|
||||
private val microphoneActionLabel: TextView
|
||||
private val locationActionLabel: TextView
|
||||
private val notificationActionLabel: 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)
|
||||
microphoneActionLabel = view.findViewById<AppCompatTextView>(R.id.microphone_action_label)
|
||||
locationActionLabel = view.findViewById<AppCompatTextView>(R.id.location_action_label)
|
||||
notificationActionLabel = view.findViewById<AppCompatTextView>(R.id.notification_action_label)
|
||||
}
|
||||
|
||||
override fun updateView() = Consumer<QuickSettingsState> { state ->
|
||||
when (state.mode) {
|
||||
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()
|
||||
}
|
||||
is QuickSettingsState.Mode.ActionLabelUpdated -> {
|
||||
bindPhoneFeatureItem(
|
||||
state.mode.phoneFeature.actionLabel,
|
||||
state.mode.phoneFeature
|
||||
)
|
||||
}
|
||||
is QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid -> {
|
||||
checkFeaturesBlockedByAndroid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindUrl(url: String) {
|
||||
urlLabel.text = url.toUri().hostWithoutCommonPrefixes
|
||||
}
|
||||
|
||||
private fun bindSecurityInfo(isSecured: Boolean) {
|
||||
val stringId: Int
|
||||
val drawableId: Int
|
||||
val drawableTint: Int
|
||||
|
||||
if (isSecured) {
|
||||
stringId = R.string.quick_settings_sheet_secure_connection
|
||||
drawableId = R.drawable.mozac_ic_lock
|
||||
drawableTint = R.color.photonGreen50
|
||||
} else {
|
||||
stringId = R.string.quick_settings_sheet_insecure_connection
|
||||
drawableId = R.drawable.mozac_ic_globe
|
||||
drawableTint = R.color.photonRed50
|
||||
}
|
||||
|
||||
val icon = AppCompatResources.getDrawable(context, drawableId)
|
||||
icon?.setTint(ContextCompat.getColor(context, drawableTint))
|
||||
securityInfoLabel.setText(stringId)
|
||||
securityInfoLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
|
||||
}
|
||||
|
||||
private fun bindPhoneFeatureItem(actionLabel: TextView, phoneFeature: PhoneFeature) {
|
||||
if (!phoneFeature.isAndroidPermissionGranted(context)) {
|
||||
handleBlockedByAndroidAction(actionLabel, phoneFeature)
|
||||
} else {
|
||||
bindPhoneAction(actionLabel, phoneFeature)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
actionEmitter.onNext(
|
||||
QuickSettingsAction.SelectBlockedByAndroid(
|
||||
feature.androidPermissionsList
|
||||
)
|
||||
)
|
||||
}
|
||||
blockedByAndroidPhoneFeatures.add(phoneFeature)
|
||||
}
|
||||
|
||||
private fun bindPhoneAction(actionLabel: TextView, phoneFeature: PhoneFeature) {
|
||||
actionLabel.text = phoneFeature.getActionLabel(context = context, settings = settings)
|
||||
actionLabel.textColorResource = toolbarTextColorId
|
||||
actionLabel.isEnabled = false
|
||||
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() {
|
||||
val clonedList = blockedByAndroidPhoneFeatures.toTypedArray()
|
||||
clonedList.forEach { phoneFeature ->
|
||||
if (phoneFeature.isAndroidPermissionGranted(context)) {
|
||||
val actionLabel = phoneFeature.actionLabel
|
||||
bindPhoneAction(actionLabel, phoneFeature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PhoneFeature.actionLabel
|
||||
get(): TextView {
|
||||
return when (this) {
|
||||
CAMERA -> cameraActionLabel
|
||||
LOCATION -> locationActionLabel
|
||||
MICROPHONE -> microphoneActionLabel
|
||||
NOTIFICATION -> notificationActionLabel
|
||||
}
|
||||
}
|
||||
}
|
|
@ -117,8 +117,12 @@ class Settings private constructor(context: Context) {
|
|||
}
|
||||
|
||||
fun getSitePermissionsPhoneFeatureCameraAction(): SitePermissionsRules.Action {
|
||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_camera), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
return if (shouldRecommendedSettingsBeActivated) {
|
||||
getSitePermissionsRecommendedSettingsRules().camera
|
||||
} else {
|
||||
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_camera), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSitePermissionsPhoneFeatureMicrophoneAction(action: SitePermissionsRules.Action) {
|
||||
|
@ -128,8 +132,12 @@ class Settings private constructor(context: Context) {
|
|||
}
|
||||
|
||||
fun getSitePermissionsPhoneFeatureMicrophoneAction(): SitePermissionsRules.Action {
|
||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_microphone), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
return if (shouldRecommendedSettingsBeActivated) {
|
||||
getSitePermissionsRecommendedSettingsRules().microphone
|
||||
} else {
|
||||
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_microphone), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSitePermissionsPhoneFeatureNotificationAction(action: SitePermissionsRules.Action) {
|
||||
|
@ -139,8 +147,12 @@ class Settings private constructor(context: Context) {
|
|||
}
|
||||
|
||||
fun getSitePermissionsPhoneFeatureNotificationAction(): SitePermissionsRules.Action {
|
||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_notification), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
return if (shouldRecommendedSettingsBeActivated) {
|
||||
getSitePermissionsRecommendedSettingsRules().notification
|
||||
} else {
|
||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_notification), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSitePermissionsPhoneFeatureLocation(action: SitePermissionsRules.Action) {
|
||||
|
@ -150,8 +162,12 @@ class Settings private constructor(context: Context) {
|
|||
}
|
||||
|
||||
fun getSitePermissionsPhoneFeatureLocation(): SitePermissionsRules.Action {
|
||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_location), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
return if (shouldRecommendedSettingsBeActivated) {
|
||||
getSitePermissionsRecommendedSettingsRules().location
|
||||
} else {
|
||||
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_location), 1)
|
||||
.toSitePermissionsRulesAction()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSitePermissionsRecommendedSettingsRules() = SitePermissionsRules(
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
<?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:id="@+id/quick_action_sheet"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/quick_settings_sheet"
|
||||
android:background="?attr/toolbarColor">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/url"
|
||||
style="@style/QuickSettingsText"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
android:layout_width="wrap_content"
|
||||
tools:text="https://wikipedia.org"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/security_info"
|
||||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
android:layout_width="wrap_content"
|
||||
tools:drawableStartCompat="@drawable/mozac_ic_lock"
|
||||
tools:drawableTint="@color/photonGreen50"
|
||||
tools:text="Secure connection"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/url"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/line_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/homeDividerColor"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/security_info"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_icon"
|
||||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
android:layout_width="wrap_content"
|
||||
app:drawableStartCompat="@drawable/ic_camera"
|
||||
android:text="@string/preference_phone_feature_camera"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/line_divider"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_action_label"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||
tools:text="Allowed"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/line_divider"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/microphone_icon"
|
||||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
app:drawableStartCompat="@drawable/ic_microphone"
|
||||
android:text="@string/preference_phone_feature_microphone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/camera_icon"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/microphone_action_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||
tools:text="Blocked by Android"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/camera_action_label"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_icon"
|
||||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
app:drawableStartCompat="@drawable/ic_notification"
|
||||
android:text="@string/preference_phone_feature_notification"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/microphone_icon"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_action_label"
|
||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
tools:text="Blocked"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/microphone_action_label"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_icon"
|
||||
style="@style/QuickSettingsText.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
app:drawableStartCompat="@drawable/ic_location"
|
||||
android:text="@string/preference_phone_feature_location"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_icon"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_action_label"
|
||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/quicksettings_item_height"
|
||||
tools:text="Blocked"
|
||||
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>
|
||||
|
|
@ -27,4 +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>
|
||||
|
||||
<!--Quick Settings-->
|
||||
<dimen name="quicksettings_item_height">46dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -39,6 +39,8 @@
|
|||
<string name="preference_option_phone_feature_block">Block</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
|
||||
|
@ -47,4 +49,14 @@
|
|||
<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-->
|
||||
<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>
|
||||
</resources>
|
|
@ -222,4 +222,23 @@
|
|||
<style name="progressBarStyleHorizontal" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
|
||||
<item name="android:progressDrawable">@drawable/progress_gradient</item>
|
||||
</style>
|
||||
|
||||
<style name="QuickSettingsText">
|
||||
<item name="android:textColor">?attr/toolbarTextColor</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:paddingStart">16dp</item>
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
<item name="android:layout_alignParentStart">true</item>
|
||||
</style>
|
||||
|
||||
<style name="QuickSettingsText.Icon">
|
||||
<item name="android:drawablePadding">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="QuickSettingsText.PermissionItemEnd">
|
||||
<item name="android:layout_alignParentEnd">true</item>
|
||||
<item name="android:paddingEnd">24dp</item>
|
||||
<item name="android:gravity">end|center_vertical</item>
|
||||
<item name="android:background">?android:attr/selectableItemBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue