Wires up controller, store and interactor. (#13324)
* For #13320 - Wires up the search store, controller and interactor for the new search experience * For #13323 - Navigates to new search experience from the browser when enabledmaster
parent
6492773fc7
commit
ccb5b0b641
|
@ -17,6 +17,7 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) {
|
|||
FromGlobal(0),
|
||||
FromHome(R.id.homeFragment),
|
||||
FromSearch(R.id.searchFragment),
|
||||
FromSearchDialog(R.id.searchDialogFragment),
|
||||
FromSettings(R.id.settingsFragment),
|
||||
FromSyncedTabs(R.id.syncedTabsFragment),
|
||||
FromBookmarks(R.id.bookmarkFragment),
|
||||
|
|
|
@ -86,6 +86,7 @@ import org.mozilla.fenix.library.history.HistoryFragmentDirections
|
|||
import org.mozilla.fenix.perf.Performance
|
||||
import org.mozilla.fenix.perf.StartupTimeline
|
||||
import org.mozilla.fenix.search.SearchFragmentDirections
|
||||
import org.mozilla.fenix.searchdialog.SearchDialogFragmentDirections
|
||||
import org.mozilla.fenix.session.NotificationSessionObserver
|
||||
import org.mozilla.fenix.settings.SettingsFragmentDirections
|
||||
import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections
|
||||
|
@ -556,6 +557,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(customTabSessionId, true)
|
||||
BrowserDirection.FromSearch ->
|
||||
SearchFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromSearchDialog ->
|
||||
SearchDialogFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromSettings ->
|
||||
SettingsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromSyncedTabs ->
|
||||
|
|
|
@ -81,6 +81,9 @@ class DefaultBrowserToolbarController(
|
|||
private val onCloseTab: (Session) -> Unit
|
||||
) : BrowserToolbarController {
|
||||
|
||||
private val useNewSearchExperience
|
||||
get() = activity.settings().useNewSearchExperience
|
||||
|
||||
private val currentSession
|
||||
get() = customTabSession ?: activity.components.core.sessionManager.selectedSession
|
||||
|
||||
|
@ -91,10 +94,17 @@ class DefaultBrowserToolbarController(
|
|||
internal var ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
override fun handleToolbarPaste(text: String) {
|
||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||
sessionId = currentSession?.id,
|
||||
pastedText = text
|
||||
)
|
||||
val directions = if (useNewSearchExperience) {
|
||||
BrowserFragmentDirections.actionGlobalSearchDialog(
|
||||
sessionId = currentSession?.id,
|
||||
pastedText = text
|
||||
)
|
||||
} else {
|
||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||
sessionId = currentSession?.id,
|
||||
pastedText = text
|
||||
)
|
||||
}
|
||||
navController.nav(R.id.browserFragment, directions, getToolbarNavOptions(activity))
|
||||
}
|
||||
|
||||
|
@ -117,9 +127,15 @@ class DefaultBrowserToolbarController(
|
|||
Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)
|
||||
)
|
||||
|
||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||
currentSession?.id
|
||||
)
|
||||
val directions = if (useNewSearchExperience) {
|
||||
BrowserFragmentDirections.actionGlobalSearchDialog(
|
||||
currentSession?.id
|
||||
)
|
||||
} else {
|
||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||
currentSession?.id
|
||||
)
|
||||
}
|
||||
|
||||
navController.nav(R.id.browserFragment, directions, getToolbarNavOptions(activity))
|
||||
}
|
||||
|
|
|
@ -674,7 +674,9 @@ class HomeFragment : Fragment() {
|
|||
|
||||
private fun navigateToSearch() {
|
||||
val directions = if (requireContext().settings().useNewSearchExperience) {
|
||||
HomeFragmentDirections.actionGlobalSearchDialog()
|
||||
HomeFragmentDirections.actionGlobalSearchDialog(
|
||||
sessionId = null
|
||||
)
|
||||
} else {
|
||||
HomeFragmentDirections.actionGlobalSearch(
|
||||
sessionId = null
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/* 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.searchdialog
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.support.ktx.kotlin.isUrl
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.components.metrics.MetricsUtils
|
||||
import org.mozilla.fenix.components.searchengine.CustomSearchEngineStore
|
||||
import org.mozilla.fenix.crashes.CrashListActivity
|
||||
import org.mozilla.fenix.ext.navigateSafe
|
||||
import org.mozilla.fenix.search.SearchController
|
||||
import org.mozilla.fenix.search.SearchFragmentAction
|
||||
import org.mozilla.fenix.search.SearchFragmentStore
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@Suppress("TooManyFunctions", "LongParameterList")
|
||||
class SearchDialogController(
|
||||
private val activity: HomeActivity,
|
||||
private val sessionManager: SessionManager,
|
||||
private val store: SearchFragmentStore,
|
||||
private val navController: NavController,
|
||||
private val settings: Settings,
|
||||
private val metrics: MetricController,
|
||||
private val clearToolbarFocus: () -> Unit
|
||||
) : SearchController {
|
||||
|
||||
override fun handleUrlCommitted(url: String) {
|
||||
when (url) {
|
||||
"about:crashes" -> {
|
||||
// The list of past crashes can be accessed via "settings > about", but desktop and
|
||||
// fennec users may be used to navigating to "about:crashes". So we intercept this here
|
||||
// and open the crash list activity instead.
|
||||
activity.startActivity(Intent(activity, CrashListActivity::class.java))
|
||||
}
|
||||
"moz://a" -> openSearchOrUrl(SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.MANIFESTO))
|
||||
else -> if (url.isNotBlank()) {
|
||||
openSearchOrUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openSearchOrUrl(url: String) {
|
||||
activity.openToBrowserAndLoad(
|
||||
searchTermOrURL = url,
|
||||
newTab = store.state.tabId == null,
|
||||
from = BrowserDirection.FromSearchDialog,
|
||||
engine = store.state.searchEngineSource.searchEngine
|
||||
)
|
||||
|
||||
val event = if (url.isUrl()) {
|
||||
Event.EnteredUrl(false)
|
||||
} else {
|
||||
settings.incrementActiveSearchCount()
|
||||
|
||||
val searchAccessPoint = when (store.state.searchAccessPoint) {
|
||||
Event.PerformedSearch.SearchAccessPoint.NONE -> Event.PerformedSearch.SearchAccessPoint.ACTION
|
||||
else -> store.state.searchAccessPoint
|
||||
}
|
||||
|
||||
searchAccessPoint?.let { sap ->
|
||||
MetricsUtils.createSearchEvent(
|
||||
store.state.searchEngineSource.searchEngine,
|
||||
activity,
|
||||
sap
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
event?.let { metrics.track(it) }
|
||||
}
|
||||
|
||||
override fun handleEditingCancelled() {
|
||||
clearToolbarFocus()
|
||||
}
|
||||
|
||||
override fun handleTextChanged(text: String) {
|
||||
// Display the search shortcuts on each entry of the search fragment (see #5308)
|
||||
val textMatchesCurrentUrl = store.state.url == text
|
||||
val textMatchesCurrentSearch = store.state.searchTerms == text
|
||||
|
||||
store.dispatch(SearchFragmentAction.UpdateQuery(text))
|
||||
store.dispatch(
|
||||
SearchFragmentAction.ShowSearchShortcutEnginePicker(
|
||||
(textMatchesCurrentUrl || textMatchesCurrentSearch || text.isEmpty()) &&
|
||||
settings.shouldShowSearchShortcuts
|
||||
)
|
||||
)
|
||||
store.dispatch(
|
||||
SearchFragmentAction.AllowSearchSuggestionsInPrivateModePrompt(
|
||||
text.isNotEmpty() &&
|
||||
activity.browsingModeManager.mode.isPrivate &&
|
||||
!settings.shouldShowSearchSuggestionsInPrivate &&
|
||||
!settings.showSearchSuggestionsInPrivateOnboardingFinished
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun handleUrlTapped(url: String) {
|
||||
clearToolbarFocus()
|
||||
|
||||
activity.openToBrowserAndLoad(
|
||||
searchTermOrURL = url,
|
||||
newTab = store.state.tabId == null,
|
||||
from = BrowserDirection.FromSearchDialog
|
||||
)
|
||||
|
||||
metrics.track(Event.EnteredUrl(false))
|
||||
}
|
||||
|
||||
override fun handleSearchTermsTapped(searchTerms: String) {
|
||||
settings.incrementActiveSearchCount()
|
||||
clearToolbarFocus()
|
||||
|
||||
activity.openToBrowserAndLoad(
|
||||
searchTermOrURL = searchTerms,
|
||||
newTab = store.state.tabId == null,
|
||||
from = BrowserDirection.FromSearchDialog,
|
||||
engine = store.state.searchEngineSource.searchEngine,
|
||||
forceSearch = true
|
||||
)
|
||||
|
||||
val searchAccessPoint = when (store.state.searchAccessPoint) {
|
||||
Event.PerformedSearch.SearchAccessPoint.NONE -> Event.PerformedSearch.SearchAccessPoint.SUGGESTION
|
||||
else -> store.state.searchAccessPoint
|
||||
}
|
||||
|
||||
val event = searchAccessPoint?.let { sap ->
|
||||
MetricsUtils.createSearchEvent(
|
||||
store.state.searchEngineSource.searchEngine,
|
||||
activity,
|
||||
sap
|
||||
)
|
||||
}
|
||||
event?.let { metrics.track(it) }
|
||||
}
|
||||
|
||||
override fun handleSearchShortcutEngineSelected(searchEngine: SearchEngine) {
|
||||
store.dispatch(SearchFragmentAction.SearchShortcutEngineSelected(searchEngine))
|
||||
val isCustom =
|
||||
CustomSearchEngineStore.isCustomSearchEngine(activity, searchEngine.identifier)
|
||||
metrics.track(Event.SearchShortcutSelected(searchEngine, isCustom))
|
||||
}
|
||||
|
||||
override fun handleSearchShortcutsButtonClicked() {
|
||||
val isOpen = store.state.showSearchShortcuts
|
||||
store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(!isOpen))
|
||||
}
|
||||
|
||||
override fun handleClickSearchEngineSettings() {
|
||||
val directions = SearchDialogFragmentDirections.actionGlobalSearchEngineFragment()
|
||||
navController.navigateSafe(R.id.searchDialogFragment, directions)
|
||||
}
|
||||
|
||||
override fun handleExistingSessionSelected(session: Session) {
|
||||
clearToolbarFocus()
|
||||
sessionManager.select(session)
|
||||
activity.openToBrowser(
|
||||
from = BrowserDirection.FromSearchDialog
|
||||
)
|
||||
}
|
||||
|
||||
override fun handleExistingSessionSelected(tabId: String) {
|
||||
val session = sessionManager.findSessionById(tabId)
|
||||
if (session != null) {
|
||||
handleExistingSessionSelected(session)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,88 +9,43 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.android.synthetic.main.fragment_search.view.*
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.session.Session
|
||||
import kotlinx.android.synthetic.main.fragment_search_dialog.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.state.selector.findTab
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import mozilla.components.support.ktx.android.view.hideKeyboard
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.logDebug
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.search.SearchEngineSource
|
||||
import org.mozilla.fenix.search.SearchFragmentState
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarInteractor
|
||||
import org.mozilla.fenix.search.SearchFragmentStore
|
||||
import org.mozilla.fenix.search.SearchInteractor
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarView
|
||||
import org.mozilla.fenix.search.toolbar.ToolbarInteractor
|
||||
import org.mozilla.fenix.search.toolbar.ToolbarView
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class TempSearchInteractor(val onTextChangedCallback: (String) -> Unit) : ToolbarInteractor, AwesomeBarInteractor {
|
||||
override fun onUrlCommitted(url: String) {
|
||||
logDebug("boek", "onUrlCommitted $url")
|
||||
}
|
||||
|
||||
override fun onEditingCanceled() {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onTextChanged(text: String) {
|
||||
onTextChangedCallback.invoke(text)
|
||||
}
|
||||
|
||||
override fun onUrlTapped(url: String) {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onSearchTermsTapped(searchTerms: String) {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onSearchShortcutEngineSelected(searchEngine: SearchEngine) {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onClickSearchEngineSettings() {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onExistingSessionSelected(session: Session) {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onExistingSessionSelected(tabId: String) {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
}
|
||||
|
||||
override fun onSearchShortcutsButtonClicked() {
|
||||
logDebug("boek", "onEditingCanceled")
|
||||
typealias SearchDialogFragmentStore = SearchFragmentStore
|
||||
typealias SearchDialogInteractor = SearchInteractor
|
||||
fun Settings.shouldShowSearchSuggestions(isPrivate: Boolean): Boolean {
|
||||
return if (isPrivate) {
|
||||
shouldShowSearchSuggestions && shouldShowSearchSuggestionsInPrivate
|
||||
} else {
|
||||
shouldShowSearchSuggestions
|
||||
}
|
||||
}
|
||||
|
||||
class SearchDialogFragment : AppCompatDialogFragment() {
|
||||
|
||||
private lateinit var interactor: SearchDialogInteractor
|
||||
private lateinit var store: SearchDialogFragmentStore
|
||||
private lateinit var toolbarView: ToolbarView
|
||||
private lateinit var awesomeBarView: AwesomeBarView
|
||||
private val tempInteractor = TempSearchInteractor {
|
||||
view?.awesomeBar?.visibility = if (it.isEmpty()) View.INVISIBLE else View.VISIBLE
|
||||
|
||||
awesomeBarView.update(
|
||||
SearchFragmentState(
|
||||
query = it,
|
||||
url = "",
|
||||
searchTerms = "",
|
||||
searchEngineSource = SearchEngineSource.Default(requireComponents.search.provider.getDefaultEngine(requireContext())),
|
||||
defaultEngineSource = SearchEngineSource.Default(requireComponents.search.provider.getDefaultEngine(requireContext())),
|
||||
showSearchSuggestions = true,
|
||||
showSearchSuggestionsHint = false,
|
||||
showSearchShortcuts = false,
|
||||
areShortcutsAvailable = false,
|
||||
showClipboardSuggestions = true,
|
||||
showHistorySuggestions = true,
|
||||
showBookmarkSuggestions = true,
|
||||
tabId = null,
|
||||
pastedText = null,
|
||||
searchAccessPoint = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -103,10 +58,26 @@ class SearchDialogFragment : AppCompatDialogFragment() {
|
|||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_search_dialog, container, false)
|
||||
store = SearchDialogFragmentStore(setUpState())
|
||||
|
||||
interactor = SearchDialogInteractor(
|
||||
SearchDialogController(
|
||||
activity = requireActivity() as HomeActivity,
|
||||
sessionManager = requireComponents.core.sessionManager,
|
||||
store = store,
|
||||
navController = findNavController(),
|
||||
settings = requireContext().settings(),
|
||||
metrics = requireComponents.analytics.metrics,
|
||||
clearToolbarFocus = {
|
||||
toolbarView.view.hideKeyboard()
|
||||
toolbarView.view.clearFocus()
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
toolbarView = ToolbarView(
|
||||
requireContext(),
|
||||
tempInteractor,
|
||||
interactor,
|
||||
null,
|
||||
false,
|
||||
view.toolbar,
|
||||
|
@ -115,10 +86,60 @@ class SearchDialogFragment : AppCompatDialogFragment() {
|
|||
|
||||
awesomeBarView = AwesomeBarView(
|
||||
requireContext(),
|
||||
tempInteractor,
|
||||
interactor,
|
||||
view.awesomeBar
|
||||
)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
consumeFrom(store) {
|
||||
awesomeBar?.visibility = if (it.query.isEmpty()) View.INVISIBLE else View.VISIBLE
|
||||
toolbarView.update(it)
|
||||
awesomeBarView.update(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpState(): SearchFragmentState {
|
||||
val activity = activity as HomeActivity
|
||||
val settings = activity.settings()
|
||||
val args by navArgs<SearchDialogFragmentArgs>()
|
||||
val tabId = args.sessionId
|
||||
val tab = tabId?.let { requireComponents.core.store.state.findTab(it) }
|
||||
val url = tab?.content?.url.orEmpty()
|
||||
val currentSearchEngine = SearchEngineSource.Default(
|
||||
requireComponents.search.provider.getDefaultEngine(requireContext())
|
||||
)
|
||||
val isPrivate = activity.browsingModeManager.mode.isPrivate
|
||||
val areShortcutsAvailable =
|
||||
requireContext().components.search.provider.installedSearchEngines(requireContext())
|
||||
.list.size >= MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS
|
||||
return SearchFragmentState(
|
||||
query = url,
|
||||
url = url,
|
||||
searchTerms = tab?.content?.searchTerms.orEmpty(),
|
||||
searchEngineSource = currentSearchEngine,
|
||||
defaultEngineSource = currentSearchEngine,
|
||||
showSearchSuggestions = settings.shouldShowSearchSuggestions(isPrivate),
|
||||
showSearchSuggestionsHint = false,
|
||||
showSearchShortcuts = settings.shouldShowSearchShortcuts &&
|
||||
url.isEmpty() &&
|
||||
areShortcutsAvailable,
|
||||
areShortcutsAvailable = areShortcutsAvailable,
|
||||
showClipboardSuggestions = settings.shouldShowClipboardSuggestions,
|
||||
showHistorySuggestions = settings.shouldShowHistorySuggestions,
|
||||
showBookmarkSuggestions = settings.shouldShowBookmarkSuggestions,
|
||||
tabId = tabId,
|
||||
pastedText = args.pastedText,
|
||||
searchAccessPoint = args.searchAccessPoint
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS = 2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,21 @@
|
|||
<dialog
|
||||
android:id="@+id/searchDialogFragment"
|
||||
android:name="org.mozilla.fenix.searchdialog.SearchDialogFragment"
|
||||
tools:layout="@layout/fragment_search_dialog" />
|
||||
tools:layout="@layout/fragment_search_dialog">
|
||||
<argument
|
||||
android:name="session_id"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="pastedText"
|
||||
android:defaultValue="@null"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="search_access_point"
|
||||
android:defaultValue="NONE"
|
||||
app:argType="org.mozilla.fenix.components.metrics.Event$PerformedSearch$SearchAccessPoint" />
|
||||
</dialog>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/searchFragment"
|
||||
|
|
Loading…
Reference in New Issue