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

Commit

Permalink
For #13507 - Extracts review prompt behavior into ReviewPromptController
Browse files Browse the repository at this point in the history
  • Loading branch information
boek committed Aug 27, 2020
1 parent e96eb25 commit 56b730f
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 76 deletions.
2 changes: 2 additions & 0 deletions app/src/main/java/org/mozilla/fenix/HomeActivity.kt
Expand Up @@ -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.
}

Expand Down
@@ -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
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/org/mozilla/fenix/components/Services.kt
Expand Up @@ -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

Expand Down Expand Up @@ -44,4 +45,11 @@ class Services(
}
)
}

val reviewPromptController by lazy {
ReviewPromptController(
context,
FenixReviewSettings(context.settings())
)
}
}
18 changes: 2 additions & 16 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Expand Up @@ -176,6 +176,8 @@ class HomeFragment : Fragment() {
if (!onboarding.userHasBeenOnboarded()) {
requireComponents.analytics.metrics.track(Event.OpenedAppFirstRun)
}

requireComponents.services.reviewPromptController.promptReview(requireActivity())
}
}

Expand Down Expand Up @@ -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))
Expand Down
68 changes: 11 additions & 57 deletions app/src/main/java/org/mozilla/fenix/utils/Settings.kt
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/res/values/preference_keys.xml
Expand Up @@ -58,9 +58,8 @@
<string name="pref_key_open_in_app_opened" translatable="false">pref_key_open_in_app_opened</string>
<string name="pref_key_install_pwa_opened" translatable="false">pref_key_install_pwa_opened</string>
<string name="pref_key_install_pwa_visits" translatable="false">pref_key_install_pwa_visits</string>
<string name="pref_key_feedback_prompt_shown" translatable="false">pref_key_feedback_prompt_shown</string>
<string name="pref_key_times_opened_after_install" translatable="false">pref_key_times_opened_after_install</string>
<string name="pref_key_time_prompt_shown" translatable="false">pref_key_time_prompt_shown</string>
<string name="pref_key_times_app_opened" translatable="false">pref_key_times_app_opened</string>
<string name="pref_key_last_review_prompt_shown_time" translatable="false">pref_key_last_review_prompt_shown_time</string>

<!-- Data Choices -->
<string name="pref_key_telemetry" translatable="false">pref_key_telemetry</string>
Expand Down

0 comments on commit 56b730f

Please sign in to comment.