Closes #16946: Refactor PwaOnboardingObserver to use browser store

upstream-sync
Christian Sadilek 4 years ago
parent 46813dd89c
commit d8f3127e08

@ -53,6 +53,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
private var readerModeAvailable = false
private var openInAppOnboardingObserver: OpenInAppOnboardingObserver? = null
private var pwaOnboardingObserver: PwaOnboardingObserver? = null
override fun onCreateView(
inflater: LayoutInflater,
@ -179,17 +180,15 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
}
if (!settings.userKnowsAboutPwas) {
@Suppress("DEPRECATION")
// TODO Use browser store instead of session observer: https://github.com/mozilla-mobile/fenix/issues/16946
session?.register(
PwaOnboardingObserver(
navController = findNavController(),
settings = settings,
webAppUseCases = context.components.useCases.webAppUseCases
),
owner = this,
autoPause = true
)
pwaOnboardingObserver = PwaOnboardingObserver(
store = context.components.core.store,
lifecycleOwner = this,
navController = findNavController(),
settings = settings,
webAppUseCases = context.components.useCases.webAppUseCases
).also {
it.start()
}
}
subscribeToTabCollections()
@ -205,6 +204,8 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
getSessionById()?.unregister(openInAppOnboardingObserver!!)
openInAppOnboardingObserver = null
}
pwaOnboardingObserver?.stop()
}
private fun subscribeToTabCollections() {

@ -4,9 +4,18 @@
package org.mozilla.fenix.shortcut
import androidx.lifecycle.LifecycleOwner
import androidx.navigation.NavController
import mozilla.components.browser.session.Session
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.mapNotNull
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.pwa.WebAppUseCases
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.ext.nav
@ -15,22 +24,41 @@ import org.mozilla.fenix.utils.Settings
/**
* Displays the [PwaOnboardingDialogFragment] info dialog when a PWA is opened in the browser for the third time.
*/
@ExperimentalCoroutinesApi
class PwaOnboardingObserver(
private val store: BrowserStore,
private val lifecycleOwner: LifecycleOwner,
private val navController: NavController,
private val settings: Settings,
private val webAppUseCases: WebAppUseCases
) : Session.Observer {
) {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading && webAppUseCases.isInstallable() && !settings.userKnowsAboutPwas) {
settings.incrementVisitedInstallableCount()
if (settings.shouldShowPwaCfr) {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment()
navController.nav(R.id.browserFragment, directions)
settings.lastCfrShownTimeInMillis = System.currentTimeMillis()
settings.userKnowsAboutPwas = true
private var scope: CoroutineScope? = null
fun start() {
scope = store.flowScoped(lifecycleOwner) { flow ->
flow.mapNotNull { state ->
state.selectedTab
}
.ifChanged {
it.content.webAppManifest
}
.collect {
if (webAppUseCases.isInstallable() && !settings.userKnowsAboutPwas) {
settings.incrementVisitedInstallableCount()
if (settings.shouldShowPwaCfr) {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment()
navController.nav(R.id.browserFragment, directions)
settings.lastCfrShownTimeInMillis = System.currentTimeMillis()
settings.userKnowsAboutPwas = true
}
}
}
}
}
fun stop() {
scope?.cancel()
}
}

@ -0,0 +1,116 @@
/* 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.shortcut
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.navigation.NavController
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.pwa.WebAppUseCases
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.utils.Settings
@ExperimentalCoroutinesApi
class PwaOnboardingObserverTest {
private lateinit var store: BrowserStore
private lateinit var lifecycleOwner: MockedLifecycleOwner
private lateinit var pwaOnboardingObserver: PwaOnboardingObserver
private lateinit var navigationController: NavController
private lateinit var settings: Settings
private lateinit var webAppUseCases: WebAppUseCases
private val testDispatcher = TestCoroutineDispatcher()
@get:Rule
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
@Before
fun setUp() {
store = BrowserStore(
BrowserState(
tabs = listOf(
createTab(url = "https://firefox.com", id = "1")
), selectedTabId = "1"
)
)
lifecycleOwner = MockedLifecycleOwner(Lifecycle.State.STARTED)
navigationController = mockk(relaxed = true)
settings = mockk(relaxed = true)
webAppUseCases = mockk(relaxed = true)
pwaOnboardingObserver = PwaOnboardingObserver(
store = store,
lifecycleOwner = lifecycleOwner,
navController = navigationController,
settings = settings,
webAppUseCases = webAppUseCases
)
}
@Test
fun `GIVEN cfr should not yet be shown WHEN installable page is loaded THEN counter is incremented`() {
pwaOnboardingObserver.start()
every { webAppUseCases.isInstallable() } returns true
store.dispatch(ContentAction.UpdateWebAppManifestAction("1", mockk())).joinBlocking()
verify { settings.incrementVisitedInstallableCount() }
verify(exactly = 0) { navigationController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment())
}
}
@Test
fun `GIVEN cfr should be shown WHEN installable page is loaded THEN we navigate to onboarding fragment`() {
pwaOnboardingObserver.start()
every { webAppUseCases.isInstallable() } returns true
every { settings.shouldShowPwaCfr } returns true
store.dispatch(ContentAction.UpdateWebAppManifestAction("1", mockk())).joinBlocking()
verify { settings.incrementVisitedInstallableCount() }
verify { navigationController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment())
}
}
@Test
fun `GIVEN web app is not installable WHEN page with manifest is loaded THEN nothing happens`() {
pwaOnboardingObserver.start()
every { webAppUseCases.isInstallable() } returns false
store.dispatch(ContentAction.UpdateWebAppManifestAction("1", mockk())).joinBlocking()
verify(exactly = 0) { settings.incrementVisitedInstallableCount() }
verify(exactly = 0) { navigationController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionBrowserFragmentToPwaOnboardingDialogFragment())
}
}
internal class MockedLifecycleOwner(initialState: Lifecycle.State) : LifecycleOwner {
val lifecycleRegistry = LifecycleRegistry(this).apply {
currentState = initialState
}
override fun getLifecycle(): Lifecycle = lifecycleRegistry
}
}
Loading…
Cancel
Save