diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index ed9b83c58..5c6bc0492 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -40,7 +40,7 @@ import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.resetPoliciesAfter import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.shortcut.FirstTimePwaObserver +import org.mozilla.fenix.shortcut.PwaOnboardingObserver import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay /** @@ -156,9 +156,9 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { } session?.register(toolbarSessionObserver, viewLifecycleOwner, autoPause = true) - if (settings.shouldShowFirstTimePwaFragment) { + if (!settings.userKnowsAboutPwas) { session?.register( - FirstTimePwaObserver( + PwaOnboardingObserver( navController = findNavController(), settings = settings, webAppUseCases = context.components.useCases.webAppUseCases diff --git a/app/src/main/java/org/mozilla/fenix/perf/Performance.kt b/app/src/main/java/org/mozilla/fenix/perf/Performance.kt index 4b0a73b22..f1eb26ffb 100644 --- a/app/src/main/java/org/mozilla/fenix/perf/Performance.kt +++ b/app/src/main/java/org/mozilla/fenix/perf/Performance.kt @@ -74,6 +74,6 @@ object Performance { * Disables the first time PWA popup. */ private fun disableFirstTimePWAPopup(context: Context) { - Settings.getInstance(context).userKnowsAboutPWAs = true + Settings.getInstance(context).userKnowsAboutPwas = true } } diff --git a/app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaFragment.kt b/app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingDialogFragment.kt similarity index 88% rename from app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaFragment.kt rename to app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingDialogFragment.kt index c42c2842f..43b9099fc 100644 --- a/app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingDialogFragment.kt @@ -16,9 +16,9 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.requireComponents /** - * Dialog displayed the first time the user navigates to an installable web app. + * Dialog displayed the third time the user navigates to an installable web app. */ -class FirstTimePwaFragment : DialogFragment() { +class PwaOnboardingDialogFragment : DialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setStyle(STYLE_NO_TITLE, R.style.CreateShortcutDialogStyle) @@ -28,7 +28,7 @@ class FirstTimePwaFragment : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? = inflater.inflate(R.layout.fragment_pwa_first_time, container, false) + ): View? = inflater.inflate(R.layout.fragment_pwa_onboarding, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaObserver.kt b/app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingObserver.kt similarity index 59% rename from app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaObserver.kt rename to app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingObserver.kt index 2c19810d7..5e312df86 100644 --- a/app/src/main/java/org/mozilla/fenix/shortcut/FirstTimePwaObserver.kt +++ b/app/src/main/java/org/mozilla/fenix/shortcut/PwaOnboardingObserver.kt @@ -14,20 +14,23 @@ import org.mozilla.fenix.ext.nav import org.mozilla.fenix.utils.Settings /** - * Displays the [FirstTimePwaFragment] info dialog when a PWA is first opened in the browser. + * Displays the [PwaOnboardingDialogFragment] info dialog when a PWA is opened in the browser for the third time. */ -class FirstTimePwaObserver( +class PwaOnboardingObserver( private val navController: NavController, private val settings: Settings, private val webAppUseCases: WebAppUseCases ) : Session.Observer { override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) { - if (webAppUseCases.isInstallable() && settings.shouldShowFirstTimePwaFragment) { - val directions = BrowserFragmentDirections.actionBrowserFragmentToFirstTimePwaFragment() - navController.nav(R.id.browserFragment, directions) - - settings.userKnowsAboutPWAs = true + if (webAppUseCases.isInstallable() && !settings.userKnowsAboutPwas) { + settings.incrementVisitedInstallableCount() + if (settings.shouldShowPwaOnboarding) { + val directions = + BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment() + navController.nav(R.id.browserFragment, directions) + settings.userKnowsAboutPwas = true + } } } } 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 b6d4530c6..a76825bef 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -53,6 +53,7 @@ class Settings private constructor( const val showLoginsSecureWarningSyncMaxCount = 1 const val showLoginsSecureWarningMaxCount = 1 const val trackingProtectionOnboardingMaximumCount = 1 + const val pwaVisitsToShowPromptMaxCount = 3 const val FENIX_PREFERENCES = "fenix_preferences" private const val showSearchWidgetCFRMaxCount = 3 @@ -146,9 +147,18 @@ class Settings private constructor( // If any of the prefs have been modified, quit displaying the fenix moved tip fun shouldDisplayFenixMovingTip(): Boolean = - preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip), true) && - preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_firefox_nightly_tip), true) && - preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_tip), true) + preferences.getBoolean( + appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip), + true + ) && + preferences.getBoolean( + appContext.getString(R.string.pref_key_migrating_from_firefox_nightly_tip), + true + ) && + preferences.getBoolean( + appContext.getString(R.string.pref_key_migrating_from_fenix_tip), + true + ) private val activeSearchCount by intPreference( appContext.getPreferenceKey(R.string.pref_key_search_count), @@ -167,9 +177,9 @@ class Settings private constructor( fun shouldDisplaySearchWidgetCFR(): Boolean = isActiveSearcher && - searchWidgetCFRDismissCount < showSearchWidgetCFRMaxCount && - !searchWidgetInstalled && - !searchWidgetCFRManuallyDismissed + searchWidgetCFRDismissCount < showSearchWidgetCFRMaxCount && + !searchWidgetInstalled && + !searchWidgetCFRManuallyDismissed private val searchWidgetCFRDisplayCount by intPreference( appContext.getPreferenceKey(R.string.pref_key_search_widget_cfr_display_count), @@ -236,10 +246,10 @@ class Settings private constructor( val isCrashReportingEnabled: Boolean get() = isCrashReportEnabledInBuild && - preferences.getBoolean( - appContext.getPreferenceKey(R.string.pref_key_crash_reporter), - true - ) + preferences.getBoolean( + appContext.getPreferenceKey(R.string.pref_key_crash_reporter), + true + ) val isRemoteDebuggingEnabled by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_remote_debugging), @@ -267,7 +277,7 @@ class Settings private constructor( val shouldShowTrackingProtectionOnboarding: Boolean get() = !isOverrideTPPopupsForPerformanceTest && (trackingProtectionOnboardingCount < trackingProtectionOnboardingMaximumCount && - !trackingProtectionOnboardingShownThisSession) + !trackingProtectionOnboardingShownThisSession) var showSecretDebugMenuThisSession = false @@ -418,14 +428,14 @@ class Settings private constructor( BrowsingMode.Normal } } - set(value) { val lastKnownModeWasPrivate = (value == BrowsingMode.Private) preferences.edit() .putBoolean( - appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private), - lastKnownModeWasPrivate) + appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private), + lastKnownModeWasPrivate + ) .apply() field = value @@ -495,7 +505,9 @@ class Settings private constructor( } val accessibilityServicesEnabled: Boolean - get() { return touchExplorationIsEnabled || switchServiceIsEnabled } + get() { + return touchExplorationIsEnabled || switchServiceIsEnabled + } val toolbarSettingString: String get() = when { @@ -569,22 +581,41 @@ class Settings private constructor( default = false ) - val shouldShowFirstTimePwaFragment: Boolean + fun incrementVisitedInstallableCount() { + preferences.edit().putInt( + appContext.getPreferenceKey(R.string.pref_key_install_pwa_visits), + pwaInstallableVisitCount + 1 + ).apply() + } + + @VisibleForTesting(otherwise = PRIVATE) + internal val pwaInstallableVisitCount by intPreference( + appContext.getPreferenceKey(R.string.pref_key_install_pwa_visits), + default = 0 + ) + + private val userNeedsToVisitInstallableSites: Boolean + get() = pwaInstallableVisitCount < pwaVisitsToShowPromptMaxCount + + val shouldShowPwaOnboarding: Boolean get() { + // We only want to show this on the 3rd time a user visits a site + if (userNeedsToVisitInstallableSites) return false + // ShortcutManager::pinnedShortcuts is only available on Oreo+ - if (!userKnowsAboutPWAs && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val alreadyHavePWaInstalled = + if (!userKnowsAboutPwas && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val alreadyHavePwaInstalled = appContext.getSystemService(ShortcutManager::class.java) .pinnedShortcuts.size > 0 // Users know about PWAs onboarding if they already have PWAs installed. - userKnowsAboutPWAs = alreadyHavePWaInstalled + userKnowsAboutPwas = alreadyHavePwaInstalled } // Show dialog only if user does not know abut PWAs - return !userKnowsAboutPWAs + return !userKnowsAboutPwas } - var userKnowsAboutPWAs by booleanPreference( + var userKnowsAboutPwas by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_user_knows_about_pwa), default = false ) @@ -809,8 +840,12 @@ class Settings private constructor( var savedLoginsSortingStrategy: SortingStrategy get() { return when (savedLoginsSortingStrategyString) { - SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> SortingStrategy.Alphabetically(appContext) - SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> SortingStrategy.LastUsed(appContext) + SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> SortingStrategy.Alphabetically( + appContext + ) + SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> SortingStrategy.LastUsed( + appContext + ) else -> SortingStrategy.Alphabetically(appContext) } } diff --git a/app/src/main/res/layout/fragment_pwa_first_time.xml b/app/src/main/res/layout/fragment_pwa_onboarding.xml similarity index 98% rename from app/src/main/res/layout/fragment_pwa_first_time.xml rename to app/src/main/res/layout/fragment_pwa_onboarding.xml index 395a933a3..1c188eb89 100644 --- a/app/src/main/res/layout/fragment_pwa_first_time.xml +++ b/app/src/main/res/layout/fragment_pwa_onboarding.xml @@ -10,7 +10,7 @@ android:layout_height="match_parent" android:background="@drawable/scrim_background" android:fitsSystemWindows="true" - tools:context="org.mozilla.fenix.shortcut.FirstTimePwaFragment"> + tools:context="org.mozilla.fenix.shortcut.PwaOnboardingDialogFragment"> + android:id="@+id/action_browserFragment_to_pwaOnboardingDialogFragment" + app:destination="@id/pwaOnboardingDialogFragment" /> @@ -680,10 +680,9 @@ android:name="org.mozilla.fenix.shortcut.CreateShortcutFragment" tools:layout="@layout/fragment_create_shortcut" /> + android:id="@+id/pwaOnboardingDialogFragment" + android:name="org.mozilla.fenix.shortcut.PwaOnboardingDialogFragment" + tools:layout="@layout/fragment_pwa_onboarding" /> pref_key_private_mode_opened pref_key_open_in_app_opened pref_key_install_pwa_opened + pref_key_install_pwa_visits pref_key_telemetry diff --git a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt index 79f90813e..789a58954 100644 --- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt +++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt @@ -351,6 +351,31 @@ class SettingsTest { assertTrue(settings.shouldShowSearchSuggestions) } + @Test + fun showPwaFragment() { + // When just created + // Then + assertFalse(settings.shouldShowPwaOnboarding) + + // When visited once + settings.incrementVisitedInstallableCount() + + // Then + assertFalse(settings.shouldShowPwaOnboarding) + + // When visited twice + settings.incrementVisitedInstallableCount() + + // Then + assertFalse(settings.shouldShowPwaOnboarding) + + // When visited thrice + settings.incrementVisitedInstallableCount() + + // Then + assertTrue(settings.shouldShowPwaOnboarding) + } + @Test fun sitePermissionsPhoneFeatureCameraAction() { // When just created