For #6846: Added quick actions for nav bar in home
parent
1d604d327c
commit
e5e62fa79a
|
@ -4,19 +4,12 @@
|
||||||
|
|
||||||
package org.mozilla.fenix.components.toolbar
|
package org.mozilla.fenix.components.toolbar
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.PopupWindow
|
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.annotation.VisibleForTesting
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
@ -24,28 +17,25 @@ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_
|
||||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
|
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
|
||||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
|
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
|
||||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
|
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.*
|
|
||||||
import kotlinx.android.synthetic.main.component_browser_top_toolbar.*
|
import kotlinx.android.synthetic.main.component_browser_top_toolbar.*
|
||||||
import kotlinx.android.synthetic.main.component_browser_top_toolbar.view.*
|
import kotlinx.android.synthetic.main.component_browser_top_toolbar.view.*
|
||||||
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
|
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.state.selector.selectedTab
|
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||||
import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior
|
import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior
|
||||||
import mozilla.components.browser.toolbar.display.DisplayToolbar
|
import mozilla.components.browser.toolbar.display.DisplayToolbar
|
||||||
import mozilla.components.support.ktx.android.util.dpToFloat
|
import mozilla.components.support.ktx.android.util.dpToFloat
|
||||||
import mozilla.components.support.utils.URLStringUtils
|
import mozilla.components.support.utils.URLStringUtils
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
|
||||||
import org.mozilla.fenix.customtabs.CustomTabToolbarIntegration
|
import org.mozilla.fenix.customtabs.CustomTabToolbarIntegration
|
||||||
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
|
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
|
||||||
import org.mozilla.fenix.ext.bookmarkStorage
|
import org.mozilla.fenix.ext.bookmarkStorage
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.settings
|
import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.theme.ThemeManager
|
import org.mozilla.fenix.theme.ThemeManager
|
||||||
|
import org.mozilla.fenix.utils.ToolbarPopupWindow
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
interface BrowserToolbarViewInteractor {
|
interface BrowserToolbarViewInteractor {
|
||||||
fun onBrowserToolbarPaste(text: String)
|
fun onBrowserToolbarPaste(text: String)
|
||||||
|
@ -90,56 +80,12 @@ class BrowserToolbarView(
|
||||||
val isCustomTabSession = customTabSession != null
|
val isCustomTabSession = customTabSession != null
|
||||||
|
|
||||||
view.display.setOnUrlLongClickListener {
|
view.display.setOnUrlLongClickListener {
|
||||||
val clipboard = view.context.components.clipboardHandler
|
ToolbarPopupWindow.show(
|
||||||
val customView = LayoutInflater.from(view.context)
|
WeakReference(view),
|
||||||
.inflate(R.layout.browser_toolbar_popup_window, null)
|
customTabSession,
|
||||||
val popupWindow = PopupWindow(
|
interactor::onBrowserToolbarPasteAndGo,
|
||||||
customView,
|
interactor::onBrowserToolbarPaste
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
view.context.resources.getDimensionPixelSize(R.dimen.context_menu_height),
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
popupWindow.elevation =
|
|
||||||
view.context.resources.getDimension(R.dimen.mozac_browser_menu_elevation)
|
|
||||||
|
|
||||||
// This is a workaround for SDK<23 to allow popup dismissal on outside or back button press
|
|
||||||
// See: https://github.com/mozilla-mobile/fenix/issues/10027
|
|
||||||
popupWindow.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
|
|
||||||
customView.paste.isVisible = !clipboard.text.isNullOrEmpty() && !isCustomTabSession
|
|
||||||
customView.paste_and_go.isVisible =
|
|
||||||
!clipboard.text.isNullOrEmpty() && !isCustomTabSession
|
|
||||||
|
|
||||||
customView.copy.setOnClickListener {
|
|
||||||
popupWindow.dismiss()
|
|
||||||
clipboard.text = getUrlForClipboard(it.context.components.core.store, customTabSession)
|
|
||||||
|
|
||||||
FenixSnackbar.make(
|
|
||||||
view = view,
|
|
||||||
duration = Snackbar.LENGTH_SHORT,
|
|
||||||
isDisplayedWithBrowserToolbar = true
|
|
||||||
)
|
|
||||||
.setText(view.context.getString(R.string.browser_toolbar_url_copied_to_clipboard_snackbar))
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
customView.paste.setOnClickListener {
|
|
||||||
popupWindow.dismiss()
|
|
||||||
interactor.onBrowserToolbarPaste(clipboard.text!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
customView.paste_and_go.setOnClickListener {
|
|
||||||
popupWindow.dismiss()
|
|
||||||
interactor.onBrowserToolbarPasteAndGo(clipboard.text!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
popupWindow.showAsDropDown(
|
|
||||||
view,
|
|
||||||
view.context.resources.getDimensionPixelSize(R.dimen.context_menu_x_offset),
|
|
||||||
0,
|
|
||||||
Gravity.START
|
|
||||||
)
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,9 +232,9 @@ class BrowserToolbarView(
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
SCROLL_FLAG_SCROLL or
|
SCROLL_FLAG_SCROLL or
|
||||||
SCROLL_FLAG_ENTER_ALWAYS or
|
SCROLL_FLAG_ENTER_ALWAYS or
|
||||||
SCROLL_FLAG_SNAP or
|
SCROLL_FLAG_SNAP or
|
||||||
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
|
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,15 +243,5 @@ class BrowserToolbarView(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TOOLBAR_ELEVATION = 16
|
private const val TOOLBAR_ELEVATION = 16
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
internal fun getUrlForClipboard(store: BrowserStore, customTabSession: Session? = null): String? {
|
|
||||||
return if (customTabSession != null) {
|
|
||||||
customTabSession.url
|
|
||||||
} else {
|
|
||||||
val selectedTab = store.state.selectedTab
|
|
||||||
selectedTab?.readerState?.activeUrl ?: selectedTab?.content?.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,7 @@ import org.mozilla.fenix.settings.SupportUtils.SumoTopic.HELP
|
||||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||||
import org.mozilla.fenix.theme.ThemeManager
|
import org.mozilla.fenix.theme.ThemeManager
|
||||||
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
||||||
|
import org.mozilla.fenix.utils.ToolbarPopupWindow
|
||||||
import org.mozilla.fenix.utils.allowUndo
|
import org.mozilla.fenix.utils.allowUndo
|
||||||
import org.mozilla.fenix.whatsnew.WhatsNew
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
@ -338,6 +339,16 @@ class HomeFragment : Fragment() {
|
||||||
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
|
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view.toolbar_wrapper.setOnLongClickListener {
|
||||||
|
ToolbarPopupWindow.show(
|
||||||
|
WeakReference(view),
|
||||||
|
handlePasteAndGo = sessionControlInteractor::onPasteAndGo,
|
||||||
|
handlePaste = sessionControlInteractor::onPaste,
|
||||||
|
copyVisible = false
|
||||||
|
)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
view.tab_button.setOnClickListener {
|
view.tab_button.setOnClickListener {
|
||||||
openTabTray()
|
openTabTray()
|
||||||
}
|
}
|
||||||
|
@ -465,7 +476,11 @@ class HomeFragment : Fragment() {
|
||||||
isSelected,
|
isSelected,
|
||||||
engineSessionState = state
|
engineSessionState = state
|
||||||
)
|
)
|
||||||
findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToBrowserFragment(null))
|
findNavController().navigate(
|
||||||
|
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
operation = { },
|
operation = { },
|
||||||
anchorView = snackbarAnchorView
|
anchorView = snackbarAnchorView
|
||||||
|
|
|
@ -15,6 +15,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.tab.collections.ext.restore
|
import mozilla.components.feature.tab.collections.ext.restore
|
||||||
import mozilla.components.feature.tabs.TabsUseCases
|
import mozilla.components.feature.tabs.TabsUseCases
|
||||||
import mozilla.components.feature.top.sites.TopSite
|
import mozilla.components.feature.top.sites.TopSite
|
||||||
|
import mozilla.components.support.ktx.kotlin.isUrl
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
@ -24,9 +25,13 @@ import org.mozilla.fenix.components.TabCollectionStorage
|
||||||
import org.mozilla.fenix.components.TopSiteStorage
|
import org.mozilla.fenix.components.TopSiteStorage
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
|
import org.mozilla.fenix.components.metrics.MetricsUtils
|
||||||
import org.mozilla.fenix.components.tips.Tip
|
import org.mozilla.fenix.components.tips.Tip
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
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
|
||||||
|
@ -120,8 +125,21 @@ interface SessionControlController {
|
||||||
*/
|
*/
|
||||||
fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean)
|
fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [TipInteractor.onCloseTip]
|
||||||
|
*/
|
||||||
fun handleCloseTip(tip: Tip)
|
fun handleCloseTip(tip: Tip)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [ToolbarInteractor.onPasteAndGo]
|
||||||
|
*/
|
||||||
|
fun handlePasteAndGo(clipboardText: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [ToolbarInteractor.onPaste]
|
||||||
|
*/
|
||||||
|
fun handlePaste(clipboardText: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
|
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
|
||||||
*/
|
*/
|
||||||
|
@ -347,4 +365,37 @@ class DefaultSessionControlController(
|
||||||
)
|
)
|
||||||
navController.nav(R.id.homeFragment, directions)
|
navController.nav(R.id.homeFragment, directions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun handlePasteAndGo(clipboardText: String) {
|
||||||
|
activity.openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = clipboardText,
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromHome,
|
||||||
|
engine = activity.components.search.provider.getDefaultEngine(activity)
|
||||||
|
)
|
||||||
|
|
||||||
|
val event = if (clipboardText.isUrl()) {
|
||||||
|
Event.EnteredUrl(false)
|
||||||
|
} else {
|
||||||
|
val searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.ACTION
|
||||||
|
activity.settings().incrementActiveSearchCount()
|
||||||
|
searchAccessPoint.let { sap ->
|
||||||
|
MetricsUtils.createSearchEvent(
|
||||||
|
activity.components.search.provider.getDefaultEngine(activity),
|
||||||
|
activity,
|
||||||
|
sap
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event?.let { activity.metrics.track(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handlePaste(clipboardText: String) {
|
||||||
|
val directions = HomeFragmentDirections.actionGlobalSearch(
|
||||||
|
sessionId = null,
|
||||||
|
pastedText = clipboardText
|
||||||
|
)
|
||||||
|
navController.nav(R.id.homeFragment, directions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,18 @@ interface CollectionInteractor {
|
||||||
fun onAddTabsToCollectionTapped()
|
fun onAddTabsToCollectionTapped()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ToolbarInteractor {
|
||||||
|
/**
|
||||||
|
* Navigates to browser with clipboard text.
|
||||||
|
*/
|
||||||
|
fun onPasteAndGo(clipboardText: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to search with clipboard text.
|
||||||
|
*/
|
||||||
|
fun onPaste(clipboardText: String)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for onboarding related actions in the [SessionControlInteractor].
|
* Interface for onboarding related actions in the [SessionControlInteractor].
|
||||||
*/
|
*/
|
||||||
|
@ -163,7 +175,8 @@ interface TopSiteInteractor {
|
||||||
@SuppressWarnings("TooManyFunctions")
|
@SuppressWarnings("TooManyFunctions")
|
||||||
class SessionControlInteractor(
|
class SessionControlInteractor(
|
||||||
private val controller: SessionControlController
|
private val controller: SessionControlController
|
||||||
) : CollectionInteractor, OnboardingInteractor, TopSiteInteractor, TipInteractor, TabSessionInteractor {
|
) : CollectionInteractor, OnboardingInteractor, TopSiteInteractor, TipInteractor,
|
||||||
|
TabSessionInteractor, ToolbarInteractor {
|
||||||
override fun onCollectionAddTabTapped(collection: TabCollection) {
|
override fun onCollectionAddTabTapped(collection: TabCollection) {
|
||||||
controller.handleCollectionAddTabTapped(collection)
|
controller.handleCollectionAddTabTapped(collection)
|
||||||
}
|
}
|
||||||
|
@ -235,4 +248,12 @@ class SessionControlInteractor(
|
||||||
override fun onPrivateBrowsingLearnMoreClicked() {
|
override fun onPrivateBrowsingLearnMoreClicked() {
|
||||||
controller.handlePrivateBrowsingLearnMoreClicked()
|
controller.handlePrivateBrowsingLearnMoreClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPasteAndGo(clipboardText: String) {
|
||||||
|
controller.handlePasteAndGo(clipboardText)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPaste(clipboardText: String) {
|
||||||
|
controller.handlePaste(clipboardText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/* 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.utils
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.PopupWindow
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.*
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.browser.state.selector.selectedTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
object ToolbarPopupWindow {
|
||||||
|
fun show(
|
||||||
|
view: WeakReference<View>,
|
||||||
|
customTabSession: Session? = null,
|
||||||
|
handlePasteAndGo: (String) -> Unit,
|
||||||
|
handlePaste: (String) -> Unit,
|
||||||
|
copyVisible: Boolean = true
|
||||||
|
) {
|
||||||
|
val context = view.get()?.context ?: return
|
||||||
|
val isCustomTabSession = customTabSession != null
|
||||||
|
val clipboard = context.components.clipboardHandler
|
||||||
|
val customView = LayoutInflater.from(context)
|
||||||
|
.inflate(R.layout.browser_toolbar_popup_window, null)
|
||||||
|
val popupWindow = PopupWindow(
|
||||||
|
customView,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
context.resources.getDimensionPixelSize(R.dimen.context_menu_height),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
popupWindow.elevation =
|
||||||
|
context.resources.getDimension(R.dimen.mozac_browser_menu_elevation)
|
||||||
|
|
||||||
|
// This is a workaround for SDK<23 to allow popup dismissal on outside or back button press
|
||||||
|
// See: https://github.com/mozilla-mobile/fenix/issues/10027
|
||||||
|
popupWindow.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
|
||||||
|
customView.copy.isVisible = copyVisible
|
||||||
|
|
||||||
|
customView.paste.isVisible = !clipboard.text.isNullOrEmpty() && !isCustomTabSession
|
||||||
|
customView.paste_and_go.isVisible =
|
||||||
|
!clipboard.text.isNullOrEmpty() && !isCustomTabSession
|
||||||
|
|
||||||
|
customView.copy.setOnClickListener {
|
||||||
|
popupWindow.dismiss()
|
||||||
|
clipboard.text = getUrlForClipboard(
|
||||||
|
it.context.components.core.store,
|
||||||
|
customTabSession
|
||||||
|
)
|
||||||
|
|
||||||
|
view.get()?.let {
|
||||||
|
FenixSnackbar.make(
|
||||||
|
view = it,
|
||||||
|
duration = Snackbar.LENGTH_SHORT,
|
||||||
|
isDisplayedWithBrowserToolbar = true
|
||||||
|
)
|
||||||
|
.setText(context.getString(R.string.browser_toolbar_url_copied_to_clipboard_snackbar))
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customView.paste.setOnClickListener {
|
||||||
|
popupWindow.dismiss()
|
||||||
|
handlePaste(clipboard.text!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
customView.paste_and_go.setOnClickListener {
|
||||||
|
popupWindow.dismiss()
|
||||||
|
handlePasteAndGo(clipboard.text!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
view.get()?.let {
|
||||||
|
popupWindow.showAsDropDown(
|
||||||
|
it,
|
||||||
|
context.resources.getDimensionPixelSize(R.dimen.context_menu_x_offset),
|
||||||
|
0,
|
||||||
|
Gravity.START
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
internal fun getUrlForClipboard(
|
||||||
|
store: BrowserStore,
|
||||||
|
customTabSession: Session? = null
|
||||||
|
): String? {
|
||||||
|
return if (customTabSession != null) {
|
||||||
|
customTabSession.url
|
||||||
|
} else {
|
||||||
|
val selectedTab = store.state.selectedTab
|
||||||
|
selectedTab?.readerState?.activeUrl ?: selectedTab?.content?.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,9 @@ import io.mockk.verify
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||||
import kotlinx.coroutines.test.TestCoroutineScope
|
import kotlinx.coroutines.test.TestCoroutineScope
|
||||||
|
import mozilla.components.browser.search.SearchEngine
|
||||||
|
import mozilla.components.browser.search.SearchEngineManager
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.concept.engine.Engine
|
import mozilla.components.concept.engine.Engine
|
||||||
import mozilla.components.feature.tab.collections.TabCollection
|
import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.tabs.TabsUseCases
|
import mozilla.components.feature.tabs.TabsUseCases
|
||||||
|
@ -24,13 +25,19 @@ import org.junit.Test
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.components.Analytics
|
||||||
import org.mozilla.fenix.components.TabCollectionStorage
|
import org.mozilla.fenix.components.TabCollectionStorage
|
||||||
import org.mozilla.fenix.components.TopSiteStorage
|
import org.mozilla.fenix.components.TopSiteStorage
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
import org.mozilla.fenix.components.tips.Tip
|
import org.mozilla.fenix.components.tips.Tip
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import org.mozilla.fenix.ext.metrics
|
||||||
|
import org.mozilla.fenix.ext.searchEngineManager
|
||||||
|
import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@ -44,7 +51,6 @@ class DefaultSessionControlControllerTest {
|
||||||
private val navController: NavController = mockk(relaxed = true)
|
private val navController: NavController = mockk(relaxed = true)
|
||||||
private val metrics: MetricController = mockk(relaxed = true)
|
private val metrics: MetricController = mockk(relaxed = true)
|
||||||
private val sessionManager: SessionManager = mockk(relaxed = true)
|
private val sessionManager: SessionManager = mockk(relaxed = true)
|
||||||
private val store: BrowserStore = mockk(relaxed = true)
|
|
||||||
private val engine: Engine = mockk(relaxed = true)
|
private val engine: Engine = mockk(relaxed = true)
|
||||||
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
||||||
private val topSiteStorage: TopSiteStorage = mockk(relaxed = true)
|
private val topSiteStorage: TopSiteStorage = mockk(relaxed = true)
|
||||||
|
@ -55,6 +61,10 @@ class DefaultSessionControlControllerTest {
|
||||||
private val showTabTray: () -> Unit = mockk(relaxed = true)
|
private val showTabTray: () -> Unit = mockk(relaxed = true)
|
||||||
private val showDeleteCollectionPrompt: (tabCollection: TabCollection, title: String?, message: String) -> Unit =
|
private val showDeleteCollectionPrompt: (tabCollection: TabCollection, title: String?, message: String) -> Unit =
|
||||||
mockk(relaxed = true)
|
mockk(relaxed = true)
|
||||||
|
private val searchEngine = mockk<SearchEngine>(relaxed = true)
|
||||||
|
private val searchEngineManager = mockk<SearchEngineManager>(relaxed = true)
|
||||||
|
private val settings: Settings = mockk(relaxed = true)
|
||||||
|
private val analytics: Analytics = mockk(relaxed = true)
|
||||||
|
|
||||||
private lateinit var controller: DefaultSessionControlController
|
private lateinit var controller: DefaultSessionControlController
|
||||||
|
|
||||||
|
@ -70,6 +80,13 @@ class DefaultSessionControlControllerTest {
|
||||||
every { navController.currentDestination } returns mockk {
|
every { navController.currentDestination } returns mockk {
|
||||||
every { id } returns R.id.homeFragment
|
every { id } returns R.id.homeFragment
|
||||||
}
|
}
|
||||||
|
every { activity.components.settings } returns settings
|
||||||
|
every { activity.components.search.provider.getDefaultEngine(activity) } returns searchEngine
|
||||||
|
every { activity.settings() } returns settings
|
||||||
|
every { activity.searchEngineManager } returns searchEngineManager
|
||||||
|
every { searchEngineManager.defaultSearchEngine } returns searchEngine
|
||||||
|
every { activity.components.analytics } returns analytics
|
||||||
|
every { analytics.metrics } returns metrics
|
||||||
|
|
||||||
controller = DefaultSessionControlController(
|
controller = DefaultSessionControlController(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
|
@ -155,7 +172,10 @@ class DefaultSessionControlControllerTest {
|
||||||
}
|
}
|
||||||
val tab = mockk<ComponentTab>()
|
val tab = mockk<ComponentTab>()
|
||||||
every {
|
every {
|
||||||
activity.resources.getString(R.string.delete_tab_and_collection_dialog_title, "Collection")
|
activity.resources.getString(
|
||||||
|
R.string.delete_tab_and_collection_dialog_title,
|
||||||
|
"Collection"
|
||||||
|
)
|
||||||
} returns "Delete Collection?"
|
} returns "Delete Collection?"
|
||||||
every {
|
every {
|
||||||
activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
|
activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
|
||||||
|
@ -252,11 +272,13 @@ class DefaultSessionControlControllerTest {
|
||||||
controller.handleSelectTopSite(topSiteUrl, true)
|
controller.handleSelectTopSite(topSiteUrl, true)
|
||||||
verify { metrics.track(Event.TopSiteOpenInNewTab) }
|
verify { metrics.track(Event.TopSiteOpenInNewTab) }
|
||||||
verify { metrics.track(Event.TopSiteOpenDefault) }
|
verify { metrics.track(Event.TopSiteOpenDefault) }
|
||||||
verify { tabsUseCases.addTab.invoke(
|
verify {
|
||||||
topSiteUrl,
|
tabsUseCases.addTab.invoke(
|
||||||
selectTab = true,
|
topSiteUrl,
|
||||||
startLoading = true
|
selectTab = true,
|
||||||
) }
|
startLoading = true
|
||||||
|
)
|
||||||
|
}
|
||||||
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,11 +288,13 @@ class DefaultSessionControlControllerTest {
|
||||||
|
|
||||||
controller.handleSelectTopSite(topSiteUrl, false)
|
controller.handleSelectTopSite(topSiteUrl, false)
|
||||||
verify { metrics.track(Event.TopSiteOpenInNewTab) }
|
verify { metrics.track(Event.TopSiteOpenInNewTab) }
|
||||||
verify { tabsUseCases.addTab.invoke(
|
verify {
|
||||||
topSiteUrl,
|
tabsUseCases.addTab.invoke(
|
||||||
selectTab = true,
|
topSiteUrl,
|
||||||
startLoading = true
|
selectTab = true,
|
||||||
) }
|
startLoading = true
|
||||||
|
)
|
||||||
|
}
|
||||||
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,4 +365,43 @@ class DefaultSessionControlControllerTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlePasteAndGo() {
|
||||||
|
controller.handlePasteAndGo("text")
|
||||||
|
|
||||||
|
verify {
|
||||||
|
activity.openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = "text",
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromHome,
|
||||||
|
engine = searchEngine
|
||||||
|
)
|
||||||
|
settings.incrementActiveSearchCount()
|
||||||
|
metrics.track(any<Event.PerformedSearch>())
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.handlePasteAndGo("https://mozilla.org")
|
||||||
|
verify {
|
||||||
|
activity.openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = "https://mozilla.org",
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromHome,
|
||||||
|
engine = searchEngine
|
||||||
|
)
|
||||||
|
metrics.track(any<Event.EnteredUrl>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlePaste() {
|
||||||
|
controller.handlePaste("text")
|
||||||
|
|
||||||
|
verify {
|
||||||
|
navController.navigate(
|
||||||
|
match<NavDirections> { it.actionId == R.id.action_global_search },
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,4 +98,16 @@ class SessionControlInteractorTest {
|
||||||
interactor.onAddTabsToCollectionTapped()
|
interactor.onAddTabsToCollectionTapped()
|
||||||
verify { controller.handleCreateCollection() }
|
verify { controller.handleCreateCollection() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onPaste() {
|
||||||
|
interactor.onPaste("text")
|
||||||
|
verify { controller.handlePaste("text") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onPasteAndGo() {
|
||||||
|
interactor.onPasteAndGo("text")
|
||||||
|
verify { controller.handlePasteAndGo("text") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
package org.mozilla.fenix.components.toolbar
|
package org.mozilla.fenix.utils
|
||||||
|
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -14,11 +14,10 @@ import mozilla.components.browser.state.store.BrowserStore
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarView.Companion.getUrlForClipboard
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
class BrowserToolbarViewTest {
|
class ToolbarPopupWindowTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getUrlForClipboard() {
|
fun getUrlForClipboard() {
|
||||||
|
@ -26,18 +25,23 @@ class BrowserToolbarViewTest {
|
||||||
every { customTabSession.url } returns "https://mozilla.org"
|
every { customTabSession.url } returns "https://mozilla.org"
|
||||||
|
|
||||||
// Custom tab
|
// Custom tab
|
||||||
assertEquals("https://mozilla.org", getUrlForClipboard(mockk(), customTabSession))
|
assertEquals(
|
||||||
|
"https://mozilla.org",
|
||||||
|
ToolbarPopupWindow.getUrlForClipboard(mockk(), customTabSession)
|
||||||
|
)
|
||||||
|
|
||||||
// Regular tab
|
// Regular tab
|
||||||
val regularTab = createTab(url = "http://firefox.com")
|
val regularTab = createTab(url = "http://firefox.com")
|
||||||
var store = BrowserStore(BrowserState(tabs = listOf(regularTab), selectedTabId = regularTab.id))
|
var store =
|
||||||
assertEquals(regularTab.content.url, getUrlForClipboard(store))
|
BrowserStore(BrowserState(tabs = listOf(regularTab), selectedTabId = regularTab.id))
|
||||||
|
assertEquals(regularTab.content.url, ToolbarPopupWindow.getUrlForClipboard(store))
|
||||||
|
|
||||||
// Reader Tab
|
// Reader Tab
|
||||||
val readerTab = createTab(url = "moz-extension://1234",
|
val readerTab = createTab(
|
||||||
|
url = "moz-extension://1234",
|
||||||
readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123")
|
readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123")
|
||||||
)
|
)
|
||||||
store = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id))
|
store = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id))
|
||||||
assertEquals(readerTab.readerState.activeUrl, getUrlForClipboard(store))
|
assertEquals(readerTab.readerState.activeUrl, ToolbarPopupWindow.getUrlForClipboard(store))
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue