parent
c9ea048431
commit
dce16964c0
|
@ -1044,6 +1044,50 @@ history:
|
|||
- fenix-core@mozilla.com
|
||||
expires: "2020-09-01"
|
||||
|
||||
tip:
|
||||
displayed:
|
||||
type: event
|
||||
description: >
|
||||
The tip was displayed
|
||||
extra_keys:
|
||||
identifier:
|
||||
description: "The identifier of the tip displayed"
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/9328
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/9836
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-09-01"
|
||||
pressed:
|
||||
type: event
|
||||
description: >
|
||||
The tip's button was pressed
|
||||
extra_keys:
|
||||
identifier:
|
||||
description: "The identifier of the tip the action was taken on"
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/9328
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/9836
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-09-01"
|
||||
closed:
|
||||
type: event
|
||||
description: >
|
||||
The tip was closed
|
||||
extra_keys:
|
||||
identifier:
|
||||
description: "The identifier of the tip closed"
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issues/9328
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/9836
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-09-01"
|
||||
|
||||
reader_mode:
|
||||
available:
|
||||
type: event
|
||||
|
|
|
@ -43,4 +43,9 @@ object FeatureFlags {
|
|||
* Enables picture-in-picture feature
|
||||
*/
|
||||
val pictureInPicture = Config.channel.isNightlyOrDebug
|
||||
|
||||
/**
|
||||
* Enables tip feature
|
||||
*/
|
||||
val tips = Config.channel.isDebug
|
||||
}
|
||||
|
|
|
@ -10,16 +10,16 @@ import android.content.Intent
|
|||
import androidx.core.net.toUri
|
||||
import mozilla.components.feature.addons.AddonManager
|
||||
import mozilla.components.feature.addons.amo.AddonCollectionProvider
|
||||
import mozilla.components.feature.addons.migration.DefaultSupportedAddonsChecker
|
||||
import mozilla.components.feature.addons.migration.SupportedAddonsChecker
|
||||
import mozilla.components.feature.addons.update.AddonUpdater
|
||||
import mozilla.components.feature.addons.update.DefaultAddonUpdater
|
||||
import mozilla.components.feature.addons.migration.SupportedAddonsChecker
|
||||
import mozilla.components.feature.addons.migration.DefaultSupportedAddonsChecker
|
||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
import org.mozilla.fenix.BuildConfig
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.utils.Mockable
|
||||
import org.mozilla.fenix.utils.ClipboardHandler
|
||||
import org.mozilla.fenix.utils.Mockable
|
||||
import org.mozilla.fenix.wifi.WifiConnectionMonitor
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.mozilla.fenix.GleanMetrics.SearchWidget
|
|||
import org.mozilla.fenix.GleanMetrics.SyncAccount
|
||||
import org.mozilla.fenix.GleanMetrics.SyncAuth
|
||||
import org.mozilla.fenix.GleanMetrics.Tab
|
||||
import org.mozilla.fenix.GleanMetrics.Tip
|
||||
import org.mozilla.fenix.GleanMetrics.ToolbarSettings
|
||||
import org.mozilla.fenix.GleanMetrics.TopSites
|
||||
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
||||
|
@ -498,6 +499,18 @@ private val Event.wrapper: EventWrapper<*>?
|
|||
is Event.AddonsOpenInToolbarMenu -> EventWrapper<NoExtraKeys>(
|
||||
{ Addons.openAddonInToolbarMenu.record(it) }
|
||||
)
|
||||
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) }
|
||||
)
|
||||
// Don't record other events in Glean:
|
||||
is Event.AddBookmark -> null
|
||||
is Event.OpenedBookmark -> null
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.mozilla.fenix.GleanMetrics.Events
|
|||
import org.mozilla.fenix.GleanMetrics.Library
|
||||
import org.mozilla.fenix.GleanMetrics.Logins
|
||||
import org.mozilla.fenix.GleanMetrics.SearchShortcuts
|
||||
import org.mozilla.fenix.GleanMetrics.Tip
|
||||
import org.mozilla.fenix.GleanMetrics.ToolbarSettings
|
||||
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -193,6 +194,21 @@ sealed class Event {
|
|||
}
|
||||
}
|
||||
|
||||
data class TipDisplayed(val identifier: String) : Event() {
|
||||
override val extras: Map<Tip.displayedKeys, String>?
|
||||
get() = hashMapOf(Tip.displayedKeys.identifier to identifier)
|
||||
}
|
||||
|
||||
data class TipPressed(val identifier: String) : Event() {
|
||||
override val extras: Map<Tip.pressedKeys, String>?
|
||||
get() = hashMapOf(Tip.pressedKeys.identifier to identifier)
|
||||
}
|
||||
|
||||
data class TipClosed(val identifier: String) : Event() {
|
||||
override val extras: Map<Tip.closedKeys, String>?
|
||||
get() = hashMapOf(Tip.closedKeys.identifier to identifier)
|
||||
}
|
||||
|
||||
data class ToolbarPositionChanged(val position: Position) : Event() {
|
||||
enum class Position { TOP, BOTTOM }
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ object MozillaProductDetector {
|
|||
enum class MozillaProducts(val productName: String) {
|
||||
// Browsers
|
||||
FIREFOX("org.mozilla.firefox"),
|
||||
FIREFOX_NIGHTLY("org.mozilla.fennec_aurora"),
|
||||
FIREFOX_BETA("org.mozilla.firefox_beta"),
|
||||
FIREFOX_AURORA("org.mozilla.fennec_aurora"),
|
||||
FIREFOX_NIGHTLY("org.mozilla.fennec"),
|
||||
FIREFOX_FDROID("org.mozilla.fennec_fdroid"),
|
||||
FIREFOX_LITE("org.mozilla.rocket"),
|
||||
REFERENCE_BROWSER("org.mozilla.reference.browser"),
|
||||
REFERENCE_BROWSER_DEBUG("org.mozilla.reference.browser.debug"),
|
||||
FENIX("org.mozilla.fenix"),
|
||||
FENIX_NIGHTLY("org.mozilla.fenix.nightly"),
|
||||
FOCUS("org.mozilla.focus"),
|
||||
KLAR("org.mozilla.klar"),
|
||||
|
||||
|
@ -43,7 +43,7 @@ object MozillaProductDetector {
|
|||
return mozillaProducts
|
||||
}
|
||||
|
||||
private fun packageIsInstalled(context: Context, packageName: String): Boolean {
|
||||
fun packageIsInstalled(context: Context, packageName: String): Boolean {
|
||||
try {
|
||||
context.packageManager.getPackageInfo(packageName, 0)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.components.tips
|
||||
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
|
||||
sealed class TipType {
|
||||
data class Button(val text: String, val action: () -> Unit) : TipType()
|
||||
}
|
||||
|
||||
open class Tip(
|
||||
val type: TipType,
|
||||
val identifier: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val learnMoreURL: String?
|
||||
)
|
||||
|
||||
interface TipProvider {
|
||||
val tip: Tip?
|
||||
val shouldDisplay: Boolean
|
||||
}
|
||||
|
||||
interface TipManager {
|
||||
fun getTip(): Tip?
|
||||
}
|
||||
|
||||
class FenixTipManager(
|
||||
private val providers: List<TipProvider>
|
||||
) : TipManager {
|
||||
override fun getTip(): Tip? {
|
||||
if (!FeatureFlags.tips) { return null }
|
||||
return providers
|
||||
.firstOrNull { it.shouldDisplay }
|
||||
?.tip
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.components.tips.providers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FENIX
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FENIX_NIGHTLY
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FIREFOX_NIGHTLY
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.components.tips.TipProvider
|
||||
import org.mozilla.fenix.components.tips.TipType
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
|
||||
/**
|
||||
* Tip explaining to users the migration of Fenix channels
|
||||
*/
|
||||
class MigrationTipProvider(private val context: Context) : TipProvider {
|
||||
|
||||
override val tip: Tip? =
|
||||
when (context.packageName) {
|
||||
FENIX.productName -> firefoxPreviewMovedTip()
|
||||
FIREFOX_NIGHTLY.productName -> getNightlyMigrationTip()
|
||||
FENIX_NIGHTLY.productName -> getNightlyMigrationTip()
|
||||
else -> null
|
||||
}
|
||||
|
||||
override val shouldDisplay: Boolean = context.settings().shouldDisplayFenixMovingTip()
|
||||
|
||||
private fun firefoxPreviewMovedTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun firefoxPreviewMovedPreviewInstalledTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button_preview_installed),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header_preview_installed),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description_preview_installed),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun firefoxPreviewMovedPreviewNotInstalledTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button_preview_not_installed),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header_preview_not_installed),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description_preview_not_installed),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun getNightlyMigrationTip(): Tip? {
|
||||
return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) {
|
||||
firefoxPreviewMovedPreviewInstalledTip()
|
||||
} else {
|
||||
firefoxPreviewMovedPreviewNotInstalledTip()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFirefoxMovedButtonAction() {
|
||||
when (context.packageName) {
|
||||
FENIX.productName -> context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_BETA_PLAY_STORE_URL))
|
||||
)
|
||||
FIREFOX_NIGHTLY.productName -> getNightlyMigrationAction()
|
||||
FENIX_NIGHTLY.productName -> getNightlyMigrationAction()
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNightlyMigrationAction() {
|
||||
return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) {
|
||||
context.startActivity(context.packageManager.getLaunchIntentForPackage(FENIX.productName))
|
||||
} else {
|
||||
context.startActivity(Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdentifier(): String {
|
||||
return when (context.packageName) {
|
||||
FENIX.productName -> context.getString(R.string.pref_key_migrating_from_fenix_tip)
|
||||
FIREFOX_NIGHTLY.productName -> context.getString(R.string.pref_key_migrating_from_firefox_nightly_tip)
|
||||
FENIX_NIGHTLY.productName -> context.getString(R.string.pref_key_migrating_from_fenix_nightly_tip)
|
||||
else -> { "" }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,6 +77,8 @@ import org.mozilla.fenix.components.PrivateShortcutCreateManager
|
|||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.tips.FenixTipManager
|
||||
import org.mozilla.fenix.components.tips.providers.MigrationTipProvider
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.hideToolbar
|
||||
import org.mozilla.fenix.ext.metrics
|
||||
|
@ -199,7 +201,8 @@ class HomeFragment : Fragment() {
|
|||
expandedCollections = emptySet(),
|
||||
mode = currentMode.getCurrentMode(),
|
||||
tabs = emptyList(),
|
||||
topSites = requireComponents.core.topSiteStorage.cachedTopSites
|
||||
topSites = requireComponents.core.topSiteStorage.cachedTopSites,
|
||||
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -379,7 +382,8 @@ class HomeFragment : Fragment() {
|
|||
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
||||
mode = currentMode.getCurrentMode(),
|
||||
tabs = getListOfSessions().toTabs(),
|
||||
topSites = components.core.topSiteStorage.cachedTopSites
|
||||
topSites = components.core.topSiteStorage.cachedTopSites,
|
||||
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import mozilla.components.feature.top.sites.TopSite
|
|||
import mozilla.components.lib.state.Action
|
||||
import mozilla.components.lib.state.State
|
||||
import mozilla.components.lib.state.Store
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
|
||||
/**
|
||||
* The [Store] for holding the [HomeFragmentState] and applying [HomeFragmentAction]s.
|
||||
|
@ -58,7 +59,8 @@ data class HomeFragmentState(
|
|||
val expandedCollections: Set<Long>,
|
||||
val mode: Mode,
|
||||
val tabs: List<Tab>,
|
||||
val topSites: List<TopSite>
|
||||
val topSites: List<TopSite>,
|
||||
val tip: Tip? = null
|
||||
) : State
|
||||
|
||||
sealed class HomeFragmentAction : Action {
|
||||
|
@ -66,7 +68,8 @@ sealed class HomeFragmentAction : Action {
|
|||
val tabs: List<Tab>,
|
||||
val topSites: List<TopSite>,
|
||||
val mode: Mode,
|
||||
val collections: List<TabCollection>
|
||||
val collections: List<TabCollection>,
|
||||
val tip: Tip? = null
|
||||
) :
|
||||
HomeFragmentAction()
|
||||
|
||||
|
@ -77,6 +80,7 @@ sealed class HomeFragmentAction : Action {
|
|||
data class ModeChange(val mode: Mode, val tabs: List<Tab> = emptyList()) : HomeFragmentAction()
|
||||
data class TabsChange(val tabs: List<Tab>) : HomeFragmentAction()
|
||||
data class TopSitesChange(val topSites: List<TopSite>) : HomeFragmentAction()
|
||||
data class RemoveTip(val tip: Tip) : HomeFragmentAction()
|
||||
}
|
||||
|
||||
private fun homeFragmentStateReducer(
|
||||
|
@ -88,7 +92,8 @@ private fun homeFragmentStateReducer(
|
|||
collections = action.collections,
|
||||
mode = action.mode,
|
||||
tabs = action.tabs,
|
||||
topSites = action.topSites
|
||||
topSites = action.topSites,
|
||||
tip = action.tip
|
||||
)
|
||||
is HomeFragmentAction.CollectionExpanded -> {
|
||||
val newExpandedCollection = state.expandedCollections.toMutableSet()
|
||||
|
@ -105,5 +110,6 @@ private fun homeFragmentStateReducer(
|
|||
is HomeFragmentAction.ModeChange -> state.copy(mode = action.mode, tabs = action.tabs)
|
||||
is HomeFragmentAction.TabsChange -> state.copy(tabs = action.tabs)
|
||||
is HomeFragmentAction.TopSitesChange -> state.copy(topSites = action.topSites)
|
||||
is HomeFragmentAction.RemoveTip -> { state.copy(tip = null) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,16 +17,17 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import kotlinx.android.synthetic.main.tab_list_row.*
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.home.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoContentMessageViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoContentMessageWithActionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.SaveTabGroupViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
|
||||
|
@ -41,10 +42,12 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTh
|
|||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingWhatsNewViewHolder
|
||||
|
||||
import org.mozilla.fenix.home.tips.ButtonTipViewHolder
|
||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
|
||||
sealed class AdapterItem(@LayoutRes val viewType: Int) {
|
||||
data class TipItem(val tip: Tip) : AdapterItem(
|
||||
ButtonTipViewHolder.LAYOUT_ID)
|
||||
data class TabHeader(val isPrivate: Boolean, val hasTabs: Boolean) : AdapterItem(TabHeaderViewHolder.LAYOUT_ID)
|
||||
data class TabItem(val tab: Tab) : AdapterItem(TabViewHolder.LAYOUT_ID) {
|
||||
override fun sameAs(other: AdapterItem) = other is TabItem && tab.sessionId == other.tab.sessionId
|
||||
|
@ -164,6 +167,7 @@ class SessionControlAdapter(
|
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
return when (viewType) {
|
||||
ButtonTipViewHolder.LAYOUT_ID -> ButtonTipViewHolder(view, interactor)
|
||||
TabHeaderViewHolder.LAYOUT_ID -> TabHeaderViewHolder(view, interactor)
|
||||
TopSiteHeaderViewHolder.LAYOUT_ID -> TopSiteHeaderViewHolder(view)
|
||||
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, interactor)
|
||||
|
@ -196,6 +200,10 @@ class SessionControlAdapter(
|
|||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
when (holder) {
|
||||
is ButtonTipViewHolder -> {
|
||||
val tipItem = item as AdapterItem.TipItem
|
||||
holder.bind(tipItem.tip)
|
||||
}
|
||||
is TabHeaderViewHolder -> {
|
||||
val tabHeader = item as AdapterItem.TabHeader
|
||||
holder.bind(tabHeader.isPrivate, tabHeader.hasTabs)
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.mozilla.fenix.home.HomeFragmentAction
|
|||
import org.mozilla.fenix.home.HomeFragmentDirections
|
||||
import org.mozilla.fenix.home.HomeFragmentStore
|
||||
import org.mozilla.fenix.home.Tab
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
|
||||
|
@ -163,6 +164,8 @@ interface SessionControlController {
|
|||
* @see [TabSessionInteractor.onOpenNewTabClicked]
|
||||
*/
|
||||
fun handleonOpenNewTabClicked()
|
||||
|
||||
fun handleCloseTip(tip: Tip)
|
||||
}
|
||||
|
||||
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
||||
|
@ -386,6 +389,10 @@ class DefaultSessionControlController(
|
|||
openSearchScreen()
|
||||
}
|
||||
|
||||
override fun handleCloseTip(tip: Tip) {
|
||||
fragmentStore.dispatch(HomeFragmentAction.RemoveTip(tip))
|
||||
}
|
||||
|
||||
private fun showCollectionCreationFragment(
|
||||
step: SaveCollectionStep,
|
||||
selectedTabIds: Array<String>? = null,
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.view.View
|
|||
import mozilla.components.feature.tab.collections.Tab
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
|
||||
/**
|
||||
* Interface for collection related actions in the [SessionControlInteractor].
|
||||
|
@ -104,6 +105,13 @@ interface OnboardingInteractor {
|
|||
fun onReadPrivacyNoticeClicked()
|
||||
}
|
||||
|
||||
interface TipInteractor {
|
||||
/**
|
||||
* Dismisses the tip view adapter
|
||||
*/
|
||||
fun onCloseTip(tip: Tip)
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for tab related actions in the [SessionControlInteractor].
|
||||
*/
|
||||
|
@ -205,7 +213,7 @@ interface TopSiteInteractor {
|
|||
@SuppressWarnings("TooManyFunctions")
|
||||
class SessionControlInteractor(
|
||||
private val controller: SessionControlController
|
||||
) : CollectionInteractor, OnboardingInteractor, TabSessionInteractor, TopSiteInteractor {
|
||||
) : CollectionInteractor, OnboardingInteractor, TabSessionInteractor, TopSiteInteractor, TipInteractor {
|
||||
override fun onCloseTab(sessionId: String) {
|
||||
controller.handleCloseTab(sessionId)
|
||||
}
|
||||
|
@ -301,4 +309,8 @@ class SessionControlInteractor(
|
|||
override fun onOpenNewTabClicked() {
|
||||
controller.handleonOpenNewTabClicked()
|
||||
}
|
||||
|
||||
override fun onCloseTip(tip: Tip) {
|
||||
controller.handleCloseTip(tip)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.mozilla.fenix.home.HomeScreenViewModel
|
|||
import org.mozilla.fenix.home.Mode
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.home.Tab
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
|
||||
val noTabMessage = AdapterItem.NoContentMessageWithAction(
|
||||
R.string.no_open_tabs_header_2,
|
||||
|
@ -39,10 +40,13 @@ private fun normalModeAdapterItems(
|
|||
tabs: List<Tab>,
|
||||
topSites: List<TopSite>,
|
||||
collections: List<TabCollection>,
|
||||
expandedCollections: Set<Long>
|
||||
expandedCollections: Set<Long>,
|
||||
tip: Tip?
|
||||
): List<AdapterItem> {
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
tip?.let { items.add(AdapterItem.TipItem(it)) }
|
||||
|
||||
if (topSites.isNotEmpty()) {
|
||||
items.add(AdapterItem.TopSiteHeader)
|
||||
items.add(AdapterItem.TopSiteList(topSites))
|
||||
|
@ -150,7 +154,7 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
|
|||
}
|
||||
|
||||
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
|
||||
is Mode.Normal -> normalModeAdapterItems(tabs, topSites, collections, expandedCollections)
|
||||
is Mode.Normal -> normalModeAdapterItems(tabs, topSites, collections, expandedCollections, tip)
|
||||
is Mode.Private -> privateModeAdapterItems(tabs)
|
||||
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package org.mozilla.fenix.home.tips
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.button_tip_item.view.*
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.components.tips.TipType
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
|
||||
|
||||
class ButtonTipViewHolder(
|
||||
val view: View,
|
||||
val interactor: SessionControlInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
var tip: Tip? = null
|
||||
|
||||
fun bind(tip: Tip) {
|
||||
require(tip.type is TipType.Button)
|
||||
|
||||
this.tip = tip
|
||||
|
||||
view.apply {
|
||||
context.components.analytics.metrics.track(Event.TipDisplayed(tip.identifier))
|
||||
|
||||
tip_header_text.text = tip.title
|
||||
tip_description_text.text = tip.description
|
||||
tip_button.text = tip.type.text
|
||||
|
||||
if (tip.learnMoreURL == null) {
|
||||
tip_learn_more.visibility = View.GONE
|
||||
} else {
|
||||
val learnMoreText = context.getString(R.string.search_suggestions_onboarding_learn_more_link)
|
||||
val textWithLink = SpannableString(learnMoreText).apply {
|
||||
setSpan(UnderlineSpan(), 0, learnMoreText.length, 0)
|
||||
}
|
||||
|
||||
tip_learn_more.text = textWithLink
|
||||
|
||||
tip_learn_more.setOnClickListener {
|
||||
(context as HomeActivity).openToBrowserAndLoad(
|
||||
searchTermOrURL = tip.learnMoreURL,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tip_button.setOnClickListener {
|
||||
tip.type.action.invoke()
|
||||
context.components.analytics.metrics.track(
|
||||
Event.TipPressed(tip.identifier)
|
||||
)
|
||||
}
|
||||
|
||||
tip_close.setOnClickListener {
|
||||
context.components.analytics.metrics.track(Event.TipClosed(tip.identifier))
|
||||
|
||||
context.settings().preferences
|
||||
.edit()
|
||||
.putBoolean(tip.identifier, false)
|
||||
.apply()
|
||||
|
||||
interactor.onCloseTip(tip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.button_tip_item
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ import org.mozilla.fenix.ext.showToolbar
|
|||
/**
|
||||
* Lets the user customize the UI.
|
||||
*/
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class CustomizationFragment : PreferenceFragmentCompat() {
|
||||
private lateinit var radioLightTheme: RadioButtonPreference
|
||||
private lateinit var radioDarkTheme: RadioButtonPreference
|
||||
|
|
|
@ -42,6 +42,7 @@ class PrivateBrowsingFragment : PreferenceFragmentCompat() {
|
|||
|
||||
findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_open_links_in_a_private_tab))?.apply {
|
||||
onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||
isChecked = context.settings().openLinksInAPrivateTab
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>(getPreferenceKey
|
||||
|
|
|
@ -21,13 +21,15 @@ import java.util.Locale
|
|||
|
||||
object SupportUtils {
|
||||
const val RATE_APP_URL = "market://details?id=" + BuildConfig.APPLICATION_ID
|
||||
const val MOZILLA_MANIFESTO_URL = "https://www.mozilla.org/en-GB/about/manifesto/"
|
||||
const val POCKET_TRENDING_URL = "https://getpocket.com/fenix-top-articles"
|
||||
const val WIKIPEDIA_URL = "https://www.wikipedia.org/"
|
||||
const val FENIX_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}"
|
||||
const val FIREFOX_BETA_PLAY_STORE_URL = "market://details?id=org.mozilla.firefox_beta"
|
||||
const val FIREFOX_NIGHTLY_PLAY_STORE_URL = "market://details?id=org.mozilla.fennec_aurora"
|
||||
const val YOUTUBE_URL = "https://www.youtube.com/"
|
||||
|
||||
enum class SumoTopic(internal val topicStr: String) {
|
||||
FENIX_MOVING("sync-delist"),
|
||||
HELP("faq-android"),
|
||||
PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"),
|
||||
YOUR_RIGHTS("your-rights"),
|
||||
|
|
|
@ -137,6 +137,12 @@ class Settings private constructor(
|
|||
default = true
|
||||
)
|
||||
|
||||
// If any of the prefs have been modified, quit displaying the fenix moved tip
|
||||
fun shouldDisplayFenixMovingTip(): Boolean =
|
||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip), true) &&
|
||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_firefox_nightly_tip), true) &&
|
||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_tip), true)
|
||||
|
||||
var defaultSearchEngineName by stringPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_search_engine),
|
||||
default = ""
|
||||
|
@ -250,6 +256,11 @@ class Settings private constructor(
|
|||
default = true
|
||||
)
|
||||
|
||||
fun isDefaultBrowser(): Boolean {
|
||||
val browsers = BrowsersCache.all(appContext)
|
||||
return browsers.isDefaultBrowser
|
||||
}
|
||||
|
||||
val shouldUseAutoBatteryTheme by booleanPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_auto_battery_theme),
|
||||
default = false
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="225"
|
||||
android:endColor="#B833E1"
|
||||
android:angle="45"
|
||||
android:endColor="#AB71FF"
|
||||
android:startColor="#7542E5"
|
||||
android:type="linear" />
|
||||
<size
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tip_card"
|
||||
android:background="@drawable/cfr_background_gradient"
|
||||
style="@style/OnboardingCardLight"
|
||||
android:padding="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_header_text"
|
||||
android:layout_width="0dp"
|
||||
android:maxLines="2"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="Header text"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tip_close"
|
||||
android:tint="@color/primary_text_dark_theme"
|
||||
app:srcCompat="@drawable/ic_close"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/create_collection_close"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_description_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
tools:text="Tip description"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_header_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_learn_more"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search_suggestions_onboarding_learn_more_link"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
app:layout_constraintEnd_toEndOf="@id/tip_description_text"
|
||||
app:layout_constraintStart_toStartOf="@id/tip_description_text"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_description_text"
|
||||
tools:textColor="@color/accent_high_contrast_private_theme"/>
|
||||
|
||||
<Button
|
||||
style="@style/NeutralButton"
|
||||
android:id="@+id/tip_button"
|
||||
android:layout_marginTop="8dp"
|
||||
tools:text="Call to action"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_learn_more"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -6,13 +6,12 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingEnd="?android:attr/scrollbarSize">
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/pbm_shortcut_popup_background">
|
||||
android:background="@drawable/cfr_background_gradient">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cfr_message"
|
||||
|
@ -38,21 +38,22 @@
|
|||
app:fontFamily="@font/metropolis_medium" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cfr_pos_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/rounded_gray_corners"
|
||||
android:text="@string/cfr_pos_button_text"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/above_dark_theme"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cfr_message"
|
||||
style="@style/MetropolisButton"/>
|
||||
android:id="@+id/cfr_pos_button"
|
||||
style="@style/MetropolisButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/rounded_gray_corners"
|
||||
android:text="@string/cfr_pos_button_text"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/above_dark_theme"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cfr_message" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cfr_neg_button"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
android:id="@+id/top_site_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="4dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
|
|
|
@ -160,4 +160,9 @@
|
|||
<string name="pref_key_top_sites_size" translatable="false">pref_key_top_sites_size</string>
|
||||
|
||||
<string name="pref_key_show_first_time_pwa" translatable="false">pref_key_show_first_time_pwa</string>
|
||||
|
||||
<string name="pref_key_migrating_from_fenix_nightly_tip" translatable="false">pref_key_migrating_from_fenix_nightly_tip</string>
|
||||
<string name="pref_key_migrating_from_firefox_nightly_tip" translatable="false">pref_key_migrating_from_firefox_nightly_tip</string>
|
||||
<string name="pref_key_migrating_from_fenix_tip" translatable="false">pref_key_migrating_from_fenix_tip</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -842,6 +842,34 @@
|
|||
<!-- Text for the snackbar to show the user that the deletion of browsing data is in progress -->
|
||||
<string name="deleting_browsing_data_in_progress">Deleting browsing data…</string>
|
||||
|
||||
<!-- Tips -->
|
||||
<!-- text for firefox preview moving tip header "Firefox Preview" and "Firefox Nightly" are intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header">Firefox Preview is now Firefox Nightly</string>
|
||||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description">
|
||||
Firefox Nightly gets updated every night and has experimental new features.
|
||||
However, it may be less stable. Download our beta browser for a more stable experience.</string>
|
||||
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_button">Get Mozilla Firefox Browser</string>
|
||||
|
||||
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly has moved</string>
|
||||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description_preview_installed">
|
||||
This app will no longer receive security updates. Stop using this app and switch to the new Nightly.
|
||||
\n\nTo transfer your bookmarks, logins, and history to another app, create a Firefox account.</string>
|
||||
<!-- text for firefox preview moving tip button -->
|
||||
<string name="tip_firefox_preview_moved_button_preview_installed">Switch to the new Nightly</string>
|
||||
|
||||
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header_preview_not_installed">Firefox Nightly has moved</string>
|
||||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description_preview_not_installed">
|
||||
This app will no longer receive security updates. Get the new Nightly and stop using this app.
|
||||
\n\nTo transfer your bookmarks, logins, and history to another app, create a Firefox account.</string>
|
||||
<!-- text for firefox preview moving tip button -->
|
||||
<string name="tip_firefox_preview_moved_button_preview_not_installed">Get the new Nightly</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<!-- Text for onboarding welcome message
|
||||
The first parameter is the name of the app (e.g. Firefox Preview) -->
|
||||
|
|
|
@ -177,6 +177,9 @@ The following metrics are added to the ping:
|
|||
| sync_auth.sign_up |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User registered a new Firefox Account, and was signed into it |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-09-01 |
|
||||
| tab.media_pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-09-01 |
|
||||
| tab.media_play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-09-01 |
|
||||
| tip.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was closed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|<ul><li>identifier: The identifier of the tip closed</li></ul>|2020-09-01 |
|
||||
| tip.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|<ul><li>identifier: The identifier of the tip displayed</li></ul>|2020-09-01 |
|
||||
| tip.pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip's button was pressed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|<ul><li>identifier: The identifier of the tip the action was taken on</li></ul>|2020-09-01 |
|
||||
| toolbar_settings.changed_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user selected a new position for the toolbar |[1](https://github.com/mozilla-mobile/fenix/pull/6608)|<ul><li>position: A string that indicates the new position of the toolbar TOP or BOTTOM</li></ul>|2020-09-01 |
|
||||
| top_sites.open_in_new_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-09-01 |
|
||||
| top_sites.open_in_private_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new private tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-09-01 |
|
||||
|
|
Loading…
Reference in New Issue