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 {
|
private fun hasExistingAddonInstallationDialogFragment(): Boolean {
|
||||||
return parentFragmentManager.findFragmentByTag(INSTALLATION_DIALOG_FRAGMENT_TAG)
|
return parentFragmentManager.findFragmentByTag(INSTALLATION_DIALOG_FRAGMENT_TAG)
|
||||||
as? AddonInstallationDialogFragment != null
|
as? AddonInstallationDialogFragment != null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPermissionDialog(addon: Addon) {
|
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
|
package org.mozilla.fenix.tabtray
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.appservices.places.BookmarkRoot
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
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.base.profiler.Profiler
|
||||||
import mozilla.components.concept.engine.prompt.ShareData
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
|
import mozilla.components.concept.storage.BookmarksStorage
|
||||||
import mozilla.components.concept.tabstray.Tab
|
import mozilla.components.concept.tabstray.Tab
|
||||||
import mozilla.components.feature.tabs.TabsUseCases
|
import mozilla.components.feature.tabs.TabsUseCases
|
||||||
import org.mozilla.fenix.BrowserDirection
|
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.BrowsingMode
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||||
import org.mozilla.fenix.components.TabCollectionStorage
|
import org.mozilla.fenix.components.TabCollectionStorage
|
||||||
import org.mozilla.fenix.ext.sessionsOfType
|
|
||||||
import org.mozilla.fenix.home.HomeFragment
|
import org.mozilla.fenix.home.HomeFragment
|
||||||
import mozilla.components.browser.storage.sync.Tab as SyncTab
|
import mozilla.components.browser.storage.sync.Tab as SyncTab
|
||||||
|
|
||||||
|
@ -29,13 +35,16 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
interface TabTrayController {
|
interface TabTrayController {
|
||||||
fun onNewTabTapped(private: Boolean)
|
fun handleNewTabTapped(private: Boolean)
|
||||||
fun onTabTrayDismissed()
|
fun handleTabTrayDismissed()
|
||||||
fun handleTabSettingsClicked()
|
fun handleTabSettingsClicked()
|
||||||
fun onShareTabsClicked(private: Boolean)
|
fun handleShareTabsOfTypeClicked(private: Boolean)
|
||||||
fun onSyncedTabClicked(syncTab: SyncTab)
|
fun handleShareSelectedTabsClicked(selectedTabs: Set<Tab>)
|
||||||
fun onSaveToCollectionClicked(selectedTabs: Set<Tab>)
|
fun handleSyncedTabClicked(syncTab: SyncTab)
|
||||||
fun onCloseAllTabsClicked(private: Boolean)
|
fun handleSaveToCollectionClicked(selectedTabs: Set<Tab>)
|
||||||
|
fun handleBookmarkSelectedTabs(selectedTabs: Set<Tab>)
|
||||||
|
fun handleDeleteSelectedTabs(selectedTabs: Set<Tab>)
|
||||||
|
fun handleCloseAllTabsClicked(private: Boolean)
|
||||||
fun handleBackPressed(): Boolean
|
fun handleBackPressed(): Boolean
|
||||||
fun onModeRequested(): TabTrayDialogFragmentState.Mode
|
fun onModeRequested(): TabTrayDialogFragmentState.Mode
|
||||||
fun handleAddSelectedTab(tab: Tab)
|
fun handleAddSelectedTab(tab: Tab)
|
||||||
|
@ -68,8 +77,12 @@ class DefaultTabTrayController(
|
||||||
private val activity: HomeActivity,
|
private val activity: HomeActivity,
|
||||||
private val profiler: Profiler?,
|
private val profiler: Profiler?,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
|
private val browserStore: BrowserStore,
|
||||||
private val browsingModeManager: BrowsingModeManager,
|
private val browsingModeManager: BrowsingModeManager,
|
||||||
private val tabCollectionStorage: TabCollectionStorage,
|
private val tabCollectionStorage: TabCollectionStorage,
|
||||||
|
private val bookmarksStorage: BookmarksStorage,
|
||||||
|
private val scope: CoroutineScope,
|
||||||
|
private val tabsUseCases: TabsUseCases,
|
||||||
private val navController: NavController,
|
private val navController: NavController,
|
||||||
private val dismissTabTray: () -> Unit,
|
private val dismissTabTray: () -> Unit,
|
||||||
private val dismissTabTrayAndNavigateHome: (String) -> Unit,
|
private val dismissTabTrayAndNavigateHome: (String) -> Unit,
|
||||||
|
@ -77,10 +90,12 @@ class DefaultTabTrayController(
|
||||||
private val tabTrayDialogFragmentStore: TabTrayDialogFragmentStore,
|
private val tabTrayDialogFragmentStore: TabTrayDialogFragmentStore,
|
||||||
private val selectTabUseCase: TabsUseCases.SelectTabUseCase,
|
private val selectTabUseCase: TabsUseCases.SelectTabUseCase,
|
||||||
private val showChooseCollectionDialog: (List<Session>) -> Unit,
|
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 {
|
) : TabTrayController {
|
||||||
|
|
||||||
override fun onNewTabTapped(private: Boolean) {
|
override fun handleNewTabTapped(private: Boolean) {
|
||||||
val startTime = profiler?.getProfilerTime()
|
val startTime = profiler?.getProfilerTime()
|
||||||
browsingModeManager.mode = BrowsingMode.fromBoolean(private)
|
browsingModeManager.mode = BrowsingMode.fromBoolean(private)
|
||||||
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true))
|
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true))
|
||||||
|
@ -95,11 +110,11 @@ class DefaultTabTrayController(
|
||||||
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalTabSettingsFragment())
|
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalTabSettingsFragment())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTabTrayDismissed() {
|
override fun handleTabTrayDismissed() {
|
||||||
dismissTabTray()
|
dismissTabTray()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
override fun handleSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
||||||
val sessionList = selectedTabs.map {
|
val sessionList = selectedTabs.map {
|
||||||
sessionManager.findSessionById(it.id) ?: return
|
sessionManager.findSessionById(it.id) ?: return
|
||||||
}
|
}
|
||||||
|
@ -117,9 +132,19 @@ class DefaultTabTrayController(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShareTabsClicked(private: Boolean) {
|
override fun handleShareTabsOfTypeClicked(private: Boolean) {
|
||||||
val tabs = getListOfSessions(private)
|
val tabs = browserStore.state.getNormalOrPrivateTabs(private)
|
||||||
val data = tabs.map {
|
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)
|
ShareData(url = it.url, title = it.title)
|
||||||
}
|
}
|
||||||
val directions = TabTrayDialogFragmentDirections.actionGlobalShareFragment(
|
val directions = TabTrayDialogFragmentDirections.actionGlobalShareFragment(
|
||||||
|
@ -128,7 +153,40 @@ class DefaultTabTrayController(
|
||||||
navController.navigate(directions)
|
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(
|
activity.openToBrowserAndLoad(
|
||||||
searchTermOrURL = syncTab.active().url,
|
searchTermOrURL = syncTab.active().url,
|
||||||
newTab = true,
|
newTab = true,
|
||||||
|
@ -137,7 +195,7 @@ class DefaultTabTrayController(
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
override fun handleCloseAllTabsClicked(private: Boolean) {
|
||||||
val sessionsToClose = if (private) {
|
val sessionsToClose = if (private) {
|
||||||
HomeFragment.ALL_PRIVATE_TABS
|
HomeFragment.ALL_PRIVATE_TABS
|
||||||
} else {
|
} 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 {
|
override fun onModeRequested(): TabTrayDialogFragmentState.Mode {
|
||||||
return tabTrayDialogFragmentStore.state.mode
|
return tabTrayDialogFragmentStore.state.mode
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.appservices.places.BookmarkRoot
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.state.selector.findTab
|
import mozilla.components.browser.state.selector.findTab
|
||||||
import mozilla.components.browser.state.state.TabSessionState
|
import mozilla.components.browser.state.state.TabSessionState
|
||||||
|
@ -71,7 +72,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
||||||
|
|
||||||
private val snackbarAnchor: View?
|
private val snackbarAnchor: View?
|
||||||
get() = if (tabTrayView.fabView.new_tab_button.isVisible ||
|
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,
|
/* 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,
|
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
|
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)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Suppress("LongMethod")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val activity = activity as HomeActivity
|
val activity = activity as HomeActivity
|
||||||
|
@ -194,8 +197,12 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
||||||
activity = activity,
|
activity = activity,
|
||||||
profiler = activity.components.core.engine.profiler,
|
profiler = activity.components.core.engine.profiler,
|
||||||
sessionManager = activity.components.core.sessionManager,
|
sessionManager = activity.components.core.sessionManager,
|
||||||
|
browserStore = activity.components.core.store,
|
||||||
|
tabsUseCases = activity.components.useCases.tabsUseCases,
|
||||||
|
scope = lifecycleScope,
|
||||||
browsingModeManager = activity.browsingModeManager,
|
browsingModeManager = activity.browsingModeManager,
|
||||||
tabCollectionStorage = activity.components.core.tabCollectionStorage,
|
tabCollectionStorage = activity.components.core.tabCollectionStorage,
|
||||||
|
bookmarksStorage = activity.components.core.bookmarksStorage,
|
||||||
navController = findNavController(),
|
navController = findNavController(),
|
||||||
dismissTabTray = ::dismissAllowingStateLoss,
|
dismissTabTray = ::dismissAllowingStateLoss,
|
||||||
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome,
|
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome,
|
||||||
|
@ -203,7 +210,9 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
|
||||||
tabTrayDialogFragmentStore = tabTrayDialogStore,
|
tabTrayDialogFragmentStore = tabTrayDialogStore,
|
||||||
selectTabUseCase = selectTabUseCase,
|
selectTabUseCase = selectTabUseCase,
|
||||||
showChooseCollectionDialog = ::showChooseCollectionDialog,
|
showChooseCollectionDialog = ::showChooseCollectionDialog,
|
||||||
showAddNewCollectionDialog = ::showAddNewCollectionDialog
|
showAddNewCollectionDialog = ::showAddNewCollectionDialog,
|
||||||
|
showUndoSnackbarForTabs = ::showUndoSnackbarForTabs,
|
||||||
|
showBookmarksSnackbar = ::showBookmarksSnackbar
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
store = tabTrayDialogStore,
|
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) {
|
private fun showUndoSnackbarForTab(sessionId: String) {
|
||||||
val store = requireComponents.core.store
|
val store = requireComponents.core.store
|
||||||
val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
|
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 {
|
override fun onBackPressed(): Boolean {
|
||||||
if (!tabTrayView.onBackPressed()) {
|
if (!tabTrayView.onBackPressed()) {
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -22,7 +22,22 @@ interface TabTrayInteractor {
|
||||||
/**
|
/**
|
||||||
* Called when user clicks the share tabs button.
|
* 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.
|
* Called when user clicks the tab settings button.
|
||||||
|
@ -91,11 +106,11 @@ interface TabTrayInteractor {
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
class TabTrayFragmentInteractor(private val controller: TabTrayController) : TabTrayInteractor {
|
class TabTrayFragmentInteractor(private val controller: TabTrayController) : TabTrayInteractor {
|
||||||
override fun onNewTabTapped(private: Boolean) {
|
override fun onNewTabTapped(private: Boolean) {
|
||||||
controller.onNewTabTapped(private)
|
controller.handleNewTabTapped(private)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTabTrayDismissed() {
|
override fun onTabTrayDismissed() {
|
||||||
controller.onTabTrayDismissed()
|
controller.handleTabTrayDismissed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTabSettingsClicked() {
|
override fun onTabSettingsClicked() {
|
||||||
|
@ -106,20 +121,32 @@ class TabTrayFragmentInteractor(private val controller: TabTrayController) : Tab
|
||||||
controller.handleRecentlyClosedClicked()
|
controller.handleRecentlyClosedClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShareTabsClicked(private: Boolean) {
|
override fun onShareTabsOfTypeClicked(private: Boolean) {
|
||||||
controller.onShareTabsClicked(private)
|
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>) {
|
override fun onSaveToCollectionClicked(selectedTabs: Set<Tab>) {
|
||||||
controller.onSaveToCollectionClicked(selectedTabs)
|
controller.handleSaveToCollectionClicked(selectedTabs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||||
controller.onCloseAllTabsClicked(private)
|
controller.handleCloseAllTabsClicked(private)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSyncedTabClicked(syncTab: SyncTab) {
|
override fun onSyncedTabClicked(syncTab: SyncTab) {
|
||||||
controller.onSyncedTabClicked(syncTab)
|
controller.handleSyncedTabClicked(syncTab)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed(): Boolean {
|
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.view.*
|
||||||
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
||||||
import kotlinx.android.synthetic.main.tabs_tray_tab_counter.*
|
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.Dispatchers.Main
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mozilla.components.browser.menu.BrowserMenu
|
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.getNormalOrPrivateTabs
|
||||||
import mozilla.components.browser.state.selector.normalTabs
|
import mozilla.components.browser.state.selector.normalTabs
|
||||||
import mozilla.components.browser.state.selector.privateTabs
|
import mozilla.components.browser.state.selector.privateTabs
|
||||||
|
@ -90,6 +89,9 @@ class TabTrayView(
|
||||||
private val tabTrayItemMenu: TabTrayItemMenu
|
private val tabTrayItemMenu: TabTrayItemMenu
|
||||||
private var menu: BrowserMenu? = null
|
private var menu: BrowserMenu? = null
|
||||||
|
|
||||||
|
private val multiselectSelectionMenu: MultiselectSelectionMenu
|
||||||
|
private var multiselectMenu: BrowserMenu? = null
|
||||||
|
|
||||||
private var tabsTouchHelper: TabsTouchHelper
|
private var tabsTouchHelper: TabsTouchHelper
|
||||||
private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate)
|
private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate)
|
||||||
|
|
||||||
|
@ -230,7 +232,7 @@ class TabTrayView(
|
||||||
hasOpenTabs = checkOpenTabs
|
hasOpenTabs = checkOpenTabs
|
||||||
) {
|
) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
|
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsOfTypeClicked(
|
||||||
isPrivateModeSelected
|
isPrivateModeSelected
|
||||||
)
|
)
|
||||||
is TabTrayItemMenu.Item.OpenTabSettings -> interactor.onTabSettingsClicked()
|
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 {
|
view.tab_tray_overflow.setOnClickListener {
|
||||||
components.analytics.metrics.track(Event.TabsTrayMenuOpened)
|
components.analytics.metrics.track(Event.TabsTrayMenuOpened)
|
||||||
menu = tabTrayItemMenu.menuBuilder.build(container.context)
|
menu = tabTrayItemMenu.menuBuilder.build(container.context)
|
||||||
menu?.show(it)
|
menu?.show(it)?.also { popupMenu ->
|
||||||
?.also { pu ->
|
(popupMenu.contentView as? CardView)?.setCardBackgroundColor(
|
||||||
(pu.contentView as? CardView)?.setCardBackgroundColor(
|
ContextCompat.getColor(
|
||||||
ContextCompat.getColor(
|
view.context,
|
||||||
view.context,
|
R.color.foundation_normal_theme
|
||||||
R.color.foundation_normal_theme
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustNewTabButtonsForNormalMode()
|
adjustNewTabButtonsForNormalMode()
|
||||||
|
@ -469,6 +483,8 @@ class TabTrayView(
|
||||||
fabView.new_tab_button.isVisible = false
|
fabView.new_tab_button.isVisible = false
|
||||||
view.tab_tray_new_tab.isVisible = false
|
view.tab_tray_new_tab.isVisible = false
|
||||||
view.collect_multi_select.isVisible = state.mode.selectedItems.isNotEmpty()
|
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(
|
view.multiselect_title.text = view.context.getString(
|
||||||
R.string.tab_tray_multi_select_title,
|
R.string.tab_tray_multi_select_title,
|
||||||
|
@ -477,6 +493,20 @@ class TabTrayView(
|
||||||
view.collect_multi_select.setOnClickListener {
|
view.collect_multi_select.setOnClickListener {
|
||||||
interactor.onSaveToCollectionClicked(state.mode.selectedItems)
|
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 {
|
view.exit_multi_select.setOnClickListener {
|
||||||
interactor.onBackPressed()
|
interactor.onBackPressed()
|
||||||
}
|
}
|
||||||
|
@ -544,6 +574,8 @@ class TabTrayView(
|
||||||
private fun toggleUIMultiselect(multiselect: Boolean) {
|
private fun toggleUIMultiselect(multiselect: Boolean) {
|
||||||
view.multiselect_title.isVisible = multiselect
|
view.multiselect_title.isVisible = multiselect
|
||||||
view.collect_multi_select.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.exit_multi_select.isVisible = multiselect
|
||||||
|
|
||||||
view.topBar.setBackgroundColor(
|
view.topBar.setBackgroundColor(
|
||||||
|
@ -707,9 +739,7 @@ class TabTrayView(
|
||||||
// We offset the tab index by the number of items in the other adapters.
|
// 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`.
|
// We add the offset, because the layoutManager is initialized with `reverseLayout`.
|
||||||
return if (view.context.settings().listTabView) {
|
return if (view.context.settings().listTabView) {
|
||||||
selectedBrowserTabIndex +
|
selectedBrowserTabIndex + collectionsButtonAdapter.itemCount + syncedTabsController.adapter.itemCount
|
||||||
collectionsButtonAdapter.itemCount +
|
|
||||||
syncedTabsController.adapter.itemCount
|
|
||||||
} else {
|
} else {
|
||||||
selectedBrowserTabIndex
|
selectedBrowserTabIndex
|
||||||
}
|
}
|
||||||
|
@ -719,75 +749,18 @@ class TabTrayView(
|
||||||
private const val TAB_COUNT_SHOW_CFR = 6
|
private const val TAB_COUNT_SHOW_CFR = 6
|
||||||
private const val DEFAULT_TAB_ID = 0
|
private const val DEFAULT_TAB_ID = 0
|
||||||
private const val PRIVATE_TAB_ID = 1
|
private const val PRIVATE_TAB_ID = 1
|
||||||
|
|
||||||
// Minimum number of list items for which to show the tabs tray as expanded.
|
// Minimum number of list items for which to show the tabs tray as expanded.
|
||||||
private const val EXPAND_AT_LIST_SIZE = 4
|
private const val EXPAND_AT_LIST_SIZE = 4
|
||||||
|
|
||||||
// Minimum number of grid items for which to show the tabs tray as expanded.
|
// 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 EXPAND_AT_GRID_SIZE = 3
|
||||||
private const val SLIDE_OFFSET = 0
|
private const val SLIDE_OFFSET = 0
|
||||||
private const val SELECTION_DELAY = 500
|
private const val SELECTION_DELAY = 500
|
||||||
private const val NORMAL_HANDLE_PERCENT_WIDTH = 0.1F
|
private const val NORMAL_HANDLE_PERCENT_WIDTH = 0.1F
|
||||||
private const val COLUMN_WIDTH_DP = 180
|
private const val COLUMN_WIDTH_DP = 180
|
||||||
|
|
||||||
// The remaining padding offset needed to provide a 16dp column spacing between the grid items.
|
// The remaining padding offset needed to provide a 16dp column spacing between the grid items.
|
||||||
const val GRID_ITEM_PARENT_PADDING = 8
|
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_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintWidth_percent="0.1" />
|
app:layout_constraintWidth_percent="0.1" />
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/infoBanner"
|
android:id="@+id/infoBanner"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/foundation_normal_theme"
|
android:background="@color/foundation_normal_theme"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/topBar"/>
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/topBar" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tab_tray_empty_view"
|
android:id="@+id/tab_tray_empty_view"
|
||||||
|
@ -76,7 +75,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:textColor="@color/contrast_text_normal_theme"
|
android:textColor="@color/contrast_text_normal_theme"
|
||||||
android:textSize="18sp"
|
android:textSize="20sp"
|
||||||
|
app:fontFamily="@font/metropolis_semibold"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/collect_multi_select"
|
app:layout_constraintEnd_toStartOf="@id/collect_multi_select"
|
||||||
|
@ -86,34 +86,12 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="3 selected" />
|
tools:text="3 selected" />
|
||||||
|
|
||||||
<TextView
|
<include layout="@layout/tabstray_multiselect_items" />
|
||||||
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" />
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:id="@+id/tab_layout"
|
android:id="@+id/tab_layout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
app:tabMaxWidth="0dp"
|
|
||||||
android:background="@color/foundation_normal_theme"
|
android:background="@color/foundation_normal_theme"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
@ -121,6 +99,7 @@
|
||||||
app:tabGravity="fill"
|
app:tabGravity="fill"
|
||||||
app:tabIconTint="@color/tab_icon"
|
app:tabIconTint="@color/tab_icon"
|
||||||
app:tabIndicatorColor="@color/accent_normal_theme"
|
app:tabIndicatorColor="@color/accent_normal_theme"
|
||||||
|
app:tabMaxWidth="0dp"
|
||||||
app:tabRippleColor="@android:color/transparent">
|
app:tabRippleColor="@android:color/transparent">
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabItem
|
<com.google.android.material.tabs.TabItem
|
||||||
|
@ -150,8 +129,8 @@
|
||||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
||||||
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
|
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
|
||||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
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
|
<ImageButton
|
||||||
android:id="@+id/tab_tray_overflow"
|
android:id="@+id/tab_tray_overflow"
|
||||||
|
@ -161,11 +140,11 @@
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/open_tabs_menu"
|
android:contentDescription="@string/open_tabs_menu"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
app:tint="@color/tab_tray_heading_icon_menu_normal_theme"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
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>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
<!-- 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
|
- 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/. -->
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
style="@style/NeutralButton"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
android:layout_margin="8dp"
|
||||||
style="@style/NeutralButton"
|
android:text="@string/tabs_tray_select_tabs" />
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:text="@string/save_to_collection"
|
|
||||||
app:icon="@drawable/ic_tab_collection" />
|
|
||||||
|
|
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>
|
<string name="tab_tray_menu_home">Go home</string>
|
||||||
<!-- Shortcut action to toggle private mode -->
|
<!-- Shortcut action to toggle private mode -->
|
||||||
<string name="tab_tray_menu_toggle">Toggle tab mode</string>
|
<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 -->
|
<!-- 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>
|
<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 -->
|
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
|
||||||
<string name="close_tab">Close tab</string>
|
<string name="close_tab">Close tab</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
|
<!-- 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>
|
<string name="snackbar_tab_closed">Tab closed</string>
|
||||||
<!-- Text shown in snackbar when user closes all tabs -->
|
<!-- Text shown in snackbar when user closes all tabs -->
|
||||||
<string name="snackbar_tabs_closed">Tabs closed</string>
|
<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 -->
|
<!-- Text shown in snackbar when user adds a site to top sites -->
|
||||||
<string name="snackbar_added_to_top_sites">Added to top sites!</string>
|
<string name="snackbar_added_to_top_sites">Added to top sites!</string>
|
||||||
<!-- Text shown in snackbar when user closes a private tab -->
|
<!-- 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.NavDestination
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
import io.mockk.Runs
|
import io.mockk.Runs
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -16,9 +17,14 @@ import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.mockk.verifyOrder
|
import io.mockk.verifyOrder
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineScope
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
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.base.profiler.Profiler
|
||||||
|
import mozilla.components.concept.storage.BookmarksStorage
|
||||||
import mozilla.components.concept.tabstray.Tab
|
import mozilla.components.concept.tabstray.Tab
|
||||||
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
|
||||||
|
@ -40,6 +46,14 @@ class DefaultTabTrayControllerTest {
|
||||||
private val profiler: Profiler? = mockk(relaxed = true)
|
private val profiler: Profiler? = mockk(relaxed = true)
|
||||||
private val navController: NavController = mockk()
|
private val navController: NavController = mockk()
|
||||||
private val sessionManager: SessionManager = mockk(relaxed = true)
|
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 browsingModeManager: BrowsingModeManager = mockk(relaxed = true)
|
||||||
private val dismissTabTray: (() -> Unit) = mockk(relaxed = true)
|
private val dismissTabTray: (() -> Unit) = mockk(relaxed = true)
|
||||||
private val dismissTabTrayAndNavigateHome: ((String) -> 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 showChooseCollectionDialog: ((List<Session>) -> Unit) = mockk(relaxed = true)
|
||||||
private val showAddNewCollectionDialog: ((List<Session>) -> Unit) = mockk(relaxed = true)
|
private val showAddNewCollectionDialog: ((List<Session>) -> Unit) = mockk(relaxed = true)
|
||||||
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
||||||
|
private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true)
|
||||||
private val tabCollection: TabCollection = mockk()
|
private val tabCollection: TabCollection = mockk()
|
||||||
private val cachedTabCollections: List<TabCollection> = listOf(tabCollection)
|
private val cachedTabCollections: List<TabCollection> = listOf(tabCollection)
|
||||||
private val currentDestination: NavDestination = mockk(relaxed = true)
|
private val currentDestination: NavDestination = mockk(relaxed = true)
|
||||||
private val tabTrayFragmentStore: TabTrayDialogFragmentStore = mockk(relaxed = true)
|
private val tabTrayFragmentStore: TabTrayDialogFragmentStore = mockk(relaxed = true)
|
||||||
private val selectTabUseCase: TabsUseCases.SelectTabUseCase = 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
|
private lateinit var controller: DefaultTabTrayController
|
||||||
|
|
||||||
|
@ -87,16 +105,22 @@ class DefaultTabTrayControllerTest {
|
||||||
activity = activity,
|
activity = activity,
|
||||||
profiler = profiler,
|
profiler = profiler,
|
||||||
sessionManager = sessionManager,
|
sessionManager = sessionManager,
|
||||||
|
browserStore = store,
|
||||||
browsingModeManager = browsingModeManager,
|
browsingModeManager = browsingModeManager,
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
|
bookmarksStorage = bookmarksStorage,
|
||||||
|
scope = TestCoroutineScope(),
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
tabsUseCases = tabsUseCases,
|
||||||
dismissTabTray = dismissTabTray,
|
dismissTabTray = dismissTabTray,
|
||||||
dismissTabTrayAndNavigateHome = dismissTabTrayAndNavigateHome,
|
dismissTabTrayAndNavigateHome = dismissTabTrayAndNavigateHome,
|
||||||
registerCollectionStorageObserver = registerCollectionStorageObserver,
|
registerCollectionStorageObserver = registerCollectionStorageObserver,
|
||||||
tabTrayDialogFragmentStore = tabTrayFragmentStore,
|
tabTrayDialogFragmentStore = tabTrayFragmentStore,
|
||||||
selectTabUseCase = selectTabUseCase,
|
selectTabUseCase = selectTabUseCase,
|
||||||
showChooseCollectionDialog = showChooseCollectionDialog,
|
showChooseCollectionDialog = showChooseCollectionDialog,
|
||||||
showAddNewCollectionDialog = showAddNewCollectionDialog
|
showAddNewCollectionDialog = showAddNewCollectionDialog,
|
||||||
|
showUndoSnackbarForTabs = showUndoSnackbarForTabs,
|
||||||
|
showBookmarksSnackbar = showBookmarksSavedSnackbar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +137,7 @@ class DefaultTabTrayControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onNewTabTapped() {
|
fun onNewTabTapped() {
|
||||||
controller.onNewTabTapped(private = false)
|
controller.handleNewTabTapped(private = false)
|
||||||
|
|
||||||
verifyOrder {
|
verifyOrder {
|
||||||
browsingModeManager.mode = BrowsingMode.fromBoolean(false)
|
browsingModeManager.mode = BrowsingMode.fromBoolean(false)
|
||||||
|
@ -125,7 +149,7 @@ class DefaultTabTrayControllerTest {
|
||||||
dismissTabTray()
|
dismissTabTray()
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.onNewTabTapped(private = true)
|
controller.handleNewTabTapped(private = true)
|
||||||
|
|
||||||
verifyOrder {
|
verifyOrder {
|
||||||
browsingModeManager.mode = BrowsingMode.fromBoolean(true)
|
browsingModeManager.mode = BrowsingMode.fromBoolean(true)
|
||||||
|
@ -140,7 +164,7 @@ class DefaultTabTrayControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onTabTrayDismissed() {
|
fun onTabTrayDismissed() {
|
||||||
controller.onTabTrayDismissed()
|
controller.handleTabTrayDismissed()
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
dismissTabTray()
|
dismissTabTray()
|
||||||
|
@ -152,7 +176,7 @@ class DefaultTabTrayControllerTest {
|
||||||
val navDirectionsSlot = slot<NavDirections>()
|
val navDirectionsSlot = slot<NavDirections>()
|
||||||
every { navController.navigate(capture(navDirectionsSlot)) } just Runs
|
every { navController.navigate(capture(navDirectionsSlot)) } just Runs
|
||||||
|
|
||||||
controller.onShareTabsClicked(private = false)
|
controller.handleShareTabsOfTypeClicked(private = false)
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
navController.navigate(capture(navDirectionsSlot))
|
navController.navigate(capture(navDirectionsSlot))
|
||||||
|
@ -164,7 +188,7 @@ class DefaultTabTrayControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onCloseAllTabsClicked() {
|
fun onCloseAllTabsClicked() {
|
||||||
controller.onCloseAllTabsClicked(private = false)
|
controller.handleCloseAllTabsClicked(private = false)
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
dismissTabTrayAndNavigateHome(any())
|
dismissTabTrayAndNavigateHome(any())
|
||||||
|
@ -173,7 +197,7 @@ class DefaultTabTrayControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onSyncedTabClicked() {
|
fun onSyncedTabClicked() {
|
||||||
controller.onSyncedTabClicked(mockk(relaxed = true))
|
controller.handleSyncedTabClicked(mockk(relaxed = true))
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
activity.openToBrowserAndLoad(any(), true, BrowserDirection.FromTabTray)
|
activity.openToBrowserAndLoad(any(), true, BrowserDirection.FromTabTray)
|
||||||
|
@ -242,13 +266,53 @@ class DefaultTabTrayControllerTest {
|
||||||
fun onSaveToCollectionClicked() {
|
fun onSaveToCollectionClicked() {
|
||||||
val tab = Tab("1234", "mozilla.org")
|
val tab = Tab("1234", "mozilla.org")
|
||||||
|
|
||||||
controller.onSaveToCollectionClicked(setOf(tab))
|
controller.handleSaveToCollectionClicked(setOf(tab))
|
||||||
verify {
|
verify {
|
||||||
registerCollectionStorageObserver()
|
registerCollectionStorageObserver()
|
||||||
showChooseCollectionDialog(listOf(session))
|
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
|
@Test
|
||||||
fun handleSetUpAutoCloseTabsClicked() {
|
fun handleSetUpAutoCloseTabsClicked() {
|
||||||
controller.handleSetUpAutoCloseTabsClicked()
|
controller.handleSetUpAutoCloseTabsClicked()
|
||||||
|
|
|
@ -13,13 +13,40 @@ class TabTrayFragmentInteractorTest {
|
||||||
private val controller = mockk<TabTrayController>(relaxed = true)
|
private val controller = mockk<TabTrayController>(relaxed = true)
|
||||||
private val interactor = TabTrayFragmentInteractor(controller)
|
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
|
@Test
|
||||||
fun onNewTabTapped() {
|
fun onNewTabTapped() {
|
||||||
interactor.onNewTabTapped(private = true)
|
interactor.onNewTabTapped(private = true)
|
||||||
verify { controller.onNewTabTapped(true) }
|
verify { controller.handleNewTabTapped(true) }
|
||||||
|
|
||||||
interactor.onNewTabTapped(private = false)
|
interactor.onNewTabTapped(private = false)
|
||||||
verify { controller.onNewTabTapped(false) }
|
verify { controller.handleNewTabTapped(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -34,38 +61,38 @@ class TabTrayFragmentInteractorTest {
|
||||||
@Test
|
@Test
|
||||||
fun onTabTrayDismissed() {
|
fun onTabTrayDismissed() {
|
||||||
interactor.onTabTrayDismissed()
|
interactor.onTabTrayDismissed()
|
||||||
verify { controller.onTabTrayDismissed() }
|
verify { controller.handleTabTrayDismissed() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onShareTabsClicked() {
|
fun onShareTabsClicked() {
|
||||||
interactor.onShareTabsClicked(private = true)
|
interactor.onShareTabsOfTypeClicked(private = true)
|
||||||
verify { controller.onShareTabsClicked(true) }
|
verify { controller.handleShareTabsOfTypeClicked(true) }
|
||||||
|
|
||||||
interactor.onShareTabsClicked(private = false)
|
interactor.onShareTabsOfTypeClicked(private = false)
|
||||||
verify { controller.onShareTabsClicked(false) }
|
verify { controller.handleShareTabsOfTypeClicked(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onSaveToCollectionClicked() {
|
fun onSaveToCollectionClicked() {
|
||||||
val tab = Tab("1234", "mozilla.org")
|
val tab = Tab("1234", "mozilla.org")
|
||||||
interactor.onSaveToCollectionClicked(setOf(tab))
|
interactor.onSaveToCollectionClicked(setOf(tab))
|
||||||
verify { controller.onSaveToCollectionClicked(setOf(tab)) }
|
verify { controller.handleSaveToCollectionClicked(setOf(tab)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onCloseAllTabsClicked() {
|
fun onCloseAllTabsClicked() {
|
||||||
interactor.onCloseAllTabsClicked(private = false)
|
interactor.onCloseAllTabsClicked(private = false)
|
||||||
verify { controller.onCloseAllTabsClicked(false) }
|
verify { controller.handleCloseAllTabsClicked(false) }
|
||||||
|
|
||||||
interactor.onCloseAllTabsClicked(private = true)
|
interactor.onCloseAllTabsClicked(private = true)
|
||||||
verify { controller.onCloseAllTabsClicked(true) }
|
verify { controller.handleCloseAllTabsClicked(true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onSyncedTabClicked() {
|
fun onSyncedTabClicked() {
|
||||||
interactor.onSyncedTabClicked(mockk(relaxed = true))
|
interactor.onSyncedTabClicked(mockk(relaxed = true))
|
||||||
verify { controller.onSyncedTabClicked(any()) }
|
verify { controller.handleSyncedTabClicked(any()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue
Block a user