From 376ebe7e70fbde7de7a8d5c2595319faec1e652f Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Wed, 27 Mar 2019 12:18:37 -0400 Subject: [PATCH] Closes #1078: Added UI for managing phone feature permissions. --- .../fenix/settings/SitePermissionsFragment.kt | 41 ++++- .../SitePermissionsManagePhoneFeature.kt | 164 ++++++++++++++++++ .../site_permissions_button_background.xml | 9 + ..._manage_site_permissions_feature_phone.xml | 84 +++++++++ app/src/main/res/navigation/nav_graph.xml | 23 ++- app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 12 +- app/src/main/res/values/styles.xml | 16 ++ .../res/xml/site_permissions_preferences.xml | 75 ++++---- 9 files changed, 379 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeature.kt create mode 100644 app/src/main/res/drawable/site_permissions_button_background.xml create mode 100644 app/src/main/res/layout/fragment_manage_site_permissions_feature_phone.xml diff --git a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt index 378e9adaa1..274ef77f1a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsFragment.kt @@ -6,11 +6,19 @@ package org.mozilla.fenix.settings import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.Navigation import androidx.preference.Preference +import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.PreferenceFragmentCompat import org.jetbrains.anko.support.v4.defaultSharedPreferences import org.mozilla.fenix.R +import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature +import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.NOTIFICATION +import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.LOCATION +import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.CAMERA +import org.mozilla.fenix.settings.SitePermissionsManagePhoneFeature.PhoneFeature.MICROPHONE +@SuppressWarnings("TooManyFunctions") class SitePermissionsFragment : PreferenceFragmentCompat() { private lateinit var categoryPhoneFeatures: Preference @@ -19,7 +27,6 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - (activity as AppCompatActivity).title = getString(R.string.preferences_site_permissions) (activity as AppCompatActivity).supportActionBar?.show() } @@ -53,7 +60,7 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { radioCustomSettings = requireNotNull(findPreference(keyCustomSettings)) radioCustomSettings.onClickListener { - toggleCategoryPhoneFeatureVisibility() + categoryPhoneFeatures.isVisible = true } } @@ -62,7 +69,7 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { radioRecommendSettings = requireNotNull(findPreference(keyRecommendSettings)) radioRecommendSettings.onClickListener { - toggleCategoryPhoneFeatureVisibility() + categoryPhoneFeatures.isVisible = false } } @@ -75,9 +82,33 @@ class SitePermissionsFragment : PreferenceFragmentCompat() { if (isCategoryActivate) { categoryPhoneFeatures.isVisible = true } + initPhoneFeature(CAMERA) + initPhoneFeature(LOCATION) + initPhoneFeature(MICROPHONE) + initPhoneFeature(NOTIFICATION) + } + + private fun initPhoneFeature(phoneFeature: PhoneFeature) { + val keyPreference = getPreferenceKeyBy(phoneFeature) + val cameraPhoneFeatures: Preference = requireNotNull(findPreference(keyPreference)) + + cameraPhoneFeatures.onPreferenceClickListener = OnPreferenceClickListener { + navigateToPhoneFeature(phoneFeature) + true + } + } + + private fun getPreferenceKeyBy(phoneFeature: PhoneFeature): String { + return when (phoneFeature) { + CAMERA -> getString(R.string.pref_key_phone_feature_camera) + LOCATION -> getString(R.string.pref_key_phone_feature_location) + MICROPHONE -> getString(R.string.pref_key_phone_feature_microphone) + NOTIFICATION -> getString(R.string.pref_key_phone_feature_notification) + } } - private fun toggleCategoryPhoneFeatureVisibility() { - categoryPhoneFeatures.isVisible = !categoryPhoneFeatures.isVisible + private fun navigateToPhoneFeature(phoneFeature: PhoneFeature) { + val directions = SitePermissionsFragmentDirections.actionSitePermissionsToManagePhoneFeatures(phoneFeature.id) + Navigation.findNavController(view!!).navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeature.kt b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeature.kt new file mode 100644 index 0000000000..e14f37c0c5 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/SitePermissionsManagePhoneFeature.kt @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import android.Manifest.permission.ACCESS_COARSE_LOCATION +import android.Manifest.permission.CAMERA +import android.Manifest.permission.RECORD_AUDIO +import android.Manifest.permission.ACCESS_FINE_LOCATION +import android.content.Intent +import android.graphics.Color +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import android.text.SpannableString +import android.text.SpannableStringBuilder +import android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.Button +import android.widget.RadioButton +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.text.HtmlCompat +import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT +import androidx.fragment.app.Fragment +import mozilla.components.support.ktx.android.content.isPermissionGranted +import org.mozilla.fenix.R + +class SitePermissionsManagePhoneFeature : Fragment() { + + private lateinit var phoneFeature: PhoneFeature + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + phoneFeature = SitePermissionsManagePhoneFeatureArgs + .fromBundle(requireArguments()) + .permission.toPhoneFeature() + + (activity as AppCompatActivity).title = phoneFeature.label + (activity as AppCompatActivity).supportActionBar?.show() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val rootView = inflater.inflate(R.layout.fragment_manage_site_permissions_feature_phone, container, false) + + initAskToAllowRadio(rootView) + + initBockedByAndroidContainer(rootView) + + return rootView + } + + private fun initAskToAllowRadio(rootView: View) { + val radio = rootView.findViewById(R.id.ask_to_allow_switch) + val askToAllowText = getString(R.string.preference_option_phone_feature_ask_to_allow) + val recommendedText = getString(R.string.phone_feature_recommended) + val recommendedTextSize = resources.getDimensionPixelSize(R.dimen.phone_feature_label_recommended_text_size) + val recommendedSpannable = SpannableString(recommendedText) + + recommendedSpannable.setSpan( + ForegroundColorSpan(Color.GRAY), + 0, + recommendedSpannable.length, + SPAN_EXCLUSIVE_INCLUSIVE + ) + + recommendedSpannable.setSpan( + AbsoluteSizeSpan(recommendedTextSize), 0, + recommendedSpannable.length, + SPAN_EXCLUSIVE_INCLUSIVE + ) + + radio.text = with(SpannableStringBuilder()) { + append(askToAllowText) + append("\n") + append(recommendedSpannable) + this + } + } + + private fun initBockedByAndroidContainer(rootView: View) { + if (!phoneFeature.isAndroidPermissionGranted) { + val containerView = rootView.findViewById(R.id.permissions_blocked_container) + containerView.visibility = VISIBLE + + val descriptionLabel = rootView.findViewById(R.id.blocked_by_android_explanation_label) + val text = getString(R.string.phone_feature_blocked_by_android_explanation, phoneFeature.label) + descriptionLabel.text = HtmlCompat.fromHtml(text, FROM_HTML_MODE_COMPACT) + + initSettingsButton(rootView) + } + } + + enum class PhoneFeature(val id: Int) { + CAMERA(CAMERA_PERMISSION), + LOCATION(LOCATION_PERMISSION), + MICROPHONE(MICROPHONE_PERMISSION), + NOTIFICATION(NOTIFICATION_PERMISSION) + } + + private val PhoneFeature.label: String + get() { + return when (this) { + PhoneFeature.CAMERA -> getString(R.string.preference_phone_feature_camera) + PhoneFeature.LOCATION -> getString(R.string.preference_phone_feature_location) + PhoneFeature.MICROPHONE -> getString(R.string.preference_phone_feature_microphone) + PhoneFeature.NOTIFICATION -> getString(R.string.preference_phone_feature_notification) + } + } + + @Suppress("SpreadOperator") + private val PhoneFeature.isAndroidPermissionGranted: Boolean + get() { + val permissions = when (this) { + PhoneFeature.CAMERA -> arrayOf(CAMERA) + PhoneFeature.LOCATION -> arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) + PhoneFeature.MICROPHONE -> arrayOf(RECORD_AUDIO) + PhoneFeature.NOTIFICATION -> { + return true + } + } + return requireContext().isPermissionGranted(*permissions) + } + + private fun Int.toPhoneFeature(): PhoneFeature { + return requireNotNull(PhoneFeature.values().find { feature -> + this == feature.id + }) { + "$this is a invalid PhoneFeature" + } + } + + private fun initSettingsButton(rootView: View) { + val button = rootView.findViewById