diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
index 3aea224cf..c66b2ea1c 100644
--- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
@@ -232,6 +232,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
setAppAllStartTelemetry(intent.toSafeIntent())
+ components.services.reviewPromptController.trackApplicationLaunch()
+
StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE.
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt
new file mode 100644
index 000000000..36109b893
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt
@@ -0,0 +1,76 @@
+/* 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
+
+import android.app.Activity
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import com.google.android.play.core.ktx.launchReview
+import com.google.android.play.core.ktx.requestReview
+import com.google.android.play.core.review.ReviewManagerFactory
+import org.mozilla.fenix.utils.Settings
+
+/**
+ * Interface that describes the settings needed to track the Review Prompt.
+ */
+interface ReviewSettings {
+ var numberOfAppLaunches: Int
+ val isDefaultBrowser: Boolean
+ var lastReviewPromptTimeInMillis: Long
+}
+
+/**
+ * Wraps `Settings` to conform to `ReviewSettings`.
+ */
+class FenixReviewSettings(
+ val settings: Settings
+): ReviewSettings {
+ override var numberOfAppLaunches: Int
+ get() = settings.numberOfAppLaunches
+ set(value) { settings.numberOfAppLaunches = value }
+ override val isDefaultBrowser: Boolean
+ get() = settings.isDefaultBrowser()
+ override var lastReviewPromptTimeInMillis: Long
+ get() = settings.lastReviewPromptTimeInMillis
+ set(value) { settings.lastReviewPromptTimeInMillis = value }
+}
+
+/**
+ * Controls the Review Prompt behavior.
+ */
+class ReviewPromptController(
+ private val context: Context,
+ private val reviewSettings: ReviewSettings,
+ private val timeNowInMillis: () -> Long = { System.currentTimeMillis() }
+) {
+ suspend fun promptReview(activity: Activity) {
+ if (shouldShowPrompt()) {
+ val manager = ReviewManagerFactory.create(context)
+ val reviewInfo = manager.requestReview()
+ manager.launchReview(activity, reviewInfo)
+
+ reviewSettings.lastReviewPromptTimeInMillis = timeNowInMillis()
+ }
+ }
+
+ fun trackApplicationLaunch() {
+ reviewSettings.numberOfAppLaunches = reviewSettings.numberOfAppLaunches + 1
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ fun shouldShowPrompt(): Boolean {
+ if (!reviewSettings.isDefaultBrowser) { return false }
+
+ val hasOpenedFiveTimes = reviewSettings.numberOfAppLaunches >= 5
+ val apprxFourMonthsAgo = timeNowInMillis() - (APPRX_MONTH_IN_MILLIS * 4)
+ val hasNotBeenPromptedLastFourMonths = reviewSettings.lastReviewPromptTimeInMillis <= apprxFourMonthsAgo
+
+ return hasOpenedFiveTimes && hasNotBeenPromptedLastFourMonths
+ }
+
+ companion object {
+ private const val APPRX_MONTH_IN_MILLIS: Long = 1000L * 60L * 60L * 24L * 30L
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mozilla/fenix/components/Services.kt b/app/src/main/java/org/mozilla/fenix/components/Services.kt
index cdd549f86..fd099ffdd 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Services.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Services.kt
@@ -14,6 +14,7 @@ import mozilla.components.feature.app.links.AppLinksInterceptor
import mozilla.components.service.fxa.manager.FxaAccountManager
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
+import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.Mockable
@@ -44,4 +45,11 @@ class Services(
}
)
}
+
+ val reviewPromptController by lazy {
+ ReviewPromptController(
+ context,
+ FenixReviewSettings(context.settings())
+ )
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
index c5d679575..cac266fe2 100644
--- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
@@ -176,6 +176,8 @@ class HomeFragment : Fragment() {
if (!onboarding.userHasBeenOnboarded()) {
requireComponents.analytics.metrics.track(Event.OpenedAppFirstRun)
}
+
+ requireComponents.services.reviewPromptController.promptReview(requireActivity())
}
}
@@ -570,26 +572,10 @@ class HomeFragment : Fragment() {
recommendPrivateBrowsingShortcut()
}
- // In-app review prompt
- requireContext().settings().incrementNumTimesOpenedAfterInstall()
- handleInAppReviewPrompt()
-
// We only want this observer live just before we navigate away to the collection creation screen
requireComponents.core.tabCollectionStorage.unregister(collectionStorageObserver)
}
- private fun handleInAppReviewPrompt() {
- if (requireContext().settings().shouldShowUserFeedbackPrompt) {
- lifecycleScope.launch {
- val manager = ReviewManagerFactory.create(requireContext())
- val reviewInfo = manager.requestReview()
- manager.launchReview(requireActivity(), reviewInfo)
-
- requireContext().settings().incrementNumTimesFeedbackPromptShown()
- }
- }
- }
-
private fun dispatchModeChanges(mode: Mode) {
if (mode != Mode.fromBrowsingMode(browsingModeManager.mode)) {
homeFragmentStore.dispatch(HomeFragmentAction.ModeChange(mode))
diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
index c9d4f0fb3..c953fdd50 100644
--- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
+++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
@@ -102,11 +102,21 @@ class Settings(private val appContext: Context) : PreferencesHolder {
appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE)
var showTopFrecentSites by featureFlagPreference(
- appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
+ appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
default = false,
featureFlag = FeatureFlags.topFrecentSite
)
+ var numberOfAppLaunches by intPreference(
+ appContext.getPreferenceKey(R.string.pref_key_times_app_opened),
+ default = 0
+ )
+
+ var lastReviewPromptTimeInMillis by longPreference(
+ appContext.getPreferenceKey(R.string.pref_key_last_review_prompt_shown_time),
+ default = 0L
+ )
+
var waitToShowPageUntilFirstPaint by featureFlagPreference(
appContext.getPreferenceKey(R.string.pref_key_wait_first_paint),
default = false,
@@ -795,62 +805,6 @@ class Settings(private val appContext: Context) : PreferencesHolder {
0
)
- private val numTimesFeedbackPromptShown: Int
- get() = preferences.getInt(
- appContext.getPreferenceKey(R.string.pref_key_feedback_prompt_shown),
- 0
- )
-
- fun incrementNumTimesFeedbackPromptShown() {
- preferences.edit().putInt(
- appContext.getPreferenceKey(R.string.pref_key_feedback_prompt_shown),
- numTimesFeedbackPromptShown + 1
- ).apply()
- }
-
- private val numTimesOpenedAfterInstall: Int
- get() = preferences.getInt(
- appContext.getPreferenceKey(R.string.pref_key_times_opened_after_install),
- 0
- )
-
- fun incrementNumTimesOpenedAfterInstall() {
- preferences.edit().putInt(
- appContext.getPreferenceKey(R.string.pref_key_times_opened_after_install),
- numTimesOpenedAfterInstall + 1
- ).apply()
- }
-
- private val timeWhenPromptWasLastShown: Int
- get() = preferences.getInt(
- appContext.getPreferenceKey(R.string.pref_key_time_prompt_shown),
- 0
- )
-
- @RequiresApi(Build.VERSION_CODES.O)
- fun incrementTimeWhenPromptWasLastShown() {
- preferences.edit().putInt(
- appContext.getPreferenceKey(R.string.pref_key_time_prompt_shown),
- LocalDate.now().dayOfYear
- ).apply()
- }
-
- /*
- * User feedback prompt is shown when Firefox is set as the default browser and after the
- * 5th time the user opened the app. This prompt should only be shown once every four months.
- */
- @RequiresApi(Build.VERSION_CODES.O)
- val shouldShowUserFeedbackPrompt: Boolean =
- numTimesOpenedAfterInstall >= 5
- && isDefaultBrowser()
- && (hasBeenFourMonthsSince(timeWhenPromptWasLastShown))
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun hasBeenFourMonthsSince(timeWhenPromptWasLastShown: Int): Boolean {
- val numDays = LocalDate.now().dayOfYear - timeWhenPromptWasLastShown
- return numDays >= MIN_DAYS_SINCE_FEEDBACK_PROMPT
- }
-
val showPrivateModeContextualFeatureRecommender: Boolean
get() {
val focusInstalled = MozillaProductDetector
diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml
index 21e4e4b21..0043ba7b3 100644
--- a/app/src/main/res/values/preference_keys.xml
+++ b/app/src/main/res/values/preference_keys.xml
@@ -58,9 +58,8 @@
pref_key_open_in_app_opened
pref_key_install_pwa_opened
pref_key_install_pwa_visits
- pref_key_feedback_prompt_shown
- pref_key_times_opened_after_install
- pref_key_time_prompt_shown
+ pref_key_times_app_opened
+ pref_key_last_review_prompt_shown_time
pref_key_telemetry