mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-03 23:15:31 +00:00
For #20507 - Inactive tabs telemetry
Two new events are added: - "inactive_tabs_expanded" for when the inactive tabs section is expanded - "inactive_tabs_collapsed" for when the inactive tabs section is collapsed For tracking when an inactive tab is opened / closed I've repurposed the existing tabs tray telemetry (since the functionality uses the same code) - tabs_tray.opened_existing_tab - tabs_tray.closed_existing_tab to support an extra "source" key indicating the feature from which a tab was opened or closed. The current values for this new key are: - "Tabs tray" for when a tab was opened/closed from tabs tray - "Inactive tabs" for when a tab was openes/closed from the Inactive tabs section of the tabs tray.
This commit is contained in:
parent
0fa9363f99
commit
09e8d34c26
@ -2625,7 +2625,11 @@ tabs_tray:
|
||||
opened_existing_tab:
|
||||
type: event
|
||||
description: |
|
||||
A user opened an existing tab
|
||||
A user opened an existing tab from a particular app feature.
|
||||
extra_keys:
|
||||
source:
|
||||
description: |
|
||||
From which app feature was an existing tab opened.
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/11273
|
||||
data_reviews:
|
||||
@ -2633,15 +2637,20 @@ tabs_tray:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068
|
||||
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20508#issuecomment-902160532
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: "2022-02-01"
|
||||
expires: "2022-08-01"
|
||||
closed_existing_tab:
|
||||
type: event
|
||||
description: |
|
||||
A user closed an existing tab
|
||||
A user closed an existing tab from a particular app feature.
|
||||
extra_keys:
|
||||
source:
|
||||
description: |
|
||||
From which app feature was an existing tab closed.
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/11273
|
||||
data_reviews:
|
||||
@ -2649,11 +2658,12 @@ tabs_tray:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068
|
||||
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20508#issuecomment-902160532
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: "2022-02-01"
|
||||
expires: "2022-08-01"
|
||||
private_mode_tapped:
|
||||
type: event
|
||||
description: |
|
||||
@ -2811,6 +2821,34 @@ tabs_tray:
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: "2022-08-01"
|
||||
inactive_tabs_expanded:
|
||||
type: event
|
||||
description: |
|
||||
A user tapped the "Inactive tabs" header to expand
|
||||
the group of inactive tabs.
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/20507
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20508#issuecomment-901336677
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: "2022-08-01"
|
||||
inactive_tabs_collapsed:
|
||||
type: event
|
||||
description: |
|
||||
A user tapped the "Inactive tabs" header to collapse
|
||||
the group of inactive tabs.
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/20507
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/20508#issuecomment-901336677
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- android-probes@mozilla.com
|
||||
expires: "2022-08-01"
|
||||
|
||||
collections:
|
||||
renamed:
|
||||
|
@ -21,6 +21,7 @@ import org.mozilla.fenix.GleanMetrics.Logins
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp
|
||||
import org.mozilla.fenix.GleanMetrics.SearchShortcuts
|
||||
import org.mozilla.fenix.GleanMetrics.TabsTray
|
||||
import org.mozilla.fenix.GleanMetrics.Tip
|
||||
import org.mozilla.fenix.GleanMetrics.ToolbarSettings
|
||||
import org.mozilla.fenix.GleanMetrics.TopSites
|
||||
@ -175,8 +176,12 @@ sealed class Event {
|
||||
// Tab tray
|
||||
object TabsTrayOpened : Event()
|
||||
object TabsTrayClosed : Event()
|
||||
object OpenedExistingTab : Event()
|
||||
object ClosedExistingTab : Event()
|
||||
data class OpenedExistingTab(val source: String) : Event() {
|
||||
override val extras = mapOf(TabsTray.openedExistingTabKeys.source to source)
|
||||
}
|
||||
data class ClosedExistingTab(val source: String) : Event() {
|
||||
override val extras = mapOf(TabsTray.closedExistingTabKeys.source to source)
|
||||
}
|
||||
object TabsTrayPrivateModeTapped : Event()
|
||||
object TabsTrayNormalModeTapped : Event()
|
||||
object TabsTraySyncedModeTapped : Event()
|
||||
@ -187,6 +192,8 @@ sealed class Event {
|
||||
object TabsTrayShareAllTabsPressed : Event()
|
||||
object TabsTrayCloseAllTabsPressed : Event()
|
||||
object TabsTrayRecentlyClosedPressed : Event()
|
||||
object TabsTrayInactiveTabsExpanded : Event()
|
||||
object TabsTrayInactiveTabsCollapsed : Event()
|
||||
|
||||
object ProgressiveWebAppOpenFromHomescreenTap : Event()
|
||||
object ProgressiveWebAppInstallAsShortcut : Event()
|
||||
|
@ -650,11 +650,13 @@ private val Event.wrapper: EventWrapper<*>?
|
||||
is Event.TabsTrayClosed -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.closed.record(it) }
|
||||
)
|
||||
is Event.OpenedExistingTab -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.openedExistingTab.record(it) }
|
||||
is Event.OpenedExistingTab -> EventWrapper(
|
||||
{ TabsTray.openedExistingTab.record(it) },
|
||||
{ TabsTray.openedExistingTabKeys.valueOf(it) }
|
||||
)
|
||||
is Event.ClosedExistingTab -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.closedExistingTab.record(it) }
|
||||
is Event.ClosedExistingTab -> EventWrapper(
|
||||
{ TabsTray.closedExistingTab.record(it) },
|
||||
{ TabsTray.closedExistingTabKeys.valueOf(it) }
|
||||
)
|
||||
is Event.TabsTrayPrivateModeTapped -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.privateModeTapped.record(it) }
|
||||
@ -686,6 +688,12 @@ private val Event.wrapper: EventWrapper<*>?
|
||||
is Event.TabsTrayRecentlyClosedPressed -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.inactiveTabsRecentlyClosed.record(it) }
|
||||
)
|
||||
is Event.TabsTrayInactiveTabsExpanded -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.inactiveTabsExpanded.record(it) }
|
||||
)
|
||||
is Event.TabsTrayInactiveTabsCollapsed -> EventWrapper<NoExtraKeys>(
|
||||
{ TabsTray.inactiveTabsCollapsed.record(it) }
|
||||
)
|
||||
is Event.AutoPlaySettingVisited -> EventWrapper<NoExtraKeys>(
|
||||
{ Autoplay.visitedSetting.record(it) }
|
||||
)
|
||||
|
@ -35,11 +35,11 @@ class TrayPagerAdapter(
|
||||
|
||||
private val normalAdapter by lazy {
|
||||
ConcatAdapter(
|
||||
BrowserTabsAdapter(context, browserInteractor, store),
|
||||
InactiveTabsAdapter(context, browserInteractor)
|
||||
BrowserTabsAdapter(context, browserInteractor, store, TABS_TRAY_FEATURE_NAME),
|
||||
InactiveTabsAdapter(context, browserInteractor, INACTIVE_TABS_FEATURE_NAME)
|
||||
)
|
||||
}
|
||||
private val privateAdapter by lazy { BrowserTabsAdapter(context, browserInteractor, store) }
|
||||
private val privateAdapter by lazy { BrowserTabsAdapter(context, browserInteractor, store, TABS_TRAY_FEATURE_NAME) }
|
||||
private val syncedTabsAdapter by lazy { SyncedTabsAdapter(TabClickDelegate(navInteractor)) }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractPageViewHolder {
|
||||
@ -98,6 +98,10 @@ class TrayPagerAdapter(
|
||||
companion object {
|
||||
const val TRAY_TABS_COUNT = 3
|
||||
|
||||
// Telemetry keys for identifying from which app features the a was opened / closed.
|
||||
const val TABS_TRAY_FEATURE_NAME = "Tabs tray"
|
||||
const val INACTIVE_TABS_FEATURE_NAME = "Inactive tabs"
|
||||
|
||||
val POSITION_NORMAL_TABS = Page.NormalTabs.ordinal
|
||||
val POSITION_PRIVATE_TABS = Page.PrivateTabs.ordinal
|
||||
val POSITION_SYNCED_TABS = Page.SyncedTabs.ordinal
|
||||
|
@ -35,19 +35,28 @@ import org.mozilla.fenix.ext.removeTouchDelegate
|
||||
import org.mozilla.fenix.ext.showAndEnable
|
||||
import org.mozilla.fenix.ext.toShortUrl
|
||||
import org.mozilla.fenix.selection.SelectionHolder
|
||||
import org.mozilla.fenix.selection.SelectionInteractor
|
||||
import org.mozilla.fenix.tabstray.TabsTrayState
|
||||
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
import org.mozilla.fenix.tabstray.ext.isSelect
|
||||
|
||||
/**
|
||||
* A RecyclerView ViewHolder implementation for "tab" items.
|
||||
*
|
||||
* @param itemView [View] that displays a "tab".
|
||||
* @param imageLoader [ImageLoader] used to load tab thumbnails.
|
||||
* @param trayStore [TabsTrayStore] containing the complete state of tabs tray and methods to update that.
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
* @param store [BrowserStore] containing the complete state of the browser and methods to update that.
|
||||
* @param metrics [MetricController] used for handling telemetry events.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
abstract class AbstractBrowserTabViewHolder(
|
||||
itemView: View,
|
||||
private val imageLoader: ImageLoader,
|
||||
private val trayStore: TabsTrayStore,
|
||||
private val selectionHolder: SelectionHolder<Tab>?,
|
||||
@VisibleForTesting
|
||||
internal val featureName: String,
|
||||
private val store: BrowserStore = itemView.context.components.core.store,
|
||||
private val metrics: MetricController = itemView.context.components.analytics.metrics
|
||||
) : TabViewHolder(itemView) {
|
||||
@ -91,7 +100,7 @@ abstract class AbstractBrowserTabViewHolder(
|
||||
if (selectionHolder != null) {
|
||||
setSelectionInteractor(tab, selectionHolder, browserTrayInteractor)
|
||||
} else {
|
||||
itemView.setOnClickListener { browserTrayInteractor.open(tab) }
|
||||
itemView.setOnClickListener { browserTrayInteractor.open(tab, featureName) }
|
||||
}
|
||||
|
||||
if (tab.thumbnail != null) {
|
||||
@ -202,12 +211,12 @@ abstract class AbstractBrowserTabViewHolder(
|
||||
private fun setSelectionInteractor(
|
||||
item: Tab,
|
||||
holder: SelectionHolder<Tab>,
|
||||
interactor: SelectionInteractor<Tab>
|
||||
interactor: BrowserTrayInteractor
|
||||
) {
|
||||
itemView.setOnClickListener {
|
||||
val selected = holder.selectedItems
|
||||
when {
|
||||
selected.isEmpty() && trayStore.state.mode.isSelect().not() -> interactor.open(item)
|
||||
selected.isEmpty() && trayStore.state.mode.isSelect().not() -> interactor.open(item, featureName)
|
||||
item in selected -> interactor.deselect(item)
|
||||
else -> interactor.select(item)
|
||||
}
|
||||
|
@ -21,14 +21,22 @@ import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
|
||||
/**
|
||||
* A RecyclerView ViewHolder implementation for "tab" items with grid layout.
|
||||
*
|
||||
* @param imageLoader [ImageLoader] used to load tab thumbnails.
|
||||
* @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that.
|
||||
* @param selectionHolder [SelectionHolder]<[Tab]> for helping with selecting any number of displayed [Tab]s.
|
||||
* @param itemView [View] that displays a "tab".
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
*/
|
||||
class BrowserTabGridViewHolder(
|
||||
imageLoader: ImageLoader,
|
||||
override val browserTrayInteractor: BrowserTrayInteractor,
|
||||
store: TabsTrayStore,
|
||||
selectionHolder: SelectionHolder<Tab>? = null,
|
||||
itemView: View
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder) {
|
||||
itemView: View,
|
||||
featureName: String
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder, featureName) {
|
||||
|
||||
private val closeButton: AppCompatImageButton = itemView.findViewById(R.id.mozac_browser_tabstray_close)
|
||||
|
||||
|
@ -15,14 +15,22 @@ import kotlin.math.max
|
||||
|
||||
/**
|
||||
* A RecyclerView ViewHolder implementation for "tab" items with list layout.
|
||||
*/
|
||||
*
|
||||
* @param imageLoader [ImageLoader] used to load tab thumbnails.
|
||||
* @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that.
|
||||
* @param selectionHolder [SelectionHolder]<[Tab]> for helping with selecting any number of displayed [Tab]s.
|
||||
* @param itemView [View] that displays a "tab".
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
*/
|
||||
class BrowserTabListViewHolder(
|
||||
imageLoader: ImageLoader,
|
||||
override val browserTrayInteractor: BrowserTrayInteractor,
|
||||
store: TabsTrayStore,
|
||||
selectionHolder: SelectionHolder<Tab>? = null,
|
||||
itemView: View
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder) {
|
||||
itemView: View,
|
||||
featureName: String
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder, featureName) {
|
||||
override val thumbnailSize: Int
|
||||
get() = max(
|
||||
itemView.resources.getDimensionPixelSize(R.dimen.tab_tray_list_item_thumbnail_height),
|
||||
|
@ -17,6 +17,7 @@ import mozilla.components.concept.tabstray.TabsTray
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.base.observer.ObserverRegistry
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.Components
|
||||
import org.mozilla.fenix.databinding.TabTrayGridItemBinding
|
||||
import org.mozilla.fenix.databinding.TabTrayItemBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
@ -25,11 +26,18 @@ import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] for browser tabs.
|
||||
*
|
||||
* @param context [Context] used for various platform interactions or accessing [Components]
|
||||
* @param interactor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that.
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
* @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry].
|
||||
*/
|
||||
class BrowserTabsAdapter(
|
||||
private val context: Context,
|
||||
private val interactor: BrowserTrayInteractor,
|
||||
private val store: TabsTrayStore,
|
||||
private val featureName: String,
|
||||
delegate: Observable<TabsTray.Observer> = ObserverRegistry()
|
||||
) : TabsAdapter<AbstractBrowserTabViewHolder>(delegate) {
|
||||
|
||||
@ -62,9 +70,9 @@ class BrowserTabsAdapter(
|
||||
|
||||
return when (viewType) {
|
||||
ViewType.GRID.layoutRes ->
|
||||
BrowserTabGridViewHolder(imageLoader, interactor, store, selectionHolder, view)
|
||||
BrowserTabGridViewHolder(imageLoader, interactor, store, selectionHolder, view, featureName)
|
||||
else ->
|
||||
BrowserTabListViewHolder(imageLoader, interactor, store, selectionHolder, view)
|
||||
BrowserTabListViewHolder(imageLoader, interactor, store, selectionHolder, view, featureName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,12 +84,12 @@ class BrowserTabsAdapter(
|
||||
ViewType.GRID.layoutRes -> {
|
||||
val gridBinding = TabTrayGridItemBinding.bind(holder.itemView)
|
||||
selectedMaskView = gridBinding.checkboxInclude.selectedMask
|
||||
gridBinding.mozacBrowserTabstrayClose.setOnClickListener { interactor.close(tab) }
|
||||
gridBinding.mozacBrowserTabstrayClose.setOnClickListener { interactor.close(tab, featureName) }
|
||||
}
|
||||
ViewType.LIST.layoutRes -> {
|
||||
val listBinding = TabTrayItemBinding.bind(holder.itemView)
|
||||
selectedMaskView = listBinding.checkboxInclude.selectedMask
|
||||
listBinding.mozacBrowserTabstrayClose.setOnClickListener { interactor.close(tab) }
|
||||
listBinding.mozacBrowserTabstrayClose.setOnClickListener { interactor.close(tab, featureName) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,20 @@ import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
interface BrowserTrayInteractor : SelectionInteractor<Tab>, UserInteractionHandler {
|
||||
|
||||
/**
|
||||
* Close the tab.
|
||||
* Open a tab.
|
||||
*
|
||||
* @param tab [Tab] to open in browser.
|
||||
* @param source app feature from which the [tab] was opened.
|
||||
*/
|
||||
fun close(tab: Tab)
|
||||
fun open(tab: Tab, source: String? = null)
|
||||
|
||||
/**
|
||||
* Close the tab.
|
||||
*
|
||||
* @param tab [Tab] to close.
|
||||
* @param source app feature from which the [tab] was closed.
|
||||
*/
|
||||
fun close(tab: Tab, source: String? = null)
|
||||
|
||||
/**
|
||||
* TabTray's Floating Action Button clicked.
|
||||
@ -65,14 +76,21 @@ class DefaultBrowserTrayInteractor(
|
||||
* See [SelectionInteractor.open]
|
||||
*/
|
||||
override fun open(item: Tab) {
|
||||
selectTabWrapper.invoke(item.id)
|
||||
open(item, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* See [BrowserTrayInteractor.open].
|
||||
*/
|
||||
override fun open(tab: Tab, source: String?) {
|
||||
selectTabWrapper.invoke(tab.id, source)
|
||||
}
|
||||
|
||||
/**
|
||||
* See [BrowserTrayInteractor.close].
|
||||
*/
|
||||
override fun close(tab: Tab) {
|
||||
removeTabWrapper.invoke(tab.id)
|
||||
override fun close(tab: Tab, source: String?) {
|
||||
removeTabWrapper.invoke(tab.id, source)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,9 +52,17 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A RecyclerView ViewHolder implementation for an inactive tab view.
|
||||
*
|
||||
* @param itemView the inactive tab [View].
|
||||
* @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
*/
|
||||
class TabViewHolder(
|
||||
itemView: View,
|
||||
private val browserTrayInteractor: BrowserTrayInteractor
|
||||
private val browserTrayInteractor: BrowserTrayInteractor,
|
||||
private val featureName: String
|
||||
) : InactiveTabViewHolder(itemView) {
|
||||
|
||||
private val binding = InactiveTabListItemBinding.bind(itemView)
|
||||
@ -65,7 +73,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite
|
||||
val url = tab.url.toShortUrl(components.publicSuffixList).take(MAX_URI_LENGTH)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
browserTrayInteractor.open(tab)
|
||||
browserTrayInteractor.open(tab, featureName)
|
||||
}
|
||||
|
||||
binding.siteListItem.apply {
|
||||
@ -75,7 +83,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite
|
||||
R.drawable.mozac_ic_close,
|
||||
R.string.content_description_close_button
|
||||
) {
|
||||
browserTrayInteractor.close(tab)
|
||||
browserTrayInteractor.close(tab, featureName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import mozilla.components.concept.tabstray.Tab as TabsTrayTab
|
||||
import mozilla.components.concept.tabstray.Tabs
|
||||
import mozilla.components.concept.tabstray.TabsTray
|
||||
import mozilla.components.support.base.observer.ObserverRegistry
|
||||
import org.mozilla.fenix.components.Components
|
||||
import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder
|
||||
import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder
|
||||
import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.RecentlyClosedHolder
|
||||
@ -32,10 +33,16 @@ private typealias Observable = ComponentObservable<TabsTray.Observer>
|
||||
|
||||
/**
|
||||
* The [ListAdapter] for displaying the list of inactive tabs.
|
||||
*
|
||||
* @param context [Context] used for various platform interactions or accessing [Components]
|
||||
* @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
* @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry].
|
||||
*/
|
||||
class InactiveTabsAdapter(
|
||||
private val context: Context,
|
||||
private val browserTrayInteractor: BrowserTrayInteractor,
|
||||
private val featureName: String,
|
||||
delegate: Observable = ObserverRegistry()
|
||||
) : Adapter(DiffCallback), TabsTray, Observable by delegate {
|
||||
|
||||
@ -47,7 +54,7 @@ class InactiveTabsAdapter(
|
||||
|
||||
return when (viewType) {
|
||||
HeaderHolder.LAYOUT_ID -> HeaderHolder(view, inactiveTabsInteractor)
|
||||
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, browserTrayInteractor)
|
||||
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, browserTrayInteractor, featureName)
|
||||
FooterHolder.LAYOUT_ID -> FooterHolder(view)
|
||||
RecentlyClosedHolder.LAYOUT_ID -> RecentlyClosedHolder(view, browserTrayInteractor)
|
||||
else -> throw IllegalStateException("Unknown viewType: $viewType")
|
||||
|
@ -8,11 +8,14 @@ import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.tabstray.TabsTray
|
||||
import mozilla.components.feature.tabs.ext.toTabs
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
|
||||
class InactiveTabsController(
|
||||
private val browserStore: BrowserStore,
|
||||
private val tabFilter: (TabSessionState) -> Boolean,
|
||||
private val tray: TabsTray
|
||||
private val tray: TabsTray,
|
||||
private val metrics: MetricController
|
||||
) {
|
||||
/**
|
||||
* Updates the inactive card to be expanded to display all the tabs, or collapsed with only
|
||||
@ -21,6 +24,13 @@ class InactiveTabsController(
|
||||
fun updateCardExpansion(isExpanded: Boolean) {
|
||||
InactiveTabsState.isExpanded = isExpanded
|
||||
|
||||
metrics.track(
|
||||
when (isExpanded) {
|
||||
true -> Event.TabsTrayInactiveTabsExpanded
|
||||
false -> Event.TabsTrayInactiveTabsCollapsed
|
||||
}
|
||||
)
|
||||
|
||||
val tabs = browserStore.state.toTabs { tabFilter.invoke(it) }
|
||||
|
||||
tray.updateTabs(tabs)
|
||||
|
@ -68,7 +68,7 @@ class NormalBrowserTrayList @JvmOverloads constructor(
|
||||
}
|
||||
val tabsAdapter = concatAdapter.inactiveTabsAdapter.apply {
|
||||
inactiveTabsInteractor = DefaultInactiveTabsInteractor(
|
||||
InactiveTabsController(store, tabFilter, this)
|
||||
InactiveTabsController(store, tabFilter, this, context.components.analytics.metrics)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,19 +13,27 @@ class SelectTabUseCaseWrapper(
|
||||
private val selectTab: TabsUseCases.SelectTabUseCase,
|
||||
private val onSelect: (String) -> Unit
|
||||
) : TabsUseCases.SelectTabUseCase {
|
||||
override fun invoke(tabId: String) {
|
||||
metrics.track(Event.OpenedExistingTab)
|
||||
operator fun invoke(tabId: String, source: String? = null) {
|
||||
metrics.track(Event.OpenedExistingTab(source ?: "unknown"))
|
||||
selectTab(tabId)
|
||||
onSelect(tabId)
|
||||
}
|
||||
|
||||
override fun invoke(tabId: String) {
|
||||
invoke(tabId, null)
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveTabUseCaseWrapper(
|
||||
private val metrics: MetricController,
|
||||
private val onRemove: (String) -> Unit
|
||||
private val onRemove: (String) -> Unit,
|
||||
) : TabsUseCases.RemoveTabUseCase {
|
||||
override fun invoke(tabId: String) {
|
||||
metrics.track(Event.ClosedExistingTab)
|
||||
operator fun invoke(tabId: String, source: String? = null) {
|
||||
metrics.track(Event.ClosedExistingTab(source ?: "unknown"))
|
||||
onRemove(tabId)
|
||||
}
|
||||
|
||||
override fun invoke(tabId: String) {
|
||||
invoke(tabId, null)
|
||||
}
|
||||
}
|
||||
|
@ -207,12 +207,24 @@ class GleanMetricsServiceTest {
|
||||
assertTrue(TabsTray.closed.testHasValue())
|
||||
|
||||
assertFalse(TabsTray.openedExistingTab.testHasValue())
|
||||
gleanService.track(Event.OpenedExistingTab)
|
||||
gleanService.track(Event.OpenedExistingTab("Test"))
|
||||
assertTrue(TabsTray.openedExistingTab.testHasValue())
|
||||
var events = TabsTray.openedExistingTab.testGetValue()
|
||||
assertEquals(1, events.size)
|
||||
assertEquals("tabs_tray", events[0].category)
|
||||
assertEquals("opened_existing_tab", events[0].name)
|
||||
assertEquals(1, events[0].extra!!.size)
|
||||
assertEquals("Test", events[0].extra!!["source"])
|
||||
|
||||
assertFalse(TabsTray.closedExistingTab.testHasValue())
|
||||
gleanService.track(Event.ClosedExistingTab)
|
||||
gleanService.track(Event.ClosedExistingTab("Test"))
|
||||
assertTrue(TabsTray.closedExistingTab.testHasValue())
|
||||
events = TabsTray.closedExistingTab.testGetValue()
|
||||
assertEquals(1, events.size)
|
||||
assertEquals("tabs_tray", events[0].category)
|
||||
assertEquals("closed_existing_tab", events[0].name)
|
||||
assertEquals(1, events[0].extra!!.size)
|
||||
assertEquals("Test", events[0].extra!!["source"])
|
||||
|
||||
assertFalse(TabsTray.privateModeTapped.testHasValue())
|
||||
gleanService.track(Event.TabsTrayPrivateModeTapped)
|
||||
|
@ -44,7 +44,7 @@ class AbstractBrowserTabViewHolderTest {
|
||||
|
||||
holder.itemView.performClick()
|
||||
|
||||
verify { interactor.open(any()) }
|
||||
verify { interactor.open(any(), holder.featureName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -65,7 +65,7 @@ class AbstractBrowserTabViewHolderTest {
|
||||
|
||||
holder.itemView.performClick()
|
||||
|
||||
verify { interactor.open(any()) }
|
||||
verify { interactor.open(any(), holder.featureName) }
|
||||
assertTrue(selectionHolder.invoked)
|
||||
}
|
||||
|
||||
@ -77,8 +77,9 @@ class AbstractBrowserTabViewHolderTest {
|
||||
selectionHolder: SelectionHolder<Tab>?,
|
||||
store: BrowserStore,
|
||||
metrics: MetricController,
|
||||
override val browserTrayInteractor: BrowserTrayInteractor
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, trayStore, selectionHolder, store, metrics) {
|
||||
override val browserTrayInteractor: BrowserTrayInteractor,
|
||||
featureName: String = "Test"
|
||||
) : AbstractBrowserTabViewHolder(itemView, imageLoader, trayStore, selectionHolder, featureName, store, metrics) {
|
||||
override val thumbnailSize: Int
|
||||
get() = 30
|
||||
|
||||
|
@ -30,7 +30,7 @@ class BrowserTabsAdapterTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN bind with payloads is called THEN update the holder`() {
|
||||
val adapter = BrowserTabsAdapter(context, interactor, store)
|
||||
val adapter = BrowserTabsAdapter(context, interactor, store, "Test")
|
||||
val holder = mockk<AbstractBrowserTabViewHolder>(relaxed = true)
|
||||
|
||||
adapter.updateTabs(
|
||||
@ -53,7 +53,7 @@ class BrowserTabsAdapterTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN the selection holder is set THEN update the selected tab`() {
|
||||
val adapter = BrowserTabsAdapter(context, interactor, store)
|
||||
val adapter = BrowserTabsAdapter(context, interactor, store, "Test")
|
||||
val binding = TabTrayItemBinding.inflate(LayoutInflater.from(testContext))
|
||||
val holder = spyk(
|
||||
BrowserTabListViewHolder(
|
||||
@ -61,7 +61,8 @@ class BrowserTabsAdapterTest {
|
||||
browserTrayInteractor = interactor,
|
||||
store = store,
|
||||
selectionHolder = null,
|
||||
itemView = binding.root
|
||||
itemView = binding.root,
|
||||
featureName = "Test"
|
||||
)
|
||||
)
|
||||
val tab = createTab("tab1")
|
||||
|
@ -15,6 +15,8 @@ import mozilla.components.concept.tabstray.TabsTray
|
||||
import org.junit.Assert.assertEquals
|
||||
import mozilla.components.browser.state.state.createTab as createTabState
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class InactiveTabsControllerTest {
|
||||
@Test
|
||||
@ -31,7 +33,7 @@ class InactiveTabsControllerTest {
|
||||
)
|
||||
val tray: TabsTray = mockk(relaxed = true)
|
||||
val tabsSlot = slot<Tabs>()
|
||||
val controller = InactiveTabsController(store, filter, tray)
|
||||
val controller = InactiveTabsController(store, filter, tray, mockk(relaxed = true))
|
||||
|
||||
controller.updateCardExpansion(true)
|
||||
|
||||
@ -40,4 +42,30 @@ class InactiveTabsControllerTest {
|
||||
assertEquals(2, tabsSlot.captured.list.size)
|
||||
assertEquals("1", tabsSlot.captured.list.first().id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN expanded THEN track telemetry event`() {
|
||||
val metrics: MetricController = mockk(relaxed = true)
|
||||
val store = BrowserStore(BrowserState())
|
||||
val controller = InactiveTabsController(
|
||||
store, mockk(relaxed = true), mockk(relaxed = true), metrics
|
||||
)
|
||||
|
||||
controller.updateCardExpansion(true)
|
||||
|
||||
verify { metrics.track(Event.TabsTrayInactiveTabsExpanded) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN collapsed THEN track telemetry event`() {
|
||||
val metrics: MetricController = mockk(relaxed = true)
|
||||
val store = BrowserStore(BrowserState())
|
||||
val controller = InactiveTabsController(
|
||||
store, mockk(relaxed = true), mockk(relaxed = true), metrics
|
||||
)
|
||||
|
||||
controller.updateCardExpansion(false)
|
||||
|
||||
verify { metrics.track(Event.TabsTrayInactiveTabsCollapsed) }
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class RemoveTabUseCaseWrapperTest {
|
||||
val metricController = mockk<MetricController>(relaxed = true)
|
||||
|
||||
@Test
|
||||
fun `WHEN invoked THEN metrics, use case and callback are triggered`() {
|
||||
fun `WHEN invoked with no source name THEN metrics with unknown source, use case and callback are triggered`() {
|
||||
var actualTabId: String? = null
|
||||
val onRemove: (String) -> Unit = { tabId ->
|
||||
actualTabId = tabId
|
||||
@ -25,7 +25,21 @@ class RemoveTabUseCaseWrapperTest {
|
||||
|
||||
wrapper("123")
|
||||
|
||||
verify { metricController.track(Event.ClosedExistingTab) }
|
||||
verify { metricController.track(Event.ClosedExistingTab("unknown")) }
|
||||
assertEquals("123", actualTabId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN invoked with a source name THEN metrics containing the source, use case and callback are triggered`() {
|
||||
var actualTabId: String? = null
|
||||
val onRemove: (String) -> Unit = { tabId ->
|
||||
actualTabId = tabId
|
||||
}
|
||||
val wrapper = RemoveTabUseCaseWrapper(metricController, onRemove)
|
||||
|
||||
wrapper("123", "Test")
|
||||
|
||||
verify { metricController.track(Event.ClosedExistingTab("Test")) }
|
||||
assertEquals("123", actualTabId)
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,25 @@ class SelectTabUseCaseWrapperTest {
|
||||
val selectUseCase: TabsUseCases.SelectTabUseCase = mockk(relaxed = true)
|
||||
|
||||
@Test
|
||||
fun `WHEN invoked THEN metrics, use case and callback are triggered`() {
|
||||
fun `WHEN invoked with no source name THEN metrics with unknown source, use case and callback are triggered`() {
|
||||
val onSelect: (String) -> Unit = mockk(relaxed = true)
|
||||
val wrapper = SelectTabUseCaseWrapper(metricController, selectUseCase, onSelect)
|
||||
|
||||
wrapper("123")
|
||||
|
||||
verify { metricController.track(Event.OpenedExistingTab) }
|
||||
verify { metricController.track(Event.OpenedExistingTab("unknown")) }
|
||||
verify { selectUseCase("123") }
|
||||
verify { onSelect("123") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN invoked with a source name THEN metrics, use case and callback are triggered`() {
|
||||
val onSelect: (String) -> Unit = mockk(relaxed = true)
|
||||
val wrapper = SelectTabUseCaseWrapper(metricController, selectUseCase, onSelect)
|
||||
|
||||
wrapper("123", "Test")
|
||||
|
||||
verify { metricController.track(Event.OpenedExistingTab("Test")) }
|
||||
verify { selectUseCase("123") }
|
||||
verify { onSelect("123") }
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class AbstractBrowserPageViewHolderTest {
|
||||
val store: TabsTrayStore = TabsTrayStore()
|
||||
val interactor = mockk<TabsTrayInteractor>(relaxed = true)
|
||||
val browserTrayInteractor = mockk<BrowserTrayInteractor>(relaxed = true)
|
||||
val adapter = BrowserTabsAdapter(testContext, browserTrayInteractor, store)
|
||||
val adapter = BrowserTabsAdapter(testContext, browserTrayInteractor, store, "Test")
|
||||
|
||||
@Test
|
||||
fun `WHEN tabs inserted THEN show tray`() {
|
||||
|
Loading…
Reference in New Issue
Block a user