Closes #18948: Update tabstray telemetry (#19004)

upstream-sync
Roger Yang 3 years ago committed by GitHub
parent cea869c276
commit 8246f81c97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2473,6 +2473,19 @@ tabs_tray:
notification_emails:
- fenix-core@mozilla.com
expires: "2021-08-01"
synced_mode_tapped:
type: event
description: |
A user switched to synced mode
bugs:
- https://github.com/mozilla-mobile/fenix/issues/18948
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/19004
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
expires: "2022-08-01"
new_tab_tapped:
type: event
description: |

@ -182,6 +182,7 @@ sealed class Event {
object ClosedExistingTab : Event()
object TabsTrayPrivateModeTapped : Event()
object TabsTrayNormalModeTapped : Event()
object TabsTraySyncedModeTapped : Event()
object NewTabTapped : Event()
object NewPrivateTabTapped : Event()
object TabsTrayMenuOpened : Event()

@ -674,6 +674,9 @@ private val Event.wrapper: EventWrapper<*>?
is Event.TabsTrayNormalModeTapped -> EventWrapper<NoExtraKeys>(
{ TabsTray.normalModeTapped.record(it) }
)
is Event.TabsTraySyncedModeTapped -> EventWrapper<NoExtraKeys>(
{ TabsTray.syncedModeTapped.record(it) }
)
is Event.NewTabTapped -> EventWrapper<NoExtraKeys>(
{ TabsTray.newTabTapped.record(it) }
)

@ -9,8 +9,11 @@ import com.google.android.material.tabs.TabLayout
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.base.feature.LifecycleAwareFeature
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.tabstray.TrayPagerAdapter.Companion.POSITION_NORMAL_TABS
import org.mozilla.fenix.tabstray.TrayPagerAdapter.Companion.POSITION_PRIVATE_TABS
import org.mozilla.fenix.utils.Do
/**
* Selected the selected pager depending on the [BrowserStore] state and synchronizes user actions
@ -20,10 +23,11 @@ class TabLayoutMediator(
private val tabLayout: TabLayout,
interactor: TabsTrayInteractor,
private val browserStore: BrowserStore,
trayStore: TabsTrayStore
trayStore: TabsTrayStore,
private val metrics: MetricController
) : LifecycleAwareFeature {
private val observer = TabLayoutObserver(interactor, trayStore)
private val observer = TabLayoutObserver(interactor, trayStore, metrics)
/**
* Start observing the [TabLayout] and select the current tab for initial state.
@ -57,7 +61,8 @@ class TabLayoutMediator(
*/
internal class TabLayoutObserver(
private val interactor: TabsTrayInteractor,
private val trayStore: TabsTrayStore
private val trayStore: TabsTrayStore,
private val metrics: MetricController
) : TabLayout.OnTabSelectedListener {
private var initialScroll = true
@ -74,6 +79,12 @@ internal class TabLayoutObserver(
interactor.setCurrentTrayPosition(tab.position, animate)
trayStore.dispatch(TabsTrayAction.PageSelected(tab.toPage()))
Do exhaustive when (tab.toPage()) {
Page.NormalTabs -> metrics.track(Event.TabsTrayNormalModeTapped)
Page.PrivateTabs -> metrics.track(Event.TabsTrayPrivateModeTapped)
Page.SyncedTabs -> metrics.track(Event.TabsTraySyncedModeTapped)
}
}
override fun onTabUnselected(tab: TabLayout.Tab) = Unit

@ -51,6 +51,7 @@ class DefaultTabsTrayController(
"DefaultTabTrayController.onNewTabTapped",
startTime
)
sendNewTabEvent(isPrivate)
}
override fun onSyncStarted() {
@ -68,4 +69,14 @@ class DefaultTabsTrayController(
store.dispatch(TabsTrayAction.SyncCompleted)
}
}
private fun sendNewTabEvent(isPrivateModeSelected: Boolean) {
val eventToSend = if (isPrivateModeSelected) {
Event.NewPrivateTabTapped
} else {
Event.NewTabTapped
}
metrics.track(eventToSend)
}
}

@ -92,6 +92,8 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
super.onViewCreated(view, savedInstanceState)
val activity = activity as HomeActivity
requireComponents.analytics.metrics.track(Event.TabsTrayOpened)
val navigationInteractor =
DefaultNavigationInteractor(
context = requireContext(),
@ -99,8 +101,8 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
browserStore = requireComponents.core.store,
navController = findNavController(),
metrics = requireComponents.analytics.metrics,
dismissTabTray = ::dismissAllowingStateLoss,
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome,
dismissTabTray = ::dismissTabsTray,
dismissTabTrayAndNavigateHome = ::dismissTabsTrayAndNavigateHome,
bookmarksUseCase = requireComponents.useCases.bookmarksUseCases,
collectionStorage = requireComponents.core.tabCollectionStorage
)
@ -144,8 +146,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
behavior.addBottomSheetCallback(
TraySheetBehaviorCallback(
behavior,
navigationInteractor,
requireComponents.analytics.metrics
navigationInteractor
)
)
@ -167,7 +168,8 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
tabLayout = tab_layout,
interactor = this,
browserStore = requireComponents.core.store,
trayStore = tabsTrayStore
trayStore = tabsTrayStore,
metrics = requireComponents.analytics.metrics
), owner = this,
view = view
)
@ -233,7 +235,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
}
override fun navigateToBrowser() {
dismissAllowingStateLoss()
dismissTabsTray()
val navController = findNavController()
@ -299,10 +301,15 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
private val homeViewModel: HomeScreenViewModel by activityViewModels()
private fun dismissTabTrayAndNavigateHome(sessionId: String) {
private fun dismissTabsTrayAndNavigateHome(sessionId: String) {
homeViewModel.sessionToDelete = sessionId
val directions = NavGraphDirections.actionGlobalHome()
findNavController().navigateBlockingForAsyncNavGraph(directions)
dismissTabsTray()
}
private fun dismissTabsTray() {
dismissAllowingStateLoss()
requireComponents.analytics.metrics.track(Event.TabsTrayClosed)
}
}

@ -85,6 +85,7 @@ class TabsTrayInfoBannerBinding(
}
) {
navigationInteractor.onTabSettingsClicked()
metrics?.track(Event.TabsTrayCfrTapped)
settings.shouldShowGridViewBanner = false
}
} else {
@ -110,6 +111,7 @@ class TabsTrayInfoBannerBinding(
}
) {
navigationInteractor.onTabSettingsClicked()
metrics?.track(Event.TabsTrayCfrTapped)
settings.shouldShowAutoCloseTabsBanner = false
}
} else {

@ -214,6 +214,7 @@ abstract class TabsTrayViewHolder(
itemView.setOnLongClickListener {
if (holder.selectedItems.isEmpty()) {
metrics.track(Event.CollectionTabLongPressed)
interactor.select(item)
true
} else {

@ -8,18 +8,14 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
class TraySheetBehaviorCallback(
private val behavior: BottomSheetBehavior<ConstraintLayout>,
private val trayInteractor: NavigationInteractor,
private val metrics: MetricController
private val trayInteractor: NavigationInteractor
) : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == STATE_HIDDEN) {
metrics.track(Event.TabsTrayClosed)
trayInteractor.onTabTrayDismissed()
} else if (newState == BottomSheetBehavior.STATE_HALF_EXPANDED) {
// We only support expanded and collapsed states.

@ -26,6 +26,8 @@ import org.mozilla.fenix.GleanMetrics.History
import org.mozilla.fenix.GleanMetrics.Metrics
import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine
import org.mozilla.fenix.GleanMetrics.SyncedTabs
import org.mozilla.fenix.GleanMetrics.TabsTray
import org.mozilla.fenix.GleanMetrics.TabsTrayCfr
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.BrowsersCache
@ -258,4 +260,67 @@ class GleanMetricsServiceTest {
assertEquals(1, events[0].extra!!.size)
assertEquals("123", events[0].extra!!["addon_id"])
}
@Test
fun `TabsTray events are correctly recorded`() {
assertFalse(TabsTray.opened.testHasValue())
gleanService.track(Event.TabsTrayOpened)
assertTrue(TabsTray.opened.testHasValue())
assertFalse(TabsTray.closed.testHasValue())
gleanService.track(Event.TabsTrayClosed)
assertTrue(TabsTray.closed.testHasValue())
assertFalse(TabsTray.openedExistingTab.testHasValue())
gleanService.track(Event.OpenedExistingTab)
assertTrue(TabsTray.openedExistingTab.testHasValue())
assertFalse(TabsTray.closedExistingTab.testHasValue())
gleanService.track(Event.ClosedExistingTab)
assertTrue(TabsTray.closedExistingTab.testHasValue())
assertFalse(TabsTray.privateModeTapped.testHasValue())
gleanService.track(Event.TabsTrayPrivateModeTapped)
assertTrue(TabsTray.privateModeTapped.testHasValue())
assertFalse(TabsTray.normalModeTapped.testHasValue())
gleanService.track(Event.TabsTrayNormalModeTapped)
assertTrue(TabsTray.normalModeTapped.testHasValue())
assertFalse(TabsTray.syncedModeTapped.testHasValue())
gleanService.track(Event.TabsTraySyncedModeTapped)
assertTrue(TabsTray.syncedModeTapped.testHasValue())
assertFalse(TabsTray.newTabTapped.testHasValue())
gleanService.track(Event.NewTabTapped)
assertTrue(TabsTray.newTabTapped.testHasValue())
assertFalse(TabsTray.newPrivateTabTapped.testHasValue())
gleanService.track(Event.NewPrivateTabTapped)
assertTrue(TabsTray.newPrivateTabTapped.testHasValue())
assertFalse(TabsTray.menuOpened.testHasValue())
gleanService.track(Event.TabsTrayMenuOpened)
assertTrue(TabsTray.menuOpened.testHasValue())
assertFalse(TabsTray.saveToCollection.testHasValue())
gleanService.track(Event.TabsTraySaveToCollectionPressed)
assertTrue(TabsTray.saveToCollection.testHasValue())
assertFalse(TabsTray.shareAllTabs.testHasValue())
gleanService.track(Event.TabsTrayShareAllTabsPressed)
assertTrue(TabsTray.shareAllTabs.testHasValue())
assertFalse(TabsTray.closeAllTabs.testHasValue())
gleanService.track(Event.TabsTrayCloseAllTabsPressed)
assertTrue(TabsTray.closeAllTabs.testHasValue())
assertFalse(TabsTrayCfr.dismiss.testHasValue())
gleanService.track(Event.TabsTrayCfrDismissed)
assertTrue(TabsTrayCfr.dismiss.testHasValue())
assertFalse(TabsTrayCfr.goToSettings.testHasValue())
gleanService.track(Event.TabsTrayCfrTapped)
assertTrue(TabsTrayCfr.goToSettings.testHasValue())
}
}

@ -26,7 +26,7 @@ class TabLayoutMediatorTest {
val store = createStore("123")
val tabLayout: TabLayout = mockk(relaxed = true)
val tab: TabLayout.Tab = mockk(relaxed = true)
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk())
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk(), mockk())
every { tabLayout.getTabAt(POSITION_NORMAL_TABS) }.answers { tab }
@ -40,7 +40,7 @@ class TabLayoutMediatorTest {
val store = createStore("456")
val tabLayout: TabLayout = mockk(relaxed = true)
val tab: TabLayout.Tab = mockk(relaxed = true)
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk())
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk(), mockk())
every { tabLayout.getTabAt(POSITION_PRIVATE_TABS) }.answers { tab }
@ -53,7 +53,7 @@ class TabLayoutMediatorTest {
fun `lifecycle methods adds and removes observer`() {
val store = createStore("456")
val tabLayout: TabLayout = mockk(relaxed = true)
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk())
val mediator = TabLayoutMediator(tabLayout, mockk(relaxed = true), store, mockk(), mockk())
mediator.start()

@ -13,20 +13,24 @@ import mozilla.components.support.test.middleware.CaptureActionsMiddleware
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
class TabLayoutObserverTest {
private val interactor = mockk<TabsTrayInteractor>(relaxed = true)
private lateinit var store: TabsTrayStore
private lateinit var metrics: MetricController
private val middleware = CaptureActionsMiddleware<TabsTrayState, TabsTrayAction>()
@Before
fun setup() {
store = TabsTrayStore(middlewares = listOf(middleware))
metrics = mockk(relaxed = true)
}
@Test
fun `WHEN tab is selected THEN notify the interactor`() {
val observer = TabLayoutObserver(interactor, store)
val observer = TabLayoutObserver(interactor, store, metrics)
val tab = mockk<TabLayout.Tab>()
every { tab.position } returns 1
@ -35,25 +39,54 @@ class TabLayoutObserverTest {
store.waitUntilIdle()
verify { interactor.setCurrentTrayPosition(1, false) }
verify { metrics.track(Event.TabsTrayPrivateModeTapped) }
middleware.assertLastAction(TabsTrayAction.PageSelected::class) {
assertTrue(it.page == Page.PrivateTabs)
}
every { tab.position } returns 0
observer.onTabSelected(tab)
store.waitUntilIdle()
verify { interactor.setCurrentTrayPosition(0, true) }
verify { metrics.track(Event.TabsTrayNormalModeTapped) }
middleware.assertLastAction(TabsTrayAction.PageSelected::class) {
assertTrue(it.page == Page.NormalTabs)
}
every { tab.position } returns 2
observer.onTabSelected(tab)
store.waitUntilIdle()
verify { interactor.setCurrentTrayPosition(2, true) }
verify { metrics.track(Event.TabsTraySyncedModeTapped) }
middleware.assertLastAction(TabsTrayAction.PageSelected::class) {
assertTrue(it.page == Page.SyncedTabs)
}
}
@Test
fun `WHEN observer is first started THEN do not smooth scroll`() {
val store = TabsTrayStore()
val observer = TabLayoutObserver(interactor, store)
val observer = TabLayoutObserver(interactor, store, metrics)
val tab = mockk<TabLayout.Tab>()
every { tab.position } returns 1
observer.onTabSelected(tab)
verify { interactor.setCurrentTrayPosition(1, false) }
verify { metrics.track(Event.TabsTrayPrivateModeTapped) }
observer.onTabSelected(tab)
verify { interactor.setCurrentTrayPosition(1, true) }
verify { metrics.track(Event.TabsTrayPrivateModeTapped) }
}
}

@ -22,6 +22,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.tabstray.TabsTrayInfoBannerBinding.Companion.TAB_COUNT_SHOW_CFR
@ -100,9 +101,11 @@ class TabsTrayInfoBannerTest {
assert(view.visibility == VISIBLE)
binding.banner?.actionToPerform?.invoke()
verify { interactor.onTabSettingsClicked() }
verify(exactly = 1) { interactor.onTabSettingsClicked() }
assert(!settings.shouldShowGridViewBanner)
assert(settings.shouldShowAutoCloseTabsBanner)
verify(exactly = 1) { metrics.track(Event.TabsTrayCfrTapped) }
verify(exactly = 0) { metrics.track(Event.TabsTrayCfrDismissed) }
}
@Test
@ -130,9 +133,11 @@ class TabsTrayInfoBannerTest {
assert(view.visibility == VISIBLE)
binding.banner?.actionToPerform?.invoke()
verify { interactor.onTabSettingsClicked() }
verify(exactly = 1) { interactor.onTabSettingsClicked() }
assert(!settings.shouldShowGridViewBanner)
assert(!settings.shouldShowAutoCloseTabsBanner)
verify(exactly = 1) { metrics.track(Event.TabsTrayCfrTapped) }
verify(exactly = 0) { metrics.track(Event.TabsTrayCfrDismissed) }
}
@Test
@ -162,6 +167,8 @@ class TabsTrayInfoBannerTest {
verify(exactly = 0) { interactor.onTabSettingsClicked() }
assert(!settings.shouldShowGridViewBanner)
assert(settings.shouldShowAutoCloseTabsBanner)
verify(exactly = 0) { metrics.track(Event.TabsTrayCfrTapped) }
verify(exactly = 1) { metrics.track(Event.TabsTrayCfrDismissed) }
}
@Test
@ -191,6 +198,8 @@ class TabsTrayInfoBannerTest {
verify(exactly = 0) { interactor.onTabSettingsClicked() }
assert(settings.shouldShowGridViewBanner)
assert(!settings.shouldShowAutoCloseTabsBanner)
verify(exactly = 0) { metrics.track(Event.TabsTrayCfrTapped) }
verify(exactly = 1) { metrics.track(Event.TabsTrayCfrDismissed) }
}
@Test

@ -16,27 +16,23 @@ import io.mockk.Called
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
class TraySheetBehaviorCallbackTest {
@Test
fun `WHEN state is hidden THEN invoke interactor`() {
val interactor = mockk<NavigationInteractor>(relaxed = true)
val metrics = mockk<MetricController>(relaxed = true)
val callback = TraySheetBehaviorCallback(mockk(), interactor, metrics)
val callback = TraySheetBehaviorCallback(mockk(), interactor)
callback.onStateChanged(mockk(), STATE_HIDDEN)
verify { interactor.onTabTrayDismissed() }
verify { metrics.track(Event.TabsTrayClosed) }
}
@Test
fun `WHEN state is half-expanded THEN close the tray`() {
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
val callback = TraySheetBehaviorCallback(behavior, mockk(), mockk())
val callback = TraySheetBehaviorCallback(behavior, mockk())
callback.onStateChanged(mockk(), STATE_HALF_EXPANDED)
@ -47,9 +43,7 @@ class TraySheetBehaviorCallbackTest {
fun `WHEN other states are invoked THEN do nothing`() {
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
val interactor = mockk<NavigationInteractor>(relaxed = true)
val metrics = mockk<MetricController>(relaxed = true)
val callback = TraySheetBehaviorCallback(behavior, interactor, metrics)
val callback = TraySheetBehaviorCallback(behavior, interactor)
callback.onStateChanged(mockk(), STATE_COLLAPSED)
callback.onStateChanged(mockk(), STATE_DRAGGING)
@ -58,6 +52,5 @@ class TraySheetBehaviorCallbackTest {
verify { behavior wasNot Called }
verify { interactor wasNot Called }
verify { metrics wasNot Called }
}
}

@ -242,6 +242,7 @@ In addition to those built-in metrics, the following metrics are added to the pi
| tabs_tray.private_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to private mode |[mozilla-mobile/fenix#12036](https://github.com/mozilla-mobile/fenix/pull/12036), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 |
| tabs_tray.save_to_collection |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the save to collection button in the tabs tray |[mozilla-mobile/fenix#12036](https://github.com/mozilla-mobile/fenix/pull/12036), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 |
| tabs_tray.share_all_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the share all tabs button in the three dot menu within the tabs tray |[mozilla-mobile/fenix#12036](https://github.com/mozilla-mobile/fenix/pull/12036), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 |
| tabs_tray.synced_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to synced mode |[mozilla-mobile/fenix#19004](https://github.com/mozilla-mobile/fenix/pull/19004)||2022-08-01 |2 |
| tip.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was closed |[mozilla-mobile/fenix#9836](https://github.com/mozilla-mobile/fenix/pull/9836), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)|<ul><li>identifier: The identifier of the tip closed</li></ul>|2021-08-01 |2 |
| tip.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was displayed |[mozilla-mobile/fenix#9836](https://github.com/mozilla-mobile/fenix/pull/9836), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)|<ul><li>identifier: The identifier of the tip displayed</li></ul>|2021-08-01 |2 |
| tip.pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip's button was pressed |[mozilla-mobile/fenix#9836](https://github.com/mozilla-mobile/fenix/pull/9836), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)|<ul><li>identifier: The identifier of the tip the action was taken on</li></ul>|2021-08-01 |2 |

Loading…
Cancel
Save