GleanMetricsService.kt 36.3 KB
Newer Older
1
2
3
/* 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/. */
4

5
6
7
package org.mozilla.fenix.components.metrics

import android.content.Context
8
9
10
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.search.ext.legacy
import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine
11
import mozilla.components.service.fxa.manager.SyncEnginesStorage
12
import mozilla.components.service.glean.Glean
13
import mozilla.components.service.glean.private.NoExtraKeys
14
import mozilla.components.support.base.log.logger.Logger
15
import org.mozilla.fenix.Config
16
import org.mozilla.fenix.GleanMetrics.AboutPage
17
import org.mozilla.fenix.GleanMetrics.Addons
18
import org.mozilla.fenix.GleanMetrics.AppTheme
19
import org.mozilla.fenix.GleanMetrics.Autoplay
Colin Lee's avatar
Colin Lee committed
20
import org.mozilla.fenix.GleanMetrics.BookmarksManagement
21
import org.mozilla.fenix.GleanMetrics.BrowserSearch
22
import org.mozilla.fenix.GleanMetrics.Collections
Colin Lee's avatar
Colin Lee committed
23
import org.mozilla.fenix.GleanMetrics.ContextMenu
24
import org.mozilla.fenix.GleanMetrics.ContextualHintTrackingProtection
25
import org.mozilla.fenix.GleanMetrics.CrashReporter
Colin Lee's avatar
Colin Lee committed
26
import org.mozilla.fenix.GleanMetrics.CustomTab
27
import org.mozilla.fenix.GleanMetrics.DownloadNotification
28
import org.mozilla.fenix.GleanMetrics.ErrorPage
29
30
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.FindInPage
31
import org.mozilla.fenix.GleanMetrics.History
32
import org.mozilla.fenix.GleanMetrics.LoginDialog
33
import org.mozilla.fenix.GleanMetrics.Logins
ekager's avatar
ekager committed
34
import org.mozilla.fenix.GleanMetrics.MasterPassword
35
import org.mozilla.fenix.GleanMetrics.MediaNotification
36
import org.mozilla.fenix.GleanMetrics.MediaState
37
import org.mozilla.fenix.GleanMetrics.Metrics
38
import org.mozilla.fenix.GleanMetrics.Onboarding
39
import org.mozilla.fenix.GleanMetrics.Pings
ekager's avatar
ekager committed
40
import org.mozilla.fenix.GleanMetrics.Pocket
41
import org.mozilla.fenix.GleanMetrics.Preferences
42
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode
43
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut
44
import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp
45
import org.mozilla.fenix.GleanMetrics.ReaderMode
46
import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine
47
import org.mozilla.fenix.GleanMetrics.SearchShortcuts
48
import org.mozilla.fenix.GleanMetrics.SearchSuggestions
49
import org.mozilla.fenix.GleanMetrics.SearchWidget
50
51
import org.mozilla.fenix.GleanMetrics.SyncAccount
import org.mozilla.fenix.GleanMetrics.SyncAuth
52
import org.mozilla.fenix.GleanMetrics.Tab
53
import org.mozilla.fenix.GleanMetrics.Tabs
54
import org.mozilla.fenix.GleanMetrics.TabsTray
55
import org.mozilla.fenix.GleanMetrics.Tip
56
import org.mozilla.fenix.GleanMetrics.ToolbarSettings
57
import org.mozilla.fenix.GleanMetrics.TopSites
58
import org.mozilla.fenix.GleanMetrics.TrackingProtection
59
import org.mozilla.fenix.GleanMetrics.UserSpecifiedSearchEngines
60
import org.mozilla.fenix.GleanMetrics.VoiceSearch
61
import org.mozilla.fenix.components.toolbar.ToolbarPosition
62
import org.mozilla.fenix.ext.components
63
import org.mozilla.fenix.ext.settings
64
import org.mozilla.fenix.utils.BrowsersCache
65

Jeff Boek's avatar
Jeff Boek committed
66
private class EventWrapper<T : Enum<T>>(
67
    private val recorder: ((Map<T, String>?) -> Unit),
68
    private val keyMapper: ((String) -> T)? = null
Jeff Boek's avatar
Jeff Boek committed
69
) {
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

    /**
     * Converts snake_case string to camelCase.
     */
    private fun String.asCamelCase(): String {
        val parts = split("_")
        val builder = StringBuilder()

        for ((index, part) in parts.withIndex()) {
            if (index == 0) {
                builder.append(part)
            } else {
                builder.append(part[0].toUpperCase())
                builder.append(part.substring(1))
            }
85
86
        }

87
88
89
        return builder.toString()
    }

Jeff Boek's avatar
Jeff Boek committed
90
91
    fun track(event: Event) {
        val extras = if (keyMapper != null) {
92
93
94
            event.extras?.mapKeys { (key) ->
                keyMapper.invoke(key.toString().asCamelCase())
            }
Jeff Boek's avatar
Jeff Boek committed
95
96
97
98
        } else {
            null
        }

99
        this.recorder(extras)
Jeff Boek's avatar
Jeff Boek committed
100
101
102
    }
}

103
private val Event.wrapper: EventWrapper<*>?
104
    get() = when (this) {
105
106
107
108
        is Event.OpenedApp -> EventWrapper(
            { Events.appOpened.record(it) },
            { Events.appOpenedKeys.valueOf(it) }
        )
109
        is Event.AppReceivedIntent -> EventWrapper(
110
111
112
            { Events.appReceivedIntent.record(it) },
            { Events.appReceivedIntentKeys.valueOf(it) }
        )
113
        is Event.AppAllStartup -> EventWrapper(
114
115
116
            { Events.appOpenedAllStartup.record(it) },
            { Events.appOpenedAllStartupKeys.valueOf(it) }
        )
117
118
119
120
121
122
123
124
125
        is Event.SearchBarTapped -> EventWrapper(
            { Events.searchBarTapped.record(it) },
            { Events.searchBarTappedKeys.valueOf(it) }
        )
        is Event.EnteredUrl -> EventWrapper(
            { Events.enteredUrl.record(it) },
            { Events.enteredUrlKeys.valueOf(it) }
        )
        is Event.PerformedSearch -> EventWrapper(
Jeff Boek's avatar
Jeff Boek committed
126
127
128
129
            {
                Metrics.searchCount[this.eventSource.countLabel].add(1)
                Events.performedSearch.record(it)
            },
130
131
            { Events.performedSearchKeys.valueOf(it) }
        )
132
133
134
135
136
137
138
139
140
141
        is Event.SearchWithAds -> EventWrapper<NoExtraKeys>(
            {
                BrowserSearch.withAds[label].add(1)
            }
        )
        is Event.SearchAdClicked -> EventWrapper<NoExtraKeys>(
            {
                BrowserSearch.adClicks[label].add(1)
            }
        )
142
143
144
145
146
        is Event.SearchInContent -> EventWrapper<NoExtraKeys>(
            {
                BrowserSearch.inContent[label].add(1)
            }
        )
147
148
149
150
        is Event.SearchShortcutSelected -> EventWrapper(
            { SearchShortcuts.selected.record(it) },
            { SearchShortcuts.selectedKeys.valueOf(it) }
        )
151
152
153
154
155
156
157
158
159
160
161
162
        is Event.LoginDialogPromptDisplayed -> EventWrapper<NoExtraKeys>(
            { LoginDialog.displayed.record(it) }
        )
        is Event.LoginDialogPromptCancelled -> EventWrapper<NoExtraKeys>(
            { LoginDialog.cancelled.record(it) }
        )
        is Event.LoginDialogPromptSave -> EventWrapper<NoExtraKeys>(
            { LoginDialog.saved.record(it) }
        )
        is Event.LoginDialogPromptNeverSave -> EventWrapper<NoExtraKeys>(
            { LoginDialog.neverSave.record(it) }
        )
163
164
165
166
167
168
169
170
171
        is Event.FindInPageOpened -> EventWrapper<NoExtraKeys>(
            { FindInPage.opened.record(it) }
        )
        is Event.FindInPageClosed -> EventWrapper<NoExtraKeys>(
            { FindInPage.closed.record(it) }
        )
        is Event.FindInPageSearchCommitted -> EventWrapper<NoExtraKeys>(
            { FindInPage.searchedPage.record(it) }
        )
172
173
174
175
        is Event.ContextMenuItemTapped -> EventWrapper(
            { ContextMenu.itemTapped.record(it) },
            { ContextMenu.itemTappedKeys.valueOf(it) }
        )
176
        is Event.CrashReporterOpened -> EventWrapper<NoExtraKeys>(
177
            { CrashReporter.opened.record(it) }
178
179
        )
        is Event.CrashReporterClosed -> EventWrapper(
180
            { CrashReporter.closed.record(it) },
181
182
            { CrashReporter.closedKeys.valueOf(it) }
        )
183
        is Event.BrowserMenuItemTapped -> EventWrapper(
184
            { Events.browserMenuAction.record(it) },
185
186
            { Events.browserMenuActionKeys.valueOf(it) }
        )
Colin Lee's avatar
Colin Lee committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
        is Event.OpenedBookmarkInNewTab -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.openInNewTab.record(it) }
        )
        is Event.OpenedBookmarksInNewTabs -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.openInNewTabs.record(it) }
        )
        is Event.OpenedBookmarkInPrivateTab -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.openInPrivateTab.record(it) }
        )
        is Event.OpenedBookmarksInPrivateTabs -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.openInPrivateTabs.record(it) }
        )
        is Event.EditedBookmark -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.edited.record(it) }
        )
        is Event.MovedBookmark -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.moved.record(it) }
        )
        is Event.RemoveBookmark -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.removed.record(it) }
        )
        is Event.RemoveBookmarks -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.multiRemoved.record(it) }
        )
        is Event.ShareBookmark -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.shared.record(it) }
        )
        is Event.CopyBookmark -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.copied.record(it) }
        )
        is Event.AddBookmarkFolder -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.folderAdd.record(it) }
        )
220
221
222
        is Event.RemoveBookmarkFolder -> EventWrapper<NoExtraKeys>(
            { BookmarksManagement.folderRemove.record(it) }
        )
223
224
225
226
227
228
229
230
231
        is Event.CustomTabsMenuOpened -> EventWrapper<NoExtraKeys>(
            { CustomTab.menu.record(it) }
        )
        is Event.CustomTabsActionTapped -> EventWrapper<NoExtraKeys>(
            { CustomTab.actionButton.record(it) }
        )
        is Event.CustomTabsClosed -> EventWrapper<NoExtraKeys>(
            { CustomTab.closed.record(it) }
        )
232
233
234
        is Event.UriOpened -> EventWrapper<NoExtraKeys>(
            { Events.totalUriCount.add(1) }
        )
235
        is Event.ErrorPageVisited -> EventWrapper(
236
            { ErrorPage.visitedError.record(it) },
237
238
            { ErrorPage.visitedErrorKeys.valueOf(it) }
        )
239
240
        is Event.SyncAuthOpened -> EventWrapper<NoExtraKeys>(
            { SyncAuth.opened.record(it) }
241
        )
242
243
        is Event.SyncAuthClosed -> EventWrapper<NoExtraKeys>(
            { SyncAuth.closed.record(it) }
244
        )
245
246
247
248
249
250
        is Event.SyncAuthUseEmail -> EventWrapper<NoExtraKeys>(
            { SyncAuth.useEmail.record(it) }
        )
        is Event.SyncAuthUseEmailProblem -> EventWrapper<NoExtraKeys>(
            { SyncAuth.useEmailProblem.record(it) }
        )
251
252
        is Event.SyncAuthSignIn -> EventWrapper<NoExtraKeys>(
            { SyncAuth.signIn.record(it) }
253
        )
Grisha Kruglov's avatar
Grisha Kruglov committed
254
255
256
257
258
259
260
261
262
263
264
265
        is Event.SyncAuthSignUp -> EventWrapper<NoExtraKeys>(
            { SyncAuth.signUp.record(it) }
        )
        is Event.SyncAuthPaired -> EventWrapper<NoExtraKeys>(
            { SyncAuth.paired.record(it) }
        )
        is Event.SyncAuthOtherExternal -> EventWrapper<NoExtraKeys>(
            { SyncAuth.otherExternal.record(it) }
        )
        is Event.SyncAuthRecovered -> EventWrapper<NoExtraKeys>(
            { SyncAuth.recovered.record(it) }
        )
266
267
268
        is Event.SyncAuthSignOut -> EventWrapper<NoExtraKeys>(
            { SyncAuth.signOut.record(it) }
        )
269
270
        is Event.SyncAuthScanPairing -> EventWrapper<NoExtraKeys>(
            { SyncAuth.scanPairing.record(it) }
271
        )
272
273
        is Event.SyncAccountOpened -> EventWrapper<NoExtraKeys>(
            { SyncAccount.opened.record(it) }
274
        )
275
276
277
        is Event.SyncAccountSyncNow -> EventWrapper<NoExtraKeys>(
            { SyncAccount.syncNow.record(it) }
        )
278
279
280
281
282
283
        is Event.SignInToSendTab -> EventWrapper<NoExtraKeys>(
            { SyncAccount.signInToSendTab.record(it) }
        )
        is Event.SendTab -> EventWrapper<NoExtraKeys>(
            { SyncAccount.sendTab.record(it) }
        )
284
285
286
287
        is Event.PreferenceToggled -> EventWrapper(
            { Events.preferenceToggled.record(it) },
            { Events.preferenceToggledKeys.valueOf(it) }
        )
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
        is Event.HistoryOpened -> EventWrapper<NoExtraKeys>(
            { History.opened.record(it) }
        )
        is Event.HistoryItemShared -> EventWrapper<NoExtraKeys>(
            { History.shared.record(it) }
        )
        is Event.HistoryItemOpened -> EventWrapper<NoExtraKeys>(
            { History.openedItem.record(it) }
        )
        is Event.HistoryItemRemoved -> EventWrapper<NoExtraKeys>(
            { History.removed.record(it) }
        )
        is Event.HistoryAllItemsRemoved -> EventWrapper<NoExtraKeys>(
            { History.removedAll.record(it) }
        )
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
        is Event.CollectionRenamed -> EventWrapper<NoExtraKeys>(
            { Collections.renamed.record(it) }
        )
        is Event.CollectionTabRestored -> EventWrapper<NoExtraKeys>(
            { Collections.tabRestored.record(it) }
        )
        is Event.CollectionAllTabsRestored -> EventWrapper<NoExtraKeys>(
            { Collections.allTabsRestored.record(it) }
        )
        is Event.CollectionTabRemoved -> EventWrapper<NoExtraKeys>(
            { Collections.tabRemoved.record(it) }
        )
        is Event.CollectionShared -> EventWrapper<NoExtraKeys>(
            { Collections.shared.record(it) }
        )
        is Event.CollectionRemoved -> EventWrapper<NoExtraKeys>(
            { Collections.removed.record(it) }
        )
        is Event.CollectionTabSelectOpened -> EventWrapper<NoExtraKeys>(
            { Collections.tabSelectOpened.record(it) }
        )
324
325
326
        is Event.ReaderModeAvailable -> EventWrapper<NoExtraKeys>(
            { ReaderMode.available.record(it) }
        )
327
328
329
        is Event.ReaderModeOpened -> EventWrapper<NoExtraKeys>(
            { ReaderMode.opened.record(it) }
        )
330
331
332
        is Event.ReaderModeClosed -> EventWrapper<NoExtraKeys>(
            { ReaderMode.closed.record(it) }
        )
333
334
335
        is Event.ReaderModeAppearanceOpened -> EventWrapper<NoExtraKeys>(
            { ReaderMode.appearance.record(it) }
        )
336
337
338
339
340
341
342
343
344
345
        is Event.CollectionTabLongPressed -> EventWrapper<NoExtraKeys>(
            { Collections.longPress.record(it) }
        )
        is Event.CollectionSaveButtonPressed -> EventWrapper(
            { Collections.saveButton.record(it) },
            { Collections.saveButtonKeys.valueOf(it) }
        )
        is Event.CollectionAddTabPressed -> EventWrapper<NoExtraKeys>(
            { Collections.addTabButton.record(it) }
        )
346
347
348
        is Event.CollectionRenamePressed -> EventWrapper<NoExtraKeys>(
            { Collections.renameButton.record(it) }
        )
349
350
351
352
353
354
355
356
        is Event.CollectionSaved -> EventWrapper(
            { Collections.saved.record(it) },
            { Collections.savedKeys.valueOf(it) }
        )
        is Event.CollectionTabsAdded -> EventWrapper(
            { Collections.tabsAdded.record(it) },
            { Collections.tabsAddedKeys.valueOf(it) }
        )
357
358
359
360
361
362
        is Event.SearchWidgetNewTabPressed -> EventWrapper<NoExtraKeys>(
            { SearchWidget.newTabButton.record(it) }
        )
        is Event.SearchWidgetVoiceSearchPressed -> EventWrapper<NoExtraKeys>(
            { SearchWidget.voiceButton.record(it) }
        )
363
364
365
366
367
368
        is Event.PrivateBrowsingSnackbarUndoTapped -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingMode.snackbarUndo.record(it) }
        )
        is Event.PrivateBrowsingNotificationTapped -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingMode.notificationTapped.record(it) }
        )
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
        is Event.PrivateBrowsingCreateShortcut -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.createShortcut.record(it) }
        )
        is Event.PrivateBrowsingAddShortcutCFR -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.cfrAddShortcut.record(it) }
        )
        is Event.PrivateBrowsingCancelCFR -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.cfrCancel.record(it) }
        )
        is Event.PrivateBrowsingPinnedShortcutPrivateTab -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.pinnedShortcutPriv.record(it) }
        )
        is Event.PrivateBrowsingStaticShortcutTab -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.staticShortcutTab.record(it) }
        )
        is Event.PrivateBrowsingStaticShortcutPrivateTab -> EventWrapper<NoExtraKeys>(
            { PrivateBrowsingShortcut.staticShortcutPriv.record(it) }
        )
387
388
        is Event.WhatsNewTapped -> EventWrapper<NoExtraKeys>(
            { Events.whatsNewTapped.record(it) }
389
        )
390
391
392
393
394
395
        is Event.TabMediaPlay -> EventWrapper<NoExtraKeys>(
            { Tab.mediaPlay.record(it) }
        )
        is Event.TabMediaPause -> EventWrapper<NoExtraKeys>(
            { Tab.mediaPause.record(it) }
        )
396
397
398
399
400
401
402
403
404
        is Event.MediaPlayState -> EventWrapper<NoExtraKeys>(
            { MediaState.play.record(it) }
        )
        is Event.MediaPauseState -> EventWrapper<NoExtraKeys>(
            { MediaState.pause.record(it) }
        )
        is Event.MediaStopState -> EventWrapper<NoExtraKeys>(
            { MediaState.stop.record(it) }
        )
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
        is Event.InAppNotificationDownloadOpen -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.inAppOpen.record(it) }
        )
        is Event.InAppNotificationDownloadTryAgain -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.inAppTryAgain.record(it) }
        )
        is Event.NotificationDownloadCancel -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.cancel.record(it) }
        )
        is Event.NotificationDownloadOpen -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.open.record(it) }
        )
        is Event.NotificationDownloadPause -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.pause.record(it) }
        )
        is Event.NotificationDownloadResume -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.resume.record(it) }
        )
        is Event.NotificationDownloadTryAgain -> EventWrapper<NoExtraKeys>(
            { DownloadNotification.tryAgain.record(it) }
        )
426
427
428
429
430
431
        is Event.NotificationMediaPlay -> EventWrapper<NoExtraKeys>(
            { MediaNotification.play.record(it) }
        )
        is Event.NotificationMediaPause -> EventWrapper<NoExtraKeys>(
            { MediaNotification.pause.record(it) }
        )
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
        is Event.TrackingProtectionTrackerList -> EventWrapper<NoExtraKeys>(
            { TrackingProtection.etpTrackerList.record(it) }
        )
        is Event.TrackingProtectionIconPressed -> EventWrapper<NoExtraKeys>(
            { TrackingProtection.etpShield.record(it) }
        )
        is Event.TrackingProtectionSettingsPanel -> EventWrapper<NoExtraKeys>(
            { TrackingProtection.panelSettings.record(it) }
        )
        is Event.TrackingProtectionSettings -> EventWrapper<NoExtraKeys>(
            { TrackingProtection.etpSettings.record(it) }
        )
        is Event.TrackingProtectionException -> EventWrapper<NoExtraKeys>(
            { TrackingProtection.exceptionAdded.record(it) }
        )
        is Event.TrackingProtectionSettingChanged -> EventWrapper(
            { TrackingProtection.etpSettingChanged.record(it) },
            { TrackingProtection.etpSettingChangedKeys.valueOf(it) }
        )
451
452
453
454
        is Event.OpenedLink -> EventWrapper(
            { Events.openedLink.record(it) },
            { Events.openedLinkKeys.valueOf(it) }
        )
455
456
457
458
459
460
461
462
463
464
465
466
        is Event.OpenLogins -> EventWrapper<NoExtraKeys>(
            { Logins.openLogins.record(it) }
        )
        is Event.OpenOneLogin -> EventWrapper<NoExtraKeys>(
            { Logins.openIndividualLogin.record(it) }
        )
        is Event.CopyLogin -> EventWrapper<NoExtraKeys>(
            { Logins.copyLogin.record(it) }
        )
        is Event.ViewLoginPassword -> EventWrapper<NoExtraKeys>(
            { Logins.viewPasswordLogin.record(it) }
        )
467
468
469
470
471
472
473
474
475
        is Event.DeleteLogin -> EventWrapper<NoExtraKeys>(
            { Logins.deleteSavedLogin.record(it) }
        )
        is Event.EditLogin -> EventWrapper<NoExtraKeys>(
            { Logins.openLoginEditor.record(it) }
        )
        is Event.EditLoginSave -> EventWrapper<NoExtraKeys>(
            { Logins.saveEditedLogin.record(it) }
        )
476
477
478
        is Event.PrivateBrowsingShowSearchSuggestions -> EventWrapper<NoExtraKeys>(
            { SearchSuggestions.enableInPrivate.record(it) }
        )
479
480
481
482
        is Event.ToolbarPositionChanged -> EventWrapper(
            { ToolbarSettings.changedPosition.record(it) },
            { ToolbarSettings.changedPositionKeys.valueOf(it) }
        )
483
484
485
486
487
488
        is Event.CustomEngineAdded -> EventWrapper<NoExtraKeys>(
            { UserSpecifiedSearchEngines.customEngineAdded.record(it) }
        )
        is Event.CustomEngineDeleted -> EventWrapper<NoExtraKeys>(
            { UserSpecifiedSearchEngines.customEngineDeleted.record(it) }
        )
489
490
491
492
        is Event.SaveLoginsSettingChanged -> EventWrapper(
            { Logins.saveLoginsSettingChanged.record(it) },
            { Logins.saveLoginsSettingChangedKeys.valueOf(it) }
        )
493
494
495
        is Event.TopSiteOpenDefault -> EventWrapper<NoExtraKeys>(
            { TopSites.openDefault.record(it) }
        )
496
497
498
499
500
501
        is Event.TopSiteOpenFrecent -> EventWrapper<NoExtraKeys>(
            { TopSites.openFrecency.record(it) }
        )
        is Event.TopSiteOpenPinned -> EventWrapper<NoExtraKeys>(
            { TopSites.openPinned.record(it) }
        )
502
503
504
505
506
507
508
509
510
        is Event.TopSiteOpenInNewTab -> EventWrapper<NoExtraKeys>(
            { TopSites.openInNewTab.record(it) }
        )
        is Event.TopSiteOpenInPrivateTab -> EventWrapper<NoExtraKeys>(
            { TopSites.openInPrivateTab.record(it) }
        )
        is Event.TopSiteRemoved -> EventWrapper<NoExtraKeys>(
            { TopSites.remove.record(it) }
        )
511
512
513
514
515
516
517
518
        is Event.TopSiteLongPress -> EventWrapper(
            { TopSites.longPress.record(it) },
            { TopSites.longPressKeys.valueOf(it) }
        )
        is Event.TopSiteSwipeCarousel -> EventWrapper(
            { TopSites.swipeCarousel.record(it) },
            { TopSites.swipeCarouselKeys.valueOf(it) }
        )
519
520
521
522
523
524
        is Event.SupportTapped -> EventWrapper<NoExtraKeys>(
            { AboutPage.supportTapped.record(it) }
        )
        is Event.PrivacyNoticeTapped -> EventWrapper<NoExtraKeys>(
            { AboutPage.privacyNoticeTapped.record(it) }
        )
ekager's avatar
ekager committed
525
526
527
528
529
530
        is Event.PocketTopSiteClicked -> EventWrapper<NoExtraKeys>(
            { Pocket.pocketTopSiteClicked.record(it) }
        )
        is Event.PocketTopSiteRemoved -> EventWrapper<NoExtraKeys>(
            { Pocket.pocketTopSiteRemoved.record(it) }
        )
531
532
533
534
        is Event.DarkThemeSelected -> EventWrapper(
            { AppTheme.darkThemeSelected.record(it) },
            { AppTheme.darkThemeSelectedKeys.valueOf(it) }
        )
535
536
537
        is Event.AddonsOpenInSettings -> EventWrapper<NoExtraKeys>(
            { Addons.openAddonsInSettings.record(it) }
        )
538
539
540
        is Event.AddonsOpenInToolbarMenu -> EventWrapper(
            { Addons.openAddonInToolbarMenu.record(it) },
            { Addons.openAddonInToolbarMenuKeys.valueOf(it) }
541
        )
542
543
544
545
546
547
548
549
550
551
552
553
        is Event.TipDisplayed -> EventWrapper(
            { Tip.displayed.record(it) },
            { Tip.displayedKeys.valueOf(it) }
        )
        is Event.TipPressed -> EventWrapper(
            { Tip.pressed.record(it) },
            { Tip.pressedKeys.valueOf(it) }
        )
        is Event.TipClosed -> EventWrapper(
            { Tip.closed.record(it) },
            { Tip.closedKeys.valueOf(it) }
        )
554
555
556
        is Event.VoiceSearchTapped -> EventWrapper<NoExtraKeys>(
            { VoiceSearch.tapped.record(it) }
        )
557
558
559
560
        is Event.TabCounterMenuItemTapped -> EventWrapper(
            { Events.tabCounterMenuAction.record(it) },
            { Events.tabCounterMenuActionKeys.valueOf(it) }
        )
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
        is Event.OnboardingPrivateBrowsing -> EventWrapper<NoExtraKeys>(
            { Onboarding.prefToggledPrivateBrowsing.record(it) }
        )
        is Event.OnboardingPrivacyNotice -> EventWrapper<NoExtraKeys>(
            { Onboarding.privacyNotice.record(it) }
        )
        is Event.OnboardingManualSignIn -> EventWrapper<NoExtraKeys>(
            { Onboarding.fxaManualSignin.record(it) }
        )
        is Event.OnboardingAutoSignIn -> EventWrapper<NoExtraKeys>(
            { Onboarding.fxaAutoSignin.record(it) }
        )
        is Event.OnboardingFinish -> EventWrapper<NoExtraKeys>(
            { Onboarding.finish.record(it) }
        )
        is Event.OnboardingTrackingProtection -> EventWrapper(
            { Onboarding.prefToggledTrackingProt.record(it) },
            { Onboarding.prefToggledTrackingProtKeys.valueOf(it) }
        )
        is Event.OnboardingThemePicker -> EventWrapper(
            { Onboarding.prefToggledThemePicker.record(it) },
            { Onboarding.prefToggledThemePickerKeys.valueOf(it) }
        )
        is Event.OnboardingToolbarPosition -> EventWrapper(
            { Onboarding.prefToggledToolbarPosition.record(it) },
            { Onboarding.prefToggledToolbarPositionKeys.valueOf(it) }
        )
588

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
        is Event.ContextualHintETPDisplayed -> EventWrapper<NoExtraKeys>(
            { ContextualHintTrackingProtection.display.record(it) }
        )

        is Event.ContextualHintETPDismissed -> EventWrapper<NoExtraKeys>(
            { ContextualHintTrackingProtection.dismiss.record(it) }
        )

        is Event.ContextualHintETPInsideTap -> EventWrapper<NoExtraKeys>(
            { ContextualHintTrackingProtection.insideTap.record(it) }
        )

        is Event.ContextualHintETPOutsideTap -> EventWrapper<NoExtraKeys>(
            { ContextualHintTrackingProtection.outsideTap.record(it) }
        )

605
        is Event.TabsTrayOpened -> EventWrapper<NoExtraKeys>(
606
            { TabsTray.opened.record(it) }
607
608
        )
        is Event.TabsTrayClosed -> EventWrapper<NoExtraKeys>(
609
            { TabsTray.closed.record(it) }
610
611
        )
        is Event.OpenedExistingTab -> EventWrapper<NoExtraKeys>(
612
            { TabsTray.openedExistingTab.record(it) }
613
614
        )
        is Event.ClosedExistingTab -> EventWrapper<NoExtraKeys>(
615
            { TabsTray.closedExistingTab.record(it) }
616
617
        )
        is Event.TabsTrayPrivateModeTapped -> EventWrapper<NoExtraKeys>(
618
            { TabsTray.privateModeTapped.record(it) }
619
620
        )
        is Event.TabsTrayNormalModeTapped -> EventWrapper<NoExtraKeys>(
621
            { TabsTray.normalModeTapped.record(it) }
622
623
        )
        is Event.NewTabTapped -> EventWrapper<NoExtraKeys>(
624
            { TabsTray.newTabTapped.record(it) }
625
626
        )
        is Event.NewPrivateTabTapped -> EventWrapper<NoExtraKeys>(
627
            { TabsTray.newPrivateTabTapped.record(it) }
628
629
        )
        is Event.TabsTrayMenuOpened -> EventWrapper<NoExtraKeys>(
630
            { TabsTray.menuOpened.record(it) }
631
632
        )
        is Event.TabsTraySaveToCollectionPressed -> EventWrapper<NoExtraKeys>(
633
            { TabsTray.saveToCollection.record(it) }
634
635
        )
        is Event.TabsTrayShareAllTabsPressed -> EventWrapper<NoExtraKeys>(
636
            { TabsTray.shareAllTabs.record(it) }
637
638
        )
        is Event.TabsTrayCloseAllTabsPressed -> EventWrapper<NoExtraKeys>(
639
640
641
642
643
644
645
646
            { TabsTray.closeAllTabs.record(it) }
        )
        Event.AutoPlaySettingVisited -> EventWrapper<NoExtraKeys>(
            { Autoplay.visitedSetting.record(it) }
        )
        is Event.AutoPlaySettingChanged -> EventWrapper(
            { Autoplay.settingChanged.record(it) },
            { Autoplay.settingChangedKeys.valueOf(it) }
647
        )
648
649
650
651
652
653
654
655
656
657
658
659
660
661
        is Event.ProgressiveWebAppOpenFromHomescreenTap -> EventWrapper<NoExtraKeys>(
            { ProgressiveWebApp.homescreenTap.record(it) }
        )
        is Event.ProgressiveWebAppInstallAsShortcut -> EventWrapper<NoExtraKeys>(
            { ProgressiveWebApp.installTap.record(it) }
        )
        is Event.ProgressiveWebAppForeground -> EventWrapper(
            { ProgressiveWebApp.foreground.record(it) },
            { ProgressiveWebApp.foregroundKeys.valueOf(it) }
        )
        is Event.ProgressiveWebAppBackground -> EventWrapper(
            { ProgressiveWebApp.background.record(it) },
            { ProgressiveWebApp.backgroundKeys.valueOf(it) }
        )
662

ekager's avatar
ekager committed
663
664
665
666
667
668
        Event.MasterPasswordMigrationDisplayed -> EventWrapper<NoExtraKeys>(
            { MasterPassword.displayed.record(it) }
        )
        Event.MasterPasswordMigrationSuccess -> EventWrapper<NoExtraKeys>(
            { MasterPassword.migration.record(it) }
        )
669
670
671
        Event.TabSettingsOpened -> EventWrapper<NoExtraKeys>(
            { Tabs.settingOpened.record(it) }
        )
ekager's avatar
ekager committed
672

673
674
675
676
677
678
679
        // Don't record other events in Glean:
        is Event.AddBookmark -> null
        is Event.OpenedBookmark -> null
        is Event.OpenedAppFirstRun -> null
        is Event.InteractWithSearchURLArea -> null
        is Event.ClearedPrivateData -> null
        is Event.DismissedOnboarding -> null
680
        is Event.FennecToFenixMigrated -> null
681
        is Event.AddonInstalled -> null
682
        is Event.SearchWidgetInstalled -> null
683
        is Event.ChangedToDefaultBrowser -> null
684
        is Event.SyncAuthFromSharedReuse, Event.SyncAuthFromSharedCopy -> null
685
686
    }

687
688
class GleanMetricsService(
    private val context: Context,
689
    private val store: Lazy<BrowserStore>,
690
691
692
    private val browsersCache: BrowsersCache = BrowsersCache,
    private val mozillaProductDetector: MozillaProductDetector = MozillaProductDetector
) : MetricsService {
693
694
    override val type = MetricServiceType.Data

695
    private val logger = Logger("GleanMetricsService")
696
697
    private var initialized = false

698
    private val activationPing = ActivationPing(context)
699
    private val installationPing = FirstSessionPing(context)
700

701
    override fun start() {
702
703
        logger.debug("Enabling Glean.")
        // Initialization of Glean already happened in FenixApplication.
704
        Glean.setUploadEnabled(true)
705

706
        if (initialized) return
707
        initialized = true
708

709
710
        // The code below doesn't need to execute immediately, so we'll add them to the visual
        // completeness task queue to be run later.
711
        context.components.performance.visualCompletenessQueue.queue.runIfReadyOrQueue {
712
713
714
715
716
            // We have to initialize Glean *on* the main thread, because it registers lifecycle
            // observers. However, the activation ping must be sent *off* of the main thread,
            // because it calls Google ad APIs that must be called *off* of the main thread.
            // These two things actually happen in parallel, but that should be ok because Glean
            // can handle events being recorded before it's initialized.
717
            Glean.registerPings(Pings)
718

719
720
            // setStartupMetrics is not a fast function. It does not need to be done before we can consider
            // ourselves initialized. So, let's do it, well, later.
721
722
            setStartupMetrics()
        }
723
    }
724

725
    internal fun setStartupMetrics() {
726
        setPreferenceMetrics()
727
        with(Metrics) {
728
729
            defaultBrowser.set(browsersCache.all(context).isDefaultBrowser)
            mozillaProductDetector.getMozillaBrowserDefault(context)?.also {
730
731
                defaultMozBrowser.set(it)
            }
732
733
734
735
736
737
738
739

            distributionId.set(
                when (Config.channel.isMozillaOnline) {
                    true -> "MozillaOnline"
                    false -> "Mozilla"
                }
            )

740
            mozillaProducts.set(mozillaProductDetector.getInstalledMozillaProducts(context))
741

742
743
744
745
746
747
748
            adjustCampaign.set(context.settings().adjustCampaignId)
            adjustAdGroup.set(context.settings().adjustAdGroup)
            adjustCreative.set(context.settings().adjustCreative)
            adjustNetwork.set(context.settings().adjustNetwork)

            searchWidgetInstalled.set(context.settings().searchWidgetInstalled)

749
750
751
752
753
754
            val openTabsCount = context.settings().openTabsCount
            hasOpenTabs.set(openTabsCount > 0)
            if (openTabsCount > 0) {
                tabsOpenCount.add(openTabsCount)
            }

755
756
757
758
759
760
761
            val topSitesSize = context.settings().topSitesSize
            hasTopSites.set(topSitesSize > 0)
            if (topSitesSize > 0) {
                topSitesCount.add(topSitesSize)
            }

            toolbarPosition.set(
762
763
764
                when (context.settings().toolbarPosition) {
                    ToolbarPosition.BOTTOM -> Event.ToolbarPositionChanged.Position.BOTTOM.name
                    ToolbarPosition.TOP -> Event.ToolbarPositionChanged.Position.TOP.name
765
766
                }
            )
767
768
769

            tabViewSetting.set(context.settings().getTabViewPingString())
            closeTabSetting.set(context.settings().getTabTimeoutPingString())
770
771
        }

772
773
774
775
776
777
778
779
        store.value.waitForSelectedOrDefaultSearchEngine { searchEngine ->
            if (searchEngine != null) {
                SearchDefaultEngine.apply {
                    code.set(searchEngine.id)
                    name.set(searchEngine.name)
                    submissionUrl.set(searchEngine.legacy().buildSearchUrl(""))
                }
            }
780

781
782
            activationPing.checkAndSend()
            installationPing.checkAndSend()
783
784
785
786
        }
    }

    private fun setPreferenceMetrics() {
787
788
789
        // We purposefully make all of our preferences the string_list format to make data analysis
        // simpler. While it makes things like booleans a bit more complicated, it means all our
        // preferences can be analyzed with the same dashboard and compared.
790
        with(Preferences) {
791
792
793
            showSearchSuggestions.set(context.settings().shouldShowSearchSuggestions.toStringList())
            remoteDebugging.set(context.settings().isRemoteDebuggingEnabled.toStringList())
            telemetry.set(context.settings().isTelemetryEnabled.toStringList())
794
            searchBrowsingHistory.set(context.settings().shouldShowHistorySuggestions.toStringList())
795
796
797
798
799
            searchBookmarks.set(context.settings().shouldShowBookmarkSuggestions.toStringList())
            showClipboardSuggestions.set(context.settings().shouldShowClipboardSuggestions.toStringList())
            showSearchShortcuts.set(context.settings().shouldShowSearchShortcuts.toStringList())
            openLinksInAPrivateTab.set(context.settings().openLinksInAPrivateTab.toStringList())
            searchSuggestionsPrivate.set(context.settings().shouldShowSearchSuggestionsInPrivate.toStringList())
800
801
            showVoiceSearch.set(context.settings().shouldShowVoiceSearch.toStringList())
            openLinksInApp.set(context.settings().openLinksInExternalApp.toStringList())
802
803
804
805
806
807
808
809
810
811
812

            val isLoggedIn =
                context.components.backgroundServices.accountManager.accountProfile() != null
            sync.set(isLoggedIn.toStringList())

            val syncedItems = SyncEnginesStorage(context).getStatus().entries.filter {
                it.value
            }.map { it.key.nativeName }

            syncItems.set(syncedItems)

813
814
815
816
817
818
819
820
821
822
823
            val toolbarPositionSelection =
                if (context.settings().shouldUseFixedTopToolbar) {
                    "fixed_top"
                } else if (context.settings().shouldUseBottomToolbar) {
                    "bottom"
                } else {
                    "top"
                }

            toolbarPosition.set(listOf(toolbarPositionSelection))

824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
            val etpSelection =
                if (!context.settings().shouldUseTrackingProtection) {
                    ""
                } else if (context.settings().useStandardTrackingProtection) {
                    "standard"
                } else if (context.settings().useStrictTrackingProtection) {
                    "strict"
                } else if (context.settings().useCustomTrackingProtection) {
                    "custom"
                } else {
                    ""
                }

            trackingProtection.set(listOf(etpSelection))

            val accessibilitySelection = mutableListOf<String>()

841
842
843
            if (context.settings().switchServiceIsEnabled) {
                accessibilitySelection.add("switch")
            }
844
845
846
847
848
849

            if (context.settings().touchExplorationIsEnabled) {
                accessibilitySelection.add("touch exploration")
            }

            accessibilityServices.set(accessibilitySelection.toList())
850

851
852
853
854
855
856
857
858
859
            val themeSelection =
                if (context.settings().shouldUseLightTheme) {
                    "light"
                } else if (context.settings().shouldUseDarkTheme) {
                    "dark"
                } else if (context.settings().shouldFollowDeviceTheme) {
                    "system"
                } else if (context.settings().shouldUseAutoBatteryTheme) {
                    "battery"
860
                } else {
861
                    ""
862
                }
863

864
            theme.set(listOf(themeSelection))
865
        }
866
867
    }

868
869
870
871
    override fun stop() {
        Glean.setUploadEnabled(false)
    }

872
    override fun track(event: Event) {
Jeff Boek's avatar
Jeff Boek committed
873
        event.wrapper?.track(event)
874
    }
875

876
    override fun shouldTrack(event: Event): Boolean {
877
        return event.wrapper != null
878
879
    }
}
880
881
882
883
884

// Helper function for making our booleans fit into the string list formatting
fun Boolean.toStringList(): List<String> {
    return listOf(this.toString())
}