From 9cdfb6db4a2eb67e83359ac5ac545fa4f7d81509 Mon Sep 17 00:00:00 2001 From: jhugman Date: Thu, 19 Nov 2020 11:17:25 +0000 Subject: [PATCH] Nimbus Global Opt Out (#16543) r=gl --- app/build.gradle | 2 +- .../SettingsSubMenuDataCollectionRobot.kt | 11 ++++++++++ .../java/org/mozilla/fenix/FeatureFlags.kt | 6 ++++++ .../org/mozilla/fenix/FenixApplication.kt | 21 ------------------- .../org/mozilla/fenix/components/Analytics.kt | 19 +++++++++++++++++ .../fenix/settings/DataChoicesFragment.kt | 7 +++++-- .../java/org/mozilla/fenix/utils/Settings.kt | 2 +- app/src/main/res/values/strings.xml | 4 ++++ .../main/res/xml/data_choices_preferences.xml | 4 ++-- buildSrc/src/main/java/Dependencies.kt | 3 +-- 10 files changed, 50 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 994dfd671..3fcbe08fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -435,11 +435,11 @@ dependencies { implementation Deps.mozilla_feature_webcompat_reporter implementation Deps.mozilla_service_digitalassetlinks - implementation Deps.mozilla_service_experiments implementation Deps.mozilla_service_sync_logins implementation Deps.mozilla_service_firefox_accounts implementation Deps.mozilla_service_glean implementation Deps.mozilla_service_location + implementation Deps.mozilla_service_nimbus implementation Deps.mozilla_support_base implementation Deps.mozilla_support_images diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt index 8fd2d9259..431680d24 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt @@ -32,10 +32,13 @@ class SettingsSubMenuDataCollectionRobot { fun verifyMarketingDataSwitchDefault() = assertMarketingDataValueSwitchDefault() + fun verifyExperimentsSwitchDefault() = assertExperimentsSwitchDefault() + fun verifyDataCollectionSubMenuItems() { verifyDataCollectionOptions() verifyUsageAndTechnicalDataSwitchDefault() verifyMarketingDataSwitchDefault() + verifyExperimentsSwitchDefault() } class Transition { @@ -76,6 +79,9 @@ private fun assertDataCollectionOptions() { onView(withText(marketingDataText)) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + + onView(withText(R.string.preference_experiments_2)).check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + onView(withText(R.string.preference_experiments_summary_2)).check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun usageAndTechnicalDataButton() = onView(withText(R.string.preference_usage_data)) @@ -87,3 +93,8 @@ private fun marketingDataButton() = onView(withText(R.string.preferences_marketi private fun assertMarketingDataValueSwitchDefault() = marketingDataButton() .assertIsEnabled(isEnabled = true) + +private fun experimentsButton() = onView(withText(R.string.preference_experiments_2)) + +private fun assertExperimentsSwitchDefault() = experimentsButton() + .assertIsEnabled(isEnabled = true) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index fa6389129..e53b31aac 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -35,4 +35,10 @@ object FeatureFlags { * Enables ETP cookie purging */ val etpCookiePurging = Config.channel.isNightlyOrDebug + + /** + * Enables the Nimbus experiments library, especially the settings toggle to opt-out of + * all experiments. + */ + val nimbusExperiments = Config.channel.isNightlyOrDebug } diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index be727ba63..67299d145 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -25,7 +25,6 @@ import mozilla.components.browser.state.action.SystemAction import mozilla.components.concept.push.PushProcessor import mozilla.components.feature.addons.update.GlobalAddonDependencyProvider import mozilla.components.lib.crash.CrashReporter -import mozilla.components.service.experiments.Experiments import mozilla.components.service.glean.Glean import mozilla.components.service.glean.config.Configuration import mozilla.components.service.glean.net.ConceptFetchHttpUploader @@ -170,25 +169,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { registerActivityLifecycleCallbacks(PerformanceActivityLifecycleCallbacks(queue)) } - fun queueInitExperiments() { - @Suppress("ControlFlowWithEmptyBody") - if (settings().isExperimentationEnabled) { - queue.runIfReadyOrQueue { - Experiments.initialize( - applicationContext = applicationContext, - onExperimentsUpdated = null, - configuration = mozilla.components.service.experiments.Configuration( - httpClient = components.core.client, - kintoEndpoint = KINTO_ENDPOINT_PROD - ) - ) - } - } else { - // We should make a better way to opt out for when we have more experiments - // See https://github.com/mozilla-mobile/fenix/issues/6278 - } - } - fun queueInitStorageAndServices() { components.performance.visualCompletenessQueue.queue.runIfReadyOrQueue { GlobalScope.launch(Dispatchers.IO) { @@ -229,7 +209,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { // We init these items in the visual completeness queue to avoid them initing in the critical // startup path, before the UI finishes drawing (i.e. visual completeness). - queueInitExperiments() queueInitStorageAndServices() queueMetrics() queueReviewPrompt() diff --git a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt index 0f9288bbc..40fd9356b 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -13,8 +13,10 @@ import mozilla.components.lib.crash.service.CrashReporterService import mozilla.components.lib.crash.service.GleanCrashReporterService import mozilla.components.lib.crash.service.MozillaSocorroService import mozilla.components.lib.crash.service.SentryService +import mozilla.components.service.nimbus.Nimbus import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.ReleaseChannel @@ -98,6 +100,23 @@ class Analytics( isMarketingDataTelemetryEnabled = { context.settings().isMarketingTelemetryEnabled } ) } + + val experiments by lazyMonitored { + Nimbus().apply { + if (FeatureFlags.nimbusExperiments) { + initialize(context) + // Global opt out state is stored in Nimbus, and shouldn't be toggled to `true` + // from the app unless the user does so from a UI control. + // However, the user may have opt-ed out of mako experiments already, so + // we should respect that setting here. + val enabled = context.settings().isExperimentationEnabled + if (!enabled) { + globalUserParticipation = enabled + } + context.settings().isExperimentationEnabled = globalUserParticipation + } + } + } } fun isSentryEnabled() = !BuildConfig.SENTRY_TOKEN.isNullOrEmpty() diff --git a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt index be6f1a274..03f7d5deb 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt @@ -7,7 +7,7 @@ package org.mozilla.fenix.settings import android.os.Bundle import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference -import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.MetricServiceType import org.mozilla.fenix.ext.components @@ -37,6 +37,9 @@ class DataChoicesFragment : PreferenceFragmentCompat() { } else { context.components.analytics.metrics.stop(MetricServiceType.Marketing) } + } else if (key == getPreferenceKey(R.string.pref_key_experimentation)) { + val enabled = context.settings().isExperimentationEnabled + context.components.analytics.experiments.globalUserParticipation = enabled } } } @@ -72,7 +75,7 @@ class DataChoicesFragment : PreferenceFragmentCompat() { requirePreference(R.string.pref_key_experimentation).apply { isChecked = context.settings().isExperimentationEnabled - isVisible = Config.channel.isReleaseOrBeta + isVisible = FeatureFlags.nimbusExperiments onPreferenceChangeListener = SharedPreferenceUpdater() } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index c1b7f4ae7..985572be6 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -231,7 +231,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) - val isExperimentationEnabled by booleanPreference( + var isExperimentationEnabled by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_experimentation), default = true ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d4dcd07ec..607517016 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -433,6 +433,10 @@ Marketing data Shares data about what features you use in %1$s with Leanplum, our mobile marketing vendor. + + Studies + + Allows Mozilla to install and run studies Experiments diff --git a/app/src/main/res/xml/data_choices_preferences.xml b/app/src/main/res/xml/data_choices_preferences.xml index 7dc5c1cc8..8026e973a 100644 --- a/app/src/main/res/xml/data_choices_preferences.xml +++ b/app/src/main/res/xml/data_choices_preferences.xml @@ -13,6 +13,6 @@ android:title="@string/preferences_marketing_data" /> + android:summary="@string/preference_experiments_summary_2" + android:title="@string/preference_experiments_2" /> diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 52d3dcfa6..6749a0365 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -129,13 +129,12 @@ object Deps { const val mozilla_service_digitalassetlinks = "org.mozilla.components:service-digitalassetlinks:${Versions.mozilla_android_components}" - const val mozilla_service_experiments = - "org.mozilla.components:service-experiments:${Versions.mozilla_android_components}" const val mozilla_service_sync_logins = "org.mozilla.components:service-sync-logins:${Versions.mozilla_android_components}" const val mozilla_service_firefox_accounts = "org.mozilla.components:service-firefox-accounts:${Versions.mozilla_android_components}" const val mozilla_service_glean = "org.mozilla.components:service-glean:${Versions.mozilla_android_components}" const val mozilla_service_location = "org.mozilla.components:service-location:${Versions.mozilla_android_components}" + const val mozilla_service_nimbus = "org.mozilla.components:service-nimbus:${Versions.mozilla_android_components}" const val mozilla_ui_colors = "org.mozilla.components:ui-colors:${Versions.mozilla_android_components}" const val mozilla_ui_icons = "org.mozilla.components:ui-icons:${Versions.mozilla_android_components}"