For #4126 - Refactor Views and layouts
Refactored `fragment_quick_settings_dialog_sheet` to now be composed of of FrameLayouts placeholders in which each independent View will inflate itself. Refactored the QuickSettingsUIView and Component to 3 standalone Views with their own lib-state components: Store, State, Actions, Reducer.master
parent
5cca5d7a70
commit
f1f74bc3d6
|
@ -37,25 +37,29 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array<String>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("ComplexMethod")
|
||||||
fun getActionLabel(
|
fun getActionLabel(
|
||||||
context: Context,
|
context: Context,
|
||||||
sitePermissions: SitePermissions? = null,
|
sitePermissions: SitePermissions? = null,
|
||||||
settings: Settings? = null
|
settings: Settings? = null
|
||||||
): String {
|
): String {
|
||||||
@StringRes val stringRes =
|
@StringRes val stringRes =
|
||||||
when (this) {
|
when (isAndroidPermissionGranted(context)) {
|
||||||
AUTOPLAY -> {
|
false -> R.string.phone_feature_blocked_by_android
|
||||||
when (getStatus(sitePermissions, settings)) {
|
else -> when (this) {
|
||||||
SitePermissions.Status.BLOCKED -> R.string.preference_option_autoplay_blocked
|
AUTOPLAY -> {
|
||||||
SitePermissions.Status.ALLOWED -> R.string.preference_option_autoplay_allowed
|
when (getStatus(sitePermissions, settings)) {
|
||||||
else -> R.string.preference_option_autoplay_allowed
|
SitePermissions.Status.BLOCKED -> R.string.preference_option_autoplay_blocked
|
||||||
|
SitePermissions.Status.ALLOWED -> R.string.preference_option_autoplay_allowed
|
||||||
|
else -> R.string.preference_option_autoplay_allowed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else -> {
|
||||||
else -> {
|
when (getStatus(sitePermissions, settings)) {
|
||||||
when (getStatus(sitePermissions, settings)) {
|
SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked
|
||||||
SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked
|
SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow
|
||||||
SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow
|
SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed
|
||||||
SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
/* 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.Context
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
import org.mozilla.fenix.ext.settings
|
|
||||||
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.UIComponentViewModelBase
|
|
||||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
|
||||||
import org.mozilla.fenix.mvi.UIView
|
|
||||||
import org.mozilla.fenix.mvi.ViewState
|
|
||||||
import org.mozilla.fenix.settings.PhoneFeature
|
|
||||||
import org.mozilla.fenix.settings.toggle
|
|
||||||
|
|
||||||
class QuickSettingsComponent(
|
|
||||||
private val container: ViewGroup,
|
|
||||||
bus: ActionBusFactory,
|
|
||||||
viewModelProvider: UIComponentViewModelProvider<QuickSettingsState, QuickSettingsChange>
|
|
||||||
) : UIComponent<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
|
||||||
bus.getManagedEmitter(QuickSettingsAction::class.java),
|
|
||||||
bus.getSafeManagedObservable(QuickSettingsChange::class.java),
|
|
||||||
viewModelProvider
|
|
||||||
) {
|
|
||||||
override fun initView(): UIView<QuickSettingsState, QuickSettingsAction, QuickSettingsChange> {
|
|
||||||
return QuickSettingsUIView(container, actionEmitter, changesObservable, container)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
bind()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toggleSitePermission(
|
|
||||||
context: Context,
|
|
||||||
featurePhone: PhoneFeature,
|
|
||||||
url: String,
|
|
||||||
sitePermissions: SitePermissions?
|
|
||||||
): SitePermissions {
|
|
||||||
|
|
||||||
return if (sitePermissions == null) {
|
|
||||||
val settings = context.settings()
|
|
||||||
val origin = requireNotNull(url.toUri().host)
|
|
||||||
var location =
|
|
||||||
settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION).toStatus()
|
|
||||||
var camera =
|
|
||||||
settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA).toStatus()
|
|
||||||
var microphone =
|
|
||||||
settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE).toStatus()
|
|
||||||
var notification =
|
|
||||||
settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION).toStatus()
|
|
||||||
|
|
||||||
when (featurePhone) {
|
|
||||||
PhoneFeature.CAMERA -> camera = camera.toggle()
|
|
||||||
PhoneFeature.LOCATION -> location = location.toggle()
|
|
||||||
PhoneFeature.MICROPHONE -> microphone = microphone.toggle()
|
|
||||||
PhoneFeature.NOTIFICATION -> notification = notification.toggle()
|
|
||||||
PhoneFeature.AUTOPLAY -> { // not supported by GV or A-C yet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.components.core.permissionStorage
|
|
||||||
.addSitePermissionException(origin, location, notification, microphone, camera)
|
|
||||||
} else {
|
|
||||||
val updatedSitePermissions = sitePermissions.toggle(featurePhone)
|
|
||||||
context.components.core.permissionStorage.updateSitePermissions(updatedSitePermissions)
|
|
||||||
updatedSitePermissions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class QuickSettingsState(val mode: Mode) : ViewState {
|
|
||||||
sealed class Mode {
|
|
||||||
data class Normal(
|
|
||||||
val url: String,
|
|
||||||
val isSecured: Boolean,
|
|
||||||
val isTrackingProtectionOn: 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 SelectReportProblem(val url: String) : QuickSettingsAction()
|
|
||||||
object SelectTrackingProtectionSettings : QuickSettingsAction()
|
|
||||||
data class ToggleTrackingProtection(val trackingProtection: Boolean) : QuickSettingsAction()
|
|
||||||
data class SelectBlockedByAndroid(val permissions: Array<String>) : QuickSettingsAction()
|
|
||||||
data class TogglePermission(val featurePhone: PhoneFeature) : QuickSettingsAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class QuickSettingsChange : Change {
|
|
||||||
data class Change(
|
|
||||||
val url: String,
|
|
||||||
val isSecured: Boolean,
|
|
||||||
val isTrackingProtectionOn: Boolean,
|
|
||||||
val sitePermissions: SitePermissions?
|
|
||||||
) : 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
class QuickSettingsViewModel(
|
|
||||||
initialState: QuickSettingsState
|
|
||||||
) : UIComponentViewModelBase<QuickSettingsState, QuickSettingsChange>(initialState, reducer) {
|
|
||||||
companion object {
|
|
||||||
val reducer: (QuickSettingsState, QuickSettingsChange) -> QuickSettingsState =
|
|
||||||
{ state, change ->
|
|
||||||
when (change) {
|
|
||||||
is QuickSettingsChange.Change -> {
|
|
||||||
state.copy(
|
|
||||||
mode = QuickSettingsState.Mode.Normal(
|
|
||||||
change.url,
|
|
||||||
change.isSecured,
|
|
||||||
change.isTrackingProtectionOn,
|
|
||||||
change.sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is QuickSettingsChange.PermissionGranted -> {
|
|
||||||
state.copy(
|
|
||||||
mode = QuickSettingsState.Mode.ActionLabelUpdated(
|
|
||||||
change.phoneFeature,
|
|
||||||
change.sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is QuickSettingsChange.PromptRestarted -> {
|
|
||||||
state.copy(
|
|
||||||
mode = QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid(
|
|
||||||
change.sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is QuickSettingsChange.Stored -> {
|
|
||||||
state.copy(
|
|
||||||
mode = QuickSettingsState.Mode.ActionLabelUpdated(
|
|
||||||
change.phoneFeature,
|
|
||||||
change.sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,9 +5,6 @@
|
||||||
package org.mozilla.fenix.settings.quicksettings
|
package org.mozilla.fenix.settings.quicksettings
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -19,75 +16,62 @@ import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.core.net.toUri
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.NestedScrollView
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.fragment.NavHostFragment.findNavController
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.view.*
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.lib.state.ext.consumeFrom
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
import org.mozilla.fenix.FeatureFlags
|
||||||
import org.mozilla.fenix.FenixViewModelProvider
|
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.IntentReceiverActivity
|
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.browser.BrowserFragment
|
import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
import org.mozilla.fenix.utils.Settings
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
|
||||||
import org.mozilla.fenix.ext.tryGetHostFromUrl
|
|
||||||
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 com.google.android.material.R as MaterialR
|
import com.google.android.material.R as MaterialR
|
||||||
|
|
||||||
private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4
|
@ObsoleteCoroutinesApi
|
||||||
|
|
||||||
@SuppressWarnings("TooManyFunctions")
|
@SuppressWarnings("TooManyFunctions")
|
||||||
class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() {
|
class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() {
|
||||||
|
private lateinit var websiteInfoStore: WebsiteInfoStore
|
||||||
|
private lateinit var websitePermissionsStore: WebsitePermissionsStore
|
||||||
|
private lateinit var websiteTrackingProtectionStore: TrackingProtectionStore
|
||||||
|
private lateinit var websiteInfoView: WebsiteInfoView
|
||||||
|
private lateinit var websitePermissionsView: WebsitePermissionsView
|
||||||
|
private lateinit var websiteTrackingProtectionView: TrackingProtectionView
|
||||||
private val safeArguments get() = requireNotNull(arguments)
|
private val safeArguments get() = requireNotNull(arguments)
|
||||||
private val sessionId: String by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).sessionId }
|
|
||||||
private val url: String by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).url }
|
|
||||||
private val isSecured: Boolean by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).isSecured }
|
|
||||||
private val isTrackingProtectionOn: Boolean by lazy {
|
|
||||||
QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).isTrackingProtectionOn
|
|
||||||
}
|
|
||||||
private val promptGravity: Int by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).gravity }
|
private val promptGravity: Int by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).gravity }
|
||||||
private lateinit var quickSettingsComponent: QuickSettingsComponent
|
|
||||||
|
|
||||||
private var sitePermissions: SitePermissions? = null
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
sitePermissions = QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).sitePermissions
|
|
||||||
|
val args = QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments)
|
||||||
val rootView = inflateRootView(container)
|
val rootView = inflateRootView(container)
|
||||||
requireComponents.core.sessionManager.findSessionById(sessionId)?.register(sessionObserver, view = rootView)
|
|
||||||
quickSettingsComponent = QuickSettingsComponent(
|
websitePermissionsStore = WebsitePermissionsStore.createStore(
|
||||||
rootView as NestedScrollView,
|
context!!, args.sitePermissions, Settings.getInstance(context!!)
|
||||||
ActionBusFactory.get(this),
|
|
||||||
FenixViewModelProvider.create(
|
|
||||||
this,
|
|
||||||
QuickSettingsViewModel::class.java
|
|
||||||
) {
|
|
||||||
QuickSettingsViewModel(
|
|
||||||
QuickSettingsState(
|
|
||||||
QuickSettingsState.Mode.Normal(
|
|
||||||
url,
|
|
||||||
isSecured,
|
|
||||||
isTrackingProtectionOn,
|
|
||||||
sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
websiteInfoStore = WebsiteInfoStore.createStore(args.url, args.isSecured)
|
||||||
|
|
||||||
|
if (!FeatureFlags.etpCategories) {
|
||||||
|
websiteTrackingProtectionStore =
|
||||||
|
TrackingProtectionStore.createStore(
|
||||||
|
args.url,
|
||||||
|
args.isTrackingProtectionOn,
|
||||||
|
context!!.settings()
|
||||||
|
)
|
||||||
|
websiteTrackingProtectionView =
|
||||||
|
TrackingProtectionView(rootView.trackingProtectionLayout)
|
||||||
|
} else {
|
||||||
|
rootView.trackingProtectionGroup.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
websiteInfoView = WebsiteInfoView(rootView.websiteInfoLayout)
|
||||||
|
websitePermissionsView = WebsitePermissionsView(rootView.websitePermissionsLayout)
|
||||||
|
|
||||||
return rootView
|
return rootView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +102,17 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
consumeFrom(websiteInfoStore) { websiteInfoView.update(it) }
|
||||||
|
consumeFrom(websitePermissionsStore) { websitePermissionsView.update(it) }
|
||||||
|
if (::websiteTrackingProtectionStore.isInitialized) {
|
||||||
|
consumeFrom(websiteTrackingProtectionStore) { websiteTrackingProtectionView.update(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun Dialog.applyCustomizationsForTopDialog(rootView: View): Dialog {
|
private fun Dialog.applyCustomizationsForTopDialog(rootView: View): Dialog {
|
||||||
addContentView(
|
addContentView(
|
||||||
rootView,
|
rootView,
|
||||||
|
@ -135,146 +130,4 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() {
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
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, sitePermissions))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun arePermissionsGranted(requestCode: Int, grantResults: IntArray) =
|
|
||||||
requestCode == REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS && grantResults.all { it == PERMISSION_GRANTED }
|
|
||||||
|
|
||||||
private fun toggleTrackingProtection(context: Context, url: String) {
|
|
||||||
val host = url.tryGetHostFromUrl()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
ExceptionDomains(context).toggle(host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
getAutoDisposeObservable<QuickSettingsAction>()
|
|
||||||
.subscribe {
|
|
||||||
when (it) {
|
|
||||||
is QuickSettingsAction.SelectBlockedByAndroid -> {
|
|
||||||
requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS)
|
|
||||||
}
|
|
||||||
is QuickSettingsAction.SelectTrackingProtectionSettings -> {
|
|
||||||
val directions =
|
|
||||||
QuickSettingsSheetDialogFragmentDirections
|
|
||||||
.actionQuickSettingsSheetDialogFragmentToTrackingProtectionFragment()
|
|
||||||
findNavController(this@QuickSettingsSheetDialogFragment).navigate(directions)
|
|
||||||
}
|
|
||||||
is QuickSettingsAction.SelectReportProblem -> {
|
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
val reportUrl =
|
|
||||||
String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, it.url)
|
|
||||||
requireComponents.useCases.tabsUseCases.addTab.invoke(reportUrl)
|
|
||||||
val sessionManager = requireComponents.core.sessionManager
|
|
||||||
if (sessionManager.findSessionById(sessionId)?.isCustomTabSession() == true) {
|
|
||||||
val intent = Intent(context, IntentReceiverActivity::class.java)
|
|
||||||
intent.action = Intent.ACTION_VIEW
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
is QuickSettingsAction.ToggleTrackingProtection -> {
|
|
||||||
val trackingEnabled = it.trackingProtection
|
|
||||||
context?.let { context: Context -> toggleTrackingProtection(context, url) }
|
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
getManagedEmitter<QuickSettingsChange>().onNext(
|
|
||||||
QuickSettingsChange.Change(
|
|
||||||
url,
|
|
||||||
isSecured,
|
|
||||||
trackingEnabled,
|
|
||||||
sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
requireContext().components.useCases.sessionUseCases.reload.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is QuickSettingsAction.TogglePermission -> {
|
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
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(sitePermissions))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sessionObserver = object : Session.Observer {
|
|
||||||
override fun onUrlChanged(session: Session, url: String) {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val host = session.url.toUri().host
|
|
||||||
val sitePermissions: SitePermissions? = host?.let {
|
|
||||||
val storage = requireContext().components.core.permissionStorage
|
|
||||||
storage.findSitePermissionsBy(it)
|
|
||||||
}
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
getManagedEmitter<QuickSettingsChange>().onNext(
|
|
||||||
QuickSettingsChange.Change(
|
|
||||||
url,
|
|
||||||
session.securityInfo.secure,
|
|
||||||
session.trackerBlockingEnabled,
|
|
||||||
sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTrackerBlockingEnabledChanged(session: Session, blockingEnabled: Boolean) {
|
|
||||||
getManagedEmitter<QuickSettingsChange>().onNext(
|
|
||||||
QuickSettingsChange.Change(
|
|
||||||
session.url,
|
|
||||||
session.securityInfo.secure,
|
|
||||||
blockingEnabled,
|
|
||||||
sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSecurityChanged(session: Session, securityInfo: Session.SecurityInfo) {
|
|
||||||
getManagedEmitter<QuickSettingsChange>().onNext(
|
|
||||||
QuickSettingsChange.Change(
|
|
||||||
session.url,
|
|
||||||
securityInfo.secure,
|
|
||||||
session.trackerBlockingEnabled,
|
|
||||||
sitePermissions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
/* 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.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.ColorRes
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.annotation.IdRes
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import io.reactivex.Observable
|
|
||||||
import io.reactivex.Observer
|
|
||||||
import io.reactivex.functions.Consumer
|
|
||||||
import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.*
|
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissions.Status.BLOCKED
|
|
||||||
import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION
|
|
||||||
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
|
||||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.ext.settings
|
|
||||||
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.Settings
|
|
||||||
|
|
||||||
typealias LabelActionPair = Pair<TextView, TextView>
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
|
||||||
class QuickSettingsUIView(
|
|
||||||
container: ViewGroup,
|
|
||||||
actionEmitter: Observer<QuickSettingsAction>,
|
|
||||||
changesObservable: Observable<QuickSettingsChange>,
|
|
||||||
override val view: View
|
|
||||||
) : UIView<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
|
||||||
container, actionEmitter, changesObservable
|
|
||||||
) {
|
|
||||||
private val blockedByAndroidPhoneFeatures = mutableListOf<PhoneFeature>()
|
|
||||||
private inline val context get() = view.context
|
|
||||||
private val settings: Settings = context.settings()
|
|
||||||
private val trackingProtectionSettingView = TrackingProtectionSettingView(view, actionEmitter)
|
|
||||||
private val labelAndActions = mapOf(
|
|
||||||
CAMERA to findLabelActionPair(R.id.camera_icon, R.id.camera_action_label),
|
|
||||||
LOCATION to findLabelActionPair(R.id.location_icon, R.id.location_action_label),
|
|
||||||
MICROPHONE to findLabelActionPair(R.id.microphone_icon, R.id.microphone_action_label),
|
|
||||||
NOTIFICATION to findLabelActionPair(R.id.notification_icon, R.id.notification_action_label)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val blockedByAndroidClickListener = View.OnClickListener {
|
|
||||||
val feature = it.tag as PhoneFeature
|
|
||||||
actionEmitter.onNext(
|
|
||||||
QuickSettingsAction.SelectBlockedByAndroid(feature.androidPermissionsList)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
private val togglePermissionClickListener = View.OnClickListener {
|
|
||||||
val feature = it.tag as PhoneFeature
|
|
||||||
actionEmitter.onNext(
|
|
||||||
QuickSettingsAction.TogglePermission(feature)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateView() = Consumer<QuickSettingsState> { state ->
|
|
||||||
when (state.mode) {
|
|
||||||
is QuickSettingsState.Mode.Normal -> {
|
|
||||||
bindUrl(state.mode.url)
|
|
||||||
bindSecurityInfo(state.mode.isSecured)
|
|
||||||
bindReportSiteIssueAction(state.mode.url)
|
|
||||||
trackingProtectionSettingView.bind(state.mode.isTrackingProtectionOn)
|
|
||||||
bindPhoneFeatureItem(CAMERA, state.mode.sitePermissions)
|
|
||||||
bindPhoneFeatureItem(MICROPHONE, state.mode.sitePermissions)
|
|
||||||
bindPhoneFeatureItem(NOTIFICATION, state.mode.sitePermissions)
|
|
||||||
bindPhoneFeatureItem(LOCATION, state.mode.sitePermissions)
|
|
||||||
}
|
|
||||||
is QuickSettingsState.Mode.ActionLabelUpdated -> {
|
|
||||||
bindPhoneFeatureItem(state.mode.phoneFeature, state.mode.sitePermissions)
|
|
||||||
}
|
|
||||||
is QuickSettingsState.Mode.CheckPendingFeatureBlockedByAndroid -> {
|
|
||||||
checkFeaturesBlockedByAndroid(state.mode.sitePermissions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindUrl(url: String) {
|
|
||||||
this.url.text = url.toUri().hostWithoutCommonPrefixes
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindReportSiteIssueAction(url: String) {
|
|
||||||
report_site_issue_action.setOnClickListener {
|
|
||||||
actionEmitter.onNext(
|
|
||||||
QuickSettingsAction.SelectReportProblem(url)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindSecurityInfo(isSecured: Boolean) {
|
|
||||||
@StringRes val stringId: Int
|
|
||||||
@DrawableRes val drawableId: Int
|
|
||||||
@ColorRes 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_broken_lock
|
|
||||||
drawableTint = R.color.photonRed50
|
|
||||||
}
|
|
||||||
|
|
||||||
val icon = context.getDrawable(drawableId)
|
|
||||||
icon?.setTint(ContextCompat.getColor(context, drawableTint))
|
|
||||||
security_info.setText(stringId)
|
|
||||||
security_info.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindPhoneFeatureItem(phoneFeature: PhoneFeature, sitePermissions: SitePermissions? = null) {
|
|
||||||
val (label, action) = labelAndActions.getValue(phoneFeature)
|
|
||||||
val shouldBeVisible = phoneFeature.shouldBeVisible(sitePermissions)
|
|
||||||
label.isVisible = shouldBeVisible
|
|
||||||
action.isVisible = shouldBeVisible
|
|
||||||
|
|
||||||
if (shouldBeVisible) {
|
|
||||||
if (phoneFeature.isAndroidPermissionGranted(context)) {
|
|
||||||
bindPhoneAction(phoneFeature, sitePermissions)
|
|
||||||
} else {
|
|
||||||
handleBlockedByAndroidAction(phoneFeature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PhoneFeature.shouldBeVisible(sitePermissions: SitePermissions?): Boolean {
|
|
||||||
return getStatus(sitePermissions, settings) != NO_DECISION
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PhoneFeature.isPermissionBlocked(sitePermissions: SitePermissions?): Boolean {
|
|
||||||
return getStatus(sitePermissions, settings) == BLOCKED
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleBlockedByAndroidAction(phoneFeature: PhoneFeature) {
|
|
||||||
val (label, action) = labelAndActions.getValue(phoneFeature)
|
|
||||||
|
|
||||||
action.setText(R.string.phone_feature_blocked_by_android)
|
|
||||||
action.tag = phoneFeature
|
|
||||||
action.setOnClickListener(blockedByAndroidClickListener)
|
|
||||||
label.isEnabled = false
|
|
||||||
blockedByAndroidPhoneFeatures.add(phoneFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindPhoneAction(phoneFeature: PhoneFeature, sitePermissions: SitePermissions? = null) {
|
|
||||||
val (label, action) = labelAndActions.getValue(phoneFeature)
|
|
||||||
|
|
||||||
action.text = phoneFeature.getActionLabel(
|
|
||||||
context = context,
|
|
||||||
sitePermissions = sitePermissions,
|
|
||||||
settings = settings
|
|
||||||
)
|
|
||||||
|
|
||||||
action.tag = phoneFeature
|
|
||||||
action.setOnClickListener(togglePermissionClickListener)
|
|
||||||
|
|
||||||
label.isEnabled = !phoneFeature.isPermissionBlocked(sitePermissions)
|
|
||||||
blockedByAndroidPhoneFeatures.remove(phoneFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkFeaturesBlockedByAndroid(sitePermissions: SitePermissions?) {
|
|
||||||
blockedByAndroidPhoneFeatures.forEach { phoneFeature ->
|
|
||||||
if (phoneFeature.isAndroidPermissionGranted(context)) {
|
|
||||||
bindPhoneAction(phoneFeature, sitePermissions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findLabelActionPair(@IdRes labelId: Int, @IdRes actionId: Int): LabelActionPair {
|
|
||||||
return view.findViewById<TextView>(labelId) to view.findViewById(actionId)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/* 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.View
|
|
||||||
import android.widget.CompoundButton
|
|
||||||
import android.widget.Switch
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import io.reactivex.Observer
|
|
||||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
|
||||||
import org.mozilla.fenix.FeatureFlags
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.ext.settings
|
|
||||||
|
|
||||||
class TrackingProtectionSettingView(
|
|
||||||
container: View,
|
|
||||||
private val actionEmitter: Observer<QuickSettingsAction>
|
|
||||||
) : View.OnClickListener, CompoundButton.OnCheckedChangeListener {
|
|
||||||
private val trackingProtectionSwitch: Switch = container.findViewById(R.id.tracking_protection)
|
|
||||||
private val trackingProtectionAction: TextView =
|
|
||||||
container.findViewById(R.id.tracking_protection_action)
|
|
||||||
private val trackingProtectionSettingView: ConstraintLayout =
|
|
||||||
container.findViewById(R.id.tracking_protection_view)
|
|
||||||
|
|
||||||
init {
|
|
||||||
trackingProtectionSwitch.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
|
||||||
start = AppCompatResources.getDrawable(
|
|
||||||
container.context,
|
|
||||||
R.drawable.ic_tracking_protection
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind(isTrackingProtectionOn: Boolean) {
|
|
||||||
trackingProtectionSettingView.visibility =
|
|
||||||
if (FeatureFlags.etpCategories) View.GONE else View.VISIBLE
|
|
||||||
val globalTPSetting = trackingProtectionSwitch.context.settings().shouldUseTrackingProtection
|
|
||||||
|
|
||||||
trackingProtectionAction.isVisible = !globalTPSetting
|
|
||||||
trackingProtectionAction.setOnClickListener(this)
|
|
||||||
|
|
||||||
trackingProtectionSwitch.isChecked = isTrackingProtectionOn
|
|
||||||
trackingProtectionSwitch.isEnabled = globalTPSetting
|
|
||||||
trackingProtectionSwitch.setOnCheckedChangeListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(view: View) {
|
|
||||||
actionEmitter.onNext(
|
|
||||||
QuickSettingsAction.SelectTrackingProtectionSettings
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
|
||||||
actionEmitter.onNext(
|
|
||||||
QuickSettingsAction.ToggleTrackingProtection(isChecked)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* 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 mozilla.components.lib.state.Action
|
||||||
|
import mozilla.components.lib.state.State
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class TrackingProtectionStore(
|
||||||
|
val initialState: TrackingProtectionState
|
||||||
|
) : Store<TrackingProtectionState, TrackingProtectionAction>(
|
||||||
|
initialState, ::trackingProtectionReducer
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun createStore(
|
||||||
|
url: String,
|
||||||
|
isTrackingProtectionOn: Boolean,
|
||||||
|
settings: Settings
|
||||||
|
) = TrackingProtectionStore(
|
||||||
|
TrackingProtectionState(
|
||||||
|
websiteUrl = url,
|
||||||
|
isTrackingProtectionEnabledPerApp = settings.shouldUseTrackingProtection,
|
||||||
|
isTrackingProtectionEnabledPerWebsite = isTrackingProtectionOn
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class TrackingProtectionState(
|
||||||
|
val websiteUrl: String,
|
||||||
|
val isTrackingProtectionEnabledPerApp: Boolean,
|
||||||
|
val isTrackingProtectionEnabledPerWebsite: Boolean
|
||||||
|
) : State
|
||||||
|
|
||||||
|
sealed class TrackingProtectionAction : Action {
|
||||||
|
object Stub1 : TrackingProtectionAction()
|
||||||
|
object Stub2 : TrackingProtectionAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trackingProtectionReducer(
|
||||||
|
state: TrackingProtectionState,
|
||||||
|
action: TrackingProtectionAction
|
||||||
|
): TrackingProtectionState {
|
||||||
|
return when (action) {
|
||||||
|
TrackingProtectionAction.Stub1 -> state
|
||||||
|
TrackingProtectionAction.Stub2 -> state
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* 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.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import kotlinx.android.synthetic.main.quicksettings_tracking_protection.*
|
||||||
|
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class TrackingProtectionView(
|
||||||
|
override val containerView: ViewGroup
|
||||||
|
) : LayoutContainer {
|
||||||
|
|
||||||
|
val view: View = LayoutInflater.from(containerView.context)
|
||||||
|
.inflate(R.layout.quicksettings_tracking_protection, containerView, true)
|
||||||
|
|
||||||
|
init {
|
||||||
|
trackingProtectionSwitch.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
|
start = AppCompatResources.getDrawable(
|
||||||
|
containerView.context,
|
||||||
|
R.drawable.ic_tracking_protection
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(state: TrackingProtectionState) {
|
||||||
|
trackingProtectionAction.isVisible = !state.isTrackingProtectionEnabledPerApp
|
||||||
|
|
||||||
|
trackingProtectionSwitch.isChecked = state.isTrackingProtectionEnabledPerWebsite
|
||||||
|
trackingProtectionSwitch.isEnabled = state.isTrackingProtectionEnabledPerApp
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* 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 androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import mozilla.components.lib.state.Action
|
||||||
|
import mozilla.components.lib.state.State
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class WebsiteInfoStore(
|
||||||
|
initialState: WebsiteInfoState
|
||||||
|
) : Store<WebsiteInfoState, WebsiteInfoAction>(
|
||||||
|
initialState, ::websiteInfoReducer
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun createStore(url: String, isSecured: Boolean): WebsiteInfoStore {
|
||||||
|
val (stringRes, iconRes, colorRes) = when (isSecured) {
|
||||||
|
true -> getSecuredWebsiteUiValues()
|
||||||
|
false -> getInsecureWebsiteUiValues()
|
||||||
|
}
|
||||||
|
return WebsiteInfoStore(WebsiteInfoState(url, stringRes, iconRes, colorRes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class WebsiteInfoState(
|
||||||
|
val url: String,
|
||||||
|
@StringRes val securityInfoRes: Int,
|
||||||
|
@DrawableRes val iconRes: Int,
|
||||||
|
@ColorRes val iconTintRes: Int
|
||||||
|
) : State
|
||||||
|
|
||||||
|
sealed class WebsiteInfoAction : Action {
|
||||||
|
object Stub1 : WebsiteInfoAction()
|
||||||
|
object Stub2 : WebsiteInfoAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun websiteInfoReducer(
|
||||||
|
state: WebsiteInfoState,
|
||||||
|
action: WebsiteInfoAction
|
||||||
|
): WebsiteInfoState {
|
||||||
|
return when (action) {
|
||||||
|
WebsiteInfoAction.Stub1 -> state
|
||||||
|
WebsiteInfoAction.Stub2 -> state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSecuredWebsiteUiValues() = Triple(
|
||||||
|
R.string.quick_settings_sheet_secure_connection,
|
||||||
|
R.drawable.mozac_ic_lock,
|
||||||
|
R.color.photonGreen50
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getInsecureWebsiteUiValues() = Triple(
|
||||||
|
R.string.quick_settings_sheet_insecure_connection,
|
||||||
|
R.drawable.mozac_ic_globe,
|
||||||
|
R.color.photonRed50
|
||||||
|
)
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* 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.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import kotlinx.android.synthetic.main.quicksettings_website_info.view.*
|
||||||
|
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
||||||
|
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class WebsiteInfoView(
|
||||||
|
override val containerView: ViewGroup
|
||||||
|
) : LayoutContainer {
|
||||||
|
val view: View = LayoutInflater.from(containerView.context)
|
||||||
|
.inflate(R.layout.quicksettings_website_info, containerView, true)
|
||||||
|
|
||||||
|
fun update(state: WebsiteInfoState) {
|
||||||
|
bindUrl(state.url)
|
||||||
|
bindSecurityInfo(state.securityInfoRes, state.iconRes, state.iconTintRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindUrl(url: String) {
|
||||||
|
view.url.text = url.toUri().hostWithoutCommonPrefixes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindSecurityInfo(
|
||||||
|
@StringRes securityInfoRes: Int,
|
||||||
|
@DrawableRes iconRes: Int,
|
||||||
|
@ColorRes iconTintRes: Int
|
||||||
|
) {
|
||||||
|
val icon = view.context.getDrawable(iconRes)
|
||||||
|
icon?.setTint(ContextCompat.getColor(view.context, iconTintRes))
|
||||||
|
view.securityInfo.setText(securityInfoRes)
|
||||||
|
view.securityInfo.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* 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.Context
|
||||||
|
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||||
|
import mozilla.components.lib.state.Action
|
||||||
|
import mozilla.components.lib.state.State
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
import org.mozilla.fenix.settings.PhoneFeature
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class WebsitePermissionsStore(
|
||||||
|
initialState: WebsitePermissionsState
|
||||||
|
) : Store<WebsitePermissionsState, WebsitePermissionAction>(
|
||||||
|
initialState, ::reducer
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun createStore(
|
||||||
|
context: Context,
|
||||||
|
permissions: SitePermissions?,
|
||||||
|
settings: Settings
|
||||||
|
) = WebsitePermissionsStore(
|
||||||
|
WebsitePermissionsState(
|
||||||
|
camera = initWebsitePermission(context, PhoneFeature.CAMERA, permissions, settings),
|
||||||
|
microphone = initWebsitePermission(context, PhoneFeature.MICROPHONE, permissions, settings),
|
||||||
|
notification = initWebsitePermission(context, PhoneFeature.NOTIFICATION, permissions, settings),
|
||||||
|
location = initWebsitePermission(context, PhoneFeature.LOCATION, permissions, settings)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun initWebsitePermission(
|
||||||
|
context: Context,
|
||||||
|
phoneFeature: PhoneFeature,
|
||||||
|
permissions: SitePermissions?,
|
||||||
|
settings: Settings
|
||||||
|
): WebsitePermission {
|
||||||
|
val shouldBeVisible = phoneFeature.shouldBeVisible(permissions, settings)
|
||||||
|
|
||||||
|
return WebsitePermission(
|
||||||
|
name = phoneFeature.name,
|
||||||
|
status = phoneFeature.getActionLabel(context, permissions, settings),
|
||||||
|
visible = shouldBeVisible,
|
||||||
|
enabled = shouldBeVisible &&
|
||||||
|
phoneFeature.isAndroidPermissionGranted(context) &&
|
||||||
|
!phoneFeature.isUserPermissionGranted(permissions, settings)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PhoneFeature.shouldBeVisible(
|
||||||
|
sitePermissions: SitePermissions?,
|
||||||
|
settings: Settings
|
||||||
|
) = getStatus(sitePermissions, settings) != SitePermissions.Status.NO_DECISION
|
||||||
|
|
||||||
|
private fun PhoneFeature.isUserPermissionGranted(
|
||||||
|
sitePermissions: SitePermissions?,
|
||||||
|
settings: Settings
|
||||||
|
) = getStatus(sitePermissions, settings) == SitePermissions.Status.BLOCKED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class WebsitePermissionsState(
|
||||||
|
val camera: WebsitePermission,
|
||||||
|
val microphone: WebsitePermission,
|
||||||
|
val notification: WebsitePermission,
|
||||||
|
val location: WebsitePermission
|
||||||
|
) : State
|
||||||
|
|
||||||
|
sealed class WebsitePermissionAction : Action {
|
||||||
|
object Stub1 : WebsitePermissionAction()
|
||||||
|
object Stub2 : WebsitePermissionAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class WebsitePermission(
|
||||||
|
val name: String,
|
||||||
|
val status: String,
|
||||||
|
val visible: Boolean,
|
||||||
|
val enabled: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun reducer(
|
||||||
|
state: WebsitePermissionsState,
|
||||||
|
action: WebsitePermissionAction
|
||||||
|
): WebsitePermissionsState {
|
||||||
|
return when (action) {
|
||||||
|
WebsitePermissionAction.Stub1 -> state
|
||||||
|
WebsitePermissionAction.Stub2 -> state
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* 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.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class WebsitePermissionsView(
|
||||||
|
override val containerView: ViewGroup
|
||||||
|
) : LayoutContainer {
|
||||||
|
private val context = containerView.context
|
||||||
|
|
||||||
|
val view: View = LayoutInflater.from(context)
|
||||||
|
.inflate(R.layout.quicksettings_permissions, containerView, true)
|
||||||
|
|
||||||
|
fun update(state: WebsitePermissionsState) {
|
||||||
|
bindPermission(state.camera,
|
||||||
|
Pair(view.findViewById(R.id.cameraIcon), view.findViewById(R.id.cameraActionLabel)))
|
||||||
|
bindPermission(state.location,
|
||||||
|
Pair(view.findViewById(R.id.locationIcon), view.findViewById(R.id.locationActionLabel)))
|
||||||
|
bindPermission(state.microphone,
|
||||||
|
Pair(view.findViewById(R.id.microphoneIcon), view.findViewById(R.id.microphoneActionLabel)))
|
||||||
|
bindPermission(state.notification,
|
||||||
|
Pair(view.findViewById(R.id.notificationIcon), view.findViewById(R.id.notificationActionLabel)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindPermission(permissionState: WebsitePermission, permissionViews: Pair<TextView, TextView>) {
|
||||||
|
val (icon, status) = permissionViews
|
||||||
|
|
||||||
|
status.text = permissionState.status
|
||||||
|
status.isEnabled = permissionState.enabled
|
||||||
|
icon.isVisible = permissionState.visible
|
||||||
|
status.isVisible = permissionState.visible
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import androidx.core.net.toUri
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.component_tracking_protection_panel.*
|
import kotlinx.android.synthetic.main.component_tracking_protection_panel.*
|
||||||
import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.url
|
|
||||||
import kotlinx.android.synthetic.main.switch_with_description.view.*
|
import kotlinx.android.synthetic.main.switch_with_description.view.*
|
||||||
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
@ -163,11 +162,11 @@ class TrackingProtectionPanelView(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) {
|
private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) {
|
||||||
tracking_protection.switchItemDescription.text =
|
trackingProtectionSwitch.switchItemDescription.text =
|
||||||
view.context.getString(if (isTrackingProtectionOn) R.string.etp_panel_on else R.string.etp_panel_off)
|
view.context.getString(if (isTrackingProtectionOn) R.string.etp_panel_on else R.string.etp_panel_off)
|
||||||
tracking_protection.switch_widget.isChecked = isTrackingProtectionOn
|
trackingProtectionSwitch.switch_widget.isChecked = isTrackingProtectionOn
|
||||||
|
|
||||||
tracking_protection.switch_widget.setOnCheckedChangeListener { _, isChecked ->
|
trackingProtectionSwitch.switch_widget.setOnCheckedChangeListener { _, isChecked ->
|
||||||
interactor.trackingProtectionToggled(isChecked)
|
interactor.trackingProtectionToggled(isChecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
<!-- 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
|
- 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/. -->
|
- 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"
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/panel_wrapper"
|
android:id="@+id/panel_wrapper"
|
||||||
|
@ -27,7 +29,7 @@
|
||||||
tools:text="https://wikipedia.org" />
|
tools:text="https://wikipedia.org" />
|
||||||
|
|
||||||
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
|
<org.mozilla.fenix.trackingprotection.SwitchWithDescription
|
||||||
android:id="@+id/tracking_protection"
|
android:id="@+id/trackingProtectionSwitch"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="@dimen/tracking_protection_item_height"
|
android:minHeight="@dimen/tracking_protection_item_height"
|
||||||
|
@ -48,7 +50,7 @@
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
|
app:layout_constraintTop_toBottomOf="@id/trackingProtectionSwitch" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/cross_site_tracking"
|
android:id="@+id/cross_site_tracking"
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
- 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/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:fillViewport="true">
|
||||||
|
@ -17,174 +17,52 @@
|
||||||
android:background="?foundation"
|
android:background="?foundation"
|
||||||
android:contentDescription="@string/quick_settings_sheet">
|
android:contentDescription="@string/quick_settings_sheet">
|
||||||
|
|
||||||
<TextView
|
<FrameLayout
|
||||||
android:id="@+id/url"
|
android:id="@+id/websiteInfoLayout"
|
||||||
style="@style/QuickSettingsText"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
app:layout_constraintBottom_toTopOf="@id/trackingProtectionDivider" />
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="https://wikipedia.org" />
|
|
||||||
|
|
||||||
<TextView
|
<FrameLayout
|
||||||
android:id="@+id/security_info"
|
android:id="@+id/trackingProtectionLayout"
|
||||||
style="@style/QuickSettingsText.Icon"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
app:layout_constraintBottom_toTopOf="@id/webSitePermissionsDivider" />
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
<FrameLayout
|
||||||
app:layout_constraintTop_toBottomOf="@id/url"
|
android:id="@+id/websitePermissionsLayout"
|
||||||
tools:drawableStart="@drawable/mozac_ic_lock"
|
android:layout_width="match_parent"
|
||||||
tools:drawableTint="@color/photonGreen50"
|
android:layout_height="wrap_content"
|
||||||
tools:text="Secure connection" />
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/line_divider_security"
|
android:id="@+id/webSitePermissionsDivider"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="?neutralFaded"
|
android:background="?neutralFaded"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintBottom_toTopOf="@id/websitePermissionsLayout" />
|
||||||
app:layout_constraintTop_toBottomOf="@id/security_info" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<View
|
||||||
android:id="@+id/tracking_protection_view"
|
android:id="@+id/trackingProtectionDivider"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="?neutralFaded"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/trackingProtectionLayout" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/trackingProtectionGroup"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toTopOf="@id/camera_icon"
|
app:constraint_referenced_ids="trackingProtectionLayout,trackingProtectionDivider" />
|
||||||
app:layout_constraintTop_toBottomOf="@id/line_divider_security">
|
|
||||||
|
|
||||||
<Switch
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/tracking_protection"
|
android:id="@+id/websitePermissionsGroup"
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:paddingEnd="24dp"
|
|
||||||
android:text="@string/preferences_tracking_protection"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/tracking_protection_action"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:drawableStart="@drawable/ic_tracking_protection" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tracking_protection_action"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:gravity="top"
|
|
||||||
android:paddingStart="48dp"
|
|
||||||
android:paddingEnd="0dp"
|
|
||||||
android:text="@string/preferences_tracking_protection_turned_off_globally"
|
|
||||||
android:textColor="?accentBright"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/report_site_issue_action"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/report_site_issue_action"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:gravity="top"
|
|
||||||
android:paddingStart="48dp"
|
|
||||||
android:paddingEnd="0dp"
|
|
||||||
android:text="@string/browser_menu_report_issue"
|
|
||||||
android:textColor="?accentUsedOnDarkBackground"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/line_divider"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection_action" />
|
|
||||||
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/line_divider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="?neutralFaded"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/camera_icon"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:drawableStart="@drawable/ic_camera"
|
|
||||||
android:text="@string/preference_phone_feature_camera"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/camera_action_label"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection_view" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/camera_action_label"
|
|
||||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:constraint_referenced_ids="websitePermissionsLayout,webSitePermissionsDivider" />
|
||||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection_view"
|
|
||||||
tools:text="Allowed" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/microphone_icon"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:drawableStart="@drawable/ic_microphone"
|
|
||||||
android:text="@string/preference_phone_feature_microphone"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/microphone_action_label"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/camera_icon" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/microphone_action_label"
|
|
||||||
style="@style/QuickSettingsText.PermissionItemEnd"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/camera_action_label"
|
|
||||||
tools:text="Blocked by Android" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notification_icon"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:drawableStart="@drawable/ic_notifications"
|
|
||||||
android:text="@string/preference_phone_feature_notification"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/notification_action_label"
|
|
||||||
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"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/microphone_action_label"
|
|
||||||
tools:text="Blocked" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_icon"
|
|
||||||
style="@style/QuickSettingsText.Icon"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/quicksettings_item_height"
|
|
||||||
android:drawableStart="@drawable/ic_location"
|
|
||||||
android:text="@string/preference_phone_feature_location"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/location_action_label"
|
|
||||||
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"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/notification_action_label"
|
|
||||||
tools:text="Blocked" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?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/permissions_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cameraActionLabel"
|
||||||
|
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/microphoneActionLabel"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/cameraIcon"
|
||||||
|
tools:text="Allowed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cameraIcon"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_camera"
|
||||||
|
android:text="@string/preference_phone_feature_camera"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/microphoneIcon"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/cameraActionLabel"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/microphoneActionLabel"
|
||||||
|
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/notificationActionLabel"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/microphoneIcon"
|
||||||
|
tools:text="Blocked by Android" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/microphoneIcon"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_microphone"
|
||||||
|
android:text="@string/preference_phone_feature_microphone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/notificationIcon"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/microphoneActionLabel"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationActionLabel"
|
||||||
|
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/locationActionLabel"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/notificationIcon"
|
||||||
|
tools:text="Blocked" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationIcon"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_notifications"
|
||||||
|
android:text="@string/preference_phone_feature_notification"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/locationIcon"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/notificationActionLabel"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/locationActionLabel"
|
||||||
|
style="@style/QuickSettingsText.PermissionItemEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/locationIcon"
|
||||||
|
tools:text="Blocked" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/locationIcon"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:drawableStart="@drawable/ic_location"
|
||||||
|
android:text="@string/preference_phone_feature_location"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/locationActionLabel"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/tracking_protection_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/trackingProtectionSwitch"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:text="@string/preferences_tracking_protection"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/trackingProtectionAction"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:drawableStart="@drawable/ic_tracking_protection" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/trackingProtectionAction"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:gravity="top"
|
||||||
|
android:paddingStart="48dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/preferences_tracking_protection_turned_off_globally"
|
||||||
|
android:textColor="?accentBright"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/reportSiteIssueAction" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/reportSiteIssueAction"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
android:gravity="top"
|
||||||
|
android:paddingStart="48dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/browser_menu_report_issue"
|
||||||
|
android:textColor="?accentUsedOnDarkBackground"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/website_info_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/url"
|
||||||
|
style="@style/QuickSettingsText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="https://wikipedia.org" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/securityInfo"
|
||||||
|
style="@style/QuickSettingsText.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/quicksettings_item_height"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/url"
|
||||||
|
tools:drawableStart="@drawable/mozac_ic_lock"
|
||||||
|
tools:drawableTint="@color/photonGreen50"
|
||||||
|
tools:text="Secure connection" />
|
||||||
|
</LinearLayout>
|
Loading…
Reference in New Issue