For #12364 - Only show PWA prompt the third time a user visits installable site
parent
ac3df6bc5e
commit
e358f95eed
|
@ -40,7 +40,7 @@ import org.mozilla.fenix.ext.navigateSafe
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.ext.resetPoliciesAfter
|
import org.mozilla.fenix.ext.resetPoliciesAfter
|
||||||
import org.mozilla.fenix.ext.settings
|
import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.shortcut.FirstTimePwaObserver
|
import org.mozilla.fenix.shortcut.PwaOnboardingObserver
|
||||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,9 +156,9 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
||||||
}
|
}
|
||||||
session?.register(toolbarSessionObserver, viewLifecycleOwner, autoPause = true)
|
session?.register(toolbarSessionObserver, viewLifecycleOwner, autoPause = true)
|
||||||
|
|
||||||
if (settings.shouldShowFirstTimePwaFragment) {
|
if (!settings.userKnowsAboutPwas) {
|
||||||
session?.register(
|
session?.register(
|
||||||
FirstTimePwaObserver(
|
PwaOnboardingObserver(
|
||||||
navController = findNavController(),
|
navController = findNavController(),
|
||||||
settings = settings,
|
settings = settings,
|
||||||
webAppUseCases = context.components.useCases.webAppUseCases
|
webAppUseCases = context.components.useCases.webAppUseCases
|
||||||
|
|
|
@ -74,6 +74,6 @@ object Performance {
|
||||||
* Disables the first time PWA popup.
|
* Disables the first time PWA popup.
|
||||||
*/
|
*/
|
||||||
private fun disableFirstTimePWAPopup(context: Context) {
|
private fun disableFirstTimePWAPopup(context: Context) {
|
||||||
Settings.getInstance(context).userKnowsAboutPWAs = true
|
Settings.getInstance(context).userKnowsAboutPwas = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NO_TITLE, R.style.CreateShortcutDialogStyle)
|
setStyle(STYLE_NO_TITLE, R.style.CreateShortcutDialogStyle)
|
||||||
|
@ -28,7 +28,7 @@ class FirstTimePwaFragment : DialogFragment() {
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
|
@ -14,20 +14,23 @@ import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.utils.Settings
|
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 navController: NavController,
|
||||||
private val settings: Settings,
|
private val settings: Settings,
|
||||||
private val webAppUseCases: WebAppUseCases
|
private val webAppUseCases: WebAppUseCases
|
||||||
) : Session.Observer {
|
) : Session.Observer {
|
||||||
|
|
||||||
override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) {
|
override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) {
|
||||||
if (webAppUseCases.isInstallable() && settings.shouldShowFirstTimePwaFragment) {
|
if (webAppUseCases.isInstallable() && !settings.userKnowsAboutPwas) {
|
||||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToFirstTimePwaFragment()
|
settings.incrementVisitedInstallableCount()
|
||||||
|
if (settings.shouldShowPwaOnboarding) {
|
||||||
|
val directions =
|
||||||
|
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment()
|
||||||
navController.nav(R.id.browserFragment, directions)
|
navController.nav(R.id.browserFragment, directions)
|
||||||
|
settings.userKnowsAboutPwas = true
|
||||||
settings.userKnowsAboutPWAs = true
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -53,6 +53,7 @@ class Settings private constructor(
|
||||||
const val showLoginsSecureWarningSyncMaxCount = 1
|
const val showLoginsSecureWarningSyncMaxCount = 1
|
||||||
const val showLoginsSecureWarningMaxCount = 1
|
const val showLoginsSecureWarningMaxCount = 1
|
||||||
const val trackingProtectionOnboardingMaximumCount = 1
|
const val trackingProtectionOnboardingMaximumCount = 1
|
||||||
|
const val pwaVisitsToShowPromptMaxCount = 3
|
||||||
const val FENIX_PREFERENCES = "fenix_preferences"
|
const val FENIX_PREFERENCES = "fenix_preferences"
|
||||||
|
|
||||||
private const val showSearchWidgetCFRMaxCount = 3
|
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
|
// If any of the prefs have been modified, quit displaying the fenix moved tip
|
||||||
fun shouldDisplayFenixMovingTip(): Boolean =
|
fun shouldDisplayFenixMovingTip(): Boolean =
|
||||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip), true) &&
|
preferences.getBoolean(
|
||||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_firefox_nightly_tip), true) &&
|
appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip),
|
||||||
preferences.getBoolean(appContext.getString(R.string.pref_key_migrating_from_fenix_tip), true)
|
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(
|
private val activeSearchCount by intPreference(
|
||||||
appContext.getPreferenceKey(R.string.pref_key_search_count),
|
appContext.getPreferenceKey(R.string.pref_key_search_count),
|
||||||
|
@ -418,14 +428,14 @@ class Settings private constructor(
|
||||||
BrowsingMode.Normal
|
BrowsingMode.Normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set(value) {
|
set(value) {
|
||||||
val lastKnownModeWasPrivate = (value == BrowsingMode.Private)
|
val lastKnownModeWasPrivate = (value == BrowsingMode.Private)
|
||||||
|
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
.putBoolean(
|
.putBoolean(
|
||||||
appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
|
appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
|
||||||
lastKnownModeWasPrivate)
|
lastKnownModeWasPrivate
|
||||||
|
)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
field = value
|
field = value
|
||||||
|
@ -495,7 +505,9 @@ class Settings private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val accessibilityServicesEnabled: Boolean
|
val accessibilityServicesEnabled: Boolean
|
||||||
get() { return touchExplorationIsEnabled || switchServiceIsEnabled }
|
get() {
|
||||||
|
return touchExplorationIsEnabled || switchServiceIsEnabled
|
||||||
|
}
|
||||||
|
|
||||||
val toolbarSettingString: String
|
val toolbarSettingString: String
|
||||||
get() = when {
|
get() = when {
|
||||||
|
@ -569,22 +581,41 @@ class Settings private constructor(
|
||||||
default = false
|
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() {
|
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+
|
// ShortcutManager::pinnedShortcuts is only available on Oreo+
|
||||||
if (!userKnowsAboutPWAs && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (!userKnowsAboutPwas && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val alreadyHavePWaInstalled =
|
val alreadyHavePwaInstalled =
|
||||||
appContext.getSystemService(ShortcutManager::class.java)
|
appContext.getSystemService(ShortcutManager::class.java)
|
||||||
.pinnedShortcuts.size > 0
|
.pinnedShortcuts.size > 0
|
||||||
|
|
||||||
// Users know about PWAs onboarding if they already have PWAs installed.
|
// 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
|
// 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),
|
appContext.getPreferenceKey(R.string.pref_key_user_knows_about_pwa),
|
||||||
default = false
|
default = false
|
||||||
)
|
)
|
||||||
|
@ -809,8 +840,12 @@ class Settings private constructor(
|
||||||
var savedLoginsSortingStrategy: SortingStrategy
|
var savedLoginsSortingStrategy: SortingStrategy
|
||||||
get() {
|
get() {
|
||||||
return when (savedLoginsSortingStrategyString) {
|
return when (savedLoginsSortingStrategyString) {
|
||||||
SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> SortingStrategy.Alphabetically(appContext)
|
SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> SortingStrategy.Alphabetically(
|
||||||
SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> SortingStrategy.LastUsed(appContext)
|
appContext
|
||||||
|
)
|
||||||
|
SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> SortingStrategy.LastUsed(
|
||||||
|
appContext
|
||||||
|
)
|
||||||
else -> SortingStrategy.Alphabetically(appContext)
|
else -> SortingStrategy.Alphabetically(appContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/scrim_background"
|
android:background="@drawable/scrim_background"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
tools:context="org.mozilla.fenix.shortcut.FirstTimePwaFragment">
|
tools:context="org.mozilla.fenix.shortcut.PwaOnboardingDialogFragment">
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
|
@ -221,8 +221,8 @@
|
||||||
android:id="@+id/action_browserFragment_to_createShortcutFragment"
|
android:id="@+id/action_browserFragment_to_createShortcutFragment"
|
||||||
app:destination="@id/createShortcutFragment" />
|
app:destination="@id/createShortcutFragment" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_browserFragment_to_firstTimePwaFragment"
|
android:id="@+id/action_browserFragment_to_pwaOnboardingDialogFragment"
|
||||||
app:destination="@id/firstTimePwaFragment" />
|
app:destination="@id/pwaOnboardingDialogFragment" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_browserFragment_to_quickSettingsSheetDialogFragment"
|
android:id="@+id/action_browserFragment_to_quickSettingsSheetDialogFragment"
|
||||||
app:destination="@id/quickSettingsSheetDialogFragment" />
|
app:destination="@id/quickSettingsSheetDialogFragment" />
|
||||||
|
@ -680,10 +680,9 @@
|
||||||
android:name="org.mozilla.fenix.shortcut.CreateShortcutFragment"
|
android:name="org.mozilla.fenix.shortcut.CreateShortcutFragment"
|
||||||
tools:layout="@layout/fragment_create_shortcut" />
|
tools:layout="@layout/fragment_create_shortcut" />
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/firstTimePwaFragment"
|
android:id="@+id/pwaOnboardingDialogFragment"
|
||||||
android:name="org.mozilla.fenix.shortcut.FirstTimePwaFragment"
|
android:name="org.mozilla.fenix.shortcut.PwaOnboardingDialogFragment"
|
||||||
android:label="fragment_pwa_first_time"
|
tools:layout="@layout/fragment_pwa_onboarding" />
|
||||||
tools:layout="@layout/fragment_pwa_first_time" />
|
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/shareFragment"
|
android:id="@+id/shareFragment"
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
<string name="pref_key_private_mode_opened" translatable="false">pref_key_private_mode_opened</string>
|
<string name="pref_key_private_mode_opened" translatable="false">pref_key_private_mode_opened</string>
|
||||||
<string name="pref_key_open_in_app_opened" translatable="false">pref_key_open_in_app_opened</string>
|
<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_opened" translatable="false">pref_key_install_pwa_opened</string>
|
||||||
|
<string name="pref_key_install_pwa_visits" translatable="false">pref_key_install_pwa_visits</string>
|
||||||
|
|
||||||
<!-- Data Choices -->
|
<!-- Data Choices -->
|
||||||
<string name="pref_key_telemetry" translatable="false">pref_key_telemetry</string>
|
<string name="pref_key_telemetry" translatable="false">pref_key_telemetry</string>
|
||||||
|
|
|
@ -351,6 +351,31 @@ class SettingsTest {
|
||||||
assertTrue(settings.shouldShowSearchSuggestions)
|
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
|
@Test
|
||||||
fun sitePermissionsPhoneFeatureCameraAction() {
|
fun sitePermissionsPhoneFeatureCameraAction() {
|
||||||
// When just created
|
// When just created
|
||||||
|
|
Loading…
Reference in New Issue