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
|
- #957 - Adds telemetry for the context menu
|
||||||
- #1036 - Adds telemetry for Find in Page
|
- #1036 - Adds telemetry for Find in Page
|
||||||
- #1049 - Add style for progress bar with gradient drawable
|
- #1049 - Add style for progress bar with gradient drawable
|
||||||
|
- #1165 - Added doorhanger to the toolbar
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
|
@ -67,6 +67,7 @@ import org.mozilla.fenix.quickactionsheet.QuickActionAction
|
||||||
import org.mozilla.fenix.quickactionsheet.QuickActionComponent
|
import org.mozilla.fenix.quickactionsheet.QuickActionComponent
|
||||||
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
||||||
import org.mozilla.fenix.utils.Settings
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
import org.mozilla.fenix.settings.quicksettings.QuickSettingsSheetDialogFragment
|
||||||
|
|
||||||
class BrowserFragment : Fragment(), BackHandler {
|
class BrowserFragment : Fragment(), BackHandler {
|
||||||
private lateinit var toolbarComponent: ToolbarComponent
|
private lateinit var toolbarComponent: ToolbarComponent
|
||||||
|
@ -255,6 +256,17 @@ class BrowserFragment : Fragment(), BackHandler {
|
||||||
view = view
|
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() {
|
override fun onResume() {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.mozilla.fenix.settings
|
package org.mozilla.fenix.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||||
import org.mozilla.fenix.R
|
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.Preference.OnPreferenceClickListener
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature
|
import org.mozilla.fenix.settings.PhoneFeature.NOTIFICATION
|
||||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.NOTIFICATION
|
import org.mozilla.fenix.settings.PhoneFeature.LOCATION
|
||||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.LOCATION
|
import org.mozilla.fenix.settings.PhoneFeature.CAMERA
|
||||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.CAMERA
|
import org.mozilla.fenix.settings.PhoneFeature.MICROPHONE
|
||||||
import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.MICROPHONE
|
|
||||||
import org.mozilla.fenix.utils.Settings
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
@SuppressWarnings("TooManyFunctions")
|
@SuppressWarnings("TooManyFunctions")
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
|
|
||||||
package org.mozilla.fenix.settings
|
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.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
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
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.BLOCKED
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.BLOCKED
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ASK_TO_ALLOW
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ASK_TO_ALLOW
|
||||||
import mozilla.components.support.ktx.android.content.isPermissionGranted
|
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
class SitePermissionsManagePhoneFeature : Fragment() {
|
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
|
private val PhoneFeature.label: String
|
||||||
get() {
|
get() {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
@ -143,18 +131,9 @@ class SitePermissionsManagePhoneFeature : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SpreadOperator")
|
|
||||||
private val PhoneFeature.isAndroidPermissionGranted: Boolean
|
private val PhoneFeature.isAndroidPermissionGranted: Boolean
|
||||||
get() {
|
get() {
|
||||||
val permissions = when (this) {
|
return this.isAndroidPermissionGranted(requireContext())
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.toPhoneFeature(): PhoneFeature {
|
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 {
|
fun getSitePermissionsPhoneFeatureCameraAction(): SitePermissionsRules.Action {
|
||||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_camera), 1)
|
return if (shouldRecommendedSettingsBeActivated) {
|
||||||
.toSitePermissionsRulesAction()
|
getSitePermissionsRecommendedSettingsRules().camera
|
||||||
|
} else {
|
||||||
|
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_camera), 1)
|
||||||
|
.toSitePermissionsRulesAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSitePermissionsPhoneFeatureMicrophoneAction(action: SitePermissionsRules.Action) {
|
fun setSitePermissionsPhoneFeatureMicrophoneAction(action: SitePermissionsRules.Action) {
|
||||||
|
@ -128,8 +132,12 @@ class Settings private constructor(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSitePermissionsPhoneFeatureMicrophoneAction(): SitePermissionsRules.Action {
|
fun getSitePermissionsPhoneFeatureMicrophoneAction(): SitePermissionsRules.Action {
|
||||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_microphone), 1)
|
return if (shouldRecommendedSettingsBeActivated) {
|
||||||
.toSitePermissionsRulesAction()
|
getSitePermissionsRecommendedSettingsRules().microphone
|
||||||
|
} else {
|
||||||
|
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_microphone), 1)
|
||||||
|
.toSitePermissionsRulesAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSitePermissionsPhoneFeatureNotificationAction(action: SitePermissionsRules.Action) {
|
fun setSitePermissionsPhoneFeatureNotificationAction(action: SitePermissionsRules.Action) {
|
||||||
|
@ -139,8 +147,12 @@ class Settings private constructor(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSitePermissionsPhoneFeatureNotificationAction(): SitePermissionsRules.Action {
|
fun getSitePermissionsPhoneFeatureNotificationAction(): SitePermissionsRules.Action {
|
||||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_notification), 1)
|
return if (shouldRecommendedSettingsBeActivated) {
|
||||||
.toSitePermissionsRulesAction()
|
getSitePermissionsRecommendedSettingsRules().notification
|
||||||
|
} else {
|
||||||
|
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_notification), 1)
|
||||||
|
.toSitePermissionsRulesAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSitePermissionsPhoneFeatureLocation(action: SitePermissionsRules.Action) {
|
fun setSitePermissionsPhoneFeatureLocation(action: SitePermissionsRules.Action) {
|
||||||
|
@ -150,8 +162,12 @@ class Settings private constructor(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSitePermissionsPhoneFeatureLocation(): SitePermissionsRules.Action {
|
fun getSitePermissionsPhoneFeatureLocation(): SitePermissionsRules.Action {
|
||||||
return preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_location), 1)
|
return if (shouldRecommendedSettingsBeActivated) {
|
||||||
.toSitePermissionsRulesAction()
|
getSitePermissionsRecommendedSettingsRules().location
|
||||||
|
} else {
|
||||||
|
preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_phone_feature_location), 1)
|
||||||
|
.toSitePermissionsRulesAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSitePermissionsRecommendedSettingsRules() = SitePermissionsRules(
|
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_drawable_padding">16dp</dimen>
|
||||||
<dimen name="radio_button_preference_vertical">12dp</dimen>
|
<dimen name="radio_button_preference_vertical">12dp</dimen>
|
||||||
<dimen name="phone_feature_label_recommended_text_size">14sp</dimen>
|
<dimen name="phone_feature_label_recommended_text_size">14sp</dimen>
|
||||||
|
|
||||||
|
<!--Quick Settings-->
|
||||||
|
<dimen name="quicksettings_item_height">46dp</dimen>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -39,6 +39,8 @@
|
||||||
<string name="preference_option_phone_feature_block">Block</string>
|
<string name="preference_option_phone_feature_block">Block</string>
|
||||||
<!--Label that indicates a permission is by the Android OS-->
|
<!--Label that indicates a permission is by the Android OS-->
|
||||||
<string name="phone_feature_blocked_by_android">Blocked by Android</string>
|
<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) -->
|
<!-- 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[
|
<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
|
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>
|
<string name="phone_feature_recommended">Recommended</string>
|
||||||
<!--Button label that take the user to the Android App setting -->
|
<!--Button label that take the user to the Android App setting -->
|
||||||
<string name="phone_feature_go_to_settings">Go to Settings</string>
|
<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>
|
</resources>
|
|
@ -222,4 +222,23 @@
|
||||||
<style name="progressBarStyleHorizontal" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
|
<style name="progressBarStyleHorizontal" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
|
||||||
<item name="android:progressDrawable">@drawable/progress_gradient</item>
|
<item name="android:progressDrawable">@drawable/progress_gradient</item>
|
||||||
</style>
|
</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>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue