From e3869f7e175935d71307ef8a78889ae51f3985b3 Mon Sep 17 00:00:00 2001 From: Ionut Cristian Bedregeanu Date: Tue, 15 Mar 2022 15:12:01 +0200 Subject: [PATCH] For #6387 - Migrate multi-tabs experiment to feature manifest language --- .experimenter.json | 35 +++++++++++++++++++ app/build.gradle | 26 ++++++++++++++ .../main/java/org/mozilla/focus/Components.kt | 7 +++- .../contextmenu/ContextMenuCandidates.kt | 10 ++---- .../main/java/org/mozilla/focus/ext/Nimbus.kt | 26 -------------- .../mozilla/focus/fragment/BrowserFragment.kt | 4 +-- .../mozilla/focus/tabs/MergeTabsMiddleware.kt | 7 ++-- build.gradle | 1 + nimbus.fml.yaml | 30 ++++++++++++++++ 9 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 .experimenter.json create mode 100644 nimbus.fml.yaml diff --git a/.experimenter.json b/.experimenter.json new file mode 100644 index 00000000000..a2f46d124db --- /dev/null +++ b/.experimenter.json @@ -0,0 +1,35 @@ +{ + "tabs": { + "description": "Nimbus feature name intended to control the multiple tabs feature in the app.", + "hasExposure": true, + "exposureDescription": "", + "variables": { + "is_multi_tab": { + "description": "Nimbus variable of [FEATURE_TABS] allowing outside control of whether the multiple tabs feature should be enabled or not.", + "type": "boolean" + } + } + }, + "onboarding-with-cfrs": { + "description": "Nimbus feature name intended to control the onboarding plus all CFRs in the app.", + "hasExposure": true, + "exposureDescription": "", + "variables": { + "is-onboarding-with-cfrs": { + "description": "If `true`, the app will show the new onboarding screen and all the cfrs", + "type": "boolean" + } + } + }, + "onboarding-without-cfrs": { + "description": "Nimbus feature name intended to control the onboarding without CFRs in the app.", + "hasExposure": true, + "exposureDescription": "", + "variables": { + "is-onboarding-without-cfrs": { + "description": "If `true`, the app will show only the new onboarding screen", + "type": "boolean" + } + } + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index cd2445bd260..69eecf1f950 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -164,6 +164,32 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { // Generate Kotlin code for the Focus Glean metrics. // ------------------------------------------------------------------------------------------------- apply plugin: "org.mozilla.telemetry.glean-gradle-plugin" +apply plugin: "org.mozilla.components.nimbus-gradle-plugin" + +nimbus { + // The path to the Nimbus feature manifest file + manifestFile = "nimbus.fml.yaml" + // The fully qualified class name for the generated features. + // If the classname begins with a '.' this is taken as a suffix to the app's package name + destinationClass = ".nimbus.FocusNimbus" + // Map from the variant name to the channel as experimenter and nimbus understand it. + // If nimbus's channels were accurately set up well for this project, then this + // shouldn't be needed. + channels = [ + focusDebug: "debug", + focusNightly: "nightly", + focusBeta: "beta", + focusRelease: "release", + klarDebug: "debug", + klarNightly: "nightly", + klarBeta: "beta", + klarRelease: "release" + ] + // This is generated by the FML and should be checked into git. + // It will be fetched by Experimenter (the Nimbus experiment website) + // and used to inform experiment configuration. + experimenterManifest = ".experimenter.json" +} configurations { // There's an interaction between Gradle's resolution of dependencies with different types diff --git a/app/src/main/java/org/mozilla/focus/Components.kt b/app/src/main/java/org/mozilla/focus/Components.kt index 3ae973b872a..89a3b681f5f 100644 --- a/app/src/main/java/org/mozilla/focus/Components.kt +++ b/app/src/main/java/org/mozilla/focus/Components.kt @@ -61,6 +61,7 @@ import org.mozilla.focus.experiments.createNimbus import org.mozilla.focus.ext.components import org.mozilla.focus.ext.settings import org.mozilla.focus.media.MediaSessionService +import org.mozilla.focus.nimbus.FocusNimbus import org.mozilla.focus.notification.PrivateNotificationMiddleware import org.mozilla.focus.search.SearchFilterMiddleware import org.mozilla.focus.search.SearchMigration @@ -186,7 +187,11 @@ class Components( val metrics: GleanMetricsService by lazy { GleanMetricsService(context) } - val experiments: NimbusApi by lazy { createNimbus(context, BuildConfig.NIMBUS_ENDPOINT) } + val experiments: NimbusApi by lazy { + createNimbus(context, BuildConfig.NIMBUS_ENDPOINT).also { api -> + FocusNimbus.api = api + } + } val adsTelemetry: AdsTelemetry by lazy { AdsTelemetry() } diff --git a/app/src/main/java/org/mozilla/focus/contextmenu/ContextMenuCandidates.kt b/app/src/main/java/org/mozilla/focus/contextmenu/ContextMenuCandidates.kt index 085b8e5915f..fd5de07f7af 100644 --- a/app/src/main/java/org/mozilla/focus/contextmenu/ContextMenuCandidates.kt +++ b/app/src/main/java/org/mozilla/focus/contextmenu/ContextMenuCandidates.kt @@ -11,9 +11,7 @@ import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuUseCases import mozilla.components.feature.contextmenu.DefaultSnackbarDelegate import mozilla.components.feature.tabs.TabsUseCases -import org.mozilla.focus.ext.FEATURE_TABS -import org.mozilla.focus.ext.components -import org.mozilla.focus.ext.isMultiTabsEnabled +import org.mozilla.focus.nimbus.FocusNimbus object ContextMenuCandidates { @Suppress("LongParameterList") @@ -25,8 +23,7 @@ object ContextMenuCandidates { snackBarParentView: View, snackbarDelegate: ContextMenuCandidate.SnackbarDelegate = DefaultSnackbarDelegate() ): List = - if (context.components.experiments.isMultiTabsEnabled) { - context.components.experiments.recordExposureEvent(FEATURE_TABS) + if (FocusNimbus.features.tabs.value().isMultiTab) { listOf( ContextMenuCandidate.createOpenInPrivateTabCandidate( context, @@ -42,8 +39,7 @@ object ContextMenuCandidates { ContextMenuCandidate.createDownloadLinkCandidate(context, contextMenuUseCases), ContextMenuCandidate.createShareLinkCandidate(context), ContextMenuCandidate.createShareImageCandidate(context, contextMenuUseCases) - ) + if (context.components.experiments.isMultiTabsEnabled) { - context.components.experiments.recordExposureEvent(FEATURE_TABS) + ) + if (FocusNimbus.features.tabs.value().isMultiTab) { listOf( ContextMenuCandidate.createOpenImageInNewTabCandidate( context, diff --git a/app/src/main/java/org/mozilla/focus/ext/Nimbus.kt b/app/src/main/java/org/mozilla/focus/ext/Nimbus.kt index 71675d150c2..e69de29bb2d 100644 --- a/app/src/main/java/org/mozilla/focus/ext/Nimbus.kt +++ b/app/src/main/java/org/mozilla/focus/ext/Nimbus.kt @@ -1,26 +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.focus.ext - -import mozilla.components.service.nimbus.NimbusApi - -/** - * Nimbus feature name intended to control the multiple tabs feature in the app. - */ -const val FEATURE_TABS = "tabs" - -/** - * Nimbus variable of [FEATURE_TABS] allowing outside control of whether the multiple tabs feature - * should be enabled or not. - */ -private const val VARIABLE_IS_MULTI_TAB = "is_multi_tab" - -/** - * Whether the multiple tabs feature support is enabled or not. - * Defaults to `true`. May be overridden by a Nimbus experiment. - */ -val NimbusApi.isMultiTabsEnabled - get() = getVariables(featureId = FEATURE_TABS, recordExposureEvent = false) - .getBool(VARIABLE_IS_MULTI_TAB) ?: true diff --git a/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt b/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt index 6a72c043f6c..1cec99e7d5e 100644 --- a/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt @@ -77,12 +77,12 @@ import org.mozilla.focus.ext.disableDynamicBehavior import org.mozilla.focus.ext.enableDynamicBehavior import org.mozilla.focus.ext.ifCustomTab import org.mozilla.focus.ext.isCustomTab -import org.mozilla.focus.ext.isMultiTabsEnabled import org.mozilla.focus.ext.requireComponents import org.mozilla.focus.ext.settings import org.mozilla.focus.ext.showAsFixed import org.mozilla.focus.ext.titleOrDomain import org.mozilla.focus.menu.browser.DefaultBrowserMenu +import org.mozilla.focus.nimbus.FocusNimbus import org.mozilla.focus.open.OpenWithFragment import org.mozilla.focus.session.ui.TabsPopup import org.mozilla.focus.settings.permissions.permissionoptions.SitePermissionOptionsStorage @@ -749,7 +749,7 @@ class BrowserFragment : TelemetryWrapper.openFullBrowser() - if (requireComponents.experiments.isMultiTabsEnabled) { + if (FocusNimbus.features.tabs.value().isMultiTab) { requireComponents.customTabsUseCases.migrate(tab.id) requireComponents.experiments.recordExposureEvent(FEATURE_TABS) } else { diff --git a/app/src/main/java/org/mozilla/focus/tabs/MergeTabsMiddleware.kt b/app/src/main/java/org/mozilla/focus/tabs/MergeTabsMiddleware.kt index 14635794050..fc266ddd7d0 100644 --- a/app/src/main/java/org/mozilla/focus/tabs/MergeTabsMiddleware.kt +++ b/app/src/main/java/org/mozilla/focus/tabs/MergeTabsMiddleware.kt @@ -14,9 +14,7 @@ import mozilla.components.browser.state.state.TabSessionState import mozilla.components.concept.engine.EngineSession import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.MiddlewareContext -import org.mozilla.focus.ext.FEATURE_TABS -import org.mozilla.focus.ext.components -import org.mozilla.focus.ext.isMultiTabsEnabled +import org.mozilla.focus.nimbus.FocusNimbus /** * If the tabs feature is disabled then this middleware will look at incoming [TabListAction.AddTabAction] @@ -31,10 +29,9 @@ class MergeTabsMiddleware( next: (BrowserAction) -> Unit, action: BrowserAction ) { - if (this.context.components.experiments.isMultiTabsEnabled || action !is TabListAction.AddTabAction) { + if (FocusNimbus.features.tabs.value().isMultiTab || action !is TabListAction.AddTabAction) { // If the feature flag for tabs is enabled then we can just let the reducer create a // new tab. - this.context.components.experiments.recordExposureEvent(FEATURE_TABS) next(action) return } diff --git a/build.gradle b/build.gradle index cb305874deb..5feaf33ed73 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ buildscript { classpath Dependencies.android_gradle_plugin classpath Dependencies.kotlin_gradle_plugin classpath "org.mozilla.components:tooling-glean-gradle:${AndroidComponents.VERSION}" + classpath "org.mozilla.components:tooling-nimbus-gradle:${AndroidComponents.VERSION}" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/nimbus.fml.yaml b/nimbus.fml.yaml new file mode 100644 index 00000000000..b8bb2fd54b7 --- /dev/null +++ b/nimbus.fml.yaml @@ -0,0 +1,30 @@ +channels: + - debug + - nightly + - beta + - release +features: + tabs: + description: Nimbus feature name intended to control the multiple tabs feature in the app. + variables: + is_multi_tab: + description: Nimbus variable of [FEATURE_TABS] allowing outside control of whether the multiple tabs feature should be enabled or not. + type: Boolean + default: true + onboarding-with-cfrs: + description: Nimbus feature name intended to control the onboarding plus all CFRs in the app. + variables: + is-onboarding-with-cfrs: + description: If `true`, the app will show the new onboarding screen and all the cfrs + type: Boolean + default: true + onboarding-without-cfrs: + description: Nimbus feature name intended to control the onboarding without CFRs in the app. + variables: + is-onboarding-without-cfrs: + description: If `true`, the app will show only the new onboarding screen + type: Boolean + default: false +types: + objects: {} + enums: {}