186 lines
7.6 KiB
Kotlin
186 lines
7.6 KiB
Kotlin
/* 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)
|
|
}
|
|
}
|