diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt index e29317e8e..69bacb902 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -76,7 +76,9 @@ val BrowserState.lastSearchGroup: RecentTab.SearchGroup? * Returns a pair containing a list of search term groups sorted by last access time, and "remainder" tabs that have * search terms but should not be in groups (because the group is of size one). */ -fun List.toSearchGroup(): Pair, List> { +fun List.toSearchGroup( + groupSet: Set = emptySet() +): Pair, List> { val data = filter { it.isNormalTabActiveWithSearchTerm(maxActiveTime) }.groupBy { @@ -100,7 +102,9 @@ fun List.toSearchGroup(): Pair, List 1 }.sortedBy { it.lastAccess } + val groups = groupings + .filter { it.tabs.size > 1 || groupSet.contains(it.searchTerm) } + .sortedBy { it.lastAccess } val remainderTabs = (groupings - groups).flatMap { it.tabs } return groups to remainderTabs diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/NavigationInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabstray/NavigationInteractor.kt index 834fb1d16..55ba2b8a6 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/NavigationInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/NavigationInteractor.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.selector.normalTabs +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.storage.sync.Tab as SyncTab import mozilla.components.concept.engine.prompt.ShareData -import mozilla.components.concept.tabstray.Tab import mozilla.components.service.fxa.manager.FxaAccountManager import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -44,9 +44,9 @@ interface NavigationInteractor { fun onAccountSettingsClicked() /** - * Called when sharing a list of [Tab]s. + * Called when sharing a list of [TabSessionState]s. */ - fun onShareTabs(tabs: Collection) + fun onShareTabs(tabs: Collection) /** * Called when clicking the share tabs button. @@ -71,12 +71,12 @@ interface NavigationInteractor { /** * Used when opening the add-to-collections user flow. */ - fun onSaveToCollections(tabs: Collection) + fun onSaveToCollections(tabs: Collection) /** - * Used when adding [Tab]s as bookmarks. + * Used when adding [TabSessionState]s as bookmarks. */ - fun onSaveToBookmarks(tabs: Collection) + fun onSaveToBookmarks(tabs: Collection) /** * Called when clicking on a SyncedTab item. @@ -138,9 +138,9 @@ class DefaultNavigationInteractor( metrics.track(Event.RecentlyClosedTabsOpened) } - override fun onShareTabs(tabs: Collection) { + override fun onShareTabs(tabs: Collection) { val data = tabs.map { - ShareData(url = it.url, title = it.title) + ShareData(url = it.content.url, title = it.content.title) } val directions = TabsTrayFragmentDirections.actionGlobalShareFragment( data = data.toTypedArray() @@ -170,7 +170,7 @@ class DefaultNavigationInteractor( dismissTabTrayAndNavigateHome(sessionsToClose) } - override fun onSaveToCollections(tabs: Collection) { + override fun onSaveToCollections(tabs: Collection) { metrics.track(Event.TabsTraySaveToCollectionPressed) tabsTrayStore.dispatch(TabsTrayAction.ExitSelectMode) @@ -195,13 +195,13 @@ class DefaultNavigationInteractor( ).show(context) } - override fun onSaveToBookmarks(tabs: Collection) { + override fun onSaveToBookmarks(tabs: Collection) { tabs.forEach { tab -> // We don't combine the context with lifecycleScope so that our jobs are not cancelled // if we leave the fragment, i.e. we still want the bookmarks to be added if the // tabs tray closes before the job is done. CoroutineScope(ioDispatcher).launch { - bookmarksUseCase.addBookmark(tab.url, tab.title) + bookmarksUseCase.addBookmark(tab.content.url, tab.content.title) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt index 120f7619c..dc65ddeac 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt @@ -13,7 +13,6 @@ import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.base.profiler.Profiler -import mozilla.components.concept.tabstray.Tab import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.lib.state.DelicateAction import org.mozilla.fenix.R @@ -47,18 +46,18 @@ interface TabsTrayController { fun handleNavigateToBrowser() /** - * Deletes the [Tab] with the specified [tabId]. + * Deletes the [TabSessionState] with the specified [tabId]. * - * @param tabId The id of the [Tab] to be removed from TabsTray. + * @param tabId The id of the [TabSessionState] to be removed from TabsTray. */ fun handleTabDeletion(tabId: String) /** * Deletes a list of [tabs]. * - * @param tabs List of [Tab]s (sessions) to be removed. + * @param tabs List of [TabSessionState]s (sessions) to be removed. */ - fun handleMultipleTabsDeletion(tabs: Collection) + fun handleMultipleTabsDeletion(tabs: Collection) /** * Navigate from TabsTray to Recently Closed section in the History fragment. @@ -70,10 +69,10 @@ interface TabsTrayController { * * ⚠️ DO NOT USE THIS OUTSIDE OF DEBUGGING/TESTING. * - * @param tabs List of [Tab]s to be removed. + * @param tabs List of [TabSessionState]s to be removed. */ fun forceTabsAsInactive( - tabs: Collection, + tabs: Collection, numOfDays: Long = DEFAULT_ACTIVE_DAYS + 1 ) @@ -132,9 +131,9 @@ class DefaultTabsTrayController( } /** - * Deletes the [Tab] with the specified [tabId]. + * Deletes the [TabSessionState] with the specified [tabId]. * - * @param tabId The id of the [Tab] to be removed from TabsTray. + * @param tabId The id of the [TabSessionState] to be removed from TabsTray. * This method has no effect if the tab does not exist. */ override fun handleTabDeletion(tabId: String) { @@ -153,10 +152,11 @@ class DefaultTabsTrayController( /** * Deletes a list of [tabs] offering an undo option. * - * @param tabs List of [Tab]s (sessions) to be removed. This method has no effect for tabs that do not exist. + * @param tabs List of [TabSessionState]s (sessions) to be removed. + * This method has no effect for tabs that do not exist. */ - override fun handleMultipleTabsDeletion(tabs: Collection) { - val isPrivate = tabs.any { it.private } + override fun handleMultipleTabsDeletion(tabs: Collection) { + val isPrivate = tabs.any { it.content.private } // If user closes all the tabs from selected tabs page dismiss tray and navigate home. if (tabs.size == browserStore.state.getNormalOrPrivateTabs(isPrivate).size) { @@ -189,7 +189,7 @@ class DefaultTabsTrayController( * ⚠️ DO NOT USE THIS OUTSIDE OF DEBUGGING/TESTING. */ @OptIn(DelicateAction::class) - override fun forceTabsAsInactive(tabs: Collection, numOfDays: Long) { + override fun forceTabsAsInactive(tabs: Collection, numOfDays: Long) { tabs.forEach { tab -> val daysSince = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(numOfDays) browserStore.apply { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt index bae7a729a..620f54a02 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt @@ -4,7 +4,7 @@ package org.mozilla.fenix.tabstray -import mozilla.components.concept.tabstray.Tab +import mozilla.components.browser.state.state.TabSessionState interface TabsTrayInteractor { /** @@ -26,14 +26,14 @@ interface TabsTrayInteractor { fun onDeleteTab(tabId: String) /** - * Invoked when [Tab]s need to be deleted. + * Invoked when [TabSessionState]s need to be deleted. */ - fun onDeleteTabs(tabs: Collection) + fun onDeleteTabs(tabs: Collection) /** * Called when clicking the debug menu option for inactive tabs. */ - fun onInactiveDebugClicked(tabs: Collection) + fun onInactiveDebugClicked(tabs: Collection) /** * Deletes all inactive tabs. @@ -61,11 +61,11 @@ class DefaultTabsTrayInteractor( controller.handleTabDeletion(tabId) } - override fun onDeleteTabs(tabs: Collection) { + override fun onDeleteTabs(tabs: Collection) { controller.handleMultipleTabsDeletion(tabs) } - override fun onInactiveDebugClicked(tabs: Collection) { + override fun onInactiveDebugClicked(tabs: Collection) { controller.forceTabsAsInactive(tabs) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt index 28c9da2f2..cba2707dd 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt @@ -4,7 +4,7 @@ package org.mozilla.fenix.tabstray -import mozilla.components.concept.tabstray.Tab +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.lib.state.Action import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.State @@ -33,7 +33,7 @@ data class TabsTrayState( /** * A set of selected tabs which we would want to perform an action on. */ - open val selectedTabs = emptySet() + open val selectedTabs = emptySet() /** * The default mode the tabs list is in. @@ -44,7 +44,7 @@ data class TabsTrayState( * The multi-select mode that the tabs list is in containing the set of currently * selected tabs. */ - data class Select(override val selectedTabs: Set) : Mode() + data class Select(override val selectedTabs: Set) : Mode() } } @@ -95,14 +95,14 @@ sealed class TabsTrayAction : Action { object ExitSelectMode : TabsTrayAction() /** - * Added a new [Tab] to the selection set. + * Added a new [TabSessionState] to the selection set. */ - data class AddSelectTab(val tab: Tab) : TabsTrayAction() + data class AddSelectTab(val tab: TabSessionState) : TabsTrayAction() /** - * Removed a [Tab] from the selection set. + * Removed a [TabSessionState] from the selection set. */ - data class RemoveSelectTab(val tab: Tab) : TabsTrayAction() + data class RemoveSelectTab(val tab: TabSessionState) : TabsTrayAction() /** * The active page in the tray that is now in focus. diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolder.kt index 34c5c1dbd..bd377a86f 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolder.kt @@ -14,17 +14,16 @@ import androidx.appcompat.widget.AppCompatImageButton import androidx.core.view.isInvisible import androidx.core.view.isVisible import mozilla.components.browser.state.selector.findTabOrCustomTab +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.tabstray.TabViewHolder +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.browser.tabstray.TabsTrayStyling import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView import mozilla.components.browser.toolbar.MAX_URI_LENGTH import mozilla.components.concept.base.images.ImageLoadRequest import mozilla.components.concept.base.images.ImageLoader import mozilla.components.concept.engine.mediasession.MediaSession -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController @@ -54,7 +53,7 @@ abstract class AbstractBrowserTabViewHolder( itemView: View, private val imageLoader: ImageLoader, private val trayStore: TabsTrayStore, - private val selectionHolder: SelectionHolder?, + private val selectionHolder: SelectionHolder?, @VisibleForTesting internal val featureName: String, private val store: BrowserStore = itemView.context.components.core.store, @@ -76,35 +75,35 @@ abstract class AbstractBrowserTabViewHolder( abstract val browserTrayInteractor: BrowserTrayInteractor abstract val thumbnailSize: Int - override var tab: Tab? = null + override var tab: TabSessionState? = null /** * Displays the data of the given session and notifies the given observable about events. */ @Suppress("ComplexMethod", "LongMethod") override fun bind( - tab: Tab, + tab: TabSessionState, isSelected: Boolean, styling: TabsTrayStyling, - observable: Observable + delegate: TabsTray.Delegate ) { this.tab = tab updateTitle(tab) updateUrl(tab) updateFavicon(tab) - updateCloseButtonDescription(tab.title) + updateCloseButtonDescription(tab.content.title) updateSelectedTabIndicator(isSelected) updateMediaState(tab) if (selectionHolder != null) { setSelectionInteractor(tab, selectionHolder, browserTrayInteractor) } else { - itemView.setOnClickListener { browserTrayInteractor.open(tab, featureName) } + itemView.setOnClickListener { browserTrayInteractor.onTabSelected(tab, featureName) } } - if (tab.thumbnail != null) { - thumbnailView.setImageBitmap(tab.thumbnail) + if (tab.content.thumbnail != null) { + thumbnailView.setImageBitmap(tab.content.thumbnail) } else { loadIntoThumbnailView(thumbnailView, tab.id) } @@ -115,31 +114,31 @@ abstract class AbstractBrowserTabViewHolder( closeView.isInvisible = trayStore.state.mode is TabsTrayState.Mode.Select } - private fun updateFavicon(tab: Tab) { - if (tab.icon != null) { + private fun updateFavicon(tab: TabSessionState) { + if (tab.content.icon != null) { faviconView?.visibility = View.VISIBLE - faviconView?.setImageBitmap(tab.icon) + faviconView?.setImageBitmap(tab.content.icon) } else { faviconView?.visibility = View.GONE } } - private fun updateTitle(tab: Tab) { - val title = if (tab.title.isNotEmpty()) { - tab.title + private fun updateTitle(tab: TabSessionState) { + val title = if (tab.content.title.isNotEmpty()) { + tab.content.title } else { - tab.url + tab.content.url } titleView.text = title } - private fun updateUrl(tab: Tab) { + private fun updateUrl(tab: TabSessionState) { // Truncate to MAX_URI_LENGTH to prevent the UI from locking up for // extremely large URLs such as data URIs or bookmarklets. The same // is done in the toolbar and awesomebar: // https://github.com/mozilla-mobile/fenix/issues/1824 // https://github.com/mozilla-mobile/android-components/issues/6985 - urlView?.text = tab.url + urlView?.text = tab.content.url .toShortUrl(itemView.context.components.publicSuffixList) .take(MAX_URI_LENGTH) } @@ -149,11 +148,7 @@ abstract class AbstractBrowserTabViewHolder( closeView.context.getString(R.string.close_tab_title, title) } - /** - * NB: Why do we query for the media state from the store, when we have [Tab.playbackState] and - * [Tab.controller] already mapped? - */ - private fun updateMediaState(tab: Tab) { + private fun updateMediaState(tab: TabSessionState) { // Media state playPauseButtonView.increaseTapArea(PLAY_PAUSE_BUTTON_EXTRA_DPS) @@ -209,14 +204,16 @@ abstract class AbstractBrowserTabViewHolder( } private fun setSelectionInteractor( - item: Tab, - holder: SelectionHolder, + item: TabSessionState, + holder: SelectionHolder, interactor: BrowserTrayInteractor ) { itemView.setOnClickListener { val selected = holder.selectedItems when { - selected.isEmpty() && trayStore.state.mode.isSelect().not() -> interactor.open(item, featureName) + selected.isEmpty() && trayStore.state.mode.isSelect().not() -> { + interactor.onTabSelected(item, featureName) + } item in selected -> interactor.deselect(item) else -> interactor.select(item) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabViewHolder.kt index 654dcf65b..ca0ae0405 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabViewHolder.kt @@ -9,11 +9,10 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.AppCompatImageButton import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.browser.tabstray.TabsTrayStyling import mozilla.components.concept.base.images.ImageLoader -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable import org.mozilla.fenix.R import org.mozilla.fenix.databinding.TabTrayGridItemBinding import org.mozilla.fenix.ext.increaseTapArea @@ -28,7 +27,8 @@ sealed class BrowserTabViewHolder(itemView: View) : RecyclerView.ViewHolder(item * @param imageLoader [ImageLoader] used to load tab thumbnails. * @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that. - * @param selectionHolder [SelectionHolder]<[Tab]> for helping with selecting any number of displayed [Tab]s. + * @param selectionHolder [SelectionHolder]<[TabSessionState]> for helping with selecting + * any number of displayed [TabSessionState]s. * @param itemView [View] that displays a "tab". * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. */ @@ -36,7 +36,7 @@ sealed class BrowserTabViewHolder(itemView: View) : RecyclerView.ViewHolder(item imageLoader: ImageLoader, override val browserTrayInteractor: BrowserTrayInteractor, store: TabsTrayStore, - selectionHolder: SelectionHolder? = null, + selectionHolder: SelectionHolder? = null, itemView: View, featureName: String ) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder, featureName) { @@ -60,12 +60,12 @@ sealed class BrowserTabViewHolder(itemView: View) : RecyclerView.ViewHolder(item } override fun bind( - tab: Tab, + tab: TabSessionState, isSelected: Boolean, styling: TabsTrayStyling, - observable: Observable + delegate: TabsTray.Delegate ) { - super.bind(tab, isSelected, styling, observable) + super.bind(tab, isSelected, styling, delegate) closeButton.increaseTapArea(GRID_ITEM_CLOSE_BUTTON_EXTRA_DPS) } @@ -81,7 +81,8 @@ sealed class BrowserTabViewHolder(itemView: View) : RecyclerView.ViewHolder(item * @param imageLoader [ImageLoader] used to load tab thumbnails. * @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that. - * @param selectionHolder [SelectionHolder]<[Tab]> for helping with selecting any number of displayed [Tab]s. + * @param selectionHolder [SelectionHolder]<[TabSessionState]> for helping with selecting + * any number of displayed [TabSessionState]s. * @param itemView [View] that displays a "tab". * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. */ @@ -89,7 +90,7 @@ sealed class BrowserTabViewHolder(itemView: View) : RecyclerView.ViewHolder(item imageLoader: ImageLoader, override val browserTrayInteractor: BrowserTrayInteractor, store: TabsTrayStore, - selectionHolder: SelectionHolder? = null, + selectionHolder: SelectionHolder? = null, itemView: View, featureName: String ) : AbstractBrowserTabViewHolder(itemView, imageLoader, store, selectionHolder, featureName) { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt index 1bb5d03e2..6b5d57019 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt @@ -9,13 +9,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM import mozilla.components.browser.thumbnails.loader.ThumbnailLoader -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable -import mozilla.components.support.base.observer.ObserverRegistry import org.mozilla.fenix.components.Components import org.mozilla.fenix.databinding.TabTrayGridItemBinding import org.mozilla.fenix.databinding.TabTrayItemBinding @@ -30,15 +27,13 @@ import org.mozilla.fenix.tabstray.TabsTrayStore * @param interactor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that. * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. - * @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry]. */ class BrowserTabsAdapter( private val context: Context, - private val interactor: BrowserTrayInteractor, + val interactor: BrowserTrayInteractor, private val store: TabsTrayStore, - private val featureName: String, - delegate: Observable = ObserverRegistry() -) : TabsAdapter(delegate) { + override val featureName: String +) : TabsAdapter(interactor), FeatureNameHolder { /** * The layout types for the tabs. @@ -51,7 +46,7 @@ class BrowserTabsAdapter( /** * Tracks the selected tabs in multi-select mode. */ - var selectionHolder: SelectionHolder? = null + var selectionHolder: SelectionHolder? = null private val selectedItemAdapterBinding = SelectedItemAdapterBinding(store, this) private val imageLoader = ThumbnailLoader(context.components.core.thumbnailStorage) @@ -113,7 +108,8 @@ class BrowserTabsAdapter( return } - if (position == selectedIndex) { + val tab = getItem(position) + if (tab.id == selectedTabId) { if (payloads.contains(PAYLOAD_HIGHLIGHT_SELECTED_ITEM)) { holder.updateSelectedTabIndicator(true) } else if (payloads.contains(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM)) { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTrayInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTrayInteractor.kt index 02c4fc211..037c5d776 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTrayInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTrayInteractor.kt @@ -4,7 +4,8 @@ package org.mozilla.fenix.tabstray.browser -import mozilla.components.concept.tabstray.Tab +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.support.base.feature.UserInteractionHandler import org.mozilla.fenix.selection.SelectionInteractor @@ -19,23 +20,23 @@ import org.mozilla.fenix.tabstray.TabsTrayStore * For interacting with UI that is specifically for [AbstractBrowserTrayList] and other browser * tab tray views. */ -interface BrowserTrayInteractor : SelectionInteractor, UserInteractionHandler { +interface BrowserTrayInteractor : SelectionInteractor, UserInteractionHandler, TabsTray.Delegate { /** * Open a tab. * - * @param tab [Tab] to open in browser. + * @param tab [TabSessionState] to open in browser. * @param source app feature from which the [tab] was opened. */ - fun open(tab: Tab, source: String? = null) + fun open(tab: TabSessionState, source: String? = null) /** * Close the tab. * - * @param tab [Tab] to close. + * @param tab [TabSessionState] to close. * @param source app feature from which the [tab] was closed. */ - fun close(tab: Tab, source: String? = null) + fun close(tab: TabSessionState, source: String? = null) /** * TabTray's Floating Action Button clicked. @@ -51,6 +52,7 @@ interface BrowserTrayInteractor : SelectionInteractor, UserInteractionHandl /** * A default implementation of [BrowserTrayInteractor]. */ +@Suppress("TooManyFunctions") class DefaultBrowserTrayInteractor( private val store: TabsTrayStore, private val trayInteractor: TabsTrayInteractor, @@ -75,35 +77,35 @@ class DefaultBrowserTrayInteractor( /** * See [SelectionInteractor.open] */ - override fun open(item: Tab) { + override fun open(item: TabSessionState) { open(item, null) } /** * See [BrowserTrayInteractor.open]. */ - override fun open(tab: Tab, source: String?) { - selectTabWrapper.invoke(tab.id, source) + override fun open(tab: TabSessionState, source: String?) { + selectTab(tab, source) } /** * See [BrowserTrayInteractor.close]. */ - override fun close(tab: Tab, source: String?) { - removeTabWrapper.invoke(tab.id, source) + override fun close(tab: TabSessionState, source: String?) { + closeTab(tab, source) } /** * See [SelectionInteractor.select] */ - override fun select(item: Tab) { + override fun select(item: TabSessionState) { store.dispatch(TabsTrayAction.AddSelectTab(item)) } /** * See [SelectionInteractor.deselect] */ - override fun deselect(item: Tab) { + override fun deselect(item: TabSessionState) { store.dispatch(TabsTrayAction.RemoveSelectTab(item)) } @@ -120,6 +122,14 @@ class DefaultBrowserTrayInteractor( return false } + override fun onTabClosed(tab: TabSessionState, source: String?) { + closeTab(tab, source) + } + + override fun onTabSelected(tab: TabSessionState, source: String?) { + selectTab(tab, source) + } + /** * See [BrowserTrayInteractor.onFabClicked] */ @@ -133,4 +143,12 @@ class DefaultBrowserTrayInteractor( override fun onRecentlyClosedClicked() { controller.handleNavigateToRecentlyClosed() } + + private fun selectTab(tab: TabSessionState, source: String? = null) { + selectTabWrapper.invoke(tab.id, source) + } + + private fun closeTab(tab: TabSessionState, source: String? = null) { + removeTabWrapper.invoke(tab.id, source) + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/FeatureNameHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/FeatureNameHolder.kt new file mode 100644 index 000000000..098fcd74d --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/FeatureNameHolder.kt @@ -0,0 +1,14 @@ +/* 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.tabstray.browser + +/** + * Contains the identifying name of the feature. + * + * This is commonly used for telemetry. + */ +interface FeatureNameHolder { + val featureName: String +} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index cf1fb7822..3c500cb5f 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -7,8 +7,9 @@ package org.mozilla.fenix.tabstray.browser import android.view.View import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.browser.toolbar.MAX_URI_LENGTH -import mozilla.components.concept.tabstray.Tab import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.FenixSnackbar @@ -108,36 +109,35 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite * A RecyclerView ViewHolder implementation for an inactive tab view. * * @param itemView the inactive tab [View]. - * @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. */ class TabViewHolder( itemView: View, - private val browserTrayInteractor: BrowserTrayInteractor, + private val delegate: TabsTray.Delegate, private val featureName: String ) : InactiveTabViewHolder(itemView) { private val binding = InactiveTabListItemBinding.bind(itemView) - fun bind(tab: Tab) { + fun bind(tab: TabSessionState) { val components = itemView.context.components - val title = tab.title.ifEmpty { tab.url.take(MAX_URI_LENGTH) } - val url = tab.url.toShortUrl(components.publicSuffixList).take(MAX_URI_LENGTH) + val title = tab.content.title.ifEmpty { tab.content.url.take(MAX_URI_LENGTH) } + val url = tab.content.url.toShortUrl(components.publicSuffixList).take(MAX_URI_LENGTH) itemView.setOnClickListener { components.analytics.metrics.track(Event.TabsTrayOpenInactiveTab) - browserTrayInteractor.open(tab, featureName) + delegate.onTabSelected(tab, featureName) } binding.siteListItem.apply { - components.core.icons.loadIntoView(iconView, tab.url) + components.core.icons.loadIntoView(iconView, tab.content.url) setText(title, url) setSecondaryButton( R.drawable.mozac_ic_close, R.string.content_description_close_button ) { components.analytics.metrics.track(Event.TabsTrayCloseInactiveTab()) - browserTrayInteractor.close(tab, featureName) + delegate.onTabClosed(tab, featureName) } } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index 6c62cc4b3..1a87a0dce 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -9,10 +9,8 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter -import mozilla.components.concept.tabstray.Tab as TabsTrayTab -import mozilla.components.concept.tabstray.Tabs -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.ObserverRegistry +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import org.mozilla.fenix.components.Components import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.AutoCloseDialogHolder @@ -20,34 +18,26 @@ import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.TabViewHolder import org.mozilla.fenix.utils.Settings -import mozilla.components.support.base.observer.Observable as ComponentObservable /** * A convenience alias for readability. */ private typealias Adapter = ListAdapter -/** - * A convenience alias for readability. - */ -private typealias Observable = ComponentObservable - /** * The [ListAdapter] for displaying the list of inactive tabs. * * @param context [Context] used for various platform interactions or accessing [Components] * @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. - * @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry]. */ class InactiveTabsAdapter( private val context: Context, private val browserTrayInteractor: BrowserTrayInteractor, private val tabsTrayInteractor: TabsTrayInteractor, - private val featureName: String, + override val featureName: String, private val settings: Settings, - delegate: Observable = ObserverRegistry() -) : Adapter(DiffCallback), TabsTray, Observable by delegate { +) : Adapter(DiffCallback), TabsTray, FeatureNameHolder { internal lateinit var inactiveTabsInteractor: InactiveTabsInteractor internal lateinit var inactiveTabsAutoCloseDialogInteractor: InactiveTabsAutoCloseDialogInteractor @@ -92,11 +82,11 @@ class InactiveTabsAdapter( } } - override fun updateTabs(tabs: Tabs) { - inActiveTabsCount = tabs.list.size + override fun updateTabs(tabs: List, selectedTabId: String?) { + inActiveTabsCount = tabs.size // Early return with an empty list to remove the header/footer items. - if (tabs.list.isEmpty()) { + if (tabs.isEmpty()) { submitList(emptyList()) return } @@ -107,7 +97,7 @@ class InactiveTabsAdapter( return } - val items = tabs.list.map { Item.Tab(it) } + val items = tabs.map { Item.Tab(it) } val footer = Item.Footer val headerItems = if (settings.shouldShowInactiveTabsAutoCloseDialog(items.size)) { listOf(Item.Header, Item.AutoCloseMessage) @@ -117,8 +107,6 @@ class InactiveTabsAdapter( submitList(headerItems + items + listOf(footer)) } - override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false - private object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { return if (oldItem is Item.Tab && newItem is Item.Tab) { @@ -147,7 +135,7 @@ class InactiveTabsAdapter( /** * A tab that is now considered inactive. */ - data class Tab(val tab: TabsTrayTab) : Item() + data class Tab(val tab: TabSessionState) : Item() /** * A dialog for when the inactive tabs section reach 20 tabs. diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt index 554c27d7b..f7175328e 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt @@ -7,8 +7,7 @@ package org.mozilla.fenix.tabstray.browser import androidx.annotation.VisibleForTesting import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.feature.tabs.ext.toTabs +import mozilla.components.browser.tabstray.TabsTray import org.mozilla.fenix.utils.Settings class InactiveTabsAutoCloseDialogController( @@ -39,7 +38,7 @@ class InactiveTabsAutoCloseDialogController( @VisibleForTesting internal fun refeshInactiveTabsSecion() { - val tabs = browserStore.state.toTabs { tabFilter.invoke(it) } - tray.updateTabs(tabs) + val tabs = browserStore.state.tabs.filter(tabFilter) + tray.updateTabs(tabs, browserStore.state.selectedTabId) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsController.kt index 2658894ee..3ac540194 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsController.kt @@ -6,8 +6,7 @@ package org.mozilla.fenix.tabstray.browser import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.feature.tabs.ext.toTabs +import mozilla.components.browser.tabstray.TabsTray import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.components.metrics.Event @@ -31,8 +30,7 @@ class InactiveTabsController( } ) - val tabs = browserStore.state.toTabs { tabFilter.invoke(it) } - - tray.updateTabs(tabs) + val tabs = browserStore.state.tabs.filter(tabFilter) + tray.updateTabs(tabs, browserStore.state.selectedTabId) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index b3add063d..04d595b50 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -9,12 +9,9 @@ import android.util.AttributeSet import androidx.recyclerview.widget.ConcatAdapter import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabViewHolder -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray import mozilla.components.feature.tabs.tabstray.TabsFeature import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.tabstray.TrayPagerAdapter.Companion.TABS_TRAY_FEATURE_NAME import org.mozilla.fenix.tabstray.ext.browserAdapter import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter import org.mozilla.fenix.tabstray.ext.isNormalTabInactive @@ -36,14 +33,12 @@ class NormalBrowserTrayList @JvmOverloads constructor( defStyleAttr: Int = 0 ) : AbstractBrowserTrayList(context, attrs, defStyleAttr) { - private val swipeDelegate = SwipeToDeleteDelegate() private val concatAdapter by lazy { adapter as ConcatAdapter } private val tabSorter by lazy { TabSorter( context.settings(), context.components.analytics.metrics, - concatAdapter, - context.components.core.store + concatAdapter ) } private val inactiveTabsFilter: (TabSessionState) -> Boolean = filter@{ @@ -79,20 +74,18 @@ class NormalBrowserTrayList @JvmOverloads constructor( TabsFeature( tabSorter, context.components.core.store, - selectTabUseCase, - removeTabUseCase, { !it.content.private }, - {} ) } private val touchHelper by lazy { TabsTouchHelper( - observable = concatAdapter.browserAdapter, + interactionDelegate = concatAdapter.browserAdapter.interactor, onViewHolderTouched = { it is TabViewHolder && swipeToDelete.isSwipeable }, - onViewHolderDraw = { context.components.settings.gridTabView.not() } + onViewHolderDraw = { context.components.settings.gridTabView.not() }, + featureNameHolder = concatAdapter.browserAdapter ) } @@ -104,8 +97,6 @@ class NormalBrowserTrayList @JvmOverloads constructor( tabsFeature.start() - concatAdapter.browserAdapter.register(swipeDelegate) - touchHelper.attachToRecyclerView(this) } @@ -114,21 +105,6 @@ class NormalBrowserTrayList @JvmOverloads constructor( tabsFeature.stop() - concatAdapter.browserAdapter.unregister(swipeDelegate) - touchHelper.attachToRecyclerView(null) } - - /** - * A delegate for handling open/selected events from swipe-to-delete gestures. - */ - inner class SwipeToDeleteDelegate : TabsTray.Observer { - override fun onTabClosed(tab: Tab) { - removeTabUseCase.invoke(tab.id, TABS_TRAY_FEATURE_NAME) - } - - override fun onTabSelected(tab: Tab) { - selectTabUseCase.invoke(tab.id) - } - } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/PrivateBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/PrivateBrowserTrayList.kt index a99f71bf2..851e54fd7 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/PrivateBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/PrivateBrowserTrayList.kt @@ -21,19 +21,16 @@ class PrivateBrowserTrayList @JvmOverloads constructor( // NB: The use cases here are duplicated because there isn't a nicer // way to share them without a better dependency injection solution. TabsFeature( - adapter as TabsAdapter, + adapter as BrowserTabsAdapter, context.components.core.store, - selectTabUseCase, - removeTabUseCase, - { it.content.private }, - { } - ) + ) { it.content.private } } private val touchHelper by lazy { TabsTouchHelper( - observable = adapter as TabsAdapter, + interactionDelegate = (adapter as BrowserTabsAdapter).delegate, onViewHolderTouched = { swipeToDelete.isSwipeable }, - onViewHolderDraw = { context.components.settings.gridTabView.not() } + onViewHolderDraw = { context.components.settings.gridTabView.not() }, + featureNameHolder = (adapter as BrowserTabsAdapter) ) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt index 17b000ada..8643b78b6 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt @@ -13,17 +13,12 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.HORIZONTAL import androidx.recyclerview.widget.RecyclerView.VERTICAL -import mozilla.components.concept.tabstray.Tabs -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.ObserverRegistry +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import org.mozilla.fenix.components.Components import org.mozilla.fenix.ext.components import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayStore -import mozilla.components.concept.tabstray.Tab as TabsTrayTab -import mozilla.components.support.base.observer.Observable - -typealias TrayObservable = Observable /** * The [ListAdapter] for displaying the list of search term tabs. @@ -31,40 +26,19 @@ typealias TrayObservable = Observable * @param context [Context] used for various platform interactions or accessing [Components] * @param browserTrayInteractor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. - * @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry]. */ @Suppress("TooManyFunctions") class TabGroupAdapter( private val context: Context, private val browserTrayInteractor: BrowserTrayInteractor, private val store: TabsTrayStore, - private val featureName: String, - delegate: TrayObservable = ObserverRegistry() -) : ListAdapter(DiffCallback), TabsTray, TrayObservable by delegate { - - // TODO use [List.toSearchGroup()] - // see https://github.com/mozilla-mobile/android-components/issues/11012 - data class Group( - /** - * A title for the tab group. - */ - val title: String, - - /** - * The list of tabs belonging to this tab group. - */ - val tabs: List, - - /** - * The last time tabs in this group was accessed. - */ - val lastAccess: Long - ) + override val featureName: String, +) : ListAdapter(DiffCallback), TabsTray, FeatureNameHolder { /** * Tracks the selected tabs in multi-select mode. */ - var selectionHolder: SelectionHolder? = null + var selectionHolder: SelectionHolder? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabGroupViewHolder { val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) @@ -81,7 +55,7 @@ class TabGroupAdapter( override fun onBindViewHolder(holder: TabGroupViewHolder, position: Int) { val group = getItem(position) - holder.bind(group, this) + holder.bind(group) } override fun getItemViewType(position: Int) = TabGroupViewHolder.LAYOUT_ID @@ -103,19 +77,15 @@ class TabGroupAdapter( /** * Not implemented; implementation is handled [List.toSearchGroups] */ - override fun updateTabs(tabs: Tabs) = throw UnsupportedOperationException("Use submitList instead.") - - /** - * Not implemented; handled by nested [RecyclerView]. - */ - override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false + override fun updateTabs(tabs: List, selectedTabId: String?) = + throw UnsupportedOperationException("Use submitList instead.") - private object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Group, newItem: Group) = oldItem.title == newItem.title - override fun areContentsTheSame(oldItem: Group, newItem: Group) = oldItem == newItem + private object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TabGroup, newItem: TabGroup) = oldItem.searchTerm == newItem.searchTerm + override fun areContentsTheSame(oldItem: TabGroup, newItem: TabGroup) = oldItem == newItem } } -internal fun TabGroupAdapter.Group.containsTabId(tabId: String): Boolean { +internal fun TabGroup.containsTabId(tabId: String): Boolean { return tabs.firstOrNull { it.id == tabId } != null } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupListAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupListAdapter.kt index 15dfdf58e..256eb92c2 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupListAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupListAdapter.kt @@ -11,13 +11,11 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM import mozilla.components.browser.tabstray.TabsTrayStyling import mozilla.components.browser.thumbnails.loader.ThumbnailLoader -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable import org.mozilla.fenix.R import org.mozilla.fenix.databinding.TabTrayGridItemBinding import org.mozilla.fenix.databinding.TabTrayItemBinding @@ -33,17 +31,15 @@ import org.mozilla.fenix.tabstray.ext.MIN_COLUMN_WIDTH_DP * @param context [Context] used for various platform interactions or accessing [Components] * @param interactor [BrowserTrayInteractor] handling tabs interactions in a tab tray. * @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that. - * @param delegate [Observable]<[TabsTray.Observer]> for observing tabs tray changes. Defaults to [ObserverRegistry]. * @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting. */ class TabGroupListAdapter( private val context: Context, private val interactor: BrowserTrayInteractor, private val store: TabsTrayStore, - private val delegate: Observable, - private val selectionHolder: SelectionHolder?, + private val selectionHolder: SelectionHolder?, private val featureName: String, -) : ListAdapter(DiffCallback) { +) : ListAdapter(DiffCallback) { private val selectedItemAdapterBinding = SelectedItemAdapterBinding(store, this) private val imageLoader = ThumbnailLoader(context.components.core.thumbnailStorage) @@ -67,7 +63,7 @@ class TabGroupListAdapter( override fun onBindViewHolder(holder: AbstractBrowserTabViewHolder, position: Int) { val tab = getItem(position) val selectedTabId = context.components.core.store.state.selectedTabId - holder.bind(tab, tab.id == selectedTabId, TabsTrayStyling(), delegate) + holder.bind(tab, tab.id == selectedTabId, TabsTrayStyling(), interactor) holder.tab?.let { holderTab -> when { context.components.settings.gridTabView -> { @@ -147,8 +143,8 @@ class TabGroupListAdapter( selectedItemAdapterBinding.stop() } - private object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Tab, newItem: Tab) = oldItem.id == newItem.id - override fun areContentsTheSame(oldItem: Tab, newItem: Tab) = oldItem == newItem + private object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TabSessionState, newItem: TabSessionState) = oldItem.id == newItem.id + override fun areContentsTheSame(oldItem: TabSessionState, newItem: TabSessionState) = oldItem == newItem } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupViewHolder.kt index d38aa5e3c..82b14b5d3 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupViewHolder.kt @@ -5,9 +5,7 @@ import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable +import mozilla.components.browser.state.state.TabSessionState import org.mozilla.fenix.R import org.mozilla.fenix.databinding.TabGroupItemBinding import org.mozilla.fenix.ext.components @@ -15,7 +13,7 @@ import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayStore import org.mozilla.fenix.tabstray.TrayPagerAdapter import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor -import org.mozilla.fenix.tabstray.browser.TabGroupAdapter +import org.mozilla.fenix.tabstray.browser.TabGroup import org.mozilla.fenix.tabstray.browser.TabGroupListAdapter /** @@ -32,27 +30,25 @@ class TabGroupViewHolder( val orientation: Int, val interactor: BrowserTrayInteractor, val store: TabsTrayStore, - val selectionHolder: SelectionHolder? = null + val selectionHolder: SelectionHolder? = null ) : RecyclerView.ViewHolder(itemView) { private val binding = TabGroupItemBinding.bind(itemView) lateinit var groupListAdapter: TabGroupListAdapter fun bind( - group: TabGroupAdapter.Group, - observable: Observable + group: TabGroup, ) { val selectedTabId = itemView.context.components.core.store.state.selectedTabId val selectedIndex = group.tabs.indexOfFirst { it.id == selectedTabId } - binding.tabGroupTitle.text = group.title + binding.tabGroupTitle.text = group.searchTerm binding.tabGroupList.apply { layoutManager = LinearLayoutManager(itemView.context, orientation, false) groupListAdapter = TabGroupListAdapter( context = itemView.context, interactor = interactor, store = store, - delegate = observable, selectionHolder = selectionHolder, featureName = TrayPagerAdapter.TAB_GROUP_FEATURE_NAME ) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt index bdbef6b85..172129a98 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt @@ -5,21 +5,20 @@ package org.mozilla.fenix.tabstray.browser import androidx.recyclerview.widget.ConcatAdapter -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.Tabs -import mozilla.components.concept.tabstray.TabsTray +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.feature.tabs.tabstray.TabsFeature -import mozilla.components.support.base.observer.Observable -import mozilla.components.support.base.observer.ObserverRegistry import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.ext.toSearchGroup import org.mozilla.fenix.tabstray.ext.browserAdapter +import org.mozilla.fenix.tabstray.ext.hasSearchTerm import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter +import org.mozilla.fenix.tabstray.ext.isActive +import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm import org.mozilla.fenix.tabstray.ext.tabGroupAdapter import org.mozilla.fenix.tabstray.ext.titleHeaderAdapter import org.mozilla.fenix.utils.Settings -import kotlin.math.max /** * An intermediary layer to consume tabs from [TabsFeature] for sorting into the various adapters. @@ -27,36 +26,31 @@ import kotlin.math.max class TabSorter( private val settings: Settings, private val metrics: MetricController, - private val concatAdapter: ConcatAdapter, - private val store: BrowserStore -) : TabsTray, Observable by ObserverRegistry() { + private val concatAdapter: ConcatAdapter +) : TabsTray { private var shouldReportMetrics: Boolean = true private val groupsSet = mutableSetOf() - override fun updateTabs(tabs: Tabs) { - val inactiveTabs = tabs.list.getInactiveTabs(settings) - val searchTermTabs = tabs.list.getSearchGroupTabs(settings) - val normalTabs = tabs.list - inactiveTabs - searchTermTabs - val selectedTabId = store.state.selectedTabId + override fun updateTabs(tabs: List, selectedTabId: String?) { + val inactiveTabs = tabs.getInactiveTabs(settings) + val searchTermTabs = tabs.getSearchGroupTabs(settings) + val normalTabs = tabs - inactiveTabs - searchTermTabs // Inactive tabs - val selectedInactiveIndex = inactiveTabs.findSelectedIndex(selectedTabId) - concatAdapter.inactiveTabsAdapter.updateTabs((Tabs(inactiveTabs, selectedInactiveIndex))) + concatAdapter.inactiveTabsAdapter.updateTabs(inactiveTabs, selectedTabId) // Tab groups // We don't need to provide a selectedId, because the [TabGroupAdapter] has that built-in with support from // NormalBrowserPageViewHolder.scrollToTab. - val (groups, remainderTabs) = searchTermTabs.toSearchGroups(groupsSet) + val (groups, remainderTabs) = searchTermTabs.toSearchGroup(groupsSet) groupsSet.clear() - groupsSet.addAll(groups.map { it.title }) - + groupsSet.addAll(groups.map { it.searchTerm }) concatAdapter.tabGroupAdapter.submitList(groups) // Normal tabs. val totalNormalTabs = (normalTabs + remainderTabs) - val selectedTabIndex = totalNormalTabs.findSelectedIndex(selectedTabId) - concatAdapter.browserAdapter.updateTabs(Tabs(totalNormalTabs, selectedTabIndex)) + concatAdapter.browserAdapter.updateTabs(totalNormalTabs, selectedTabId) // Normal tab title header. concatAdapter.titleHeaderAdapter @@ -70,19 +64,12 @@ class TabSorter( } } } - - override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false -} - -private fun List.findSelectedIndex(tabId: String?): Int { - val id = tabId ?: return -1 - return indexOfFirst { it.id == id } } /** * Returns a list of inactive tabs based on our preferences. */ -private fun List.getInactiveTabs(settings: Settings): List { +private fun List.getInactiveTabs(settings: Settings): List { val inactiveTabsEnabled = settings.inactiveTabsAreEnabled return if (inactiveTabsEnabled) { filter { !it.isActive(maxActiveTime) } @@ -94,63 +81,16 @@ private fun List.getInactiveTabs(settings: Settings): List { /** * Returns a list of search term tabs based on our preferences. */ -private fun List.getSearchGroupTabs(settings: Settings): List { +private fun List.getSearchGroupTabs(settings: Settings): List { val inactiveTabsEnabled = settings.inactiveTabsAreEnabled val tabGroupsEnabled = settings.searchTermTabGroupsAreEnabled return when { tabGroupsEnabled && inactiveTabsEnabled -> - filter { it.searchTerm.isNotBlank() && it.isActive(maxActiveTime) } + filter { it.isNormalTabActiveWithSearchTerm(maxActiveTime) } tabGroupsEnabled -> - filter { it.searchTerm.isNotBlank() } + filter { it.hasSearchTerm() } else -> emptyList() } } - -/** - * Returns true if a tab has not been selected since [maxActiveTime]. - * - * N.B: This is duplicated from [TabSessionState.isActive(Long)] to work for [Tab]. - * - * See also: https://github.com/mozilla-mobile/android-components/issues/11012 - */ -private fun Tab.isActive(maxActiveTime: Long): Boolean { - val lastActiveTime = maxOf(lastAccess, createdAt) - val now = System.currentTimeMillis() - return (now - lastActiveTime <= maxActiveTime) -} - -/** - * Creates a list of grouped search term tabs sorted by last access time and a list of tabs - * that have search terms but would only create groups with a single tab. - * - * N.B: This is duplicated from [List.toSearchGroup()] to work for [Tab]. - * - * See also: https://github.com/mozilla-mobile/android-components/issues/11012 - */ -private fun List.toSearchGroups(groupSet: Set): Pair, List> { - val data = groupBy { it.searchTerm.lowercase() } - - val groupings = data.map { mapEntry -> - // Uppercase since we use it for the title. - val searchTerm = mapEntry.key.replaceFirstChar(Char::uppercase) - val groupTabs = mapEntry.value - - // Calculate when the group was last used. - val groupMax = groupTabs.fold(0L) { acc, tab -> - max(tab.lastAccess, acc) - } - - TabGroupAdapter.Group( - title = searchTerm, - tabs = groupTabs, - lastAccess = groupMax - ) - } - - val groups = groupings.filter { it.tabs.size > 1 || groupSet.contains(it.title) }.sortedBy { it.lastAccess } - val remainderTabs = (groupings - groups).flatMap { it.tabs } - - return groups to remainderTabs -} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt index 7c2c29eee..7e99beafa 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt @@ -7,13 +7,10 @@ package org.mozilla.fenix.tabstray.browser import androidx.annotation.CallSuper import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabViewHolder +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.browser.tabstray.TabsTrayStyling -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.Tabs -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable -import mozilla.components.support.base.observer.ObserverRegistry /** * RecyclerView adapter implementation to display a list/grid of tabs. @@ -23,38 +20,33 @@ import mozilla.components.support.base.observer.ObserverRegistry * for Android UI APIs. * * TODO Let's upstream this to AC with tests. - * - * @param delegate TabsTray.Observer registry to allow `TabsAdapter` to conform to `Observable`. */ abstract class TabsAdapter( - delegate: Observable = ObserverRegistry() -) : ListAdapter(DiffCallback), TabsTray, Observable by delegate { + val delegate: TabsTray.Delegate, +) : ListAdapter(DiffCallback), TabsTray { - protected var selectedIndex: Int? = null + protected var selectedTabId: String? = null protected var styling: TabsTrayStyling = TabsTrayStyling() @CallSuper - override fun updateTabs(tabs: Tabs) { - this.selectedIndex = tabs.selectedIndex - - submitList(tabs.list) + override fun updateTabs(tabs: List, selectedTabId: String?) { + this.selectedTabId = selectedTabId - notifyObservers { onTabsUpdated() } + submitList(tabs) } @CallSuper override fun onBindViewHolder(holder: T, position: Int) { - holder.bind(getItem(position), selectedIndex == position, styling, this) + val tab = getItem(position) + holder.bind(getItem(position), tab.id == selectedTabId, styling, delegate) } - override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false - - private object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Tab, newItem: Tab): Boolean { + private object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TabSessionState, newItem: TabSessionState): Boolean { return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: Tab, newItem: Tab): Boolean { + override fun areContentsTheSame(oldItem: TabSessionState, newItem: TabSessionState): Boolean { return oldItem == newItem } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelper.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelper.kt index 25fc74c19..be67b9a99 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelper.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelper.kt @@ -10,9 +10,9 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_IDLE import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabTouchCallback -import mozilla.components.concept.tabstray.TabsTray -import mozilla.components.support.base.observer.Observable +import mozilla.components.browser.tabstray.TabsTray import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.android.content.getDrawableWithTint import mozilla.components.support.ktx.android.util.dpToPx @@ -37,10 +37,11 @@ typealias OnViewHolderToDraw = (RecyclerView.ViewHolder) -> Boolean * @param onViewHolderTouched See [OnViewHolderTouched]. */ class TabsTouchHelper( - observable: Observable, + interactionDelegate: TabsTray.Delegate, onViewHolderTouched: OnViewHolderTouched = { true }, onViewHolderDraw: OnViewHolderToDraw = { true }, - delegate: Callback = TouchCallback(observable, onViewHolderTouched, onViewHolderDraw) + featureNameHolder: FeatureNameHolder, + delegate: Callback = TouchCallback(interactionDelegate, onViewHolderTouched, onViewHolderDraw, featureNameHolder), ) : ItemTouchHelper(delegate) /** @@ -49,10 +50,12 @@ class TabsTouchHelper( * @param onViewHolderTouched invoked when a tab is about to be swiped. See [OnViewHolderTouched]. */ class TouchCallback( - observable: Observable, + delegate: TabsTray.Delegate, private val onViewHolderTouched: OnViewHolderTouched, - private val onViewHolderDraw: OnViewHolderToDraw -) : TabTouchCallback(observable) { + private val onViewHolderDraw: OnViewHolderToDraw, + featureNameHolder: FeatureNameHolder, + onRemove: (TabSessionState) -> Unit = { delegate.onTabClosed(it, featureNameHolder.featureName) } +) : TabTouchCallback(onRemove) { override fun getMovementFlags( recyclerView: RecyclerView, diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/BrowserStore.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/BrowserStore.kt index c5577a13e..7102841e9 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/BrowserStore.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/BrowserStore.kt @@ -7,12 +7,11 @@ package org.mozilla.fenix.tabstray.ext import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tab /** * Find and extract a list [TabSessionState] from the [BrowserStore] using the IDs from [tabs]. */ -fun BrowserStore.getTabSessionState(tabs: Collection): List { +fun BrowserStore.getTabSessionState(tabs: Collection): List { return tabs.mapNotNull { state.findTab(it.id) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt index 5d968c2be..b653cc644 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt @@ -6,7 +6,7 @@ package org.mozilla.fenix.tabstray.ext import mozilla.components.browser.state.state.TabSessionState -private fun TabSessionState.isActive(maxActiveTime: Long): Boolean { +fun TabSessionState.isActive(maxActiveTime: Long): Boolean { val lastActiveTime = maxOf(lastAccess, createdAt) val now = System.currentTimeMillis() return (now - lastActiveTime <= maxActiveTime) @@ -15,7 +15,7 @@ private fun TabSessionState.isActive(maxActiveTime: Long): Boolean { /** * Returns true if the [TabSessionState] has a search term. */ -private fun TabSessionState.hasSearchTerm(): Boolean { +fun TabSessionState.hasSearchTerm(): Boolean { return content.searchTerms.isNotEmpty() || !historyMetadata?.searchTerm.isNullOrBlank() } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt index f947013b1..697d76fd4 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt @@ -10,8 +10,8 @@ import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.state.selector.selectedNormalTab +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tab import org.mozilla.fenix.R import org.mozilla.fenix.ext.settings import org.mozilla.fenix.selection.SelectionHolder @@ -39,7 +39,7 @@ class NormalBrowserPageViewHolder( private val tabsTrayStore: TabsTrayStore, private val browserStore: BrowserStore, interactor: TabsTrayInteractor, -) : AbstractBrowserPageViewHolder(containerView, tabsTrayStore, interactor), SelectionHolder { +) : AbstractBrowserPageViewHolder(containerView, tabsTrayStore, interactor), SelectionHolder { /** * Holds the list of selected tabs. @@ -47,7 +47,7 @@ class NormalBrowserPageViewHolder( * Implementation notes: we do this here because we only want the normal tabs list to be able * to select tabs. */ - override val selectedItems: Set + override val selectedItems: Set get() = tabsTrayStore.state.mode.selectedTabs override val emptyStringText: String diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt index 70af10a89..40b46dffc 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt @@ -19,9 +19,9 @@ import io.mockk.verifyOrder import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.state.TabSessionState +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.tabstray.Tab import mozilla.components.feature.tabs.TabsUseCases import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -268,9 +268,8 @@ class DefaultTabsTrayControllerTest { ) ) - val privateTab: Tab = mockk { - every { private } returns true - } + val privateTab = createTab(url = "url", private = true) + every { browserStore.state } returns mockk() try { mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") @@ -297,9 +296,9 @@ class DefaultTabsTrayControllerTest { } ) ) - val normalTab: Tab = mockk { - every { private } returns false - } + + val normalTab = createTab(url = "url", private = false) + every { browserStore.state } returns mockk() try { mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") @@ -319,10 +318,8 @@ class DefaultTabsTrayControllerTest { fun `WHEN handleMultipleTabsDeletion is called to close some private tabs THEN that it uses tabsUseCases#removeTabs and shows an undo snackbar`() { var showUndoSnackbarForTabInvoked = false val controller = spyk(createController(showUndoSnackbarForTab = { showUndoSnackbarForTabInvoked = true })) - val privateTab: Tab = mockk { - every { private } returns true - every { id } returns "42" - } + val privateTab = createTab(id = "42", url = "url", private = true) + every { browserStore.state } returns mockk() try { mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") @@ -342,10 +339,8 @@ class DefaultTabsTrayControllerTest { fun `WHEN handleMultipleTabsDeletion is called to close some normal tabs THEN that it uses tabsUseCases#removeTabs and shows an undo snackbar`() { var showUndoSnackbarForTabInvoked = false val controller = spyk(createController(showUndoSnackbarForTab = { showUndoSnackbarForTabInvoked = true })) - val privateTab: Tab = mockk { - every { private } returns false - every { id } returns "24" - } + val privateTab = createTab(id = "24", url = "url", private = false) + every { browserStore.state } returns mockk() try { mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt index c0080644c..2781256bd 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt @@ -6,8 +6,8 @@ package org.mozilla.fenix.tabstray import io.mockk.mockk import io.mockk.verifySequence +import mozilla.components.browser.state.state.TabSessionState import org.junit.Test -import mozilla.components.concept.tabstray.Tab class DefaultTabsTrayInteractorTest { val controller: TabsTrayController = mockk(relaxed = true) @@ -36,7 +36,7 @@ class DefaultTabsTrayInteractorTest { @Test fun `GIVEN user deleted multiple browser tabs WHEN onDeleteTabs is called THEN the Interactor delegates the controller`() { - val tabsToDelete = listOf(mockk(), mockk()) + val tabsToDelete = listOf(mockk(), mockk()) trayInteractor.onDeleteTabs(tabsToDelete) diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/NavigationInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/NavigationInteractorTest.kt index 3da0f95d8..efa61dfba 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/NavigationInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/NavigationInteractorTest.kt @@ -36,7 +36,6 @@ import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import mozilla.components.browser.state.state.createTab as createStateTab import mozilla.components.browser.storage.sync.Tab as SyncTab -import org.mozilla.fenix.tabstray.browser.createTab as createTrayTab class NavigationInteractorTest { private lateinit var store: BrowserStore @@ -148,7 +147,7 @@ class NavigationInteractorTest { showBookmarkSnackbar = { showBookmarkSnackbarInvoked = true } - ).onSaveToBookmarks(listOf(createTrayTab())) + ).onSaveToBookmarks(listOf(createStateTab("url"))) coVerify(exactly = 1) { bookmarksUseCase.addBookmark(any(), any(), any()) } assertTrue(showBookmarkSnackbarInvoked) diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/TabsTrayStoreTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/TabsTrayStoreTest.kt index 856818b80..a6b2b2480 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/TabsTrayStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/TabsTrayStoreTest.kt @@ -4,12 +4,12 @@ package org.mozilla.fenix.tabstray +import mozilla.components.browser.state.state.createTab import mozilla.components.support.test.libstate.ext.waitUntilIdle import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test -import org.mozilla.fenix.tabstray.browser.createTab class TabsTrayStoreTest { @@ -24,7 +24,7 @@ class TabsTrayStoreTest { assertTrue(store.state.mode.selectedTabs.isEmpty()) assertTrue(store.state.mode is TabsTrayState.Mode.Select) - store.dispatch(TabsTrayAction.AddSelectTab(createTab())) + store.dispatch(TabsTrayAction.AddSelectTab(createTab(url = "url"))) store.dispatch(TabsTrayAction.ExitSelectMode) store.dispatch(TabsTrayAction.EnterSelectMode) @@ -56,7 +56,7 @@ class TabsTrayStoreTest { fun `WHEN adding a tab to selection THEN it is added to the selectedTabs`() { val store = TabsTrayStore() - store.dispatch(TabsTrayAction.AddSelectTab(createTab("tab1"))) + store.dispatch(TabsTrayAction.AddSelectTab(createTab(url = "url", id = "tab1"))) store.waitUntilIdle() @@ -66,10 +66,10 @@ class TabsTrayStoreTest { @Test fun `WHEN removing a tab THEN it is removed from the selectedTabs`() { val store = TabsTrayStore() - val tabForRemoval = createTab("tab1") + val tabForRemoval = createTab(url = "url", id = "tab1") store.dispatch(TabsTrayAction.AddSelectTab(tabForRemoval)) - store.dispatch(TabsTrayAction.AddSelectTab(createTab("tab2"))) + store.dispatch(TabsTrayAction.AddSelectTab(createTab(url = "url", id = "tab2"))) store.waitUntilIdle() diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolderTest.kt index 7dd6ec983..b8b9b493c 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTabViewHolderTest.kt @@ -8,9 +8,9 @@ import android.view.LayoutInflater import android.view.View import io.mockk.mockk import io.mockk.verify +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.base.images.ImageLoader -import mozilla.components.concept.tabstray.Tab import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertTrue import org.junit.Test @@ -20,6 +20,7 @@ import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayStore +import mozilla.components.browser.state.state.createTab @RunWith(FenixRobolectricTestRunner::class) class AbstractBrowserTabViewHolderTest { @@ -40,11 +41,11 @@ class AbstractBrowserTabViewHolderTest { interactor ) - holder.bind(createTab(), false, mockk(), mockk()) + holder.bind(createTab(url = "url"), false, mockk(), mockk()) holder.itemView.performClick() - verify { interactor.open(any(), holder.featureName) } + verify { interactor.onTabSelected(any(), holder.featureName) } } @Test @@ -61,11 +62,11 @@ class AbstractBrowserTabViewHolderTest { interactor ) - holder.bind(createTab(), false, mockk(), mockk()) + holder.bind(createTab(url = "url"), false, mockk(), mockk()) holder.itemView.performClick() - verify { interactor.open(any(), holder.featureName) } + verify { interactor.onTabSelected(any(), holder.featureName) } assertTrue(selectionHolder.invoked) } @@ -74,7 +75,7 @@ class AbstractBrowserTabViewHolderTest { itemView: View, imageLoader: ImageLoader, trayStore: TabsTrayStore, - selectionHolder: SelectionHolder?, + selectionHolder: SelectionHolder?, store: BrowserStore, metrics: MetricController, override val browserTrayInteractor: BrowserTrayInteractor, @@ -89,9 +90,9 @@ class AbstractBrowserTabViewHolderTest { } class TestSelectionHolder( - private val testItems: Set - ) : SelectionHolder { - override val selectedItems: Set + private val testItems: Set + ) : SelectionHolder { + override val selectedItems: Set get() { invoked = true return testItems diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapterTest.kt index 736270c81..d9dffc863 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapterTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapterTest.kt @@ -9,10 +9,9 @@ import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM -import mozilla.components.concept.tabstray.Tab -import mozilla.components.concept.tabstray.Tabs import mozilla.components.support.test.robolectric.testContext import org.junit.Test import org.junit.runner.RunWith @@ -20,6 +19,7 @@ import org.mozilla.fenix.databinding.TabTrayItemBinding import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayStore +import mozilla.components.browser.state.state.createTab @RunWith(FenixRobolectricTestRunner::class) class BrowserTabsAdapterTest { @@ -34,12 +34,10 @@ class BrowserTabsAdapterTest { val holder = mockk(relaxed = true) adapter.updateTabs( - Tabs( - list = listOf( - createTab("tab1") - ), - selectedIndex = 0 - ) + listOf( + createTab(url = "url", id = "tab1") + ), + selectedTabId = "tab1" ) adapter.onBindViewHolder(holder, 0, listOf(PAYLOAD_HIGHLIGHT_SELECTED_ITEM)) @@ -65,7 +63,7 @@ class BrowserTabsAdapterTest { featureName = "Test" ) ) - val tab = createTab("tab1") + val tab = createTab(url = "url", id = "tab1") every { holder.tab }.answers { tab } @@ -73,12 +71,8 @@ class BrowserTabsAdapterTest { adapter.selectionHolder = testSelectionHolder adapter.updateTabs( - Tabs( - list = listOf( - tab - ), - selectedIndex = 0 - ) + listOf(tab), + selectedTabId = "tab1" ) adapter.onBindViewHolder(holder, 0, listOf(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM)) @@ -86,10 +80,10 @@ class BrowserTabsAdapterTest { verify { holder.showTabIsMultiSelectEnabled(any(), true) } } - private val testSelectionHolder = object : SelectionHolder { - override val selectedItems: Set + private val testSelectionHolder = object : SelectionHolder { + override val selectedItems: Set get() = internalState - val internalState = mutableSetOf() + val internalState = mutableSetOf() } } diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt index 6629c7e98..3a2a0b31d 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt @@ -12,7 +12,7 @@ import io.mockk.spyk import io.mockk.verify import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.TabsTray +import mozilla.components.browser.tabstray.TabsTray import org.junit.Test import org.mozilla.fenix.utils.Settings diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsControllerTest.kt index 7656e20a1..a781ba9c1 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsControllerTest.kt @@ -10,8 +10,7 @@ import io.mockk.verify import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tabs -import mozilla.components.concept.tabstray.TabsTray +import mozilla.components.browser.tabstray.TabsTray import org.junit.Assert.assertEquals import mozilla.components.browser.state.state.createTab as createTabState import org.junit.Test @@ -32,15 +31,15 @@ class InactiveTabsControllerTest { ) ) val tray: TabsTray = mockk(relaxed = true) - val tabsSlot = slot() + val tabsSlot = slot>() val controller = InactiveTabsController(store, filter, tray, mockk(relaxed = true)) controller.updateCardExpansion(true) - verify { tray.updateTabs(capture(tabsSlot)) } + verify { tray.updateTabs(capture(tabsSlot), any()) } - assertEquals(2, tabsSlot.captured.list.size) - assertEquals("1", tabsSlot.captured.list.first().id) + assertEquals(2, tabsSlot.captured.size) + assertEquals("1", tabsSlot.captured.first().id) } @Test diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/Tab.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/Tab.kt deleted file mode 100644 index 45638e716..000000000 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/Tab.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* 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.tabstray.browser - -import mozilla.components.concept.tabstray.Tab -import java.util.UUID - -/** - * Helper for writing tests that need a [Tab]. - */ -fun createTab( - tabId: String = UUID.randomUUID().toString(), - lastAccess: Long = 0L, - searchTerm: String = "" -) = Tab( - id = tabId, - url = "https://mozilla.org", - lastAccess = lastAccess, - searchTerm = searchTerm -) diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabSorterTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabSorterTest.kt index b359433a9..057c84ae8 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabSorterTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabSorterTest.kt @@ -7,9 +7,7 @@ package org.mozilla.fenix.tabstray.browser import androidx.recyclerview.widget.ConcatAdapter import io.mockk.every import io.mockk.mockk -import mozilla.components.browser.state.state.BrowserState -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tabs +import mozilla.components.browser.state.state.createTab import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals @@ -43,26 +41,19 @@ class TabSorterTest { @Test fun `WHEN updated with one normal tab THEN adapter have only one normal tab and no header`() { - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()) - ), - selectedIndex = 0 - ) + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 1) @@ -74,28 +65,31 @@ class TabSorterTest { @Test fun `WHEN updated with one normal tab and two search term tab THEN adapter have normal tab and a search group`() { - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()), - createTab("tab2", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab3", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()), + createTab( + url = "url", + id = "tab2", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" ), - selectedIndex = 0 - ) + createTab( + url = "url", + id = "tab3", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 3) @@ -107,29 +101,37 @@ class TabSorterTest { @Test fun `WHEN updated with one normal tab, one inactive tab and two search term tab THEN adapter have normal tab, inactive tab and a search group`() { - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()), - createTab("tab2", inactiveTimestamp), - createTab("tab3", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab4", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()), + createTab( + url = "url", + id = "tab2", + lastAccess = inactiveTimestamp, + createdAt = inactiveTimestamp ), - selectedIndex = 0 - ) + createTab( + url = "url", + id = "tab3", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ), + createTab( + url = "url", + id = "tab4", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 4) @@ -142,29 +144,37 @@ class TabSorterTest { @Test fun `WHEN inactive tabs is off THEN adapter have no inactive tab`() { every { settings.inactiveTabsAreEnabled }.answers { false } - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()), - createTab("tab2", inactiveTimestamp), - createTab("tab3", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab4", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()), + createTab( + url = "url", + id = "tab2", + lastAccess = inactiveTimestamp, + createdAt = inactiveTimestamp ), - selectedIndex = 0 - ) + createTab( + url = "url", + id = "tab3", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ), + createTab( + url = "url", + id = "tab4", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 4) @@ -177,29 +187,37 @@ class TabSorterTest { @Test fun `WHEN search term tabs is off THEN adapter have no search term group`() { every { settings.searchTermTabGroupsAreEnabled }.answers { false } - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()), - createTab("tab2", inactiveTimestamp), - createTab("tab3", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab4", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()), + createTab( + url = "url", + id = "tab2", + lastAccess = inactiveTimestamp, + createdAt = inactiveTimestamp ), - selectedIndex = 0 - ) + createTab( + url = "url", + id = "tab3", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ), + createTab( + url = "url", + id = "tab4", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 4) @@ -213,29 +231,36 @@ class TabSorterTest { fun `WHEN both inactive tabs and search term tabs are off THEN adapter have only normal tabs`() { every { settings.inactiveTabsAreEnabled }.answers { false } every { settings.searchTermTabGroupsAreEnabled }.answers { false } - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis()), - createTab("tab2", inactiveTimestamp), - createTab("tab3", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab4", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab(url = "url", id = "tab1", lastAccess = System.currentTimeMillis()), + createTab( + url = "url", + id = "tab2", + lastAccess = inactiveTimestamp ), - selectedIndex = 0 - ) + createTab( + url = "url", + id = "tab3", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ), + createTab( + url = "url", + id = "tab4", + lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 4) @@ -247,26 +272,22 @@ class TabSorterTest { @Test fun `WHEN only one search term tab THEN there is no search group`() { - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis(), searchTerm = "mozilla") - ), - selectedIndex = 0 - ) + listOf( + createTab( + url = "url", id = "tab1", lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 1) @@ -278,27 +299,26 @@ class TabSorterTest { @Test fun `WHEN remove second last one search term tab THEN search group is kept even if there's only one tab`() { - val store = BrowserStore( - BrowserState( - tabs = emptyList() - ) - ) val adapter = ConcatAdapter( InactiveTabsAdapter(context, mock(), mock(), INACTIVE_TABS_FEATURE_NAME, settings), TabGroupAdapter(context, mock(), mock(), TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(), BrowserTabsAdapter(context, mock(), mock(), TABS_TRAY_FEATURE_NAME) ) - val tabSorter = TabSorter(settings, metrics, adapter, store) + val tabSorter = TabSorter(settings, metrics, adapter) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis(), searchTerm = "mozilla"), - createTab("tab2", System.currentTimeMillis(), searchTerm = "mozilla") + listOf( + createTab( + url = "url", id = "tab1", lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" ), - selectedIndex = 0 - ) + createTab( + url = "url", id = "tab2", lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 1) @@ -308,12 +328,13 @@ class TabSorterTest { assertEquals(adapter.browserAdapter.itemCount, 0) tabSorter.updateTabs( - Tabs( - list = listOf( - createTab("tab1", System.currentTimeMillis(), searchTerm = "mozilla") - ), - selectedIndex = 0 - ) + listOf( + createTab( + url = "url", id = "tab1", lastAccess = System.currentTimeMillis(), + searchTerms = "mozilla" + ) + ), + selectedTabId = "tab1" ) assertEquals(adapter.itemCount, 1) diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelperTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelperTest.kt index 67d0b7f69..05f110ce3 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelperTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TabsTouchHelperTest.kt @@ -20,12 +20,17 @@ import org.mozilla.fenix.tabstray.viewholders.SyncedTabsPageViewHolder @RunWith(FenixRobolectricTestRunner::class) class TabsTouchHelperTest { + private val featureName = object : FeatureNameHolder { + override val featureName: String + get() = "featureName" + } + @Test fun `movement flags remain unchanged if onSwipeToDelete is true`() { val recyclerView = RecyclerView(testContext) val layout = FrameLayout(testContext) val viewHolder = SyncedTabsPageViewHolder(layout, mockk()) - val callback = TouchCallback(mockk(), { true }, { false }) + val callback = TouchCallback(mockk(), { true }, { false }, featureName) assertEquals(0, callback.getDragDirs(recyclerView, viewHolder)) assertEquals( @@ -44,7 +49,7 @@ class TabsTouchHelperTest { val recyclerView = RecyclerView(testContext) val layout = FrameLayout(testContext) val viewHolder = SyncedTabsPageViewHolder(layout, mockk()) - val callback = TouchCallback(mockk(), { false }, { false }) + val callback = TouchCallback(mockk(), { false }, { false }, featureName) assertEquals(0, callback.getDragDirs(recyclerView, viewHolder)) assertEquals( diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/ext/BrowserStoreKtTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/ext/BrowserStoreKtTest.kt index 4c416870d..ac64a85a4 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/ext/BrowserStoreKtTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/ext/BrowserStoreKtTest.kt @@ -7,11 +7,10 @@ package org.mozilla.fenix.tabstray.ext import io.mockk.mockk import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tab import org.junit.Assert.assertEquals import org.junit.Test -import org.mozilla.fenix.tabstray.browser.createTab class BrowserStoreKtTest { @@ -26,9 +25,9 @@ class BrowserStoreKtTest { ) ) - val tabs = listOf( - createTab("tab1"), - createTab("tab2") + val tabs = listOf( + createTab(url = "url", id = "tab1"), + createTab(url = "url", id = "tab2"), ) val result = store.getTabSessionState(tabs) @@ -47,9 +46,9 @@ class BrowserStoreKtTest { ) ) - val tabs = listOf( - createTab("tab1"), - createTab("tab2") + val tabs = listOf( + createTab(url = "url", id = "tab1"), + createTab(url = "url", id = "tab2"), ) val result = store.getTabSessionState(tabs) diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt index fc39a0ebd..c56a80449 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt @@ -9,8 +9,8 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.widget.TextView import io.mockk.mockk +import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.concept.tabstray.Tabs import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertTrue import org.junit.Test @@ -22,7 +22,6 @@ import org.mozilla.fenix.tabstray.TabsTrayStore import org.mozilla.fenix.tabstray.browser.AbstractBrowserTrayList import org.mozilla.fenix.tabstray.browser.BrowserTabsAdapter import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor -import org.mozilla.fenix.tabstray.browser.createTab @RunWith(FenixRobolectricTestRunner::class) class AbstractBrowserPageViewHolderTest { @@ -43,14 +42,7 @@ class AbstractBrowserPageViewHolderTest { viewHolder.bind(adapter) viewHolder.attachedToWindow() - adapter.updateTabs( - Tabs( - list = listOf( - createTab("tab1") - ), - selectedIndex = 0 - ) - ) + adapter.updateTabs(listOf(createTab(url = "url", id = "tab1")), "tab1") assertTrue(trayList.visibility == VISIBLE) assertTrue(emptyList.visibility == GONE) @@ -67,12 +59,7 @@ class AbstractBrowserPageViewHolderTest { viewHolder.bind(adapter) viewHolder.attachedToWindow() - adapter.updateTabs( - Tabs( - list = emptyList(), - selectedIndex = 0 - ) - ) + adapter.updateTabs(emptyList(), "") assertTrue(trayList.visibility == GONE) assertTrue(emptyList.visibility == VISIBLE)