Bug 1822744 - Part 2: Add ToolbarController for handling home toolbar related functionalities

fenix/113.0
Gabriel Luong 1 year ago committed by mergify[bot]
parent d99eee09cd
commit b1ad36df71

@ -119,6 +119,7 @@ import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.home.sessioncontrol.SessionControlView
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
import org.mozilla.fenix.home.toolbar.DefaultToolbarController
import org.mozilla.fenix.home.topsites.DefaultTopSitesView
import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.onboarding.FenixOnboarding
@ -427,6 +428,11 @@ class HomeFragment : Fragment() {
activity = activity,
hideOnboarding = ::hideOnboardingAndOpenSearch,
),
toolbarController = DefaultToolbarController(
activity = activity,
store = components.core.store,
navController = findNavController(),
),
)
updateLayout(binding.root)

@ -27,11 +27,9 @@ import mozilla.components.feature.tab.collections.ext.invoke
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.support.ktx.android.view.showKeyboard
import mozilla.components.support.ktx.kotlin.isUrl
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Collections
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.HomeScreen
import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.GleanMetrics.Pocket
@ -148,21 +146,6 @@ interface SessionControlController {
*/
fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean)
/**
* @see [ToolbarInteractor.onPasteAndGo]
*/
fun handlePasteAndGo(clipboardText: String)
/**
* @see [ToolbarInteractor.onPaste]
*/
fun handlePaste(clipboardText: String)
/**
* @see [ToolbarInteractor.onNavigateSearch]
*/
fun handleNavigateSearch()
/**
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
*/
@ -559,51 +542,6 @@ class DefaultSessionControlController(
navController.nav(R.id.homeFragment, directions)
}
override fun handlePasteAndGo(clipboardText: String) {
val searchEngine = store.state.search.selectedOrDefaultSearchEngine
activity.openToBrowserAndLoad(
searchTermOrURL = clipboardText,
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
if (clipboardText.isUrl() || searchEngine == null) {
Events.enteredUrl.record(Events.EnteredUrlExtra(autocomplete = false))
} else {
val searchAccessPoint = MetricsUtils.Source.ACTION
MetricsUtils.recordSearchMetrics(
searchEngine,
searchEngine == store.state.search.selectedOrDefaultSearchEngine,
searchAccessPoint,
)
}
}
override fun handlePaste(clipboardText: String) {
val directions = HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null,
pastedText = clipboardText,
)
navController.nav(R.id.homeFragment, directions)
}
override fun handleNavigateSearch() {
val directions =
HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null,
)
navController.nav(
R.id.homeFragment,
directions,
BrowserAnimator.getToolbarNavOptions(activity),
)
Events.searchBarTapped.record(Events.SearchBarTappedExtra("HOME"))
}
override fun handleMessageClicked(message: Message) {
messageController.onMessagePressed(message)
}

@ -27,6 +27,7 @@ import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGrou
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHighlight
import org.mozilla.fenix.home.recentvisits.controller.RecentVisitsController
import org.mozilla.fenix.home.recentvisits.interactor.RecentVisitsInteractor
import org.mozilla.fenix.home.toolbar.ToolbarController
import org.mozilla.fenix.home.toolbar.ToolbarInteractor
import org.mozilla.fenix.onboarding.controller.OnboardingController
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
@ -233,6 +234,7 @@ class SessionControlInteractor(
private val recentVisitsController: RecentVisitsController,
private val pocketStoriesController: PocketStoriesController,
private val onboardingController: OnboardingController,
private val toolbarController: ToolbarController,
) : CollectionInteractor,
OnboardingInteractor,
TopSiteInteractor,
@ -329,15 +331,15 @@ class SessionControlInteractor(
}
override fun onPasteAndGo(clipboardText: String) {
controller.handlePasteAndGo(clipboardText)
toolbarController.handlePasteAndGo(clipboardText)
}
override fun onPaste(clipboardText: String) {
controller.handlePaste(clipboardText)
toolbarController.handlePaste(clipboardText)
}
override fun onNavigateSearch() {
controller.handleNavigateSearch()
toolbarController.handleNavigateSearch()
}
override fun onRemoveCollectionsPlaceholder() {

@ -0,0 +1,91 @@
/* 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.home.toolbar
import androidx.navigation.NavController
import mozilla.components.browser.state.state.selectedOrDefaultSearchEngine
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.ktx.kotlin.isUrl
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserAnimator
import org.mozilla.fenix.components.metrics.MetricsUtils
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.home.HomeFragmentDirections
/**
* An interface that handles the view manipulation of the home screen toolbar.
*/
interface ToolbarController {
/**
* @see [ToolbarInteractor.onPasteAndGo]
*/
fun handlePasteAndGo(clipboardText: String)
/**
* @see [ToolbarInteractor.onPaste]
*/
fun handlePaste(clipboardText: String)
/**
* @see [ToolbarInteractor.onNavigateSearch]
*/
fun handleNavigateSearch()
}
/**
* The default implementation of [ToolbarController].
*/
class DefaultToolbarController(
private val activity: HomeActivity,
private val store: BrowserStore,
private val navController: NavController,
) : ToolbarController {
override fun handlePasteAndGo(clipboardText: String) {
val searchEngine = store.state.search.selectedOrDefaultSearchEngine
activity.openToBrowserAndLoad(
searchTermOrURL = clipboardText,
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
if (clipboardText.isUrl() || searchEngine == null) {
Events.enteredUrl.record(Events.EnteredUrlExtra(autocomplete = false))
} else {
val searchAccessPoint = MetricsUtils.Source.ACTION
MetricsUtils.recordSearchMetrics(
searchEngine,
searchEngine == store.state.search.selectedOrDefaultSearchEngine,
searchAccessPoint,
)
}
}
override fun handlePaste(clipboardText: String) {
val directions = HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null,
pastedText = clipboardText,
)
navController.nav(R.id.homeFragment, directions)
}
override fun handleNavigateSearch() {
val directions =
HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null,
)
navController.nav(
R.id.homeFragment,
directions,
BrowserAnimator.getToolbarNavOptions(activity),
)
Events.searchBarTapped.record(Events.SearchBarTappedExtra("HOME"))
}
}

@ -973,67 +973,6 @@ class DefaultSessionControlControllerTest {
}
}
@Test
fun handlePasteAndGo() {
assertNull(Events.enteredUrl.testGetValue())
assertNull(Events.performedSearch.testGetValue())
createController().handlePasteAndGo("text")
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = "text",
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
}
assertNotNull(Events.performedSearch.testGetValue())
createController().handlePasteAndGo("https://mozilla.org")
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = "https://mozilla.org",
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
}
assertNotNull(Events.enteredUrl.testGetValue())
}
@Test
fun handlePaste() {
createController().handlePaste("text")
verify {
navController.navigate(
match<NavDirections> { it.actionId == R.id.action_global_search_dialog },
null,
)
}
}
@Test
fun handleNavigateSearch() {
assertNull(Events.searchBarTapped.testGetValue())
createController().handleNavigateSearch()
assertNotNull(Events.searchBarTapped.testGetValue())
val recordedEvents = Events.searchBarTapped.testGetValue()!!
assertEquals(1, recordedEvents.size)
assertEquals("HOME", recordedEvents.single().extra?.getValue("source"))
verify {
navController.navigate(
match<NavDirections> { it.actionId == R.id.action_global_search_dialog },
any<NavOptions>(),
)
}
}
@Test
fun handleRemoveCollectionsPlaceholder() {
createController().handleRemoveCollectionsPlaceholder()

@ -24,6 +24,7 @@ import org.mozilla.fenix.home.recenttabs.controller.RecentTabController
import org.mozilla.fenix.home.recentvisits.controller.RecentVisitsController
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.home.toolbar.ToolbarController
import org.mozilla.fenix.onboarding.controller.OnboardingController
class SessionControlInteractorTest {
@ -34,6 +35,7 @@ class SessionControlInteractorTest {
private val recentBookmarksController: RecentBookmarksController = mockk(relaxed = true)
private val pocketStoriesController: PocketStoriesController = mockk(relaxed = true)
private val onboardingController: OnboardingController = mockk(relaxed = true)
private val toolbarController: ToolbarController = mockk(relaxed = true)
// Note: the recent visits tests are handled in [RecentVisitsInteractorTest] and [RecentVisitsControllerTest]
private val recentVisitsController: RecentVisitsController = mockk(relaxed = true)
@ -50,6 +52,7 @@ class SessionControlInteractorTest {
recentVisitsController,
pocketStoriesController,
onboardingController,
toolbarController,
)
}
@ -137,19 +140,19 @@ class SessionControlInteractorTest {
@Test
fun onPaste() {
interactor.onPaste("text")
verify { controller.handlePaste("text") }
verify { toolbarController.handlePaste("text") }
}
@Test
fun onPasteAndGo() {
interactor.onPasteAndGo("text")
verify { controller.handlePasteAndGo("text") }
verify { toolbarController.handlePasteAndGo("text") }
}
@Test
fun onNavigateSearch() {
interactor.onNavigateSearch()
verify { controller.handleNavigateSearch() }
verify { toolbarController.handleNavigateSearch() }
}
@Test

@ -20,6 +20,7 @@ import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHigh
import org.mozilla.fenix.home.recentvisits.controller.RecentVisitsController
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.home.toolbar.ToolbarController
import org.mozilla.fenix.onboarding.controller.OnboardingController
class RecentVisitsInteractorTest {
@ -31,6 +32,7 @@ class RecentVisitsInteractorTest {
private val pocketStoriesController: PocketStoriesController = mockk(relaxed = true)
private val recentVisitsController: RecentVisitsController = mockk(relaxed = true)
private val onboardingController: OnboardingController = mockk(relaxed = true)
private val toolbarController: ToolbarController = mockk(relaxed = true)
private lateinit var interactor: SessionControlInteractor
@ -44,6 +46,7 @@ class RecentVisitsInteractorTest {
recentVisitsController,
pocketStoriesController,
onboardingController,
toolbarController,
)
}

@ -0,0 +1,141 @@
/* 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.home.toolbar
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.NavOptions
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.state.search.SearchEngine
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.SearchState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.Settings
@RunWith(FenixRobolectricTestRunner::class) // For gleanTestRule
class DefaultToolbarControllerTest {
@get:Rule
val gleanTestRule = GleanTestRule(testContext)
private val activity: HomeActivity = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true)
private val settings: Settings = mockk(relaxed = true)
private val searchEngine = SearchEngine(
id = "test",
name = "Test Engine",
icon = mockk(relaxed = true),
type = SearchEngine.Type.BUNDLED,
resultUrls = listOf("https://example.org/?q={searchTerms}"),
)
private lateinit var store: BrowserStore
@Before
fun setup() {
store = BrowserStore(
initialState = BrowserState(
search = SearchState(
regionSearchEngines = listOf(searchEngine),
),
),
)
every { navController.currentDestination } returns mockk {
every { id } returns R.id.homeFragment
}
every { activity.settings() } returns settings
}
@Test
fun `WHEN Paste & Go toolbar menu is clicked THEN open the browser with the clipboard text as the search term`() {
assertNull(Events.enteredUrl.testGetValue())
assertNull(Events.performedSearch.testGetValue())
var clipboardText = "text"
createController().handlePasteAndGo(clipboardText)
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = clipboardText,
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
}
assertNotNull(Events.performedSearch.testGetValue())
clipboardText = "https://mozilla.org"
createController().handlePasteAndGo(clipboardText)
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = clipboardText,
newTab = true,
from = BrowserDirection.FromHome,
engine = searchEngine,
)
}
assertNotNull(Events.enteredUrl.testGetValue())
}
@Test
fun `WHEN Paste toolbar menu is clicked THEN navigate to the search dialog`() {
createController().handlePaste("text")
verify {
navController.navigate(
match<NavDirections> { it.actionId == R.id.action_global_search_dialog },
null,
)
}
}
@Test
fun `WHEN the toolbar is tapped THEN navigate to the search dialog`() {
assertNull(Events.searchBarTapped.testGetValue())
createController().handleNavigateSearch()
assertNotNull(Events.searchBarTapped.testGetValue())
val recordedEvents = Events.searchBarTapped.testGetValue()!!
assertEquals(1, recordedEvents.size)
assertEquals("HOME", recordedEvents.single().extra?.getValue("source"))
verify {
navController.navigate(
match<NavDirections> { it.actionId == R.id.action_global_search_dialog },
any<NavOptions>(),
)
}
}
private fun createController() = DefaultToolbarController(
activity = activity,
store = store,
navController = navController,
)
}
Loading…
Cancel
Save