Commit b7fe809a authored by ekager's avatar ekager
Browse files

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

parent 519885da
......@@ -698,6 +698,20 @@ class HomeFragment : Fragment() {
}
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 =
HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null
......
......@@ -34,7 +34,6 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
......@@ -158,6 +157,11 @@ interface SessionControlController {
* @see [CollectionInteractor.onRemoveCollectionsPlaceholder]
*/
fun handleRemoveCollectionsPlaceholder()
/**
* @see [CollectionInteractor.onCollectionMenuOpened] and [TopSiteInteractor.onTopSiteMenuOpened]
*/
fun handleMenuOpened()
}
@Suppress("TooManyFunctions", "LargeClass")
......@@ -193,7 +197,12 @@ class DefaultSessionControlController(
)
}
override fun handleMenuOpened() {
dismissSearchDialogIfDisplayed()
}
override fun handleCollectionOpenTabClicked(tab: ComponentTab) {
dismissSearchDialogIfDisplayed()
sessionManager.restore(
activity,
engine,
......@@ -256,6 +265,7 @@ class DefaultSessionControlController(
}
override fun handleCollectionShareTabsClicked(collection: TabCollection) {
dismissSearchDialogIfDisplayed()
showShareFragment(
collection.title,
collection.tabs.map { ShareData(url = it.url, title = it.title) }
......@@ -282,6 +292,7 @@ class DefaultSessionControlController(
}
override fun handlePrivateBrowsingLearnMoreClicked() {
dismissSearchDialogIfDisplayed()
activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
(SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS),
......@@ -293,9 +304,9 @@ class DefaultSessionControlController(
override fun handleRenameTopSiteClicked(topSite: TopSite) {
activity.let {
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 =
customLayout.findViewById(R.id.top_site_title)
customLayout.findViewById(R.id.top_site_title)
topSiteLabelEditText.setText(topSite.title)
AlertDialog.Builder(it).apply {
......@@ -344,6 +355,7 @@ class DefaultSessionControlController(
}
override fun handleSelectTopSite(url: String, type: TopSite.Type) {
dismissSearchDialogIfDisplayed()
metrics.track(Event.TopSiteOpenInNewTab)
when (type) {
TopSite.Type.DEFAULT -> metrics.track(Event.TopSiteOpenDefault)
......@@ -362,6 +374,12 @@ class DefaultSessionControlController(
activity.openToBrowser(BrowserDirection.FromHome)
}
private fun dismissSearchDialogIfDisplayed() {
if (navController.currentDestination?.id == R.id.searchDialogFragment) {
navController.navigateUp()
}
}
override fun handleStartBrowsingClicked() {
hideOnboarding()
}
......
......@@ -23,6 +23,7 @@ interface TabSessionInteractor {
/**
* Interface for collection related actions in the [SessionControlInteractor].
*/
@SuppressWarnings("TooManyFunctions")
interface CollectionInteractor {
/**
* 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.
*/
fun onRemoveCollectionsPlaceholder()
/**
* User has opened collection 3 dot menu.
*/
fun onCollectionMenuOpened()
}
interface ToolbarInteractor {
......@@ -177,6 +183,11 @@ interface TopSiteInteractor {
* @param type The type of the top site.
*/
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() {
controller.handleRemoveCollectionsPlaceholder()
}
override fun onCollectionMenuOpened() {
controller.handleMenuOpened()
}
override fun onTopSiteMenuOpened() {
controller.handleMenuOpened()
}
}
......@@ -47,6 +47,7 @@ class CollectionViewHolder(
}
collection_overflow_button.setOnClickListener {
interactor.onCollectionMenuOpened()
collectionMenu.menuBuilder
.build(view.context)
.show(anchor = it)
......
......@@ -36,6 +36,7 @@ class TopSiteItemViewHolder(
}
top_site_item.setOnLongClickListener {
interactor.onTopSiteMenuOpened()
it.context.components.analytics.metrics.track(Event.TopSiteLongPress(topSite.type))
val topSiteMenu = TopSiteItemMenu(view.context, topSite.type != FRECENT) { item ->
......
......@@ -10,7 +10,9 @@ import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.os.StrictMode
......@@ -21,6 +23,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.view.accessibility.AccessibilityEvent
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
......@@ -183,6 +186,15 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
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
}
......@@ -193,9 +205,12 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
setupConstraints(view)
search_wrapper.setOnClickListener {
it.hideKeyboardAndSave()
dismissAllowingStateLoss()
// When displayed above browser, dismisses dialog on clicking scrim area
if (findNavController().previousBackStackEntry?.destination?.id == R.id.browserFragment) {
search_wrapper.setOnClickListener {
it.hideKeyboardAndSave()
dismissAllowingStateLoss()
}
}
view.search_engines_shortcut_button.setOnClickListener {
......@@ -327,6 +342,21 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
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?) {
if (requestCode == VoiceSearchActivity.SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also {
......
......@@ -434,4 +434,30 @@ class DefaultSessionControlControllerTest {
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()
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()
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() }
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment