mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-03 23:15:31 +00:00
For #16351 - Make homescreen interactive when search dialog is up
This commit is contained in:
parent
519885da43
commit
b7fe809ae4
@ -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() }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user