Issue #18538: Add BrowserTabsAdapter for tabs tray
parent
3b11b9a700
commit
2c23941823
@ -0,0 +1,84 @@
|
||||
/* 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 android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import kotlinx.android.synthetic.main.tab_tray_item.view.*
|
||||
import mozilla.components.browser.tabstray.TabViewHolder
|
||||
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
|
||||
import mozilla.components.concept.tabstray.TabsTray
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.base.observer.ObserverRegistry
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.tabtray.TabTrayViewHolder
|
||||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] for browser tabs.
|
||||
*/
|
||||
class BrowserTabsAdapter(
|
||||
private val context: Context,
|
||||
private val interactor: BrowserTrayInteractor,
|
||||
private val layoutManager: (() -> GridLayoutManager)? = null,
|
||||
delegate: Observable<TabsTray.Observer> = ObserverRegistry()
|
||||
) : TabsAdapter(delegate) {
|
||||
|
||||
/**
|
||||
* The layout types for the tabs.
|
||||
*/
|
||||
enum class ViewType {
|
||||
LIST,
|
||||
GRID
|
||||
}
|
||||
|
||||
private val imageLoader = ThumbnailLoader(context.components.core.thumbnailStorage)
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (context.settings().gridTabView) {
|
||||
// ViewType.GRID.ordinal
|
||||
R.layout.tab_tray_grid_item
|
||||
} else {
|
||||
// ViewType.LIST.ordinal
|
||||
R.layout.tab_tray_item
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabViewHolder {
|
||||
// TODO make this into separate view holders for each layout
|
||||
// For this, we need to separate the TabTrayViewHolder as well.
|
||||
// See https://github.com/mozilla-mobile/fenix/issues/18535
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
|
||||
return TabTrayViewHolder(view, imageLoader)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
|
||||
super.onBindViewHolder(holder, position)
|
||||
|
||||
holder.tab?.let { tab ->
|
||||
if (!tab.private) {
|
||||
holder.itemView.setOnLongClickListener {
|
||||
interactor.onMultiSelect(true)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
holder.itemView.setOnLongClickListener(null)
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
interactor.onOpenTab(tab)
|
||||
}
|
||||
|
||||
holder.itemView.mozac_browser_tabstray_close.setOnClickListener {
|
||||
interactor.onCloseTab(tab)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/* 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 mozilla.components.feature.tabs.TabsUseCases
|
||||
import org.mozilla.fenix.tabstray.TabsTrayInteractor
|
||||
|
||||
/**
|
||||
* For interacting with UI that extends from [BaseBrowserTrayList] and other browser tab tray views.
|
||||
*/
|
||||
interface BrowserTrayInteractor {
|
||||
|
||||
/**
|
||||
* Select the tab.
|
||||
*/
|
||||
fun onOpenTab(tab: Tab)
|
||||
|
||||
/**
|
||||
* Close the tab.
|
||||
*/
|
||||
fun onCloseTab(tab: Tab)
|
||||
|
||||
/**
|
||||
* Enable or disable multi-select mode.
|
||||
*/
|
||||
fun onMultiSelect(enabled: Boolean)
|
||||
}
|
||||
|
||||
/**
|
||||
* A default implementation of [BrowserTrayInteractor].
|
||||
*/
|
||||
class DefaultBrowserTrayInteractor(
|
||||
private val trayInteractor: TabsTrayInteractor,
|
||||
private val selectTabUseCase: TabsUseCases.SelectTabUseCase,
|
||||
private val removeUseCases: TabsUseCases.RemoveTabUseCase
|
||||
) : BrowserTrayInteractor {
|
||||
|
||||
/**
|
||||
* See [BrowserTrayInteractor.onOpenTab].
|
||||
*/
|
||||
override fun onOpenTab(tab: Tab) {
|
||||
selectTabUseCase.invoke(tab.id)
|
||||
trayInteractor.navigateToBrowser()
|
||||
}
|
||||
|
||||
/**
|
||||
* See [BrowserTrayInteractor.onCloseTab].
|
||||
*/
|
||||
override fun onCloseTab(tab: Tab) {
|
||||
removeUseCases.invoke(tab.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* See [BrowserTrayInteractor.onMultiSelect].
|
||||
*/
|
||||
override fun onMultiSelect(enabled: Boolean) {
|
||||
// TODO https://github.com/mozilla-mobile/fenix/issues/18443
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/* 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 androidx.annotation.CallSuper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.browser.tabstray.TabViewHolder
|
||||
import mozilla.components.browser.tabstray.TabsTrayStyling
|
||||
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
|
||||
|
||||
// The previous tabs adapter was very restrictive and required Fenix to jump through
|
||||
// may hoops to access and update certain methods. An abstract adapter is easier to manage
|
||||
// for Android UI APIs.
|
||||
//
|
||||
// TODO Let's upstream this to AC with tests.
|
||||
abstract class TabsAdapter(
|
||||
delegate: Observable<TabsTray.Observer> = ObserverRegistry()
|
||||
) : RecyclerView.Adapter<TabViewHolder>(), TabsTray, Observable<TabsTray.Observer> by delegate {
|
||||
private var tabs: Tabs? = null
|
||||
|
||||
var styling: TabsTrayStyling = TabsTrayStyling()
|
||||
|
||||
override fun getItemCount(): Int = tabs?.list?.size ?: 0
|
||||
|
||||
@CallSuper
|
||||
override fun updateTabs(tabs: Tabs) {
|
||||
this.tabs = tabs
|
||||
|
||||
notifyObservers { onTabsUpdated() }
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
|
||||
val tabs = tabs ?: return
|
||||
|
||||
holder.bind(tabs.list[position], isTabSelected(tabs, position), styling, this)
|
||||
}
|
||||
|
||||
final override fun isTabSelected(tabs: Tabs, position: Int): Boolean =
|
||||
tabs.selectedIndex == position
|
||||
|
||||
final override fun onTabsChanged(position: Int, count: Int) =
|
||||
notifyItemRangeChanged(position, count)
|
||||
|
||||
final override fun onTabsInserted(position: Int, count: Int) =
|
||||
notifyItemRangeInserted(position, count)
|
||||
|
||||
final override fun onTabsMoved(fromPosition: Int, toPosition: Int) =
|
||||
notifyItemMoved(fromPosition, toPosition)
|
||||
|
||||
final override fun onTabsRemoved(position: Int, count: Int) =
|
||||
notifyItemRangeRemoved(position, count)
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/* 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.feature.tabs.TabsUseCases
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class SelectTabUseCaseWrapper(
|
||||
private val metrics: MetricController,
|
||||
private val selectTab: TabsUseCases.SelectTabUseCase,
|
||||
private val onSelect: (String) -> Unit
|
||||
) : TabsUseCases.SelectTabUseCase {
|
||||
override fun invoke(tabId: String) {
|
||||
metrics.track(Event.OpenedExistingTab)
|
||||
selectTab(tabId)
|
||||
onSelect(tabId)
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveTabUseCaseWrapper(
|
||||
private val metrics: MetricController,
|
||||
private val onRemove: (String) -> Unit
|
||||
) : TabsUseCases.RemoveTabUseCase {
|
||||
override fun invoke(sessionId: String) {
|
||||
metrics.track(Event.ClosedExistingTab)
|
||||
onRemove(sessionId)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue