Bug 1817070 - Experimentation with different Re-engagement notifications

fenix/112.0
Roger Yang 1 year ago committed by mergify[bot]
parent b6901ddf07
commit 53b2db944e

@ -107,13 +107,16 @@ pre-permission-notification-prompt:
type: boolean
description: "if true, the pre-permission notification prompt is shown to the user."
re-engagement-notification:
description: A feature that shows the re-enagement notification if the user is inactive.
description: A feature that shows the re-engagement notification if the user is inactive.
hasExposure: true
exposureDescription: ""
variables:
enabled:
type: boolean
description: "If true, the re-engagement notification is shown to the inactive user."
type:
type: int
description: The type of re-engagement notification that is shown to the inactive user.
search-term-groups:
description: A feature allowing the grouping of URLs around the search term that it came from.
hasExposure: true

@ -167,12 +167,16 @@ features:
enabled: true
re-engagement-notification:
description: A feature that shows the re-enagement notification if the user is inactive.
description: A feature that shows the re-engagement notification if the user is inactive.
variables:
enabled:
description: If true, the re-engagement notification is shown to the inactive user.
type: Boolean
default: true
type:
description: The type of re-engagement notification that is shown to the inactive user.
type: Int
default: 0
pre-permission-notification-prompt:
description: A feature that shows the pre-permission notification prompt.

@ -101,10 +101,10 @@ import org.mozilla.fenix.gleanplumb.MessageNotificationWorker
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.intent.AssistIntentProcessor
import org.mozilla.fenix.home.intent.CrashReporterIntentProcessor
import org.mozilla.fenix.home.intent.DefaultBrowserIntentProcessor
import org.mozilla.fenix.home.intent.HomeDeepLinkIntentProcessor
import org.mozilla.fenix.home.intent.OpenBrowserIntentProcessor
import org.mozilla.fenix.home.intent.OpenSpecificTabIntentProcessor
import org.mozilla.fenix.home.intent.ReEngagementIntentProcessor
import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor
import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
@ -199,7 +199,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
StartSearchIntentProcessor(),
OpenBrowserIntentProcessor(this, ::getIntentSessionId),
OpenSpecificTabIntentProcessor(this),
DefaultBrowserIntentProcessor(this),
ReEngagementIntentProcessor(this, settings()),
)
}

@ -1,47 +0,0 @@
/* 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.home.intent
import android.content.Intent
import androidx.navigation.NavController
import mozilla.components.concept.engine.EngineSession
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.ext.openSetDefaultBrowserOption
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker.Companion.isReEngagementNotificationIntent
/**
* When the default browser notification is tapped we need to launch [openSetDefaultBrowserOption]
*
* This should only happens once in a user's lifetime since once the user taps on the default browser
* notification, [settings.shouldShowDefaultBrowserNotification] will return false
*/
class DefaultBrowserIntentProcessor(
private val activity: HomeActivity,
) : HomeIntentProcessor {
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
return when {
isReEngagementNotificationIntent(intent) -> {
Events.reEngagementNotifTapped.record(NoExtras())
activity.browsingModeManager.mode = BrowsingMode.Private
activity.openToBrowserAndLoad(
ReEngagementNotificationWorker.NOTIFICATION_TARGET_URL,
newTab = true,
from = BrowserDirection.FromGlobal,
flags = EngineSession.LoadUrlFlags.external(),
)
true
}
else -> false
}
}
}

@ -0,0 +1,64 @@
/* 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.home.intent
import android.content.Intent
import androidx.navigation.NavController
import androidx.navigation.navOptions
import mozilla.components.concept.engine.EngineSession
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker.Companion.isReEngagementNotificationIntent
import org.mozilla.fenix.utils.Settings
/**
* Handle when the re-engagement notification is tapped
*
* This should only happens once in a user's lifetime notification,
* [settings.shouldShowReEngagementNotification] will return false if the user already seen the
* notification.
*/
class ReEngagementIntentProcessor(
private val activity: HomeActivity,
private val settings: Settings,
) : HomeIntentProcessor {
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
return when {
isReEngagementNotificationIntent(intent) -> {
Events.reEngagementNotifTapped.record(NoExtras())
when (settings.reEngagementNotificationType) {
ReEngagementNotificationWorker.NOTIFICATION_TYPE_B -> {
val directions = NavGraphDirections.actionGlobalSearchDialog(sessionId = null)
val options = navOptions {
popUpTo(R.id.homeFragment)
}
navController.nav(null, directions, options)
}
else -> {
activity.browsingModeManager.mode = BrowsingMode.Private
activity.openToBrowserAndLoad(
ReEngagementNotificationWorker.NOTIFICATION_TARGET_URL,
newTab = true,
from = BrowserDirection.FromGlobal,
flags = EngineSession.LoadUrlFlags.external(),
)
}
}
true
}
else -> false
}
}
}

@ -78,18 +78,28 @@ class ReEngagementNotificationWorker(
)
with(applicationContext) {
return createBaseNotification(
this,
channelId,
getString(R.string.notification_re_engagement_title),
getString(R.string.notification_re_engagement_text, getString(R.string.app_name)),
pendingIntent,
)
val title = when (settings().reEngagementNotificationType) {
NOTIFICATION_TYPE_A -> getString(R.string.notification_re_engagement_A_title)
NOTIFICATION_TYPE_B -> getString(R.string.notification_re_engagement_B_title)
else -> getString(R.string.notification_re_engagement_title)
}
val text = when (settings().reEngagementNotificationType) {
NOTIFICATION_TYPE_A ->
getString(R.string.notification_re_engagement_A_text, getString(R.string.app_name))
NOTIFICATION_TYPE_B -> getString(R.string.notification_re_engagement_B_text)
else -> getString(R.string.notification_re_engagement_text, getString(R.string.app_name))
}
return createBaseNotification(this, channelId, title, text, pendingIntent)
}
}
companion object {
const val NOTIFICATION_TARGET_URL = "https://www.mozilla.org/firefox/privacy/"
const val NOTIFICATION_TYPE_A = 1
const val NOTIFICATION_TYPE_B = 2
private const val NOTIFICATION_PENDING_INTENT_TAG = "org.mozilla.fenix.re-engagement"
private const val INTENT_RE_ENGAGEMENT_NOTIFICATION = "org.mozilla.fenix.re-engagement.intent"
private const val NOTIFICATION_TAG = "org.mozilla.fenix.re-engagement.tag"

@ -667,6 +667,13 @@ class Settings(private val appContext: Context) : PreferencesHolder {
featureFlag = true,
)
/**
* Indicates if the re-engagement notification feature is enabled
*/
public val reEngagementNotificationType: Int
get() =
FxNimbus.features.reEngagementNotification.value().type
val shouldUseAutoBatteryTheme by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_auto_battery_theme),
default = false,

@ -1178,14 +1178,14 @@
%1$s is a placeholder that will be replaced by the app name. -->
<string name="notification_re_engagement_text">Browse with no saved cookies or history in %1$s</string>
<!-- Title A shown in the notification that pops up to re-engage the user -->
<string name="notification_re_engagement_A_title" tools:ignore="UnusedResources">Browse without a trace</string>
<string name="notification_re_engagement_A_title">Browse without a trace</string>
<!-- Text A shown in the notification that pops up to re-engage the user.
%1$s is a placeholder that will be replaced by the app name. -->
<string name="notification_re_engagement_A_text" tools:ignore="UnusedResources">Private browsing in %1$s doesnt save your info.</string>
<string name="notification_re_engagement_A_text">Private browsing in %1$s doesnt save your info.</string>
<!-- Title B shown in the notification that pops up to re-engage the user -->
<string name="notification_re_engagement_B_title" tools:ignore="UnusedResources">Start your first search</string>
<string name="notification_re_engagement_B_title">Start your first search</string>
<!-- Text B shown in the notification that pops up to re-engage the user -->
<string name="notification_re_engagement_B_text" tools:ignore="UnusedResources">Find something nearby. Or discover something fun.</string>
<string name="notification_re_engagement_B_text">Find something nearby. Or discover something fun.</string>
<!-- Snackbar -->
<!-- Text shown in snackbar when user deletes a collection -->

@ -22,12 +22,14 @@ import org.junit.runner.RunWith
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
import org.mozilla.fenix.utils.Settings
@RunWith(FenixRobolectricTestRunner::class)
class DefaultBrowserIntentProcessorTest {
class ReEngagementIntentProcessorTest {
@get:Rule
val gleanTestRule = GleanTestRule(testContext)
@ -36,7 +38,8 @@ class DefaultBrowserIntentProcessorTest {
fun `do not process blank intents`() {
val navController: NavController = mockk()
val out: Intent = mockk()
val result = DefaultBrowserIntentProcessor(mockk())
val settings: Settings = mockk()
val result = ReEngagementIntentProcessor(mockk(), settings)
.process(Intent(), navController, out)
assertFalse(result)
@ -45,21 +48,23 @@ class DefaultBrowserIntentProcessorTest {
}
@Test
fun `process re-engagement notification intents`() {
fun `WHEN re-engagement notification type is type A THEN load target URL`() {
val navController: NavController = mockk(relaxed = true)
val out: Intent = mockk()
val activity: HomeActivity = mockk(relaxed = true)
val browsingModeManager: BrowsingModeManager = mockk(relaxed = true)
val settings: Settings = mockk(relaxed = true)
val intent = Intent().apply {
putExtra("org.mozilla.fenix.re-engagement.intent", true)
}
every { activity.applicationContext } returns testContext
every { activity.browsingModeManager } returns browsingModeManager
every { settings.reEngagementNotificationType } returns ReEngagementNotificationWorker.NOTIFICATION_TYPE_A
assertNull(Events.reEngagementNotifTapped.testGetValue())
val result = DefaultBrowserIntentProcessor(activity)
val result = ReEngagementIntentProcessor(activity, settings)
.process(intent, navController, out)
assert(result)
@ -81,4 +86,31 @@ class DefaultBrowserIntentProcessorTest {
verify { navController wasNot Called }
verify { out wasNot Called }
}
@Test
fun `WHEN re-engagement notification type is 2 THEN open search dialog`() {
val navController: NavController = mockk(relaxed = true)
val out: Intent = mockk()
val activity: HomeActivity = mockk(relaxed = true)
val browsingModeManager: BrowsingModeManager = mockk(relaxed = true)
val settings: Settings = mockk(relaxed = true)
val intent = Intent().apply {
putExtra("org.mozilla.fenix.re-engagement.intent", true)
}
every { activity.applicationContext } returns testContext
every { activity.browsingModeManager } returns browsingModeManager
every { settings.reEngagementNotificationType } returns ReEngagementNotificationWorker.NOTIFICATION_TYPE_B
assertNull(Events.reEngagementNotifTapped.testGetValue())
val result = ReEngagementIntentProcessor(activity, settings)
.process(intent, navController, out)
assert(result)
assertNotNull(Events.reEngagementNotifTapped.testGetValue())
val directions = NavGraphDirections.actionGlobalSearchDialog(sessionId = null)
verify { navController.navigate(directions, navOptions = any()) }
}
}
Loading…
Cancel
Save