diff --git a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt index 6912e748d..2bc63e2bc 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt @@ -4,26 +4,33 @@ package org.mozilla.fenix.settings -import android.view.View import android.widget.RadioButton -import android.widget.TextView import androidx.appcompat.content.res.AppCompatResources -import androidx.core.text.HtmlCompat import androidx.preference.Preference import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative -import org.mozilla.fenix.R import org.mozilla.fenix.theme.ThemeManager fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions { - return when (featurePhone) { - PhoneFeature.CAMERA -> copy(camera = camera.toggle()) - PhoneFeature.LOCATION -> copy(location = location.toggle()) - PhoneFeature.MICROPHONE -> copy(microphone = microphone.toggle()) - PhoneFeature.NOTIFICATION -> copy(notification = notification.toggle()) - PhoneFeature.AUTOPLAY_AUDIBLE -> copy(autoplayAudible = autoplayAudible.toggle()) - PhoneFeature.AUTOPLAY_INAUDIBLE -> copy(autoplayInaudible = autoplayInaudible.toggle()) - } + return update(featurePhone, get(featurePhone).toggle()) +} + +fun SitePermissions.get(field: PhoneFeature) = when (field) { + PhoneFeature.CAMERA -> camera + PhoneFeature.LOCATION -> location + PhoneFeature.MICROPHONE -> microphone + PhoneFeature.NOTIFICATION -> notification + PhoneFeature.AUTOPLAY_AUDIBLE -> autoplayAudible + PhoneFeature.AUTOPLAY_INAUDIBLE -> autoplayInaudible +} + +fun SitePermissions.update(field: PhoneFeature, value: SitePermissions.Status) = when (field) { + PhoneFeature.CAMERA -> copy(camera = value) + PhoneFeature.LOCATION -> copy(location = value) + PhoneFeature.MICROPHONE -> copy(microphone = value) + PhoneFeature.NOTIFICATION -> copy(notification = value) + PhoneFeature.AUTOPLAY_AUDIBLE -> copy(autoplayAudible = value) + PhoneFeature.AUTOPLAY_INAUDIBLE -> copy(autoplayInaudible = value) } /** @@ -39,26 +46,6 @@ fun RadioButton.setStartCheckedIndicator() { putCompoundDrawablesRelative(start = buttonDrawable) } -fun initBlockedByAndroidView(phoneFeature: PhoneFeature, blockedByAndroidView: View) { - val context = blockedByAndroidView.context - if (!phoneFeature.isAndroidPermissionGranted(context)) { - blockedByAndroidView.visibility = View.VISIBLE - - val descriptionLabel = blockedByAndroidView.findViewById(R.id.blocked_by_android_feature_label) - val descriptionText = context.getString( - R.string.phone_feature_blocked_step_feature, - phoneFeature.getLabel(context) - ) - descriptionLabel.text = HtmlCompat.fromHtml(descriptionText, HtmlCompat.FROM_HTML_MODE_COMPACT) - - val permissionsLabel = blockedByAndroidView.findViewById(R.id.blocked_by_android_permissions_label) - val permissionsText = context.getString(R.string.phone_feature_blocked_step_permissions) - permissionsLabel.text = HtmlCompat.fromHtml(permissionsText, HtmlCompat.FROM_HTML_MODE_COMPACT) - } else { - blockedByAndroidView.visibility = View.GONE - } -} - /** * Sets the callback to be invoked when this preference is changed by the user (but before * the internal state has been updated). Allows the type of the preference to be specified. diff --git a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt index dfd1ac46a..6ed8ee578 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt @@ -8,7 +8,9 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.RECORD_AUDIO import android.content.Context +import android.os.Parcelable import androidx.annotation.StringRes +import kotlinx.android.parcel.Parcelize import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissionsRules import mozilla.components.support.ktx.android.content.isPermissionGranted @@ -21,26 +23,17 @@ import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_AUDIBLE import org.mozilla.fenix.utils.Settings import android.Manifest.permission.CAMERA as CAMERA_PERMISSION -const val ID_CAMERA_PERMISSION = 0 -const val ID_LOCATION_PERMISSION = 1 -const val ID_MICROPHONE_PERMISSION = 2 -const val ID_NOTIFICATION_PERMISSION = 3 -const val ID_AUTOPLAY_AUDIBLE_PERMISSION = 4 -const val ID_AUTOPLAY_INAUDIBLE_PERMISSION = 5 - -enum class PhoneFeature(val id: Int, val androidPermissionsList: Array) { - CAMERA(ID_CAMERA_PERMISSION, arrayOf(CAMERA_PERMISSION)), - LOCATION(ID_LOCATION_PERMISSION, arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION)), - MICROPHONE(ID_MICROPHONE_PERMISSION, arrayOf(RECORD_AUDIO)), - NOTIFICATION(ID_NOTIFICATION_PERMISSION, emptyArray()), - AUTOPLAY_AUDIBLE(ID_AUTOPLAY_AUDIBLE_PERMISSION, emptyArray()), - AUTOPLAY_INAUDIBLE(ID_AUTOPLAY_INAUDIBLE_PERMISSION, emptyArray()); +@Parcelize +enum class PhoneFeature(val androidPermissionsList: Array) : Parcelable { + CAMERA(arrayOf(CAMERA_PERMISSION)), + LOCATION(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION)), + MICROPHONE(arrayOf(RECORD_AUDIO)), + NOTIFICATION(emptyArray()), + AUTOPLAY_AUDIBLE(emptyArray()), + AUTOPLAY_INAUDIBLE(emptyArray()); fun isAndroidPermissionGranted(context: Context): Boolean { - return when (this) { - CAMERA, LOCATION, MICROPHONE -> context.isPermissionGranted(androidPermissionsList.asIterable()) - NOTIFICATION, AUTOPLAY_AUDIBLE, AUTOPLAY_INAUDIBLE -> true - } + return context.isPermissionGranted(androidPermissionsList.asIterable()) } @Suppress("ComplexMethod") @@ -78,7 +71,7 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array) sitePermissions: SitePermissions? = null, settings: Settings? = null ): SitePermissions.Status { - val status = getStatus(sitePermissions) ?: settings?.let(::getAction)?.toStatus() + val status = sitePermissions?.get(this) ?: settings?.let(::getAction)?.toStatus() return requireNotNull(status) } @@ -114,18 +107,6 @@ enum class PhoneFeature(val id: Int, val androidPermissionsList: Array) } } - private fun getStatus(sitePermissions: SitePermissions?): SitePermissions.Status? { - sitePermissions ?: return null - return when (this) { - CAMERA -> sitePermissions.camera - LOCATION -> sitePermissions.location - MICROPHONE -> sitePermissions.microphone - NOTIFICATION -> sitePermissions.notification - AUTOPLAY_AUDIBLE -> sitePermissions.autoplayAudible - AUTOPLAY_INAUDIBLE -> sitePermissions.autoplayInaudible - } - } - companion object { fun findFeatureBy(permissions: Array): PhoneFeature? { return values().find { feature -> diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt index 0f3e8ae19..16636ebfd 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt @@ -52,7 +52,7 @@ interface QuickSettingsController { * Default behavior of [QuickSettingsController]. Other implementations are possible. * * @param context [Context] used for various Android interactions. - * @param quickSettingsStore [QuickSettingsFragmentStore] holding the [State] for all Views displayed + * @param quickSettingsStore [QuickSettingsFragmentStore] holding the State for all Views displayed * in this Controller's Fragment. * @param coroutineScope [CoroutineScope] used for structed concurrency. * @param navController NavController] used for navigation. @@ -88,21 +88,20 @@ class DefaultQuickSettingsController( } override fun handlePermissionToggled(permission: WebsitePermission) { - val featureToggled = permission.getBackingFeature() + val featureToggled = permission.phoneFeature when (permission.isBlockedByAndroid) { true -> handleAndroidPermissionRequest(featureToggled.androidPermissionsList) false -> { val permissions = sitePermissions if (permissions != null) { - val newPermissions = permissions.toggle(featureToggled).also { - handlePermissionsChange(it) - } + val newPermissions = permissions.toggle(featureToggled) + handlePermissionsChange(newPermissions) sitePermissions = newPermissions quickSettingsStore.dispatch( WebsitePermissionAction.TogglePermission( - permission, + featureToggled, featureToggled.getActionLabel(context, newPermissions, settings), featureToggled.shouldBeEnabled(context, newPermissions, settings) ) @@ -117,7 +116,7 @@ class DefaultQuickSettingsController( override fun handleAndroidPermissionGranted(feature: PhoneFeature) { quickSettingsStore.dispatch( WebsitePermissionAction.TogglePermission( - feature.getCorrespondingPermission(), + feature, feature.getActionLabel(context, sitePermissions, settings), feature.shouldBeEnabled(context, sitePermissions, settings) ) @@ -150,56 +149,6 @@ class DefaultQuickSettingsController( } } - /** - * Each [WebsitePermission] is mapped after a [PhoneFeature]. - * - * Get this [WebsitePermission]'s [PhoneFeature]. - */ - @VisibleForTesting(otherwise = VisibleForTesting.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 - is WebsitePermission.AutoplayAudible -> PhoneFeature.AUTOPLAY_AUDIBLE - is WebsitePermission.AutoplayInaudible -> PhoneFeature.AUTOPLAY_INAUDIBLE - } - - /** - * Get the specific [WebsitePermission] implementation which this [PhoneFeature] is tied to. - * - * **The result only informs about the type of [WebsitePermission]. - * The resulting object's properties are just stubs and not dependable.** - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - fun PhoneFeature.getCorrespondingPermission(): WebsitePermission { - val defaultStatus = "" - val defaultEnabled = false - val defaultVisible = false - val defaultBlockedByAndroid = false - - 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_AUDIBLE -> WebsitePermission.AutoplayAudible( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid - ) - PhoneFeature.AUTOPLAY_INAUDIBLE -> WebsitePermission.AutoplayInaudible( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid - ) - } - } - /** * Navigate to toggle [SitePermissions] for the specified [PhoneFeature] * @@ -207,7 +156,7 @@ class DefaultQuickSettingsController( */ private fun navigateToManagePhoneFeature(phoneFeature: PhoneFeature) { val directions = QuickSettingsSheetDialogFragmentDirections - .actionGlobalSitePermissionsManagePhoneFeature(phoneFeature.id) + .actionGlobalSitePermissionsManagePhoneFeature(phoneFeature) navController.navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt new file mode 100644 index 000000000..ad1eae90b --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt @@ -0,0 +1,38 @@ +/* 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 org.mozilla.fenix.settings.PhoneFeature + +/** + * Parent [Action] for all the [QuickSettingsFragmentState] changes. + */ +sealed class QuickSettingsFragmentAction : Action + +/** + * All possible [WebsiteInfoState] changes as result of user / system interactions. + */ +sealed class WebsiteInfoAction : QuickSettingsFragmentAction() + +/** + * All possible [WebsitePermissionsState] changes as result of user / system interactions. + */ +sealed class WebsitePermissionAction : QuickSettingsFragmentAction() { + /** + * Change resulting from toggling a specific [WebsitePermission] for the current website. + * + * @param updatedFeature [PhoneFeature] backing a certain [WebsitePermission]. + * Allows to easily identify which permission changed + * **Must be the name of one of the properties of [WebsitePermissionsState]**. + * @param updatedStatus [String] the new [WebsitePermission#status] which will be shown to the user. + * @param updatedEnabledStatus [Boolean] the new [WebsitePermission#enabled] which will be shown to the user. + */ + class TogglePermission( + val updatedFeature: PhoneFeature, + val updatedStatus: String, + val updatedEnabledStatus: Boolean + ) : WebsitePermissionAction() +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt new file mode 100644 index 000000000..5f15579b1 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt @@ -0,0 +1,50 @@ +/* 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 + +/** + * Parent Reducer for all [QuickSettingsFragmentState]s of all Views shown in this Fragment. + */ +fun quickSettingsFragmentReducer( + state: QuickSettingsFragmentState, + action: QuickSettingsFragmentAction +): QuickSettingsFragmentState { + return when (action) { + is WebsiteInfoAction -> { + // There is no possible action that can change this View's state while it is displayed to the user. + // Every time the View is recreated it starts with a fresh state. This is the only way to display + // something different. + state + } + is WebsitePermissionAction -> state.copy( + websitePermissionsState = WebsitePermissionsStateReducer.reduce( + state.websitePermissionsState, + action + ) + ) + } +} + +object WebsitePermissionsStateReducer { + /** + * Handles creating a new [WebsitePermissionsState] based on the specific [WebsitePermissionAction] + */ + fun reduce( + state: WebsitePermissionsState, + action: WebsitePermissionAction + ): WebsitePermissionsState { + return when (action) { + is WebsitePermissionAction.TogglePermission -> { + val key = action.updatedFeature + val newWebsitePermission = state.getValue(key).copy( + status = action.updatedStatus, + isEnabled = action.updatedEnabledStatus + ) + + state + Pair(key, newWebsitePermission) + } + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt new file mode 100644 index 000000000..ecb2ef80c --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.quicksettings + +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import mozilla.components.lib.state.State +import org.mozilla.fenix.R +import org.mozilla.fenix.settings.PhoneFeature + +/** + * [State] containing all data displayed to the user by this Fragment. + * + * Partitioned further to contain mutiple states for each standalone View this Fragment holds. + */ +data class QuickSettingsFragmentState( + val webInfoState: WebsiteInfoState, + val websitePermissionsState: WebsitePermissionsState +) : State + +/** + * [State] to be rendered by [WebsiteInfoView] indicating whether the connection is secure or not. + * + * @param websiteUrl [String] the URL of the current web page. + * @param websiteTitle [String] the title of the current web page. + * @param websiteSecurityUiValues UI values to represent the security of the website. + */ +data class WebsiteInfoState( + val websiteUrl: String, + val websiteTitle: String, + val websiteSecurityUiValues: WebsiteSecurityUiValues, + val certificateName: String +) : State + +enum class WebsiteSecurityUiValues( + @StringRes val securityInfoRes: Int, + @DrawableRes val iconRes: Int, + @ColorRes val iconTintRes: Int +) { + SECURE( + R.string.quick_settings_sheet_secure_connection, + R.drawable.mozac_ic_lock, + R.color.photonGreen50 + ), + INSECURE( + R.string.quick_settings_sheet_insecure_connection, + R.drawable.mozac_ic_globe, + R.color.photonRed50 + ) +} + +/** + * [State] to be rendered by [WebsitePermissionsView] displaying all explicitly allowed or blocked + * website permissions. + */ +typealias WebsitePermissionsState = Map + +/** + * Wrapper over a website permission encompassing all it's needed state to be rendered on the screen. + * + * Contains a limited number of implementations because there is a known, finite number of permissions + * we need to display to the user. + * + * @property status The *allowed* / *blocked* permission status to be shown to the user. + * @property isVisible Whether this permission should be shown to the user. + * @property isEnabled Visual indication about whether this permission is *enabled* / *disabled* + * @property isBlockedByAndroid Whether the corresponding *dangerous* Android permission is granted + * for the app by the user or not. + */ +data class WebsitePermission( + val phoneFeature: PhoneFeature, + val status: String, + val isVisible: Boolean, + val isEnabled: Boolean, + val isBlockedByAndroid: Boolean +) diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt index 1157c73d8..e5d0000dc 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt @@ -5,21 +5,18 @@ package org.mozilla.fenix.settings.quicksettings import android.content.Context -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.lib.state.Action import mozilla.components.lib.state.Reducer import mozilla.components.lib.state.State import mozilla.components.lib.state.Store -import org.mozilla.fenix.R import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.createStore import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible import org.mozilla.fenix.utils.Settings +import java.util.EnumMap /** * [QuickSettingsSheetDialogFragment]'s unique [Store]. @@ -40,27 +37,6 @@ class QuickSettingsFragmentStore( ::quickSettingsFragmentReducer ) { companion object { - /** - * String, Drawable & Drawable Tint color used to display that the current website connection is secured. - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - val getSecuredWebsiteUiValues = Triple( - R.string.quick_settings_sheet_secure_connection, - R.drawable.mozac_ic_lock, - R.color.photonGreen50 - ) - - /** - * String, Drawable & Drawable Tint color used to display that the current website connection is - * **not** secured. - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - val getInsecureWebsiteUiValues = Triple( - R.string.quick_settings_sheet_insecure_connection, - R.drawable.mozac_ic_globe, - R.color.photonRed50 - ) - /** * Construct an initial [QuickSettingsFragmentState] for all Views displayed by the * [QuickSettingsSheetDialogFragment]. @@ -109,11 +85,8 @@ class QuickSettingsFragmentStore( isSecured: Boolean, certificateName: String ): WebsiteInfoState { - val (stringRes, iconRes, colorRes) = when (isSecured) { - true -> getSecuredWebsiteUiValues - false -> getInsecureWebsiteUiValues - } - return WebsiteInfoState(websiteUrl, websiteTitle, stringRes, iconRes, colorRes, certificateName) + val uiValues = if (isSecured) WebsiteSecurityUiValues.SECURE else WebsiteSecurityUiValues.INSECURE + return WebsiteInfoState(websiteUrl, websiteTitle, uiValues, certificateName) } /** @@ -132,26 +105,11 @@ class QuickSettingsFragmentStore( 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 autoplayAudiblePermission = - PhoneFeature.AUTOPLAY_AUDIBLE.toWebsitePermission(context, permissions, settings) - val autoplayInaudiblePermission = - PhoneFeature.AUTOPLAY_INAUDIBLE.toWebsitePermission(context, permissions, settings) - val shouldBeVisible = cameraPermission.isVisible || microphonePermission.isVisible || - notificationPermission.isVisible || locationPermission.isVisible - - return WebsitePermissionsState( - shouldBeVisible, cameraPermission, microphonePermission, - notificationPermission, locationPermission, autoplayAudiblePermission, - autoplayInaudiblePermission - ) + val state = EnumMap(PhoneFeature::class.java) + for (feature in PhoneFeature.values()) { + state[feature] = feature.toWebsitePermission(context, permissions, settings) + } + return state } /** @@ -163,425 +121,13 @@ class QuickSettingsFragmentStore( permissions: SitePermissions?, settings: Settings ): WebsitePermission { - val status = getPermissionStatus(context, permissions, settings) - 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_AUDIBLE -> WebsitePermission.AutoplayAudible( - status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid - ) - PhoneFeature.AUTOPLAY_INAUDIBLE -> WebsitePermission.AutoplayInaudible( - status.status, status.isVisible, status.isEnabled, status.isBlockedByAndroid - ) - } - } - - /** - * Helper method for getting the [WebsitePermission] properties based on a specific [PhoneFeature]. - */ - @VisibleForTesting(otherwise = VisibleForTesting.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) - ) - - /** - * Helper class acting as a temporary container of [WebsitePermission] properties. - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - data class PermissionStatus( - val status: String, - val isVisible: Boolean, - val isEnabled: Boolean, - val isBlockedByAndroid: Boolean - ) - } -} - -// ------------------------------------------------------------------------------------------------- -// States -// ------------------------------------------------------------------------------------------------- - -/** - * [State] containing all data displayed to the user by this Fragment. - * - * Partitioned further to contain mutiple states for each standalone View this Fragment holds. - */ -data class QuickSettingsFragmentState( - val webInfoState: WebsiteInfoState, - val websitePermissionsState: WebsitePermissionsState -) : State - -/** - * [State] to be rendered by [WebsiteInfoView] indicating whether the connection is secure or not. - * - * @param websiteUrl [String] the URL of the current web page. - * @param websiteTitle [String] the title of the current web page. - * @param securityInfoRes [StringRes] for the connection description. - * @param iconRes [DrawableRes] image indicating the connection status. - * @param iconTintRes [ColorRes] icon color. - */ -data class WebsiteInfoState( - val websiteUrl: String, - val websiteTitle: String, - @StringRes val securityInfoRes: Int, - @DrawableRes val iconRes: Int, - @ColorRes val iconTintRes: Int, - val certificateName: String -) : State - -/** - * /** - * [State] to be rendered by [WebsitePermissionsView] displaying all explicitly allowed or blocked - * website permissions. - * - * @param isVisible [Boolean] whether this contains data that needs to be displayed to the user. - * @param camera [WebsitePermission] containing all information about the *camera* permission. - * @param microphone [WebsitePermission] containing all information about the *microphone* permission. - * @param notification [notification] containing all information about the *notification* permission. - * @param location [WebsitePermission] containing all information about the *location* permission. -*/ - */ -data class WebsitePermissionsState( - val isVisible: Boolean, - val camera: WebsitePermission, - val microphone: WebsitePermission, - val notification: WebsitePermission, - val location: WebsitePermission, - val autoplayAudible: WebsitePermission, - val autoplayInaudible: WebsitePermission -) : State - -/** - * Wrapper over a website permission encompassing all it's needed state to be rendered on the screen. - * - * Contains a limited number of implementations because there is a known, finite number of permissions - * we need to display to the user. - */ -sealed class WebsitePermission { - /** - * The *allowed* / *blocked* permission status to be shown to the user. - */ - abstract val status: String - - /** - * Whether this permission should be shown to the user. - */ - abstract val isVisible: Boolean - - /** - * Visual indication about whether this permission is *enabled* / *disabled* - */ - abstract val isEnabled: Boolean - - /** - * Whether the corresponding *dangerous* Android permission is granted for the app by the user or not. - */ - abstract val isBlockedByAndroid: Boolean - - /** - * Helper method mimicking the default generated *copy()* method for a data class. - * Allows us using a familiar API in the reducer. - */ - abstract fun copy( - status: String = this.status, - isVisible: Boolean = this.isVisible, - isEnabled: Boolean = this.isEnabled, - isBlockedByAndroid: Boolean = this.isBlockedByAndroid - ): WebsitePermission - - /** - * Contains all information about the *camera* permission. - */ - 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 - ) - } - - /** - * Contains all information about the *microphone* permission. - */ - 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 - ) - } - - /** - * Contains all information about the *notification* permission. - */ - 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 - ) - } - - /** - * Contains all information about the *location* permission. - */ - 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 - ) - } - - /** - * Contains all information about the *autoplay audible* permission. - */ - data class AutoplayAudible( - override val status: String, - override val isVisible: Boolean, - override val isEnabled: Boolean, - override val isBlockedByAndroid: Boolean, - val name: String = "AutoplayAudible" // 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 - ) - } - - /** - * Contains all information about the *autoplay inaudible* permission. - */ - data class AutoplayInaudible( - override val status: String, - override val isVisible: Boolean, - override val isEnabled: Boolean, - override val isBlockedByAndroid: Boolean, - // helps to resolve the overload resolution ambiguity for the copy() method - val name: String = "AutoplayInaudible" - ) : WebsitePermission() { - override fun copy( - status: String, - isVisible: Boolean, - isEnabled: Boolean, - isBlockedByAndroid: Boolean - ) = copy( - status = status, - isVisible = isVisible, - isEnabled = isEnabled, - isBlockedByAndroid = isBlockedByAndroid, - name = name - ) - } -} - -// ------------------------------------------------------------------------------------------------- -// Actions -// ------------------------------------------------------------------------------------------------- - -/** - * Parent [Action] for all the [QuickSettingsFragmentState] changes. - */ -sealed class QuickSettingsFragmentAction : Action - -/** - * All possible [WebsiteInfoState] changes as result of user / system interactions. - */ -sealed class WebsiteInfoAction : QuickSettingsFragmentAction() - -/** - * All possible [WebsitePermissionsState] changes as result of user / system interactions. - */ -sealed class WebsitePermissionAction : QuickSettingsFragmentAction() { - /** - * Change resulting from toggling a specific [WebsitePermission] for the current website. - * - * @param updatedFeature [PhoneFeature] backing a certain [WebsitePermission]. - * Allows to easily identify which permission changed - * **Must be the name of one of the properties of [WebsitePermissionsState]**. - * @param updatedStatus [String] the new [WebsitePermission#status] which will be shown to the user. - * @param updatedEnabledStatus [Boolean] the new [WebsitePermission#enabled] which will be shown to the user. - */ - class TogglePermission( - val websitePermission: WebsitePermission, - val updatedStatus: String, - val updatedEnabledStatus: Boolean - ) : WebsitePermissionAction() -} - -// ------------------------------------------------------------------------------------------------- -// Reducers -// ------------------------------------------------------------------------------------------------- - -/** - * Parent [Reducer] for all [QuickSettingsFragmentState]s of all Views shown in this Fragment. - */ -fun quickSettingsFragmentReducer( - state: QuickSettingsFragmentState, - action: QuickSettingsFragmentAction -): QuickSettingsFragmentState { - return when (action) { - is WebsiteInfoAction -> state.copy( - webInfoState = WebsiteInfoStateReducer.reduce( - state.webInfoState, - action + return WebsitePermission( + phoneFeature = this, + status = getActionLabel(context, permissions, settings), + isVisible = shouldBeVisible(permissions, settings), + isEnabled = shouldBeEnabled(context, permissions, settings), + isBlockedByAndroid = !isAndroidPermissionGranted(context) ) - ) - is WebsitePermissionAction -> state.copy( - websitePermissionsState = WebsitePermissionsStateReducer.reduce( - state.websitePermissionsState, - action - ) - ) - } -} - -@Suppress("UNUSED_PARAMETER") // the action paramater is unused -object WebsiteInfoStateReducer { - /** - * Handles creating a new [WebsiteInfoState] based on the specific [WebsiteInfoAction] - */ - 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 { - /** - * Handles creating a new [WebsitePermissionsState] based on the specific [WebsitePermissionAction] - */ - 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 - ) - ) - is WebsitePermission.AutoplayAudible -> { - return state.copy( - autoplayAudible = state.autoplayAudible.copy( - status = action.updatedStatus, - isEnabled = action.updatedEnabledStatus - ) - ) - } - is WebsitePermission.AutoplayInaudible -> { - return state.copy( - autoplayInaudible = state.autoplayInaudible.copy( - status = action.updatedStatus, - isEnabled = action.updatedEnabledStatus - ) - ) - } - } - } } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt index daeb8cdf0..235024cc9 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt @@ -7,14 +7,11 @@ 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.appcompat.content.res.AppCompatResources -import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getColor import androidx.core.view.isVisible import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.quicksettings_website_info.view.* +import mozilla.components.support.ktx.android.content.getDrawableWithTint import org.mozilla.fenix.R /** @@ -38,7 +35,7 @@ class WebsiteInfoView( fun update(state: WebsiteInfoState) { bindUrl(state.websiteUrl) bindTitle(state.websiteTitle) - bindSecurityInfo(state.securityInfoRes, state.iconRes, state.iconTintRes) + bindSecurityInfo(state.websiteSecurityUiValues) bindCertificateName(state.certificateName) } @@ -56,14 +53,11 @@ class WebsiteInfoView( view.certificateInfo.isVisible = cert.isNotEmpty() } - private fun bindSecurityInfo( - @StringRes securityInfoRes: Int, - @DrawableRes iconRes: Int, - @ColorRes iconTintRes: Int - ) { - val icon = AppCompatResources.getDrawable(view.context, iconRes) - icon?.setTint(ContextCompat.getColor(view.context, iconTintRes)) - view.securityInfo.setText(securityInfoRes) - view.securityInfoIcon.setImageResource(iconRes) + private fun bindSecurityInfo(uiValues: WebsiteSecurityUiValues) { + val tint = getColor(view.context, uiValues.iconTintRes) + view.securityInfo.setText(uiValues.securityInfoRes) + view.securityInfoIcon.setImageDrawable( + view.context.getDrawableWithTint(uiValues.iconRes, tint) + ) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsitePermissionsView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsitePermissionsView.kt index 1f2c7d371..15a254de2 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsitePermissionsView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsitePermissionsView.kt @@ -10,7 +10,10 @@ import android.view.ViewGroup import android.widget.TextView import androidx.core.view.isVisible import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.quicksettings_permissions.view.* import org.mozilla.fenix.R +import org.mozilla.fenix.settings.PhoneFeature +import java.util.EnumMap /** * Contract declaring all possible user interactions with [WebsitePermissionsView] @@ -49,26 +52,31 @@ class WebsitePermissionsView( val view: View = LayoutInflater.from(context) .inflate(R.layout.quicksettings_permissions, containerView, true) + private val permissionViews: Map = EnumMap(mapOf( + PhoneFeature.CAMERA to PermissionViewHolder(view.cameraLabel, view.cameraStatus), + PhoneFeature.LOCATION to PermissionViewHolder(view.locationLabel, view.locationStatus), + PhoneFeature.MICROPHONE to PermissionViewHolder(view.microphoneLabel, view.microphoneStatus), + PhoneFeature.NOTIFICATION to PermissionViewHolder(view.notificationLabel, view.notificationStatus) + )) + /** * Allows changing what this View displays. * * @param state [WebsitePermissionsState] to be rendered. */ fun update(state: WebsitePermissionsState) { - if (state.isVisible) { + val isVisible = permissionViews.keys + .map { feature -> state.getValue(feature) } + .any { it.isVisible } + if (isVisible) { interactor.onPermissionsShown() } // If more permissions are added into this View we can display them into a list // and also use DiffUtil to only update one item in case of a permission change - bindPermission(state.camera, - Pair(view.findViewById(R.id.cameraLabel), view.findViewById(R.id.cameraStatus))) - bindPermission(state.location, - Pair(view.findViewById(R.id.locationLabel), view.findViewById(R.id.locationStatus))) - bindPermission(state.microphone, - Pair(view.findViewById(R.id.microphoneLabel), view.findViewById(R.id.microphoneStatus))) - bindPermission(state.notification, - Pair(view.findViewById(R.id.notificationLabel), view.findViewById(R.id.notificationStatus))) + for ((feature, views) in permissionViews) { + bindPermission(state.getValue(feature), views) + } } /** @@ -76,15 +84,15 @@ class WebsitePermissionsView( * which will display permission's [icon, label, status] and register user inputs. * * @param permissionState [WebsitePermission] specific permission that can be shown to the user. - * @param permissionViews Views that will render [WebsitePermission]'s state. + * @param viewHolder Views that will render [WebsitePermission]'s state. */ - private fun bindPermission(permissionState: WebsitePermission, permissionViews: Pair) { - val (label, status) = permissionViews - - status.text = permissionState.status - label.isEnabled = permissionState.isEnabled - label.isVisible = permissionState.isVisible - status.isVisible = permissionState.isVisible - status.setOnClickListener { interactor.onPermissionToggled(permissionState) } + private fun bindPermission(permissionState: WebsitePermission, viewHolder: PermissionViewHolder) { + viewHolder.label.isEnabled = permissionState.isEnabled + viewHolder.label.isVisible = permissionState.isVisible + viewHolder.status.text = permissionState.status + viewHolder.status.isVisible = permissionState.isVisible + viewHolder.status.setOnClickListener { interactor.onPermissionToggled(permissionState) } } + + data class PermissionViewHolder(val label: TextView, val status: TextView) } diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/Extensions.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/Extensions.kt index 9b6b4c8d6..4c7daaed0 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/Extensions.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/Extensions.kt @@ -4,8 +4,13 @@ package org.mozilla.fenix.settings.sitepermissions +import android.view.View +import android.widget.TextView import androidx.core.net.toUri +import androidx.core.text.HtmlCompat +import org.mozilla.fenix.R import org.mozilla.fenix.components.Components +import org.mozilla.fenix.settings.PhoneFeature /** * Try to reload a session if a session with the given [origin] it is found. @@ -15,3 +20,23 @@ internal fun Components.tryReloadTabBy(origin: String) { val session = core.sessionManager.all.find { it.url.toUri().host == origin } useCases.sessionUseCases.reload(session) } + +internal fun initBlockedByAndroidView(phoneFeature: PhoneFeature, blockedByAndroidView: View) { + val context = blockedByAndroidView.context + if (!phoneFeature.isAndroidPermissionGranted(context)) { + blockedByAndroidView.visibility = View.VISIBLE + + val descriptionLabel = blockedByAndroidView.findViewById(R.id.blocked_by_android_feature_label) + val descriptionText = context.getString( + R.string.phone_feature_blocked_step_feature, + phoneFeature.getLabel(context) + ) + descriptionLabel.text = HtmlCompat.fromHtml(descriptionText, HtmlCompat.FROM_HTML_MODE_COMPACT) + + val permissionsLabel = blockedByAndroidView.findViewById(R.id.blocked_by_android_permissions_label) + val permissionsText = context.getString(R.string.phone_feature_blocked_step_permissions) + permissionsLabel.text = HtmlCompat.fromHtml(permissionsText, HtmlCompat.FROM_HTML_MODE_COMPACT) + } else { + blockedByAndroidView.visibility = View.GONE + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt index 14763786f..b87fc747d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt @@ -108,7 +108,7 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() { private fun navigateToPhoneFeature(phoneFeature: PhoneFeature) { val directions = SitePermissionsDetailsExceptionsFragmentDirections.actionSitePermissionsToExceptionsToManagePhoneFeature( - phoneFeatureId = phoneFeature.id, + phoneFeature = phoneFeature, sitePermissions = sitePermissions ) requireView().findNavController().navigate(directions) diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsFragment.kt index 0bdd9ae30..7b034b1e8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsFragment.kt @@ -77,7 +77,7 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { private fun navigateToPhoneFeature(phoneFeature: PhoneFeature) { val directions = SitePermissionsFragmentDirections - .actionSitePermissionsToManagePhoneFeatures(phoneFeature.id) + .actionSitePermissionsToManagePhoneFeatures(phoneFeature) Navigation.findNavController(requireView()).navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt index 7e7c2e75e..b8ad9d170 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt @@ -28,29 +28,22 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar -import org.mozilla.fenix.settings.PhoneFeature -import org.mozilla.fenix.settings.initBlockedByAndroidView import org.mozilla.fenix.settings.setStartCheckedIndicator +import org.mozilla.fenix.settings.update @SuppressWarnings("TooManyFunctions") class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { - private lateinit var phoneFeature: PhoneFeature - private lateinit var sitePermissions: SitePermissions private lateinit var radioAllow: RadioButton private lateinit var radioBlock: RadioButton private lateinit var blockedByAndroidView: View + private val args by navArgs() val settings by lazy { requireContext().settings() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val args by navArgs() - - phoneFeature = args.phoneFeatureId.toPhoneFeature() - sitePermissions = args.sitePermissions - - showToolbar(phoneFeature.getLabel(requireContext())) + showToolbar(args.phoneFeature.getLabel(requireContext())) } override fun onCreateView( @@ -71,7 +64,7 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { override fun onResume() { super.onResume() - initBlockedByAndroidView(phoneFeature, blockedByAndroidView) + initBlockedByAndroidView(args.phoneFeature, blockedByAndroidView) } private fun initAskToAllowRadio(rootView: View) { @@ -87,7 +80,7 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { } private fun RadioButton.restoreState(status: SitePermissions.Status) { - if (phoneFeature.getStatus(sitePermissions) == status) { + if (args.phoneFeature.getStatus(args.sitePermissions) == status) { this.isChecked = true this.setStartCheckedIndicator() } @@ -109,7 +102,7 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { setMessage(R.string.confirm_clear_permission_site) setTitle(R.string.clear_permission) setPositiveButton(android.R.string.yes) { dialog: DialogInterface, _ -> - val defaultStatus = phoneFeature.getStatus(settings = settings) + val defaultStatus = args.phoneFeature.getStatus(settings = settings) updatedSitePermissions(defaultStatus) resetRadioButtonsStatus(defaultStatus) dialog.dismiss() @@ -147,23 +140,8 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { startActivity(intent) } - private fun Int.toPhoneFeature(): PhoneFeature { - return requireNotNull(PhoneFeature.values().find { feature -> - this == feature.id - }) { - "$this is a invalid PhoneFeature" - } - } - private fun updatedSitePermissions(status: SitePermissions.Status) { - val updatedSitePermissions = when (phoneFeature) { - PhoneFeature.CAMERA -> sitePermissions.copy(camera = status) - PhoneFeature.LOCATION -> sitePermissions.copy(location = status) - PhoneFeature.MICROPHONE -> sitePermissions.copy(microphone = status) - PhoneFeature.NOTIFICATION -> sitePermissions.copy(notification = status) - PhoneFeature.AUTOPLAY_AUDIBLE -> sitePermissions.copy(autoplayAudible = status) - PhoneFeature.AUTOPLAY_INAUDIBLE -> sitePermissions.copy(autoplayInaudible = status) - } + val updatedSitePermissions = args.sitePermissions.update(args.phoneFeature, status) viewLifecycleOwner.lifecycleScope.launch(IO) { requireComponents.core.permissionStorage.updateSitePermissions(updatedSitePermissions) launch(Main) { diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManagePhoneFeatureFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManagePhoneFeatureFragment.kt index cc06d6e57..7332dc33c 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManagePhoneFeatureFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManagePhoneFeatureFragment.kt @@ -20,10 +20,8 @@ import android.view.ViewGroup import android.widget.Button import android.widget.RadioButton import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.ask_to_allow_radio -import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.block_radio -import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.fourth_radio -import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.third_radio +import androidx.navigation.fragment.navArgs +import kotlinx.android.synthetic.main.fragment_manage_site_permissions_feature_phone.view.* import mozilla.components.feature.sitepermissions.SitePermissionsRules import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ALLOWED import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ASK_TO_ALLOW @@ -31,10 +29,8 @@ import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.BL import org.mozilla.fenix.R import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar -import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_AUDIBLE import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_INAUDIBLE -import org.mozilla.fenix.settings.initBlockedByAndroidView import org.mozilla.fenix.settings.setStartCheckedIndicator import org.mozilla.fenix.utils.Settings @@ -45,19 +41,11 @@ const val AUTOPLAY_ALLOW_ALL = 3 @SuppressWarnings("TooManyFunctions") class SitePermissionsManagePhoneFeatureFragment : Fragment() { - private lateinit var phoneFeature: PhoneFeature - private lateinit var settings: Settings + + private val args by navArgs() + private val settings by lazy { requireContext().settings() } private lateinit var blockedByAndroidView: View - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - phoneFeature = SitePermissionsManagePhoneFeatureFragmentArgs - .fromBundle(requireArguments()) - .permission.toPhoneFeature() - settings = requireContext().settings() - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -80,13 +68,13 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { override fun onResume() { super.onResume() - showToolbar(phoneFeature.getLabel(requireContext())) - initBlockedByAndroidView(phoneFeature, blockedByAndroidView) + showToolbar(args.phoneFeature.getLabel(requireContext())) + initBlockedByAndroidView(args.phoneFeature, blockedByAndroidView) } private fun initFirstRadio(rootView: View) { with(rootView.ask_to_allow_radio) { - if (phoneFeature == AUTOPLAY_AUDIBLE) { + if (args.phoneFeature == AUTOPLAY_AUDIBLE) { // Disabled because GV does not allow this setting. TODO Reenable after // https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed // text = getString(R.string.preference_option_autoplay_allowed2) @@ -111,7 +99,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { private fun initSecondRadio(rootView: View) { with(rootView.block_radio) { - if (phoneFeature == AUTOPLAY_AUDIBLE) { + if (args.phoneFeature == AUTOPLAY_AUDIBLE) { text = getCombinedLabel( getString(R.string.preference_option_autoplay_allowed_wifi_only2), getString(R.string.preference_option_autoplay_allowed_wifi_subtext) @@ -135,7 +123,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { private fun initThirdRadio(rootView: View) { with(rootView.third_radio) { - if (phoneFeature == AUTOPLAY_AUDIBLE) { + if (args.phoneFeature == AUTOPLAY_AUDIBLE) { visibility = View.VISIBLE text = getString(R.string.preference_option_autoplay_block_audio2) setOnClickListener { @@ -150,7 +138,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { private fun initFourthRadio(rootView: View) { with(rootView.fourth_radio) { - if (phoneFeature == AUTOPLAY_AUDIBLE) { + if (args.phoneFeature == AUTOPLAY_AUDIBLE) { visibility = View.VISIBLE text = getCombinedLabel( getString(R.string.preference_option_autoplay_blocked3), @@ -167,7 +155,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { } private fun RadioButton.restoreState(buttonAction: SitePermissionsRules.Action) { - if (phoneFeature.getAction(settings) == buttonAction) { + if (args.phoneFeature.getAction(settings) == buttonAction) { this.isChecked = true this.setStartCheckedIndicator() } @@ -181,7 +169,7 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { } private fun saveActionInSettings(action: SitePermissionsRules.Action) { - settings.setSitePermissionsPhoneFeatureAction(phoneFeature, action) + settings.setSitePermissionsPhoneFeatureAction(args.phoneFeature, action) } /** @@ -211,14 +199,6 @@ class SitePermissionsManagePhoneFeatureFragment : Fragment() { initSettingsButton(blockedByAndroidView) } - private fun Int.toPhoneFeature(): PhoneFeature { - return requireNotNull(PhoneFeature.values().find { feature -> - this == feature.id - }) { - "$this is a invalid PhoneFeature" - } - } - private fun initSettingsButton(rootView: View) { val button = rootView.findViewById