commit ecbfaf8159aec595013ce3edefb2be7af747a114
parent c4856b04fbfae7b54de746adf61629cb6f7f1564
Author: Marcin KoziĆski <mkozinski@mozilla.com>
Date: Thu, 2 Oct 2025 13:03:27 +0000
Bug 1992079 - Keep legacy review prompt behavior if Nimbus flag for custom review prompt disabled r=android-reviewers,twhite
Differential Revision: https://phabricator.services.mozilla.com/D267197
Diffstat:
4 files changed, 69 insertions(+), 6 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
@@ -290,6 +290,8 @@ class Components(private val context: Context) {
SetupChecklistTelemetryMiddleware(),
ReviewPromptMiddleware(
isReviewPromptFeatureEnabled = { settings.customReviewPromptFeatureEnabled },
+ numberOfAppLaunches = { settings.numberOfAppLaunches },
+ isDefaultBrowser = { settings.isDefaultBrowser },
isTelemetryEnabled = { settings.isTelemetryEnabled },
createJexlHelper = nimbus::createJexlHelper,
nimbusEventStore = nimbus.events,
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddleware.kt
@@ -26,6 +26,8 @@ private const val REVIEW_PROMPT_SHOWN_NIMBUS_EVENT_ID = "review_prompt_shown"
* [Middleware] evaluating the triggers to show a review prompt.
*
* @param isReviewPromptFeatureEnabled If returns false disables the entire feature and prompt will never be shown.
+ * @param numberOfAppLaunches Returns number of app launches.
+ * @param isDefaultBrowser Returns true if app is set as the default system browser.
* @param isTelemetryEnabled Returns true if the user allows sending technical and interaction telemetry.
* @param createJexlHelper Returns a helper for evaluating JEXL expressions.
* @param buildTriggerMainCriteria Builds a sequence of trigger's main criteria that all need to be true.
@@ -35,21 +37,20 @@ private const val REVIEW_PROMPT_SHOWN_NIMBUS_EVENT_ID = "review_prompt_shown"
*/
class ReviewPromptMiddleware(
private val isReviewPromptFeatureEnabled: () -> Boolean,
+ private val numberOfAppLaunches: () -> Int,
+ private val isDefaultBrowser: () -> Boolean,
private val isTelemetryEnabled: () -> Boolean,
private val createJexlHelper: () -> NimbusMessagingHelperInterface,
private val buildTriggerMainCriteria: (NimbusMessagingHelperInterface) -> Sequence<Boolean> =
- TiggerBuilder.mainCriteria(isReviewPromptFeatureEnabled),
+ TiggerBuilder::mainCriteria,
private val buildTriggerSubCriteria: (NimbusMessagingHelperInterface) -> Sequence<Boolean> =
TiggerBuilder::subCriteria,
private val nimbusEventStore: NimbusEventStore,
) : Middleware<AppState, AppAction> {
private object TiggerBuilder {
- fun mainCriteria(
- isReviewPromptEnabled: () -> Boolean,
- ): (NimbusMessagingHelperInterface) -> Sequence<Boolean> = { jexlHelper ->
- sequence {
- yield(isReviewPromptEnabled())
+ fun mainCriteria(jexlHelper: NimbusMessagingHelperInterface): Sequence<Boolean> {
+ return sequence {
yield(hasNotBeenPromptedLastFourMonths(jexlHelper))
yield(usedAppOnAtLeastFourOfLastSevenDays(jexlHelper))
}
@@ -90,6 +91,16 @@ class ReviewPromptMiddleware(
return
}
+ if (!isReviewPromptFeatureEnabled()) {
+ // Keep the simpler legacy behavior.
+ if (legacyReviewPromptTrigger(numberOfAppLaunches, isDefaultBrowser)) {
+ context.dispatch(ShowPlayStorePrompt)
+ } else {
+ context.dispatch(DoNotShowReviewPrompt)
+ }
+ return
+ }
+
createJexlHelper().use { jexlHelper ->
val allMainCriteriaSatisfied = buildTriggerMainCriteria(jexlHelper).all { it }
if (!allMainCriteriaSatisfied) {
@@ -158,3 +169,17 @@ internal fun usedAppOnAtLeastFourOfLastSevenDays(
): Boolean {
return jexlHelper.evalJexlSafe("'app_opened'|eventCountNonZero('Days', 7) >= 4")
}
+
+private const val NUMBER_OF_LAUNCHES_REQUIRED = 5
+
+/**
+ * Matches logic from ReviewPromptController.shouldShowPrompt, which has been deleted.
+ * Kept so we can fall back to it in case the custom review prompt is disabled with a kill-switch.
+ */
+@VisibleForTesting
+internal fun legacyReviewPromptTrigger(
+ numberOfAppLaunches: () -> Int,
+ isDefaultBrowser: () -> Boolean,
+): Boolean {
+ return isDefaultBrowser() && numberOfAppLaunches() >= NUMBER_OF_LAUNCHES_REQUIRED
+}
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/LegacyReviewPromptTriggerTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/LegacyReviewPromptTriggerTest.kt
@@ -0,0 +1,34 @@
+/* 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.reviewprompt
+
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class LegacyReviewPromptTriggerTest {
+
+ private var numberOfAppLaunches = 5
+ private var isDefaultBrowser = true
+
+ @Test
+ fun `WHEN app is the default browser AND was launched at least 5 times THEN legacy trigger is satisfied`() {
+ assertTrue(legacyReviewPromptTrigger({ numberOfAppLaunches }, { isDefaultBrowser }))
+ }
+
+ @Test
+ fun `WHEN app isn't the default browser THEN legacy trigger is not satisfied`() {
+ isDefaultBrowser = false
+
+ assertFalse(legacyReviewPromptTrigger({ numberOfAppLaunches }, { isDefaultBrowser }))
+ }
+
+ @Test
+ fun `WHEN app was launched less than 5 times THEN legacy trigger is not satisfied`() {
+ numberOfAppLaunches = 4
+
+ assertFalse(legacyReviewPromptTrigger({ numberOfAppLaunches }, { isDefaultBrowser }))
+ }
+}
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddlewareTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddlewareTest.kt
@@ -29,6 +29,8 @@ class ReviewPromptMiddlewareTest {
middlewares = listOf(
ReviewPromptMiddleware(
isReviewPromptFeatureEnabled = { true },
+ numberOfAppLaunches = { 5 },
+ isDefaultBrowser = { true },
isTelemetryEnabled = { isTelemetryEnabled },
createJexlHelper = {
object : NimbusMessagingHelperInterface {