1
0
Fork 0

Extract quick action sheet observer code (#4368)

master
Tiger Oakes 2019-08-19 11:28:33 -04:00 committed by Jeff Boek
parent 7d02354f4f
commit de14962e3f
4 changed files with 158 additions and 64 deletions

View File

@ -365,7 +365,6 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
@CallSuper
override fun onSessionSelected(session: Session) {
super.onSessionSelected(session)
if (!browserInitialized) {
// Initializing a new coroutineScope to avoid ConcurrentModificationException in ObserverRegistry
// This will be removed when ObserverRegistry is deprecated by browser-state.

View File

@ -22,7 +22,6 @@ import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -49,9 +48,8 @@ import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.quickactionsheet.DefaultQuickActionSheetController
import org.mozilla.fenix.quickactionsheet.QuickActionSheetSessionObserver
import org.mozilla.fenix.quickactionsheet.QuickActionSheetView
import java.net.MalformedURLException
import java.net.URL
/**
* Fragment used for browsing the web within the main app and external apps.
@ -61,11 +59,11 @@ import java.net.URL
@Suppress("TooManyFunctions")
class BrowserFragment : BaseBrowserFragment(), BackHandler {
private lateinit var quickActionSheetView: QuickActionSheetView
private var quickActionSheetSessionObserver: QuickActionSheetSessionObserver? = null
private val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
private var findBookmarkJob: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -93,7 +91,7 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
override fun initializeUI(view: View): Session? {
val sessionManager = requireComponents.core.sessionManager
return super.initializeUI(view)?.also { session ->
return super.initializeUI(view)?.also {
quickActionSheetView =
QuickActionSheetView(view.nestedScrollQuickAction, browserInteractor)
@ -154,8 +152,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
themeReaderViewControlsForPrivateMode(view.readerViewControlsBar)
}
updateBookmarkState(session)
consumeFrom(browserStore) {
quickActionSheetView.update(it)
browserToolbarView.update(it)
@ -165,8 +161,14 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
override fun onStart() {
super.onStart()
subscribeToSession()
subscribeToTabCollections()
quickActionSheetSessionObserver = QuickActionSheetSessionObserver(
lifecycleScope,
requireComponents,
dispatch = { action -> browserStore.dispatch(action) }
).also { observer ->
getSessionById()?.register(observer, this, autoPause = true)
}
}
override fun onResume() {
@ -306,56 +308,9 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
})
}
private fun subscribeToSession() {
val observer = object : Session.Observer {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading) {
updateBookmarkState(session)
browserStore.dispatch(QuickActionSheetAction.BounceNeededChange)
}
}
override fun onUrlChanged(session: Session, url: String) {
updateBookmarkState(session)
updateAppLinksState(session)
}
}
getSessionById()?.register(observer, this, autoPause = true)
}
override fun onSessionSelected(session: Session) {
super.onSessionSelected(session)
updateBookmarkState(session)
}
private suspend fun findBookmarkedURL(session: Session?): Boolean {
return withContext(IO) {
session?.let {
try {
val url = URL(it.url).toString()
val list = requireComponents.core.bookmarksStorage.getBookmarksWithUrl(url)
list.isNotEmpty() && list[0].url == url
} catch (e: MalformedURLException) {
false
}
} ?: false
}
}
private fun updateBookmarkState(session: Session) {
findBookmarkJob?.cancel()
findBookmarkJob = lifecycleScope.launch(IO) {
val found = findBookmarkedURL(session)
withContext(Main) {
browserStore.dispatch(QuickActionSheetAction.BookmarkedStateChange(found))
}
}
}
private fun updateAppLinksState(session: Session) {
val url = session.url
val appLinks = requireComponents.useCases.appLinksUseCases.appLinkRedirect
browserStore.dispatch(QuickActionSheetAction.AppLinkStateChange(appLinks.invoke(url).hasExternalApp()))
quickActionSheetSessionObserver?.updateBookmarkState(session)
}
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
@ -366,14 +321,14 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
override fun onTabsAdded(tabCollection: TabCollection, sessions: List<Session>) {
showTabSavedToCollectionSnackbar()
}
}
private fun showTabSavedToCollectionSnackbar() {
view?.let { view ->
FenixSnackbar.make(view, Snackbar.LENGTH_SHORT)
.setText(view.context.getString(R.string.create_collection_tab_saved))
.setAnchorView(browserToolbarView.view)
.show()
private fun showTabSavedToCollectionSnackbar() {
view?.let { view ->
FenixSnackbar.make(view, Snackbar.LENGTH_SHORT)
.setText(view.context.getString(R.string.create_collection_tab_saved))
.setAnchorView(browserToolbarView.view)
.show()
}
}
}

View File

@ -0,0 +1,68 @@
/* 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.quickactionsheet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.browser.session.Session
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
import java.net.MalformedURLException
import java.net.URL
class QuickActionSheetSessionObserver(
private val parentScope: CoroutineScope,
private val components: Components,
private val dispatch: (QuickActionSheetAction) -> Unit
) : Session.Observer {
private var findBookmarkJob: Job? = null
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading) {
updateBookmarkState(session)
dispatch(QuickActionSheetAction.BounceNeededChange)
}
}
override fun onUrlChanged(session: Session, url: String) {
updateBookmarkState(session)
updateAppLinksState(session)
}
/**
* Launches job to update the bookmark button on the quick action sheet.
*/
fun updateBookmarkState(session: Session) {
findBookmarkJob?.cancel()
findBookmarkJob = parentScope.launch(Main) {
val found = findBookmarkedURL(session)
dispatch(QuickActionSheetAction.BookmarkedStateChange(found))
}
}
/**
* Updates the app link button on the quick action sheet.
*/
private fun updateAppLinksState(session: Session) {
val url = session.url
val appLinks = components.useCases.appLinksUseCases.appLinkRedirect
dispatch(QuickActionSheetAction.AppLinkStateChange(appLinks(url).hasExternalApp()))
}
private suspend fun findBookmarkedURL(session: Session): Boolean = withContext(IO) {
try {
val url = URL(session.url).toString()
val list = components.core.bookmarksStorage.getBookmarksWithUrl(url)
list.isNotEmpty() && list[0].url == url
} catch (e: MalformedURLException) {
false
}
}
}

View File

@ -0,0 +1,72 @@
/* 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.quickactionsheet
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import mozilla.components.browser.session.Session
import mozilla.components.feature.app.links.AppLinkRedirect
import mozilla.components.feature.app.links.AppLinksUseCases
import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.toolbar.BrowserStore
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
class QuickActionSheetSessionObserverTest {
private lateinit var components: Components
private lateinit var appLinkRedirect: AppLinksUseCases.GetAppLinkRedirect
private lateinit var store: BrowserStore
private lateinit var dispatch: (QuickActionSheetAction) -> Unit
@Before
fun setup() {
components = mockk(relaxed = true)
appLinkRedirect = mockk(relaxed = true)
store = mockk(relaxed = true)
dispatch = { store.dispatch(it) }
every { components.useCases.appLinksUseCases.appLinkRedirect } returns appLinkRedirect
}
@Test
fun `onLoadingStateChanged dispatches BounceNeededChange and updates bookmark button`() {
val session: Session = mockk()
val observer = spyk(QuickActionSheetSessionObserver(mockk(), components, dispatch))
every { observer.updateBookmarkState(session) } just Runs
observer.onLoadingStateChanged(session, true)
verify(exactly = 0) { store.dispatch(QuickActionSheetAction.BounceNeededChange) }
observer.onLoadingStateChanged(session, false)
verify { observer.updateBookmarkState(session) }
verify { store.dispatch(QuickActionSheetAction.BounceNeededChange) }
}
@Test
fun `onUrlChanged updates bookmark and app link buttons`() {
val url = "https://example.com"
val session: Session = mockk()
every { session.url } returns url
val observer = spyk(QuickActionSheetSessionObserver(mockk(), components, dispatch))
every { observer.updateBookmarkState(session) } just Runs
every { appLinkRedirect.invoke(url) } returns AppLinkRedirect(mockk(), "", false)
observer.onUrlChanged(session, "")
verify { observer.updateBookmarkState(session) }
verify { appLinkRedirect.invoke("https://example.com") }
verify { store.dispatch(QuickActionSheetAction.AppLinkStateChange(true)) }
}
}