Skip to content

Commit

Permalink
Closes mozilla-mobile#4279: Migrate TabIntentProcessor to use TabsUse…
Browse files Browse the repository at this point in the history
…Cases instead of SessionManager.
  • Loading branch information
pocmo committed Jan 11, 2021
1 parent f7f7d26 commit 1a73848
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 124 deletions.
5 changes: 3 additions & 2 deletions components/feature/intent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ android {

dependencies {
implementation project(':concept-engine')
implementation project(':browser-session')
implementation project(':browser-state')
implementation project(':feature-search')
implementation project(':feature-session')
implementation project(':feature-tabs')
implementation project(':support-utils')
implementation project(':support-ktx')
implementation Dependencies.kotlin_stdlib

testImplementation project(':feature-tabs')
testImplementation project(":browser-session")
testImplementation project(':browser-search')
testImplementation project(':support-test')
testImplementation Dependencies.androidx_browser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,28 @@ import android.content.Intent.ACTION_SEARCH
import android.content.Intent.ACTION_WEB_SEARCH
import android.content.Intent.EXTRA_TEXT
import android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED
import mozilla.components.browser.session.Session
import mozilla.components.browser.state.state.SessionState.Source
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.components.support.ktx.kotlin.isUrl
import mozilla.components.support.utils.SafeIntent
import mozilla.components.support.utils.WebURLFinder

/**
* Processor for intents which should trigger session-related actions.
*
* @property sessionManager The application's [SessionManager].
* @property tabsUseCases An instance of [TabsUseCases] used to open new tabs.
* @property loadUrlUseCase A reference to [SessionUseCases.DefaultLoadUrlUseCase] used to load URLs.
* @property newTabSearchUseCase A reference to [SearchUseCases.NewTabSearchUseCase] to be used for
* ACTION_SEND intents if the provided text is not a URL.
* @property openNewTab Whether a processed intent should open a new tab or
* open URLs in the currently selected tab.
* @property isPrivate Whether a processed intent should open a new tab as private
*/
class TabIntentProcessor(
private val sessionManager: SessionManager,
private val tabsUseCases: TabsUseCases,
private val loadUrlUseCase: SessionUseCases.DefaultLoadUrlUseCase,
private val newTabSearchUseCase: SearchUseCases.NewTabSearchUseCase,
private val openNewTab: Boolean = true,
private val isPrivate: Boolean = false
) : IntentProcessor {

Expand All @@ -50,17 +46,12 @@ class TabIntentProcessor(
return if (url.isNullOrEmpty()) {
false
} else {
val existingSession = sessionManager.sessions.find { it.url == url }
if (existingSession != null) {
sessionManager.select(existingSession)
} else {
val session = createSession(url, private = isPrivate, source = Source.ACTION_VIEW)
loadUrlUseCase(
url,
session.id,
LoadUrlFlags.external()
)
}
tabsUseCases.selectOrAddTab(
url,
private = isPrivate,
source = Source.ACTION_VIEW,
flags = LoadUrlFlags.external()
)
true
}
}
Expand All @@ -77,10 +68,9 @@ class TabIntentProcessor(
} else {
val url = WebURLFinder(extraText).bestWebURL()
if (url != null) {
val session = createSession(url, private = isPrivate, source = Source.ACTION_SEND)
loadUrlUseCase(url, session.id, LoadUrlFlags.external())
addNewTab(url, Source.ACTION_SEND)
} else {
newTabSearchUseCase(extraText, Source.ACTION_SEND, openNewTab)
newTabSearchUseCase(extraText, Source.ACTION_SEND)
}
true
}
Expand All @@ -93,20 +83,19 @@ class TabIntentProcessor(
false
} else {
if (searchQuery.isUrl()) {
val session = createSession(searchQuery, private = isPrivate, source = Source.ACTION_SEARCH)
loadUrlUseCase(searchQuery, session.id, LoadUrlFlags.external())
addNewTab(searchQuery, Source.ACTION_SEARCH)
} else {
newTabSearchUseCase(searchQuery, Source.ACTION_SEARCH, openNewTab)
newTabSearchUseCase(searchQuery, Source.ACTION_SEARCH)
}
true
}
}

private fun createSession(url: String, private: Boolean = false, source: Source): Session {
return if (openNewTab) {
Session(url, private, source).also { sessionManager.add(it, selected = true) }
private fun addNewTab(url: String, source: Source) {
if (isPrivate) {
tabsUseCases.addPrivateTab(url, source = source, flags = LoadUrlFlags.external())
} else {
sessionManager.selectedSession ?: Session(url, private, source)
tabsUseCases.addTab(url, source = source, flags = LoadUrlFlags.external())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class TabIntentProcessorTest {
val sessionManager = spy(SessionManager(engine))
val useCases = SessionUseCases(store, sessionManager)
val handler =
TabIntentProcessor(sessionManager, useCases.loadUrl, searchUseCases.newTabSearch)
TabIntentProcessor(TabsUseCases(store, sessionManager), useCases.loadUrl, searchUseCases.newTabSearch)
val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_VIEW)

Expand Down Expand Up @@ -102,55 +102,13 @@ class TabIntentProcessorTest {
assertEquals(Source.ACTION_VIEW, session.source)
}

@Test
fun processViewIntentUsingSelectedSession() {
val engine = mock<Engine>()
val sessionManager = spy(SessionManager(engine))
val session = Session("http://mozilla.org")
val handler = TabIntentProcessor(
sessionManager,
sessionUseCases.loadUrl,
searchUseCases.newTabSearch,
openNewTab = false
)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_VIEW)
whenever(intent.dataString).thenReturn("http://mozilla.org")
sessionManager.add(session)

handler.process(intent)
verify(sessionManager).select(session)
verify(store, never()).dispatch(any())
}

@Test
fun processViewIntentHavingNoSelectedSession() {
whenever(sessionManager.selectedSession).thenReturn(null)

val handler = TabIntentProcessor(
sessionManager,
sessionUseCases.loadUrl,
searchUseCases.newTabSearch,
openNewTab = false
)
val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_VIEW)
whenever(intent.dataString).thenReturn("http://mozilla.org")

handler.process(intent)
val actionCaptor = argumentCaptor<EngineAction.LoadUrlAction>()
verify(store).dispatch(actionCaptor.capture())
assertEquals("http://mozilla.org", actionCaptor.value.url)
}

@Test
fun processNfcIntent() {
val engine = mock<Engine>()
val sessionManager = spy(SessionManager(engine))
val useCases = SessionUseCases(store, sessionManager)
val handler =
TabIntentProcessor(sessionManager, useCases.loadUrl, searchUseCases.newTabSearch)
TabIntentProcessor(TabsUseCases(store, sessionManager), useCases.loadUrl, searchUseCases.newTabSearch)
val intent = mock<Intent>()
whenever(intent.action).thenReturn(ACTION_NDEF_DISCOVERED)

Expand All @@ -174,50 +132,9 @@ class TabIntentProcessorTest {
assertEquals(Source.ACTION_VIEW, session.source)
}

@Test
fun processNfcIntentUsingSelectedSession() {
val engine = mock<Engine>()
val sessionManager = spy(SessionManager(engine))
val session = Session("http://mozilla.org")
val handler = TabIntentProcessor(
sessionManager,
sessionUseCases.loadUrl,
searchUseCases.newTabSearch,
openNewTab = false
)
val intent = mock<Intent>()
whenever(intent.action).thenReturn(ACTION_NDEF_DISCOVERED)
whenever(intent.dataString).thenReturn("http://mozilla.org")
sessionManager.add(session)

handler.process(intent)
verify(sessionManager).select(session)
verify(store, never()).dispatch(any())
}

@Test
fun processNfcIntentHavingNoSelectedSession() {
whenever(sessionManager.selectedSession).thenReturn(null)

val handler = TabIntentProcessor(
sessionManager,
sessionUseCases.loadUrl,
searchUseCases.newTabSearch,
openNewTab = false
)
val intent = mock<Intent>()
whenever(intent.action).thenReturn(ACTION_NDEF_DISCOVERED)
whenever(intent.dataString).thenReturn("http://mozilla.org")

handler.process(intent)
val actionCaptor = argumentCaptor<EngineAction.LoadUrlAction>()
verify(store).dispatch(actionCaptor.capture())
assertEquals("http://mozilla.org", actionCaptor.value.url)
}

@Test
fun `load URL on ACTION_SEND if text contains URL`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEND)
Expand Down Expand Up @@ -274,7 +191,7 @@ class TabIntentProcessorTest {
val searchTerms = "mozilla android"
val searchUrl = "http://search-url.com?$searchTerms"

val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEND)
Expand All @@ -299,7 +216,7 @@ class TabIntentProcessorTest {

@Test
fun `processor handles ACTION_SEND with empty text`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEND)
Expand All @@ -311,7 +228,7 @@ class TabIntentProcessorTest {

@Test
fun `processor handles ACTION_SEARCH with empty text`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEARCH)
Expand All @@ -323,7 +240,7 @@ class TabIntentProcessorTest {

@Test
fun `load URL on ACTION_SEARCH if text is an URL`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEARCH)
Expand Down Expand Up @@ -352,7 +269,7 @@ class TabIntentProcessorTest {
val searchTerms = "mozilla android"
val searchUrl = "http://search-url.com?$searchTerms"

val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_SEARCH)
Expand All @@ -377,7 +294,7 @@ class TabIntentProcessorTest {

@Test
fun `processor handles ACTION_WEB_SEARCH with empty text`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_WEB_SEARCH)
Expand All @@ -389,7 +306,7 @@ class TabIntentProcessorTest {

@Test
fun `load URL on ACTION_WEB_SEARCH if text is an URL`() {
val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_WEB_SEARCH)
Expand Down Expand Up @@ -418,7 +335,7 @@ class TabIntentProcessorTest {
val searchTerms = "mozilla android"
val searchUrl = "http://search-url.com?$searchTerms"

val handler = TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch)
val handler = TabIntentProcessor(TabsUseCases(store, sessionManager), sessionUseCases.loadUrl, searchUseCases.newTabSearch)

val intent = mock<Intent>()
whenever(intent.action).thenReturn(Intent.ACTION_WEB_SEARCH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,37 @@ class TabsUseCases(
}
}

/**
* Use case for selecting an existing tab or creating a new tab with a specific URL.
*/
class SelectOrAddUseCase(
private val store: BrowserStore,
private val sessionManager: SessionManager
) {
/**
* Selects an already existing tab displaying [url] or otherwise creates a new tab.
*/
operator fun invoke(
url: String,
private: Boolean = false,
source: Source = Source.NEW_TAB,
flags: LoadUrlFlags = LoadUrlFlags.none()
) {
val existingSession = sessionManager.sessions.find { it.url == url }
if (existingSession != null) {
sessionManager.select(existingSession)
} else {
val session = Session(url, private, source)
sessionManager.add(session, selected = true)
store.dispatch(EngineAction.LoadUrlAction(
session.id,
url,
flags
))
}
}
}

val selectTab: SelectTabUseCase by lazy { DefaultSelectTabUseCase(sessionManager) }
val removeTab: RemoveTabUseCase by lazy { DefaultRemoveTabUseCase(sessionManager) }
val addTab: AddNewTabUseCase by lazy { AddNewTabUseCase(store, sessionManager) }
Expand All @@ -357,4 +388,5 @@ class TabsUseCases(
val removePrivateTabs: RemovePrivateTabsUseCase by lazy { RemovePrivateTabsUseCase(sessionManager) }
val undo by lazy { UndoTabRemovalUseCase(store) }
val restore: RestoreUseCase by lazy { RestoreUseCase(store, sessionManager) }
val selectOrAddTab: SelectOrAddUseCase by lazy { SelectOrAddUseCase(store, sessionManager) }
}
Loading

0 comments on commit 1a73848

Please sign in to comment.