For #16351 - Make homescreen interactive when search dialog is up

upstream-sync
ekager 4 years ago
parent 519885da43
commit b7fe809ae4

@ -698,6 +698,20 @@ class HomeFragment : Fragment() {
} }
private fun navigateToSearch() { private fun navigateToSearch() {
// Dismisses the search dialog when the home content is scrolled
val recyclerView = sessionControlView!!.view
val listener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) {
findNavController().navigateUp()
recyclerView.removeOnScrollListener(this)
}
}
}
recyclerView.addOnScrollListener(listener)
val directions = val directions =
HomeFragmentDirections.actionGlobalSearchDialog( HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null sessionId = null

@ -34,7 +34,6 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.sessionsOfType import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragment import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections import org.mozilla.fenix.home.HomeFragmentDirections
@ -158,6 +157,11 @@ interface SessionControlController {
* @see [CollectionInteractor.onRemoveCollectionsPlaceholder] * @see [CollectionInteractor.onRemoveCollectionsPlaceholder]
*/ */
fun handleRemoveCollectionsPlaceholder() fun handleRemoveCollectionsPlaceholder()
/**
* @see [CollectionInteractor.onCollectionMenuOpened] and [TopSiteInteractor.onTopSiteMenuOpened]
*/
fun handleMenuOpened()
} }
@Suppress("TooManyFunctions", "LargeClass") @Suppress("TooManyFunctions", "LargeClass")
@ -193,7 +197,12 @@ class DefaultSessionControlController(
) )
} }
override fun handleMenuOpened() {
dismissSearchDialogIfDisplayed()
}
override fun handleCollectionOpenTabClicked(tab: ComponentTab) { override fun handleCollectionOpenTabClicked(tab: ComponentTab) {
dismissSearchDialogIfDisplayed()
sessionManager.restore( sessionManager.restore(
activity, activity,
engine, engine,
@ -256,6 +265,7 @@ class DefaultSessionControlController(
} }
override fun handleCollectionShareTabsClicked(collection: TabCollection) { override fun handleCollectionShareTabsClicked(collection: TabCollection) {
dismissSearchDialogIfDisplayed()
showShareFragment( showShareFragment(
collection.title, collection.title,
collection.tabs.map { ShareData(url = it.url, title = it.title) } collection.tabs.map { ShareData(url = it.url, title = it.title) }
@ -282,6 +292,7 @@ class DefaultSessionControlController(
} }
override fun handlePrivateBrowsingLearnMoreClicked() { override fun handlePrivateBrowsingLearnMoreClicked() {
dismissSearchDialogIfDisplayed()
activity.openToBrowserAndLoad( activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
(SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS), (SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS),
@ -293,9 +304,9 @@ class DefaultSessionControlController(
override fun handleRenameTopSiteClicked(topSite: TopSite) { override fun handleRenameTopSiteClicked(topSite: TopSite) {
activity.let { activity.let {
val customLayout = val customLayout =
LayoutInflater.from(it).inflate(R.layout.top_sites_rename_dialog, null) LayoutInflater.from(it).inflate(R.layout.top_sites_rename_dialog, null)
val topSiteLabelEditText: EditText = val topSiteLabelEditText: EditText =
customLayout.findViewById(R.id.top_site_title) customLayout.findViewById(R.id.top_site_title)
topSiteLabelEditText.setText(topSite.title) topSiteLabelEditText.setText(topSite.title)
AlertDialog.Builder(it).apply { AlertDialog.Builder(it).apply {
@ -344,6 +355,7 @@ class DefaultSessionControlController(
} }
override fun handleSelectTopSite(url: String, type: TopSite.Type) { override fun handleSelectTopSite(url: String, type: TopSite.Type) {
dismissSearchDialogIfDisplayed()
metrics.track(Event.TopSiteOpenInNewTab) metrics.track(Event.TopSiteOpenInNewTab)
when (type) { when (type) {
TopSite.Type.DEFAULT -> metrics.track(Event.TopSiteOpenDefault) TopSite.Type.DEFAULT -> metrics.track(Event.TopSiteOpenDefault)
@ -362,6 +374,12 @@ class DefaultSessionControlController(
activity.openToBrowser(BrowserDirection.FromHome) activity.openToBrowser(BrowserDirection.FromHome)
} }
private fun dismissSearchDialogIfDisplayed() {
if (navController.currentDestination?.id == R.id.searchDialogFragment) {
navController.navigateUp()
}
}
override fun handleStartBrowsingClicked() { override fun handleStartBrowsingClicked() {
hideOnboarding() hideOnboarding()
} }

@ -23,6 +23,7 @@ interface TabSessionInteractor {
/** /**
* Interface for collection related actions in the [SessionControlInteractor]. * Interface for collection related actions in the [SessionControlInteractor].
*/ */
@SuppressWarnings("TooManyFunctions")
interface CollectionInteractor { interface CollectionInteractor {
/** /**
* Shows the Collection Creation fragment for selecting the tabs to add to the given tab * Shows the Collection Creation fragment for selecting the tabs to add to the given tab
@ -98,6 +99,11 @@ interface CollectionInteractor {
* User has removed the collections placeholder from home. * User has removed the collections placeholder from home.
*/ */
fun onRemoveCollectionsPlaceholder() fun onRemoveCollectionsPlaceholder()
/**
* User has opened collection 3 dot menu.
*/
fun onCollectionMenuOpened()
} }
interface ToolbarInteractor { interface ToolbarInteractor {
@ -177,6 +183,11 @@ interface TopSiteInteractor {
* @param type The type of the top site. * @param type The type of the top site.
*/ */
fun onSelectTopSite(url: String, type: TopSite.Type) fun onSelectTopSite(url: String, type: TopSite.Type)
/**
* Called when top site menu is opened.
*/
fun onTopSiteMenuOpened()
} }
/** /**
@ -276,4 +287,12 @@ class SessionControlInteractor(
override fun onRemoveCollectionsPlaceholder() { override fun onRemoveCollectionsPlaceholder() {
controller.handleRemoveCollectionsPlaceholder() controller.handleRemoveCollectionsPlaceholder()
} }
override fun onCollectionMenuOpened() {
controller.handleMenuOpened()
}
override fun onTopSiteMenuOpened() {
controller.handleMenuOpened()
}
} }

@ -47,6 +47,7 @@ class CollectionViewHolder(
} }
collection_overflow_button.setOnClickListener { collection_overflow_button.setOnClickListener {
interactor.onCollectionMenuOpened()
collectionMenu.menuBuilder collectionMenu.menuBuilder
.build(view.context) .build(view.context)
.show(anchor = it) .show(anchor = it)

@ -36,6 +36,7 @@ class TopSiteItemViewHolder(
} }
top_site_item.setOnLongClickListener { top_site_item.setOnLongClickListener {
interactor.onTopSiteMenuOpened()
it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type)) it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type))
val topSiteMenu = TopSiteItemMenu(view.context, topSite.type != FRECENT) { item -> val topSiteMenu = TopSiteItemMenu(view.context, topSite.type != FRECENT) { item ->

@ -10,7 +10,9 @@ import android.app.Dialog
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.StrictMode import android.os.StrictMode
@ -21,6 +23,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewStub import android.view.ViewStub
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
@ -183,6 +186,15 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
requireComponents.core.engine.speculativeCreateSession(isPrivate) requireComponents.core.engine.speculativeCreateSession(isPrivate)
if (findNavController().previousBackStackEntry?.destination?.id == R.id.homeFragment) {
// When displayed above home, dispatches the touch events to scrim area to the HomeFragment
view.search_wrapper.background = ColorDrawable(Color.TRANSPARENT)
dialog?.window?.decorView?.setOnTouchListener { _, event ->
requireActivity().dispatchTouchEvent(event)
false
}
}
return view return view
} }
@ -193,9 +205,12 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
setupConstraints(view) setupConstraints(view)
search_wrapper.setOnClickListener { // When displayed above browser, dismisses dialog on clicking scrim area
it.hideKeyboardAndSave() if (findNavController().previousBackStackEntry?.destination?.id == R.id.browserFragment) {
dismissAllowingStateLoss() search_wrapper.setOnClickListener {
it.hideKeyboardAndSave()
dismissAllowingStateLoss()
}
} }
view.search_engines_shortcut_button.setOnClickListener { view.search_engines_shortcut_button.setOnClickListener {
@ -327,6 +342,21 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
toolbarView.view.requestFocus() toolbarView.view.requestFocus()
} }
/*
* This way of dismissing the keyboard is needed to smoothly dismiss the keyboard while the dialog
* is also dismissing. For example, when clicking a top site on home while this dialog is showing.
*/
private fun hideDeviceKeyboard() {
val imm =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
hideDeviceKeyboard()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
if (requestCode == VoiceSearchActivity.SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) { if (requestCode == VoiceSearchActivity.SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also { intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also {

@ -434,4 +434,30 @@ class DefaultSessionControlControllerTest {
fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder) fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
} }
} }
@Test
fun handleMenuOpenedWhileSearchShowing() {
every { navController.currentDestination } returns mockk {
every { id } returns R.id.searchDialogFragment
}
controller.handleMenuOpened()
verify {
navController.navigateUp()
}
}
@Test
fun handleMenuOpenedWhileSearchNotShowing() {
every { navController.currentDestination } returns mockk {
every { id } returns R.id.homeFragment
}
controller.handleMenuOpened()
verify(exactly = 0) {
navController.navigateUp()
}
}
} }

@ -116,4 +116,16 @@ class SessionControlInteractorTest {
interactor.onRemoveCollectionsPlaceholder() interactor.onRemoveCollectionsPlaceholder()
verify { controller.handleRemoveCollectionsPlaceholder() } verify { controller.handleRemoveCollectionsPlaceholder() }
} }
@Test
fun onCollectionMenuOpened() {
interactor.onCollectionMenuOpened()
verify { controller.handleMenuOpened() }
}
@Test
fun onTopSiteMenuOpened() {
interactor.onTopSiteMenuOpened()
verify { controller.handleMenuOpened() }
}
} }

@ -44,4 +44,12 @@ class TopSiteItemViewHolderTest {
view.top_site_item.performClick() view.top_site_item.performClick()
verify { interactor.onSelectTopSite("https://getpocket.com", TopSite.Type.DEFAULT) } verify { interactor.onSelectTopSite("https://getpocket.com", TopSite.Type.DEFAULT) }
} }
@Test
fun `calls interactor on long click`() {
TopSiteItemViewHolder(view, interactor).bind(pocket)
view.top_site_item.performLongClick()
verify { interactor.onTopSiteMenuOpened() }
}
} }

Loading…
Cancel
Save