Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Issue #9208: Adds in-product prompt to homescreen #9836

Merged
merged 1 commit into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/org/mozilla/fenix/FeatureFlags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ object FeatureFlags {
* Enables picture-in-picture feature
*/
val pictureInPicture = Config.channel.isNightlyOrDebug

/**
* Enables tip feature
*/
val tips = Config.channel.isDebug
}
6 changes: 3 additions & 3 deletions app/src/main/java/org/mozilla/fenix/components/Components.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),

Expand All @@ -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) {
Expand Down
39 changes: 39 additions & 0 deletions app/src/main/java/org/mozilla/fenix/components/tips/TipManager.kt
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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 -> { "" }
}
}
}
8 changes: 6 additions & 2 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
)
)
}
Expand Down Expand Up @@ -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()
)
)

Expand Down
12 changes: 9 additions & 3 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -58,15 +59,17 @@ 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 {
data class Change(
val tabs: List<Tab>,
val topSites: List<TopSite>,
val mode: Mode,
val collections: List<TabCollection>
val collections: List<TabCollection>,
val tip: Tip? = null
) :
HomeFragmentAction()

Expand All @@ -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(
Expand All @@ -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()
Expand All @@ -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) }
}
}