[fenix] For https://github.com/mozilla-mobile/fenix/issues/4126 - Handle all business logic with Interactors and a Controller
Combined all Stores into one for all Views shown in on Fragment. Used a static `createStore()` which will build the initial state residing inside the Store and not in the Fragment as to decouple the Fragment from the business logic needed to build all the needed initial States. Added Interactors that handle a MVI View's business logic for TrackingProtectionView and WebsitePermissionsView. WebsiteInfoView doesn't register any user input events and does not have any reason to change while it is displayed so it does not have an Interactor. The two Interactors will delegate Fragment's QuickSettingsController for complex Android interactions, communication with other app features or for Store updates. Also refactored the stubs from the previous commit so that with this commit the the quicksettings feature should all be working now based on lib-state.pull/600/head
parent
8f07c6fbf0
commit
9b1c705ca2
@ -0,0 +1,167 @@
|
||||
/* 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 androidx.navigation.NavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.feature.tabs.TabsUseCases.AddNewTabUseCase
|
||||
import mozilla.components.support.base.feature.OnNeedToRequestPermissions
|
||||
import org.mozilla.fenix.browser.BrowserFragment
|
||||
import org.mozilla.fenix.components.PermissionStorage
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
import org.mozilla.fenix.ext.tryGetHostFromUrl
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.toggle
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
interface QuickSettingsController {
|
||||
fun handleTrackingProtectionToggled(websiteUrl: String, trackingEnabled: Boolean)
|
||||
fun handleTrackingProtectionSettingsSelected()
|
||||
fun handleReportTrackingProblem(websiteUrl: String)
|
||||
fun handleTrackingProtectionShown()
|
||||
fun handlePermissionsShown()
|
||||
fun handlePermissionToggled(permission: WebsitePermission)
|
||||
fun handleAndroidPermissionGranted(feature: PhoneFeature)
|
||||
}
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class DefaultQuickSettingsController(
|
||||
private val context: Context,
|
||||
private val quickSettingsStore: QuickSettingsFragmentStore,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val navController: NavController,
|
||||
private val session: Session?,
|
||||
private var sitePermissions: SitePermissions?,
|
||||
private val settings: Settings,
|
||||
private val permissionStorage: PermissionStorage,
|
||||
private val trackingExceptions: ExceptionDomains,
|
||||
private val reload: ReloadUrlUseCase,
|
||||
private val addNewTab: AddNewTabUseCase,
|
||||
private val requestRuntimePermissions: OnNeedToRequestPermissions = { },
|
||||
private val reportSiteIssue: () -> Unit,
|
||||
private val displayTrackingProtection: () -> Unit,
|
||||
private val displayPermissions: () -> Unit,
|
||||
private val dismiss: () -> Unit
|
||||
) : QuickSettingsController {
|
||||
|
||||
override fun handleTrackingProtectionToggled(
|
||||
websiteUrl: String,
|
||||
trackingEnabled: Boolean
|
||||
) {
|
||||
val host = websiteUrl.tryGetHostFromUrl()
|
||||
trackingExceptions.toggle(host)
|
||||
reload(session)
|
||||
|
||||
quickSettingsStore.dispatch(
|
||||
TrackingProtectionAction.TrackingProtectionToggled(trackingEnabled)
|
||||
)
|
||||
}
|
||||
|
||||
override fun handleTrackingProtectionSettingsSelected() {
|
||||
val directions =
|
||||
QuickSettingsSheetDialogFragmentDirections
|
||||
.actionQuickSettingsSheetDialogFragmentToTrackingProtectionFragment()
|
||||
navController.navigate(directions)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@UseExperimental(ObsoleteCoroutinesApi::class)
|
||||
override fun handleReportTrackingProblem(websiteUrl: String) {
|
||||
val reportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteUrl)
|
||||
addNewTab(reportUrl)
|
||||
|
||||
if (session?.isCustomTabSession() == true) {
|
||||
reportSiteIssue()
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun handleTrackingProtectionShown() {
|
||||
displayTrackingProtection()
|
||||
}
|
||||
|
||||
override fun handlePermissionsShown() {
|
||||
displayPermissions()
|
||||
}
|
||||
|
||||
override fun handlePermissionToggled(permission: WebsitePermission) {
|
||||
val featureToggled = permission.getBackingFeature()
|
||||
|
||||
when (permission.isBlockedByAndroid) {
|
||||
true -> handleAndroidPermissionRequest(featureToggled.androidPermissionsList)
|
||||
false -> {
|
||||
sitePermissions = sitePermissions!!.toggle(featureToggled).also {
|
||||
handlePermissionsChange(it)
|
||||
}
|
||||
|
||||
quickSettingsStore.dispatch(
|
||||
WebsitePermissionAction.TogglePermission(
|
||||
permission,
|
||||
featureToggled.getActionLabel(context, sitePermissions, settings),
|
||||
featureToggled.shouldBeEnabled(context, sitePermissions, settings)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleAndroidPermissionGranted(feature: PhoneFeature) {
|
||||
quickSettingsStore.dispatch(
|
||||
WebsitePermissionAction.TogglePermission(
|
||||
feature.getCorrespondingPermission(),
|
||||
feature.getActionLabel(context, sitePermissions, settings),
|
||||
feature.shouldBeEnabled(context, sitePermissions, settings)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAndroidPermissionRequest(requestedPermissions: Array<String>) {
|
||||
requestRuntimePermissions(requestedPermissions)
|
||||
}
|
||||
|
||||
private fun handlePermissionsChange(updatedPermissions: SitePermissions) {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
permissionStorage.updateSitePermissions(updatedPermissions)
|
||||
reload(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun WebsitePermission.getBackingFeature(): PhoneFeature = when (this) {
|
||||
is WebsitePermission.Camera -> PhoneFeature.CAMERA
|
||||
is WebsitePermission.Microphone -> PhoneFeature.MICROPHONE
|
||||
is WebsitePermission.Notification -> PhoneFeature.NOTIFICATION
|
||||
is WebsitePermission.Location -> PhoneFeature.LOCATION
|
||||
}
|
||||
|
||||
private fun PhoneFeature.getCorrespondingPermission(): WebsitePermission {
|
||||
val defaultStatus = ""
|
||||
val defaultEnabled = false
|
||||
val defaultVisible = false
|
||||
val defaultBlockedByAndroid = false
|
||||
val defaultWebsitePermission: WebsitePermission? = null
|
||||
|
||||
return when (this) {
|
||||
PhoneFeature.CAMERA -> WebsitePermission.Camera(
|
||||
defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid)
|
||||
PhoneFeature.LOCATION -> WebsitePermission.Location(
|
||||
defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid)
|
||||
PhoneFeature.MICROPHONE -> WebsitePermission.Microphone(
|
||||
defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid)
|
||||
PhoneFeature.NOTIFICATION -> WebsitePermission.Notification(
|
||||
defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid)
|
||||
PhoneFeature.AUTOPLAY -> defaultWebsitePermission!! // fail-fast
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,382 @@
|
||||
/* 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 androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
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.FeatureFlags
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class QuickSettingsFragmentStore(
|
||||
initialState: QuickSettingsFragmentState
|
||||
) : Store<QuickSettingsFragmentState, QuickSettingsFragmentAction>(
|
||||
initialState,
|
||||
::quickSettingsFragmentReducer
|
||||
) {
|
||||
companion object {
|
||||
private val getSecuredWebsiteUiValues = Triple(
|
||||
R.string.quick_settings_sheet_secure_connection,
|
||||
R.drawable.mozac_ic_lock,
|
||||
R.color.photonGreen50
|
||||
)
|
||||
|
||||
private val getInsecureWebsiteUiValues = Triple(
|
||||
R.string.quick_settings_sheet_insecure_connection,
|
||||
R.drawable.mozac_ic_globe,
|
||||
R.color.photonRed50
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun createStore(
|
||||
context: Context,
|
||||
websiteUrl: String,
|
||||
isSecured: Boolean,
|
||||
isTrackingProtectionOn: Boolean,
|
||||
permissions: SitePermissions?,
|
||||
settings: Settings
|
||||
) = QuickSettingsFragmentStore(
|
||||
QuickSettingsFragmentState(
|
||||
trackingProtectionState = createTrackingProtectionState(websiteUrl, isTrackingProtectionOn, settings),
|
||||
webInfoState = createWebsiteInfoState(websiteUrl, isSecured),
|
||||
websitePermissionsState = createWebsitePermissionState(context, permissions, settings)
|
||||
)
|
||||
)
|
||||
|
||||
private fun createTrackingProtectionState(
|
||||
websiteUrl: String,
|
||||
isTrackingProtectionOn: Boolean,
|
||||
settings: Settings
|
||||
) = TrackingProtectionState(
|
||||
isVisible = FeatureFlags.etpCategories.not(),
|
||||
isTrackingProtectionEnabledPerApp = settings.shouldUseTrackingProtection,
|
||||
websiteUrl = websiteUrl,
|
||||
isTrackingProtectionEnabledPerWebsite = isTrackingProtectionOn
|
||||
)
|
||||
|
||||
private fun createWebsiteInfoState(
|
||||
websiteUrl: String,
|
||||
isSecured: Boolean
|
||||
): WebsiteInfoState {
|
||||
val (stringRes, iconRes, colorRes) = when (isSecured) {
|
||||
true -> getSecuredWebsiteUiValues
|
||||
false -> getInsecureWebsiteUiValues
|
||||
}
|
||||
return WebsiteInfoState(websiteUrl, stringRes, iconRes, colorRes)
|
||||
}
|
||||
|
||||
private fun createWebsitePermissionState(
|
||||
context: Context,
|
||||
permissions: SitePermissions?,
|
||||
settings: Settings
|
||||
): WebsitePermissionsState {
|
||||
val cameraPermission = PhoneFeature.CAMERA.toWebsitePermission(context, permissions, settings)
|
||||
val microphonePermission = PhoneFeature.MICROPHONE.toWebsitePermission(context, permissions, settings)
|
||||
val notificationPermission = PhoneFeature.NOTIFICATION.toWebsitePermission(context, permissions, settings)
|
||||
val locationPermission = PhoneFeature.LOCATION.toWebsitePermission(context, permissions, settings)
|
||||
val shouldBeVisible = cameraPermission.isVisible || microphonePermission.isVisible ||
|
||||
notificationPermission.isVisible || locationPermission.isVisible
|
||||
|
||||
return WebsitePermissionsState(shouldBeVisible, cameraPermission, microphonePermission,
|
||||
notificationPermission, locationPermission
|
||||
)
|
||||
}
|
||||
|
||||
private fun PhoneFeature.toWebsitePermission(
|
||||
context: Context,
|
||||
permissions: SitePermissions?,
|
||||
settings: Settings
|
||||
): WebsitePermission {
|
||||
val status = getPermissionStatus(context, permissions, settings)
|
||||
val nonexistentPermission: WebsitePermission? = null
|
||||
return when (this) {
|
||||
PhoneFeature.CAMERA -> WebsitePermission.Camera(
|
||||
status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid
|
||||
)
|
||||
PhoneFeature.LOCATION -> WebsitePermission.Location(
|
||||
status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid
|
||||
)
|
||||
PhoneFeature.MICROPHONE -> WebsitePermission.Microphone(
|
||||
status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid
|
||||
)
|
||||
PhoneFeature.NOTIFICATION -> WebsitePermission.Notification(
|
||||
status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid
|
||||
)
|
||||
PhoneFeature.AUTOPLAY -> nonexistentPermission!! // fail-fast
|
||||
}
|
||||
}
|
||||
|
||||
private fun PhoneFeature.getPermissionStatus(
|
||||
context: Context,
|
||||
permissions: SitePermissions?,
|
||||
settings: Settings
|
||||
) = PermissionStatus(
|
||||
status = getActionLabel(context, permissions, settings),
|
||||
isVisible = shouldBeVisible(permissions, settings),
|
||||
isEnabled = shouldBeEnabled(context, permissions, settings),
|
||||
isBlockedByAndroid = !isAndroidPermissionGranted(context)
|
||||
)
|
||||
|
||||
private data class PermissionStatus(
|
||||
val status: String,
|
||||
val isVisible: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
val isBlockedByAndroid: Boolean
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// States
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
data class QuickSettingsFragmentState(
|
||||
val trackingProtectionState: TrackingProtectionState,
|
||||
val webInfoState: WebsiteInfoState,
|
||||
val websitePermissionsState: WebsitePermissionsState
|
||||
) : State
|
||||
|
||||
data class TrackingProtectionState(
|
||||
val isVisible: Boolean,
|
||||
val websiteUrl: String,
|
||||
val isTrackingProtectionEnabledPerApp: Boolean,
|
||||
val isTrackingProtectionEnabledPerWebsite: Boolean
|
||||
) : State
|
||||
|
||||
data class WebsiteInfoState(
|
||||
val websiteUrl: String,
|
||||
@StringRes val securityInfoRes: Int,
|
||||
@DrawableRes val iconRes: Int,
|
||||
@ColorRes val iconTintRes: Int
|
||||
) : State
|
||||
|
||||
data class WebsitePermissionsState(
|
||||
val isVisible: Boolean,
|
||||
val camera: WebsitePermission,
|
||||
val microphone: WebsitePermission,
|
||||
val notification: WebsitePermission,
|
||||
val location: WebsitePermission
|
||||
) : State
|
||||
|
||||
sealed class WebsitePermission {
|
||||
abstract val status: String
|
||||
abstract val isVisible: Boolean
|
||||
abstract val isEnabled: Boolean
|
||||
abstract val isBlockedByAndroid: Boolean
|
||||
|
||||
abstract fun copy(
|
||||
status: String = this.status,
|
||||
isVisible: Boolean = this.isVisible,
|
||||
isEnabled: Boolean = this.isEnabled,
|
||||
isBlockedByAndroid: Boolean = this.isBlockedByAndroid
|
||||
): WebsitePermission
|
||||
|
||||
data class Camera(
|
||||
override val status: String,
|
||||
override val isVisible: Boolean,
|
||||
override val isEnabled: Boolean,
|
||||
override val isBlockedByAndroid: Boolean,
|
||||
val name: String = "Camera" // helps to resolve the overload resolution ambiguity for the copy() method
|
||||
) : WebsitePermission() {
|
||||
override fun copy(
|
||||
status: String,
|
||||
isVisible: Boolean,
|
||||
isEnabled: Boolean,
|
||||
isBlockedByAndroid: Boolean
|
||||
) = copy(
|
||||
status = status,
|
||||
isVisible = isVisible,
|
||||
isEnabled = isEnabled,
|
||||
isBlockedByAndroid = isBlockedByAndroid,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
|
||||
data class Microphone(
|
||||
override val status: String,
|
||||
override val isVisible: Boolean,
|
||||
override val isEnabled: Boolean,
|
||||
override val isBlockedByAndroid: Boolean,
|
||||
val name: String = "Microphone" // helps to resolve the overload resolution ambiguity for the copy() method
|
||||
) : WebsitePermission() {
|
||||
override fun copy(
|
||||
status: String,
|
||||
isVisible: Boolean,
|
||||
isEnabled: Boolean,
|
||||
isBlockedByAndroid: Boolean
|
||||
) = copy(
|
||||
status = status,
|
||||
isVisible = isVisible,
|
||||
isEnabled = isEnabled,
|
||||
isBlockedByAndroid = isBlockedByAndroid,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
|
||||
data class Notification(
|
||||
override val status: String,
|
||||
override val isVisible: Boolean,
|
||||
override val isEnabled: Boolean,
|
||||
override val isBlockedByAndroid: Boolean,
|
||||
val name: String = "Notification" // helps to resolve the overload resolution ambiguity for the copy() method
|
||||
) : WebsitePermission() {
|
||||
override fun copy(
|
||||
status: String,
|
||||
isVisible: Boolean,
|
||||
isEnabled: Boolean,
|
||||
isBlockedByAndroid: Boolean
|
||||
) = copy(
|
||||
status = status,
|
||||
isVisible = isVisible,
|
||||
isEnabled = isEnabled,
|
||||
isBlockedByAndroid = isBlockedByAndroid,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
|
||||
data class Location(
|
||||
override val status: String,
|
||||
override val isVisible: Boolean,
|
||||
override val isEnabled: Boolean,
|
||||
override val isBlockedByAndroid: Boolean,
|
||||
val name: String = "Location" // helps to resolve the overload resolution ambiguity for the copy() method
|
||||
) : WebsitePermission() {
|
||||
override fun copy(
|
||||
status: String,
|
||||
isVisible: Boolean,
|
||||
isEnabled: Boolean,
|
||||
isBlockedByAndroid: Boolean
|
||||
) = copy(
|
||||
status = status,
|
||||
isVisible = isVisible,
|
||||
isEnabled = isEnabled,
|
||||
isBlockedByAndroid = isBlockedByAndroid,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Actions
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
sealed class QuickSettingsFragmentAction : Action
|
||||
|
||||
sealed class TrackingProtectionAction : QuickSettingsFragmentAction() {
|
||||
class TrackingProtectionToggled(val trackingEnabled: Boolean) : TrackingProtectionAction()
|
||||
}
|
||||
|
||||
sealed class WebsiteInfoAction : QuickSettingsFragmentAction()
|
||||
|
||||
sealed class WebsitePermissionAction : QuickSettingsFragmentAction() {
|
||||
class TogglePermission(
|
||||
val websitePermission: WebsitePermission,
|
||||
val updatedStatus: String,
|
||||
val updatedEnabledStatus: Boolean
|
||||
) : WebsitePermissionAction()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Reducers
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
fun quickSettingsFragmentReducer(
|
||||
state: QuickSettingsFragmentState,
|
||||
action: QuickSettingsFragmentAction
|
||||
): QuickSettingsFragmentState {
|
||||
return when (action) {
|
||||
is TrackingProtectionAction -> state.copy(
|
||||
trackingProtectionState = TrackingProtectionStateReducer.reduce(
|
||||
state.trackingProtectionState,
|
||||
action
|
||||
)
|
||||
)
|
||||
is WebsiteInfoAction -> state.copy(
|
||||
webInfoState = WebsiteInfoStateReducer.reduce(
|
||||
state.webInfoState,
|
||||
action
|
||||
)
|
||||
)
|
||||
is WebsitePermissionAction -> state.copy(
|
||||
websitePermissionsState = WebsitePermissionsStateReducer.reduce(
|
||||
state.websitePermissionsState,
|
||||
action
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object TrackingProtectionStateReducer {
|
||||
fun reduce(
|
||||
state: TrackingProtectionState,
|
||||
action: TrackingProtectionAction
|
||||
): TrackingProtectionState {
|
||||
return when (action) {
|
||||
is TrackingProtectionAction.TrackingProtectionToggled -> state.copy(
|
||||
isTrackingProtectionEnabledPerWebsite = action.trackingEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
object WebsiteInfoStateReducer {
|
||||
fun reduce(
|
||||
state: WebsiteInfoState,
|
||||
action: WebsiteInfoAction
|
||||
): WebsiteInfoState {
|
||||
// There is no possible action that can change this View's state while it is displayed to the user.
|
||||
// Everytime the View is recreated it starts with a fresh state. This is the only way to display
|
||||
// something different.
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
object WebsitePermissionsStateReducer {
|
||||
fun reduce(
|
||||
state: WebsitePermissionsState,
|
||||
action: WebsitePermissionAction
|
||||
): WebsitePermissionsState {
|
||||
return when (action) {
|
||||
is WebsitePermissionAction.TogglePermission -> {
|
||||
when (action.websitePermission) {
|
||||
is WebsitePermission.Camera -> state.copy(
|
||||
camera = state.camera.copy(
|
||||
status = action.updatedStatus,
|
||||
isEnabled = action.updatedEnabledStatus
|
||||
)
|
||||
)
|
||||
is WebsitePermission.Microphone -> state.copy(
|
||||
microphone = state.microphone.copy(
|
||||
status = action.updatedStatus,
|
||||
isEnabled = action.updatedEnabledStatus
|
||||
)
|
||||
)
|
||||
is WebsitePermission.Notification -> state.copy(
|
||||
notification = state.notification.copy(
|
||||
status = action.updatedStatus,
|
||||
isEnabled = action.updatedEnabledStatus
|
||||
)
|
||||
)
|
||||
is WebsitePermission.Location -> state.copy(
|
||||
location = state.location.copy(
|
||||
status = action.updatedStatus,
|
||||
isEnabled = action.updatedEnabledStatus
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/* 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
|
||||
|
||||
class QuickSettingsInteractor(
|
||||
private val controller: QuickSettingsController
|
||||
) : WebsitePermissionInteractor, TrackingProtectionInteractor {
|
||||
|
||||
override fun onReportProblemSelected(websiteUrl: String) {
|
||||
controller.handleReportTrackingProblem(websiteUrl)
|
||||
}
|
||||
|
||||
override fun onProtectionToggled(websiteUrl: String, trackingEnabled: Boolean) {
|
||||
controller.handleTrackingProtectionToggled(websiteUrl, trackingEnabled)
|
||||
}
|
||||
|
||||
override fun onProtectionSettingsSelected() {
|
||||
controller.handleTrackingProtectionSettingsSelected()
|
||||
}
|
||||
|
||||
override fun onTrackingProtectionShown() {
|
||||
controller.handleTrackingProtectionShown()
|
||||
}
|
||||
|
||||
override fun onPermissionsShown() {
|
||||
controller.handlePermissionsShown()
|
||||
}
|
||||
|
||||
override fun onPermissionToggled(permissionState: WebsitePermission) {
|
||||
controller.handlePermissionToggled(permissionState)
|
||||
}
|
||||
}
|
@ -1,51 +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 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
|
||||
}
|
||||
}
|
@ -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 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
|
||||
)
|
@ -1,91 +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 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,26 @@
|
||||
/* 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.ext
|
||||
|
||||
import android.content.Context
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
fun PhoneFeature.shouldBeVisible(
|
||||
sitePermissions: SitePermissions?,
|
||||
settings: Settings
|
||||
) = getStatus(sitePermissions, settings) != SitePermissions.Status.NO_DECISION
|
||||
|
||||
fun PhoneFeature.shouldBeEnabled(
|
||||
context: Context,
|
||||
sitePermissions: SitePermissions?,
|
||||
settings: Settings
|
||||
) = isAndroidPermissionGranted(context) && isUserPermissionGranted(sitePermissions, settings)
|
||||
|
||||
fun PhoneFeature.isUserPermissionGranted(
|
||||
sitePermissions: SitePermissions?,
|
||||
settings: Settings
|
||||
) = getStatus(sitePermissions, settings) == SitePermissions.Status.ALLOWED
|
Loading…
Reference in New Issue