For #16132 - Revise multiselect mode UI
This commit is contained in:
parent
1f6f29ea7d
commit
a8db85fc22
|
@ -141,7 +141,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
|
|||
|
||||
private fun hasExistingAddonInstallationDialogFragment(): Boolean {
|
||||
return parentFragmentManager.findFragmentByTag(INSTALLATION_DIALOG_FRAGMENT_TAG)
|
||||
as? AddonInstallationDialogFragment != null
|
||||
as? AddonInstallationDialogFragment != null
|
||||
}
|
||||
|
||||
private fun showPermissionDialog(addon: Addon) {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* 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.tabtray
|
||||
|
||||
import android.content.Context
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
class MultiselectSelectionMenu(
|
||||
private val context: Context,
|
||||
private val onItemTapped: (Item) -> Unit = {}
|
||||
) {
|
||||
sealed class Item {
|
||||
object BookmarkTabs : Item()
|
||||
object DeleteTabs : Item()
|
||||
}
|
||||
|
||||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_multiselect_menu_item_bookmark),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTraySaveToCollectionPressed)
|
||||
onItemTapped.invoke(Item.BookmarkTabs)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_multiselect_menu_item_close),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTrayShareAllTabsPressed)
|
||||
onItemTapped.invoke(Item.DeleteTabs)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,13 +4,20 @@
|
|||
|
||||
package org.mozilla.fenix.tabtray
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.base.profiler.Profiler
|
||||
import mozilla.components.concept.engine.prompt.ShareData
|
||||
import mozilla.components.concept.storage.BookmarksStorage
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
|
@ -18,7 +25,6 @@ import org.mozilla.fenix.HomeActivity
|
|||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
import org.mozilla.fenix.ext.sessionsOfType
|
||||
import org.mozilla.fenix.home.HomeFragment
|
||||
import mozilla.components.browser.storage.sync.Tab as SyncTab
|
||||
|
||||
|
@ -29,13 +35,16 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
|
|||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
interface TabTrayController {
|
||||
fun onNewTabTapped(private: Boolean)
|
||||
fun onTabTrayDismissed()
|
||||
fun handleNewTabTapped(private: Boolean)
|
||||
fun handleTabTrayDismissed()
|
||||
fun handleTabSettingsClicked()
|
||||
fun onShareTabsClicked(private: Boolean)
|
||||
fun onSyncedTabClicked(syncTab: SyncTab)
|
||||
fun onSaveToCollectionClicked(selectedTabs: Set<Tab>)
|
||||
fun onCloseAllTabsClicked(private: Boolean)
|
||||
fun handleShareTabsOfTypeClicked(private: Boolean)
|
||||
fun handleShareSelectedTabsClicked(selectedTabs: Set<Tab>)
|
||||
fun handleSyncedTabClicked(syncTab: SyncTab)
|
||||
fun handleSaveToCollectionClicked(selectedTabs: Set<Tab>)
|
||||
fun handleBookmarkSelectedTabs(selectedTabs: Set<Tab>)
|
||||
fun handleDeleteSelectedTabs(selectedTabs: Set<Tab>)
|
||||
fun handleCloseAllTabsClicked(private: Boolean)
|
||||
fun handleBackPressed(): Boolean
|
||||
fun onModeRequested(): TabTrayDialogFragmentState.Mode
|
||||
fun handleAddSelectedTab(tab: Tab)
|
||||
|
@ -68,8 +77,12 @@ class DefaultTabTrayController(
|
|||
private val activity: HomeActivity,
|
||||
private val profiler: Profiler?,
|
||||
private val sessionManager: SessionManager,
|
||||
private val browserStore: BrowserStore,
|
||||
private val browsingModeManager: BrowsingModeManager,
|
||||
private val tabCollectionStorage: TabCollectionStorage,
|
||||
private val bookmarksStorage: BookmarksStorage,
|
||||
private val scope: CoroutineScope,
|
||||
private val tabsUseCases: TabsUseCases,
|
||||
private val navController: NavController,
|
||||
private val dismissTabTray: () -> Unit,
|
||||
private val dismissTabTrayAndNavigateHome: (String) -> Unit,
|
||||
|
@ -77,10 +90,12 @@ class DefaultTabTrayController(
|
|||
private val tabTrayDialogFragmentStore: TabTrayDialogFragmentStore,
|
||||
private val selectTabUseCase: TabsUseCases.SelectTabUseCase,
|
||||
private val showChooseCollectionDialog: (List<Session>) -> Unit,
|
||||
private val showAddNewCollectionDialog: (List<Session>) -> Unit
|
||||
private val showAddNewCollectionDialog: (List<Session>) -> Unit,
|
||||
private val showUndoSnackbarForTabs: () -> Unit,
|
||||
private val showBookmarksSnackbar: () -> Unit
|
||||
) : TabTrayController {
|
||||
|
||||
override fun onNewTabTapped(private: Boolean) {
|
||||
override fun handleNewTabTapped(private: Boolean) {
|
||||
val startTime = profiler?.getProfilerTime()
|
||||
browsingModeManager.mode = BrowsingMode.fromBoolean(private)
|
||||
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true))
|
||||
|
@ -95,11 +110,11 @@ class DefaultTabTrayController(
|
|||
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalTabSettingsFragment())
|
||||
}
|
||||
|
||||
override fun onTabTrayDismissed() {
|
||||
override fun handleTabTrayDismissed() {
|
||||
dismissTabTray()
|
||||
}
|
||||
|
||||
override fun onSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
||||
override fun handleSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
||||
val sessionList = selectedTabs.map {
|
||||
sessionManager.findSessionById(it.id) ?: return
|
||||
}
|
||||
|
@ -117,9 +132,19 @@ class DefaultTabTrayController(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onShareTabsClicked(private: Boolean) {
|
||||
val tabs = getListOfSessions(private)
|
||||
override fun handleShareTabsOfTypeClicked(private: Boolean) {
|
||||
val tabs = browserStore.state.getNormalOrPrivateTabs(private)
|
||||
val data = tabs.map {
|
||||
ShareData(url = it.content.url, title = it.content.title)
|
||||
}
|
||||
val directions = TabTrayDialogFragmentDirections.actionGlobalShareFragment(
|
||||
data = data.toTypedArray()
|
||||
)
|
||||
navController.navigate(directions)
|
||||
}
|
||||
|
||||
override fun handleShareSelectedTabsClicked(selectedTabs: Set<Tab>) {
|
||||
val data = selectedTabs.map {
|
||||
ShareData(url = it.url, title = it.title)
|
||||
}
|
||||
val directions = TabTrayDialogFragmentDirections.actionGlobalShareFragment(
|
||||
|
@ -128,7 +153,40 @@ class DefaultTabTrayController(
|
|||
navController.navigate(directions)
|
||||
}
|
||||
|
||||
override fun onSyncedTabClicked(syncTab: SyncTab) {
|
||||
override fun handleBookmarkSelectedTabs(selectedTabs: Set<Tab>) {
|
||||
selectedTabs.forEach {
|
||||
scope.launch(IO) {
|
||||
val shouldAddBookmark = bookmarksStorage.getBookmarksWithUrl(it.url)
|
||||
.firstOrNull { it.url == it.url } == null
|
||||
if (shouldAddBookmark) {
|
||||
bookmarksStorage.addItem(
|
||||
BookmarkRoot.Mobile.id,
|
||||
url = it.url,
|
||||
title = it.title,
|
||||
position = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
tabTrayDialogFragmentStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
|
||||
showBookmarksSnackbar()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun handleDeleteSelectedTabs(selectedTabs: Set<Tab>) {
|
||||
if (browserStore.state.normalTabs.size == selectedTabs.size) {
|
||||
dismissTabTrayAndNavigateHome(HomeFragment.ALL_NORMAL_TABS)
|
||||
} else {
|
||||
selectedTabs.map { it.id }.let {
|
||||
tabsUseCases.removeTabs(it)
|
||||
}
|
||||
|
||||
tabTrayDialogFragmentStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
|
||||
showUndoSnackbarForTabs()
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleSyncedTabClicked(syncTab: SyncTab) {
|
||||
activity.openToBrowserAndLoad(
|
||||
searchTermOrURL = syncTab.active().url,
|
||||
newTab = true,
|
||||
|
@ -137,7 +195,7 @@ class DefaultTabTrayController(
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||
override fun handleCloseAllTabsClicked(private: Boolean) {
|
||||
val sessionsToClose = if (private) {
|
||||
HomeFragment.ALL_PRIVATE_TABS
|
||||
} else {
|
||||
|
@ -164,11 +222,6 @@ class DefaultTabTrayController(
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
private fun getListOfSessions(private: Boolean): List<Session> {
|
||||
return sessionManager.sessionsOfType(private = private).toList()
|
||||
}
|
||||
|
||||
override fun onModeRequested(): TabTrayDialogFragmentState.Mode {
|
||||
return tabTrayDialogFragmentStore.state.mode
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.state.selector.findTab
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
|
@ -71,7 +72,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
|
||||
private val snackbarAnchor: View?
|
||||
get() = if (tabTrayView.fabView.new_tab_button.isVisible ||
|
||||
tabTrayView.mode != Mode.Normal) tabTrayView.fabView.new_tab_button
|
||||
tabTrayView.mode != Mode.Normal
|
||||
) tabTrayView.fabView.new_tab_button
|
||||
/* During selection of the tabs to the collection, the FAB is not visible,
|
||||
which leads to not attaching a needed AnchorView. That's why, we're not only checking, if it's not visible,
|
||||
but also if we're not in a "Normal" mode, so after selecting tabs for a collection, we're pushing snackbar
|
||||
|
@ -177,6 +179,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Suppress("LongMethod")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val activity = activity as HomeActivity
|
||||
|
@ -194,8 +197,12 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
activity = activity,
|
||||
profiler = activity.components.core.engine.profiler,
|
||||
sessionManager = activity.components.core.sessionManager,
|
||||
browserStore = activity.components.core.store,
|
||||
tabsUseCases = activity.components.useCases.tabsUseCases,
|
||||
scope = lifecycleScope,
|
||||
browsingModeManager = activity.browsingModeManager,
|
||||
tabCollectionStorage = activity.components.core.tabCollectionStorage,
|
||||
bookmarksStorage = activity.components.core.bookmarksStorage,
|
||||
navController = findNavController(),
|
||||
dismissTabTray = ::dismissAllowingStateLoss,
|
||||
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome,
|
||||
|
@ -203,7 +210,9 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
tabTrayDialogFragmentStore = tabTrayDialogStore,
|
||||
selectTabUseCase = selectTabUseCase,
|
||||
showChooseCollectionDialog = ::showChooseCollectionDialog,
|
||||
showAddNewCollectionDialog = ::showAddNewCollectionDialog
|
||||
showAddNewCollectionDialog = ::showAddNewCollectionDialog,
|
||||
showUndoSnackbarForTabs = ::showUndoSnackbarForTabs,
|
||||
showBookmarksSnackbar = ::showBookmarksSnackbar
|
||||
)
|
||||
),
|
||||
store = tabTrayDialogStore,
|
||||
|
@ -267,6 +276,20 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
}
|
||||
}
|
||||
|
||||
private fun showUndoSnackbarForTabs() {
|
||||
lifecycleScope.allowUndo(
|
||||
requireView().tabLayout,
|
||||
getString(R.string.snackbar_message_tabs_closed),
|
||||
getString(R.string.snackbar_deleted_undo),
|
||||
{
|
||||
requireComponents.useCases.tabsUseCases.undo.invoke()
|
||||
},
|
||||
operation = { },
|
||||
elevation = ELEVATION,
|
||||
anchorView = snackbarAnchor
|
||||
)
|
||||
}
|
||||
|
||||
private fun showUndoSnackbarForTab(sessionId: String) {
|
||||
val store = requireComponents.core.store
|
||||
val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
|
||||
|
@ -358,6 +381,26 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
|||
}
|
||||
}
|
||||
|
||||
private fun showBookmarksSnackbar() {
|
||||
val snackbar = FenixSnackbar
|
||||
.make(
|
||||
duration = FenixSnackbar.LENGTH_LONG,
|
||||
isDisplayedWithBrowserToolbar = false,
|
||||
view = (view as View)
|
||||
)
|
||||
.setAnchorView(snackbarAnchor)
|
||||
.setText(requireContext().getString(R.string.snackbar_message_bookmarks_saved))
|
||||
.setAction(requireContext().getString(R.string.snackbar_message_bookmarks_view)) {
|
||||
dismissAllowingStateLoss()
|
||||
findNavController().navigate(
|
||||
TabTrayDialogFragmentDirections.actionGlobalBookmarkFragment(BookmarkRoot.Mobile.id)
|
||||
)
|
||||
}
|
||||
|
||||
snackbar.view.elevation = ELEVATION
|
||||
snackbar.show()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
if (!tabTrayView.onBackPressed()) {
|
||||
dismiss()
|
||||
|
|
|
@ -22,7 +22,22 @@ interface TabTrayInteractor {
|
|||
/**
|
||||
* Called when user clicks the share tabs button.
|
||||
*/
|
||||
fun onShareTabsClicked(private: Boolean)
|
||||
fun onShareTabsOfTypeClicked(private: Boolean)
|
||||
|
||||
/**
|
||||
* Called when user clicks button to share selected tabs in multiselect.
|
||||
*/
|
||||
fun onShareSelectedTabsClicked(selectedTabs: Set<Tab>)
|
||||
|
||||
/**
|
||||
* Called when user clicks bookmark button in menu to bookmark selected tabs in multiselect.
|
||||
*/
|
||||
fun onBookmarkSelectedTabs(selectedTabs: Set<Tab>)
|
||||
|
||||
/**
|
||||
* Called when user clicks delete button in menu to delete selected tabs in multiselect.
|
||||
*/
|
||||
fun onDeleteSelectedTabs(selectedTabs: Set<Tab>)
|
||||
|
||||
/**
|
||||
* Called when user clicks the tab settings button.
|
||||
|
@ -91,11 +106,11 @@ interface TabTrayInteractor {
|
|||
@Suppress("TooManyFunctions")
|
||||
class TabTrayFragmentInteractor(private val controller: TabTrayController) : TabTrayInteractor {
|
||||
override fun onNewTabTapped(private: Boolean) {
|
||||
controller.onNewTabTapped(private)
|
||||
controller.handleNewTabTapped(private)
|
||||
}
|
||||
|
||||
override fun onTabTrayDismissed() {
|
||||
controller.onTabTrayDismissed()
|
||||
controller.handleTabTrayDismissed()
|
||||
}
|
||||
|
||||
override fun onTabSettingsClicked() {
|
||||
|
@ -106,20 +121,32 @@ class TabTrayFragmentInteractor(private val controller: TabTrayController) : Tab
|
|||
controller.handleRecentlyClosedClicked()
|
||||
}
|
||||
|
||||
override fun onShareTabsClicked(private: Boolean) {
|
||||
controller.onShareTabsClicked(private)
|
||||
override fun onShareTabsOfTypeClicked(private: Boolean) {
|
||||
controller.handleShareTabsOfTypeClicked(private)
|
||||
}
|
||||
|
||||
override fun onShareSelectedTabsClicked(selectedTabs: Set<Tab>) {
|
||||
controller.handleShareSelectedTabsClicked(selectedTabs)
|
||||
}
|
||||
|
||||
override fun onBookmarkSelectedTabs(selectedTabs: Set<Tab>) {
|
||||
controller.handleBookmarkSelectedTabs(selectedTabs)
|
||||
}
|
||||
|
||||
override fun onDeleteSelectedTabs(selectedTabs: Set<Tab>) {
|
||||
controller.handleDeleteSelectedTabs(selectedTabs)
|
||||
}
|
||||
|
||||
override fun onSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
||||
controller.onSaveToCollectionClicked(selectedTabs)
|
||||
controller.handleSaveToCollectionClicked(selectedTabs)
|
||||
}
|
||||
|
||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||
controller.onCloseAllTabsClicked(private)
|
||||
controller.handleCloseAllTabsClicked(private)
|
||||
}
|
||||
|
||||
override fun onSyncedTabClicked(syncTab: SyncTab) {
|
||||
controller.onSyncedTabClicked(syncTab)
|
||||
controller.handleSyncedTabClicked(syncTab)
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/* 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.tabtray
|
||||
|
||||
import android.content.Context
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
class TabTrayItemMenu(
|
||||
private val context: Context,
|
||||
private val shouldShowSaveToCollection: () -> Boolean,
|
||||
private val hasOpenTabs: () -> Boolean,
|
||||
private val onItemTapped: (Item) -> Unit = {}
|
||||
) {
|
||||
|
||||
sealed class Item {
|
||||
object ShareAllTabs : Item()
|
||||
object OpenTabSettings : Item()
|
||||
object SaveToCollection : Item()
|
||||
object CloseAllTabs : Item()
|
||||
object OpenRecentlyClosed : Item()
|
||||
}
|
||||
|
||||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_save),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTraySaveToCollectionPressed)
|
||||
onItemTapped.invoke(Item.SaveToCollection)
|
||||
}.apply { visible = shouldShowSaveToCollection },
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_share),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTrayShareAllTabsPressed)
|
||||
onItemTapped.invoke(Item.ShareAllTabs)
|
||||
}.apply { visible = hasOpenTabs },
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_tab_settings),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.OpenTabSettings)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_recently_closed),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.OpenRecentlyClosed)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_close),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTrayCloseAllTabsPressed)
|
||||
onItemTapped.invoke(Item.CloseAllTabs)
|
||||
}.apply { visible = hasOpenTabs }
|
||||
)
|
||||
}
|
||||
}
|
|
@ -30,12 +30,11 @@ import kotlinx.android.extensions.LayoutContainer
|
|||
import kotlinx.android.synthetic.main.component_tabstray.view.*
|
||||
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
||||
import kotlinx.android.synthetic.main.tabs_tray_tab_counter.*
|
||||
import kotlinx.android.synthetic.main.tabstray_multiselect_items.view.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.selector.privateTabs
|
||||
|
@ -90,6 +89,9 @@ class TabTrayView(
|
|||
private val tabTrayItemMenu: TabTrayItemMenu
|
||||
private var menu: BrowserMenu? = null
|
||||
|
||||
private val multiselectSelectionMenu: MultiselectSelectionMenu
|
||||
private var multiselectMenu: BrowserMenu? = null
|
||||
|
||||
private var tabsTouchHelper: TabsTouchHelper
|
||||
private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate)
|
||||
|
||||
|
@ -230,7 +232,7 @@ class TabTrayView(
|
|||
hasOpenTabs = checkOpenTabs
|
||||
) {
|
||||
when (it) {
|
||||
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
|
||||
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsOfTypeClicked(
|
||||
isPrivateModeSelected
|
||||
)
|
||||
is TabTrayItemMenu.Item.OpenTabSettings -> interactor.onTabSettingsClicked()
|
||||
|
@ -242,18 +244,30 @@ class TabTrayView(
|
|||
}
|
||||
}
|
||||
|
||||
multiselectSelectionMenu = MultiselectSelectionMenu(
|
||||
context = view.context
|
||||
) {
|
||||
when (it) {
|
||||
is MultiselectSelectionMenu.Item.BookmarkTabs -> interactor.onBookmarkSelectedTabs(
|
||||
mode.selectedItems
|
||||
)
|
||||
is MultiselectSelectionMenu.Item.DeleteTabs -> interactor.onDeleteSelectedTabs(
|
||||
mode.selectedItems
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
view.tab_tray_overflow.setOnClickListener {
|
||||
components.analytics.metrics.track(Event.TabsTrayMenuOpened)
|
||||
menu = tabTrayItemMenu.menuBuilder.build(container.context)
|
||||
menu?.show(it)
|
||||
?.also { pu ->
|
||||
(pu.contentView as? CardView)?.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
view.context,
|
||||
R.color.foundation_normal_theme
|
||||
)
|
||||
menu?.show(it)?.also { popupMenu ->
|
||||
(popupMenu.contentView as? CardView)?.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
view.context,
|
||||
R.color.foundation_normal_theme
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
adjustNewTabButtonsForNormalMode()
|
||||
|
@ -469,6 +483,8 @@ class TabTrayView(
|
|||
fabView.new_tab_button.isVisible = false
|
||||
view.tab_tray_new_tab.isVisible = false
|
||||
view.collect_multi_select.isVisible = state.mode.selectedItems.isNotEmpty()
|
||||
view.share_multi_select.isVisible = state.mode.selectedItems.isNotEmpty()
|
||||
view.menu_multi_select.isVisible = state.mode.selectedItems.isNotEmpty()
|
||||
|
||||
view.multiselect_title.text = view.context.getString(
|
||||
R.string.tab_tray_multi_select_title,
|
||||
|
@ -477,6 +493,20 @@ class TabTrayView(
|
|||
view.collect_multi_select.setOnClickListener {
|
||||
interactor.onSaveToCollectionClicked(state.mode.selectedItems)
|
||||
}
|
||||
view.share_multi_select.setOnClickListener {
|
||||
interactor.onShareSelectedTabsClicked(state.mode.selectedItems)
|
||||
}
|
||||
view.menu_multi_select.setOnClickListener {
|
||||
multiselectMenu = multiselectSelectionMenu.menuBuilder.build(container.context)
|
||||
multiselectMenu?.show(it)?.also { popupMenu ->
|
||||
(popupMenu.contentView as? CardView)?.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
view.context,
|
||||
R.color.foundation_normal_theme
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
view.exit_multi_select.setOnClickListener {
|
||||
interactor.onBackPressed()
|
||||
}
|
||||
|
@ -544,6 +574,8 @@ class TabTrayView(
|
|||
private fun toggleUIMultiselect(multiselect: Boolean) {
|
||||
view.multiselect_title.isVisible = multiselect
|
||||
view.collect_multi_select.isVisible = multiselect
|
||||
view.share_multi_select.isVisible = multiselect
|
||||
view.menu_multi_select.isVisible = multiselect
|
||||
view.exit_multi_select.isVisible = multiselect
|
||||
|
||||
view.topBar.setBackgroundColor(
|
||||
|
@ -707,9 +739,7 @@ class TabTrayView(
|
|||
// We offset the tab index by the number of items in the other adapters.
|
||||
// We add the offset, because the layoutManager is initialized with `reverseLayout`.
|
||||
return if (view.context.settings().listTabView) {
|
||||
selectedBrowserTabIndex +
|
||||
collectionsButtonAdapter.itemCount +
|
||||
syncedTabsController.adapter.itemCount
|
||||
selectedBrowserTabIndex + collectionsButtonAdapter.itemCount + syncedTabsController.adapter.itemCount
|
||||
} else {
|
||||
selectedBrowserTabIndex
|
||||
}
|
||||
|
@ -719,75 +749,18 @@ class TabTrayView(
|
|||
private const val TAB_COUNT_SHOW_CFR = 6
|
||||
private const val DEFAULT_TAB_ID = 0
|
||||
private const val PRIVATE_TAB_ID = 1
|
||||
|
||||
// Minimum number of list items for which to show the tabs tray as expanded.
|
||||
private const val EXPAND_AT_LIST_SIZE = 4
|
||||
|
||||
// Minimum number of grid items for which to show the tabs tray as expanded.
|
||||
private const val EXPAND_AT_GRID_SIZE = 3
|
||||
private const val SLIDE_OFFSET = 0
|
||||
private const val SELECTION_DELAY = 500
|
||||
private const val NORMAL_HANDLE_PERCENT_WIDTH = 0.1F
|
||||
private const val COLUMN_WIDTH_DP = 180
|
||||
|
||||
// The remaining padding offset needed to provide a 16dp column spacing between the grid items.
|
||||
const val GRID_ITEM_PARENT_PADDING = 8
|
||||
}
|
||||
}
|
||||
|
||||
class TabTrayItemMenu(
|
||||
private val context: Context,
|
||||
private val shouldShowSaveToCollection: () -> Boolean,
|
||||
private val hasOpenTabs: () -> Boolean,
|
||||
private val onItemTapped: (Item) -> Unit = {}
|
||||
) {
|
||||
|
||||
sealed class Item {
|
||||
object ShareAllTabs : Item()
|
||||
object OpenTabSettings : Item()
|
||||
object SaveToCollection : Item()
|
||||
object CloseAllTabs : Item()
|
||||
object OpenRecentlyClosed : Item()
|
||||
}
|
||||
|
||||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_save),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTraySaveToCollectionPressed)
|
||||
onItemTapped.invoke(Item.SaveToCollection)
|
||||
}.apply { visible = shouldShowSaveToCollection },
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_share),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTrayShareAllTabsPressed)
|
||||
onItemTapped.invoke(Item.ShareAllTabs)
|
||||
}.apply { visible = hasOpenTabs },
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_tab_settings),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.OpenTabSettings)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_recently_closed),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.OpenRecentlyClosed)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_close),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
context.components.analytics.metrics.track(Event.TabsTrayCloseAllTabsPressed)
|
||||
onItemTapped.invoke(Item.CloseAllTabs)
|
||||
}.apply { visible = hasOpenTabs }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,14 +23,13 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.1" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/infoBanner"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/foundation_normal_theme"
|
||||
app:layout_constraintTop_toBottomOf="@+id/topBar"/>
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/topBar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tab_tray_empty_view"
|
||||
|
@ -76,7 +75,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:textColor="@color/contrast_text_normal_theme"
|
||||
android:textSize="18sp"
|
||||
android:textSize="20sp"
|
||||
app:fontFamily="@font/metropolis_semibold"
|
||||
android:focusableInTouchMode="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/collect_multi_select"
|
||||
|
@ -86,34 +86,12 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="3 selected" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collect_multi_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_tray_collection_button_multiselect_content_description"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/tab_tray_save_to_collection"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/contrast_text_normal_theme"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:drawableStartCompat="@drawable/ic_tab_collection"
|
||||
app:drawableTint="@color/contrast_text_normal_theme"
|
||||
app:fontFamily="@font/metropolis_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<include layout="@layout/tabstray_multiselect_items" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
app:tabMaxWidth="0dp"
|
||||
android:background="@color/foundation_normal_theme"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -121,6 +99,7 @@
|
|||
app:tabGravity="fill"
|
||||
app:tabIconTint="@color/tab_icon"
|
||||
app:tabIndicatorColor="@color/accent_normal_theme"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabRippleColor="@android:color/transparent">
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
|
@ -150,8 +129,8 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
||||
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
|
||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
||||
app:tint="@color/primary_text_normal_theme"
|
||||
app:srcCompat="@drawable/ic_new" />
|
||||
app:srcCompat="@drawable/ic_new"
|
||||
app:tint="@color/primary_text_normal_theme" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tab_tray_overflow"
|
||||
|
@ -161,11 +140,11 @@
|
|||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/open_tabs_menu"
|
||||
android:visibility="visible"
|
||||
app:tint="@color/tab_tray_heading_icon_menu_normal_theme"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
||||
app:srcCompat="@drawable/ic_menu" />
|
||||
app:srcCompat="@drawable/ic_menu"
|
||||
app:tint="@color/tab_tray_heading_icon_menu_normal_theme" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
<!-- 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/. -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/NeutralButton"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/save_to_collection"
|
||||
app:icon="@drawable/ic_tab_collection" />
|
||||
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/NeutralButton"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/tabs_tray_select_tabs" />
|
||||
|
|
50
app/src/main/res/layout/tabstray_multiselect_items.xml
Normal file
50
app/src/main/res/layout/tabstray_multiselect_items.xml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/collect_multi_select"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_tray_collection_button_multiselect_content_description"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/share_multi_select"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_tab_collection"
|
||||
app:tint="@color/contrast_text_normal_theme" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/share_multi_select"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_tray_multiselect_share_content_description"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/menu_multi_select"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_hollow_share"
|
||||
app:tint="@color/contrast_text_normal_theme" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/menu_multi_select"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_tray_multiselect_menu_content_description"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_menu"
|
||||
app:tint="@color/contrast_text_normal_theme" />
|
||||
</merge>
|
|
@ -584,8 +584,18 @@
|
|||
<string name="tab_tray_menu_home">Go home</string>
|
||||
<!-- Shortcut action to toggle private mode -->
|
||||
<string name="tab_tray_menu_toggle">Toggle tab mode</string>
|
||||
<!-- Text shown in the multiselect menu for bookmarking selected tabs. -->
|
||||
<string name="tab_tray_multiselect_menu_item_bookmark">Bookmark</string>
|
||||
<!-- Text shown in the multiselect menu for closing selected tabs. -->
|
||||
<string name="tab_tray_multiselect_menu_item_close">Close</string>
|
||||
<!-- Content description for tabs tray multiselect share button -->
|
||||
<string name="tab_tray_multiselect_share_content_description">Share selected tabs</string>
|
||||
<!-- Content description for tabs tray multiselect menu -->
|
||||
<string name="tab_tray_multiselect_menu_content_description">Selected tabs menu</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
|
||||
<string name="remove_tab_from_collection">Remove tab from collection</string>
|
||||
<!-- Text for button to enter multiselect mode in tabs tray -->
|
||||
<string name="tabs_tray_select_tabs">Select tabs</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
|
||||
<string name="close_tab">Close tab</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
|
||||
|
@ -950,6 +960,12 @@
|
|||
<string name="snackbar_tab_closed">Tab closed</string>
|
||||
<!-- Text shown in snackbar when user closes all tabs -->
|
||||
<string name="snackbar_tabs_closed">Tabs closed</string>
|
||||
<!-- Text shown in snackbar when user closes tabs -->
|
||||
<string name="snackbar_message_tabs_closed">Tabs closed!</string>
|
||||
<!-- Text shown in snackbar when user bookmarks a list of tabs -->
|
||||
<string name="snackbar_message_bookmarks_saved">Bookmarks saved!</string>
|
||||
<!-- Text shown in snackbar action for viewing bookmarks -->
|
||||
<string name="snackbar_message_bookmarks_view">View</string>
|
||||
<!-- Text shown in snackbar when user adds a site to top sites -->
|
||||
<string name="snackbar_added_to_top_sites">Added to top sites!</string>
|
||||
<!-- Text shown in snackbar when user closes a private tab -->
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.navigation.NavController
|
|||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDirections
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
|
@ -16,9 +17,14 @@ import io.mockk.slot
|
|||
import io.mockk.verify
|
||||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.base.profiler.Profiler
|
||||
import mozilla.components.concept.storage.BookmarksStorage
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
|
@ -40,6 +46,14 @@ class DefaultTabTrayControllerTest {
|
|||
private val profiler: Profiler? = mockk(relaxed = true)
|
||||
private val navController: NavController = mockk()
|
||||
private val sessionManager: SessionManager = mockk(relaxed = true)
|
||||
var store = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = listOf(
|
||||
createTab(url = "http://firefox.com", id = "5678"),
|
||||
createTab(url = "http://mozilla.org", id = "1234")
|
||||
), selectedTabId = "1234"
|
||||
)
|
||||
)
|
||||
private val browsingModeManager: BrowsingModeManager = mockk(relaxed = true)
|
||||
private val dismissTabTray: (() -> Unit) = mockk(relaxed = true)
|
||||
private val dismissTabTrayAndNavigateHome: ((String) -> Unit) = mockk(relaxed = true)
|
||||
|
@ -47,11 +61,15 @@ class DefaultTabTrayControllerTest {
|
|||
private val showChooseCollectionDialog: ((List<Session>) -> Unit) = mockk(relaxed = true)
|
||||
private val showAddNewCollectionDialog: ((List<Session>) -> Unit) = mockk(relaxed = true)
|
||||
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
||||
private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true)
|
||||
private val tabCollection: TabCollection = mockk()
|
||||
private val cachedTabCollections: List<TabCollection> = listOf(tabCollection)
|
||||
private val currentDestination: NavDestination = mockk(relaxed = true)
|
||||
private val tabTrayFragmentStore: TabTrayDialogFragmentStore = mockk(relaxed = true)
|
||||
private val selectTabUseCase: TabsUseCases.SelectTabUseCase = mockk(relaxed = true)
|
||||
private val tabsUseCases: TabsUseCases = mockk(relaxed = true)
|
||||
private val showUndoSnackbarForTabs: (() -> Unit) = mockk(relaxed = true)
|
||||
private val showBookmarksSavedSnackbar: (() -> Unit) = mockk(relaxed = true)
|
||||
|
||||
private lateinit var controller: DefaultTabTrayController
|
||||
|
||||
|
@ -87,16 +105,22 @@ class DefaultTabTrayControllerTest {
|
|||
activity = activity,
|
||||
profiler = profiler,
|
||||
sessionManager = sessionManager,
|
||||
browserStore = store,
|
||||
browsingModeManager = browsingModeManager,
|
||||
tabCollectionStorage = tabCollectionStorage,
|
||||
bookmarksStorage = bookmarksStorage,
|
||||
scope = TestCoroutineScope(),
|
||||
navController = navController,
|
||||
tabsUseCases = tabsUseCases,
|
||||
dismissTabTray = dismissTabTray,
|
||||
dismissTabTrayAndNavigateHome = dismissTabTrayAndNavigateHome,
|
||||
registerCollectionStorageObserver = registerCollectionStorageObserver,
|
||||
tabTrayDialogFragmentStore = tabTrayFragmentStore,
|
||||
selectTabUseCase = selectTabUseCase,
|
||||
showChooseCollectionDialog = showChooseCollectionDialog,
|
||||
showAddNewCollectionDialog = showAddNewCollectionDialog
|
||||
showAddNewCollectionDialog = showAddNewCollectionDialog,
|
||||
showUndoSnackbarForTabs = showUndoSnackbarForTabs,
|
||||
showBookmarksSnackbar = showBookmarksSavedSnackbar
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -113,7 +137,7 @@ class DefaultTabTrayControllerTest {
|
|||
|
||||
@Test
|
||||
fun onNewTabTapped() {
|
||||
controller.onNewTabTapped(private = false)
|
||||
controller.handleNewTabTapped(private = false)
|
||||
|
||||
verifyOrder {
|
||||
browsingModeManager.mode = BrowsingMode.fromBoolean(false)
|
||||
|
@ -125,7 +149,7 @@ class DefaultTabTrayControllerTest {
|
|||
dismissTabTray()
|
||||
}
|
||||
|
||||
controller.onNewTabTapped(private = true)
|
||||
controller.handleNewTabTapped(private = true)
|
||||
|
||||
verifyOrder {
|
||||
browsingModeManager.mode = BrowsingMode.fromBoolean(true)
|
||||
|
@ -140,7 +164,7 @@ class DefaultTabTrayControllerTest {
|
|||
|
||||
@Test
|
||||
fun onTabTrayDismissed() {
|
||||
controller.onTabTrayDismissed()
|
||||
controller.handleTabTrayDismissed()
|
||||
|
||||
verify {
|
||||
dismissTabTray()
|
||||
|
@ -152,7 +176,7 @@ class DefaultTabTrayControllerTest {
|
|||
val navDirectionsSlot = slot<NavDirections>()
|
||||
every { navController.navigate(capture(navDirectionsSlot)) } just Runs
|
||||
|
||||
controller.onShareTabsClicked(private = false)
|
||||
controller.handleShareTabsOfTypeClicked(private = false)
|
||||
|
||||
verify {
|
||||
navController.navigate(capture(navDirectionsSlot))
|
||||
|
@ -164,7 +188,7 @@ class DefaultTabTrayControllerTest {
|
|||
|
||||
@Test
|
||||
fun onCloseAllTabsClicked() {
|
||||
controller.onCloseAllTabsClicked(private = false)
|
||||
controller.handleCloseAllTabsClicked(private = false)
|
||||
|
||||
verify {
|
||||
dismissTabTrayAndNavigateHome(any())
|
||||
|
@ -173,7 +197,7 @@ class DefaultTabTrayControllerTest {
|
|||
|
||||
@Test
|
||||
fun onSyncedTabClicked() {
|
||||
controller.onSyncedTabClicked(mockk(relaxed = true))
|
||||
controller.handleSyncedTabClicked(mockk(relaxed = true))
|
||||
|
||||
verify {
|
||||
activity.openToBrowserAndLoad(any(), true, BrowserDirection.FromTabTray)
|
||||
|
@ -242,13 +266,53 @@ class DefaultTabTrayControllerTest {
|
|||
fun onSaveToCollectionClicked() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
|
||||
controller.onSaveToCollectionClicked(setOf(tab))
|
||||
controller.handleSaveToCollectionClicked(setOf(tab))
|
||||
verify {
|
||||
registerCollectionStorageObserver()
|
||||
showChooseCollectionDialog(listOf(session))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleShareSelectedTabs() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
val navDirectionsSlot = slot<NavDirections>()
|
||||
every { navController.navigate(capture(navDirectionsSlot)) } just Runs
|
||||
|
||||
controller.handleShareSelectedTabsClicked(setOf(tab))
|
||||
|
||||
verify {
|
||||
navController.navigate(capture(navDirectionsSlot))
|
||||
}
|
||||
|
||||
assertTrue(navDirectionsSlot.isCaptured)
|
||||
assertEquals(R.id.action_global_shareFragment, navDirectionsSlot.captured.actionId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDeleteSelectedTabs() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
|
||||
controller.handleDeleteSelectedTabs(setOf(tab))
|
||||
verify {
|
||||
tabsUseCases.removeTabs(listOf(tab.id))
|
||||
tabTrayFragmentStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
|
||||
showUndoSnackbarForTabs()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleBookmarkSelectedTabs() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
coEvery { bookmarksStorage.getBookmarksWithUrl("mozilla.org") } returns listOf()
|
||||
|
||||
controller.handleBookmarkSelectedTabs(setOf(tab))
|
||||
verify {
|
||||
tabTrayFragmentStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
|
||||
showBookmarksSavedSnackbar()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleSetUpAutoCloseTabsClicked() {
|
||||
controller.handleSetUpAutoCloseTabsClicked()
|
||||
|
|
|
@ -13,13 +13,40 @@ class TabTrayFragmentInteractorTest {
|
|||
private val controller = mockk<TabTrayController>(relaxed = true)
|
||||
private val interactor = TabTrayFragmentInteractor(controller)
|
||||
|
||||
@Test
|
||||
fun onShareSelectedTabsClicked() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
val tab2 = Tab("5678", "pocket.com")
|
||||
val selectedTabs = setOf(tab, tab2)
|
||||
interactor.onShareSelectedTabsClicked(selectedTabs)
|
||||
verify { controller.handleShareSelectedTabsClicked(selectedTabs) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBookmarkSelectedTabs() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
val tab2 = Tab("5678", "pocket.com")
|
||||
val selectedTabs = setOf(tab, tab2)
|
||||
interactor.onBookmarkSelectedTabs(selectedTabs)
|
||||
verify { controller.handleBookmarkSelectedTabs(selectedTabs) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onDeleteSelectedTabs() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
val tab2 = Tab("5678", "pocket.com")
|
||||
val selectedTabs = setOf(tab, tab2)
|
||||
interactor.onDeleteSelectedTabs(selectedTabs)
|
||||
verify { controller.handleDeleteSelectedTabs(selectedTabs) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onNewTabTapped() {
|
||||
interactor.onNewTabTapped(private = true)
|
||||
verify { controller.onNewTabTapped(true) }
|
||||
verify { controller.handleNewTabTapped(true) }
|
||||
|
||||
interactor.onNewTabTapped(private = false)
|
||||
verify { controller.onNewTabTapped(false) }
|
||||
verify { controller.handleNewTabTapped(false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -34,38 +61,38 @@ class TabTrayFragmentInteractorTest {
|
|||
@Test
|
||||
fun onTabTrayDismissed() {
|
||||
interactor.onTabTrayDismissed()
|
||||
verify { controller.onTabTrayDismissed() }
|
||||
verify { controller.handleTabTrayDismissed() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onShareTabsClicked() {
|
||||
interactor.onShareTabsClicked(private = true)
|
||||
verify { controller.onShareTabsClicked(true) }
|
||||
interactor.onShareTabsOfTypeClicked(private = true)
|
||||
verify { controller.handleShareTabsOfTypeClicked(true) }
|
||||
|
||||
interactor.onShareTabsClicked(private = false)
|
||||
verify { controller.onShareTabsClicked(false) }
|
||||
interactor.onShareTabsOfTypeClicked(private = false)
|
||||
verify { controller.handleShareTabsOfTypeClicked(false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onSaveToCollectionClicked() {
|
||||
val tab = Tab("1234", "mozilla.org")
|
||||
interactor.onSaveToCollectionClicked(setOf(tab))
|
||||
verify { controller.onSaveToCollectionClicked(setOf(tab)) }
|
||||
verify { controller.handleSaveToCollectionClicked(setOf(tab)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onCloseAllTabsClicked() {
|
||||
interactor.onCloseAllTabsClicked(private = false)
|
||||
verify { controller.onCloseAllTabsClicked(false) }
|
||||
verify { controller.handleCloseAllTabsClicked(false) }
|
||||
|
||||
interactor.onCloseAllTabsClicked(private = true)
|
||||
verify { controller.onCloseAllTabsClicked(true) }
|
||||
verify { controller.handleCloseAllTabsClicked(true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onSyncedTabClicked() {
|
||||
interactor.onSyncedTabClicked(mockk(relaxed = true))
|
||||
verify { controller.onSyncedTabClicked(any()) }
|
||||
verify { controller.handleSyncedTabClicked(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue
Block a user