1
0
Fork 0

Move MetricController to its own file

master
Tiger Oakes 2020-07-29 16:06:23 -07:00 committed by Jeff Boek
parent e1653f629e
commit f6b38552d2
2 changed files with 227 additions and 220 deletions

View File

@ -0,0 +1,227 @@
/* 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.components.metrics
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts
import mozilla.components.browser.menu.facts.BrowserMenuFacts
import mozilla.components.browser.toolbar.facts.ToolbarFacts
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider
import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider
import mozilla.components.feature.awesomebar.provider.HistoryStorageSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider
import mozilla.components.feature.contextmenu.facts.ContextMenuFacts
import mozilla.components.feature.customtabs.CustomTabsFacts
import mozilla.components.feature.downloads.facts.DownloadsFacts
import mozilla.components.feature.findinpage.facts.FindInPageFacts
import mozilla.components.feature.media.facts.MediaFacts
import mozilla.components.feature.prompts.dialog.LoginDialogFacts
import mozilla.components.support.base.Component
import mozilla.components.support.base.facts.Action
import mozilla.components.support.base.facts.Fact
import mozilla.components.support.base.facts.FactProcessor
import mozilla.components.support.base.facts.Facts
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.webextensions.facts.WebExtensionFacts
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.GleanMetrics.Addons
import org.mozilla.fenix.GleanMetrics.PerfAwesomebar
import org.mozilla.fenix.search.awesomebar.ShortcutsSuggestionProvider
interface MetricController {
fun start(type: MetricServiceType)
fun stop(type: MetricServiceType)
fun track(event: Event)
companion object {
fun create(
services: List<MetricsService>,
isDataTelemetryEnabled: () -> Boolean,
isMarketingDataTelemetryEnabled: () -> Boolean
): MetricController {
return if (BuildConfig.TELEMETRY) {
ReleaseMetricController(
services,
isDataTelemetryEnabled,
isMarketingDataTelemetryEnabled
)
} else DebugMetricController()
}
}
}
@VisibleForTesting
internal class DebugMetricController(
private val logger: Logger = Logger()
) : MetricController {
override fun start(type: MetricServiceType) {
logger.debug("DebugMetricController: start")
}
override fun stop(type: MetricServiceType) {
logger.debug("DebugMetricController: stop")
}
override fun track(event: Event) {
logger.debug("DebugMetricController: track event: $event")
}
}
@VisibleForTesting
internal class ReleaseMetricController(
private val services: List<MetricsService>,
private val isDataTelemetryEnabled: () -> Boolean,
private val isMarketingDataTelemetryEnabled: () -> Boolean
) : MetricController {
private var initialized = mutableSetOf<MetricServiceType>()
init {
Facts.registerProcessor(object : FactProcessor {
override fun process(fact: Fact) {
fact.toEvent()?.also {
track(it)
}
}
})
}
override fun start(type: MetricServiceType) {
val isEnabled = isTelemetryEnabled(type)
val isInitialized = isInitialized(type)
if (!isEnabled || isInitialized) {
return
}
services
.filter { it.type == type }
.forEach { it.start() }
initialized.add(type)
}
override fun stop(type: MetricServiceType) {
val isEnabled = isTelemetryEnabled(type)
val isInitialized = isInitialized(type)
if (isEnabled || !isInitialized) {
return
}
services
.filter { it.type == type }
.forEach { it.stop() }
initialized.remove(type)
}
override fun track(event: Event) {
services
.filter { it.shouldTrack(event) }
.forEach {
val isEnabled = isTelemetryEnabled(it.type)
val isInitialized = isInitialized(it.type)
if (!isEnabled || !isInitialized) {
return@forEach
}
it.track(event)
}
}
private fun isInitialized(type: MetricServiceType): Boolean = initialized.contains(type)
private fun isTelemetryEnabled(type: MetricServiceType): Boolean = when (type) {
MetricServiceType.Data -> isDataTelemetryEnabled()
MetricServiceType.Marketing -> isMarketingDataTelemetryEnabled()
}
private fun Fact.toEvent(): Event? = when (Pair(component, item)) {
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.DISPLAY -> Event.LoginDialogPromptDisplayed
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.CANCEL -> Event.LoginDialogPromptCancelled
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.NEVER_SAVE -> Event.LoginDialogPromptNeverSave
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.SAVE -> Event.LoginDialogPromptSave
Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.CLOSE -> Event.FindInPageClosed
Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.INPUT -> Event.FindInPageSearchCommitted
Component.FEATURE_CONTEXTMENU to ContextMenuFacts.Items.ITEM -> {
metadata?.get("item")?.let { Event.ContextMenuItemTapped.create(it.toString()) }
}
Component.BROWSER_TOOLBAR to ToolbarFacts.Items.MENU -> {
metadata?.get("customTab")?.let { Event.CustomTabsMenuOpened }
}
Component.BROWSER_MENU to BrowserMenuFacts.Items.WEB_EXTENSION_MENU_ITEM -> {
metadata?.get("id")?.let { Event.AddonsOpenInToolbarMenu(it.toString()) }
}
Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.CLOSE -> Event.CustomTabsClosed
Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.ACTION_BUTTON -> Event.CustomTabsActionTapped
Component.FEATURE_DOWNLOADS to DownloadsFacts.Items.NOTIFICATION -> {
when (action) {
Action.CANCEL -> Event.NotificationDownloadCancel
Action.OPEN -> Event.NotificationDownloadOpen
Action.PAUSE -> Event.NotificationDownloadPause
Action.RESUME -> Event.NotificationDownloadResume
Action.TRY_AGAIN -> Event.NotificationDownloadTryAgain
else -> null
}
}
Component.FEATURE_MEDIA to MediaFacts.Items.NOTIFICATION -> {
when (action) {
Action.PLAY -> Event.NotificationMediaPlay
Action.PAUSE -> Event.NotificationMediaPause
else -> null
}
}
Component.FEATURE_MEDIA to MediaFacts.Items.STATE -> {
when (action) {
Action.PLAY -> Event.MediaPlayState
Action.PAUSE -> Event.MediaPauseState
Action.STOP -> Event.MediaStopState
else -> null
}
}
Component.SUPPORT_WEBEXTENSIONS to WebExtensionFacts.Items.WEB_EXTENSIONS_INITIALIZED -> {
metadata?.get("installed")?.let { installedAddons ->
if (installedAddons is List<*>) {
Addons.installedAddons.set(installedAddons.map { it.toString() })
Addons.hasInstalledAddons.set(installedAddons.size > 0)
}
}
metadata?.get("enabled")?.let { enabledAddons ->
if (enabledAddons is List<*>) {
Addons.enabledAddons.set(enabledAddons.map { it.toString() })
Addons.hasEnabledAddons.set(enabledAddons.size > 0)
}
}
null
}
Component.BROWSER_AWESOMEBAR to BrowserAwesomeBarFacts.Items.PROVIDER_DURATION -> {
metadata?.get(BrowserAwesomeBarFacts.MetadataKeys.DURATION_PAIR)?.let { providerTiming ->
require(providerTiming is Pair<*, *>) { "Expected providerTiming to be a Pair" }
when (val provider = providerTiming.first as AwesomeBar.SuggestionProvider) {
is HistoryStorageSuggestionProvider -> PerfAwesomebar.historySuggestions
is BookmarksStorageSuggestionProvider -> PerfAwesomebar.bookmarkSuggestions
is SessionSuggestionProvider -> PerfAwesomebar.sessionSuggestions
is SearchSuggestionProvider -> PerfAwesomebar.searchEngineSuggestions
is ClipboardSuggestionProvider -> PerfAwesomebar.clipboardSuggestions
is ShortcutsSuggestionProvider -> PerfAwesomebar.shortcutsSuggestions
// NB: add PerfAwesomebar.syncedTabsSuggestions once we're using SyncedTabsSuggestionProvider
else -> {
Logger("Metrics").error("Unknown suggestion provider: $provider")
null
}
}?.accumulateSamples(longArrayOf(providerTiming.second as Long))
}
null
}
else -> null
}
}

View File

@ -5,32 +5,8 @@
package org.mozilla.fenix.components.metrics package org.mozilla.fenix.components.metrics
import android.content.Context import android.content.Context
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts
import mozilla.components.browser.errorpages.ErrorType import mozilla.components.browser.errorpages.ErrorType
import mozilla.components.browser.menu.facts.BrowserMenuFacts
import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.search.SearchEngine
import mozilla.components.browser.toolbar.facts.ToolbarFacts
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider
import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider
import mozilla.components.feature.awesomebar.provider.HistoryStorageSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider
import mozilla.components.feature.contextmenu.facts.ContextMenuFacts
import mozilla.components.feature.customtabs.CustomTabsFacts
import mozilla.components.feature.downloads.facts.DownloadsFacts
import mozilla.components.feature.findinpage.facts.FindInPageFacts
import mozilla.components.feature.media.facts.MediaFacts
import mozilla.components.feature.prompts.dialog.LoginDialogFacts
import mozilla.components.support.base.Component
import mozilla.components.support.base.facts.Action
import mozilla.components.support.base.facts.Fact
import mozilla.components.support.base.facts.FactProcessor
import mozilla.components.support.base.facts.Facts
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.webextensions.facts.WebExtensionFacts
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.GleanMetrics.Addons import org.mozilla.fenix.GleanMetrics.Addons
import org.mozilla.fenix.GleanMetrics.AppTheme import org.mozilla.fenix.GleanMetrics.AppTheme
import org.mozilla.fenix.GleanMetrics.Autoplay import org.mozilla.fenix.GleanMetrics.Autoplay
@ -41,13 +17,11 @@ import org.mozilla.fenix.GleanMetrics.ErrorPage
import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Logins
import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Onboarding
import org.mozilla.fenix.GleanMetrics.PerfAwesomebar
import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.SearchShortcuts
import org.mozilla.fenix.GleanMetrics.Tip import org.mozilla.fenix.GleanMetrics.Tip
import org.mozilla.fenix.GleanMetrics.ToolbarSettings import org.mozilla.fenix.GleanMetrics.ToolbarSettings
import org.mozilla.fenix.GleanMetrics.TrackingProtection import org.mozilla.fenix.GleanMetrics.TrackingProtection
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.search.awesomebar.ShortcutsSuggestionProvider
import java.util.Locale import java.util.Locale
sealed class Event { sealed class Event {
@ -532,92 +506,6 @@ sealed class Event {
get() = null get() = null
} }
private fun Fact.toEvent(): Event? = when (Pair(component, item)) {
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.DISPLAY -> Event.LoginDialogPromptDisplayed
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.CANCEL -> Event.LoginDialogPromptCancelled
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.NEVER_SAVE -> Event.LoginDialogPromptNeverSave
Component.FEATURE_PROMPTS to LoginDialogFacts.Items.SAVE -> Event.LoginDialogPromptSave
Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.CLOSE -> Event.FindInPageClosed
Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.INPUT -> Event.FindInPageSearchCommitted
Component.FEATURE_CONTEXTMENU to ContextMenuFacts.Items.ITEM -> {
metadata?.get("item")?.let { Event.ContextMenuItemTapped.create(it.toString()) }
}
Component.BROWSER_TOOLBAR to ToolbarFacts.Items.MENU -> {
metadata?.get("customTab")?.let { Event.CustomTabsMenuOpened }
}
Component.BROWSER_MENU to BrowserMenuFacts.Items.WEB_EXTENSION_MENU_ITEM -> {
metadata?.get("id")?.let { Event.AddonsOpenInToolbarMenu(it.toString()) }
}
Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.CLOSE -> Event.CustomTabsClosed
Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.ACTION_BUTTON -> Event.CustomTabsActionTapped
Component.FEATURE_DOWNLOADS to DownloadsFacts.Items.NOTIFICATION -> {
when (action) {
Action.CANCEL -> Event.NotificationDownloadCancel
Action.OPEN -> Event.NotificationDownloadOpen
Action.PAUSE -> Event.NotificationDownloadPause
Action.RESUME -> Event.NotificationDownloadResume
Action.TRY_AGAIN -> Event.NotificationDownloadTryAgain
else -> null
}
}
Component.FEATURE_MEDIA to MediaFacts.Items.NOTIFICATION -> {
when (action) {
Action.PLAY -> Event.NotificationMediaPlay
Action.PAUSE -> Event.NotificationMediaPause
else -> null
}
}
Component.FEATURE_MEDIA to MediaFacts.Items.STATE -> {
when (action) {
Action.PLAY -> Event.MediaPlayState
Action.PAUSE -> Event.MediaPauseState
Action.STOP -> Event.MediaStopState
else -> null
}
}
Component.SUPPORT_WEBEXTENSIONS to WebExtensionFacts.Items.WEB_EXTENSIONS_INITIALIZED -> {
metadata?.get("installed")?.let { installedAddons ->
if (installedAddons is List<*>) {
Addons.installedAddons.set(installedAddons.map { it.toString() })
Addons.hasInstalledAddons.set(installedAddons.size > 0)
}
}
metadata?.get("enabled")?.let { enabledAddons ->
if (enabledAddons is List<*>) {
Addons.enabledAddons.set(enabledAddons.map { it.toString() })
Addons.hasEnabledAddons.set(enabledAddons.size > 0)
}
}
null
}
Component.BROWSER_AWESOMEBAR to BrowserAwesomeBarFacts.Items.PROVIDER_DURATION -> {
metadata?.get(BrowserAwesomeBarFacts.MetadataKeys.DURATION_PAIR)?.let { providerTiming ->
require(providerTiming is Pair<*, *>) { "Expected providerTiming to be a Pair" }
when (val provider = providerTiming.first as AwesomeBar.SuggestionProvider) {
is HistoryStorageSuggestionProvider -> PerfAwesomebar.historySuggestions
is BookmarksStorageSuggestionProvider -> PerfAwesomebar.bookmarkSuggestions
is SessionSuggestionProvider -> PerfAwesomebar.sessionSuggestions
is SearchSuggestionProvider -> PerfAwesomebar.searchEngineSuggestions
is ClipboardSuggestionProvider -> PerfAwesomebar.clipboardSuggestions
is ShortcutsSuggestionProvider -> PerfAwesomebar.shortcutsSuggestions
// NB: add PerfAwesomebar.syncedTabsSuggestions once we're using SyncedTabsSuggestionProvider
else -> {
Logger("Metrics").error("Unknown suggestion provider: $provider")
null
}
}?.accumulateSamples(longArrayOf(providerTiming.second as Long))
}
null
}
else -> null
}
enum class MetricServiceType { enum class MetricServiceType {
Data, Marketing; Data, Marketing;
} }
@ -630,111 +518,3 @@ interface MetricsService {
fun track(event: Event) fun track(event: Event)
fun shouldTrack(event: Event): Boolean fun shouldTrack(event: Event): Boolean
} }
interface MetricController {
fun start(type: MetricServiceType)
fun stop(type: MetricServiceType)
fun track(event: Event)
companion object {
fun create(
services: List<MetricsService>,
isDataTelemetryEnabled: () -> Boolean,
isMarketingDataTelemetryEnabled: () -> Boolean
): MetricController {
return if (BuildConfig.TELEMETRY) {
ReleaseMetricController(
services,
isDataTelemetryEnabled,
isMarketingDataTelemetryEnabled
)
} else DebugMetricController()
}
}
}
@VisibleForTesting
internal class DebugMetricController(
private val logger: Logger = Logger()
) : MetricController {
override fun start(type: MetricServiceType) {
logger.debug("DebugMetricController: start")
}
override fun stop(type: MetricServiceType) {
logger.debug("DebugMetricController: stop")
}
override fun track(event: Event) {
logger.debug("DebugMetricController: track event: $event")
}
}
@VisibleForTesting
internal class ReleaseMetricController(
private val services: List<MetricsService>,
private val isDataTelemetryEnabled: () -> Boolean,
private val isMarketingDataTelemetryEnabled: () -> Boolean
) : MetricController {
private var initialized = mutableSetOf<MetricServiceType>()
init {
Facts.registerProcessor(object : FactProcessor {
override fun process(fact: Fact) {
fact.toEvent()?.also {
track(it)
}
}
})
}
override fun start(type: MetricServiceType) {
val isEnabled = isTelemetryEnabled(type)
val isInitialized = isInitialized(type)
if (!isEnabled || isInitialized) {
return
}
services
.filter { it.type == type }
.forEach { it.start() }
initialized.add(type)
}
override fun stop(type: MetricServiceType) {
val isEnabled = isTelemetryEnabled(type)
val isInitialized = isInitialized(type)
if (isEnabled || !isInitialized) {
return
}
services
.filter { it.type == type }
.forEach { it.stop() }
initialized.remove(type)
}
override fun track(event: Event) {
services
.filter { it.shouldTrack(event) }
.forEach {
val isEnabled = isTelemetryEnabled(it.type)
val isInitialized = isInitialized(it.type)
if (!isEnabled || !isInitialized) {
return@forEach
}
it.track(event)
}
}
private fun isInitialized(type: MetricServiceType): Boolean = initialized.contains(type)
private fun isTelemetryEnabled(type: MetricServiceType): Boolean = when (type) {
MetricServiceType.Data -> isDataTelemetryEnabled()
MetricServiceType.Marketing -> isMarketingDataTelemetryEnabled()
}
}