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

Commit

Permalink
Issue #9208: Adds in-product prompt to homescreen
Browse files Browse the repository at this point in the history
  • Loading branch information
sblatz committed Apr 16, 2020
1 parent 8185ba7 commit bd7fd65
Show file tree
Hide file tree
Showing 27 changed files with 518 additions and 41 deletions.
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 @@ -48,4 +48,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 @@ -197,7 +199,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 @@ -374,7 +377,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) }
}
}

0 comments on commit bd7fd65

Please sign in to comment.