Commit 3849641b authored by Jeff Boek's avatar Jeff Boek
Browse files

For #6867 - Adds a ViewHolderProvider to provide a custom ViewHolder in the TabsAdapter

parent ee10c758
Loading
Loading
Loading
Loading
+20 −5
Original line number Diff line number Diff line
@@ -17,24 +17,39 @@ import mozilla.components.support.base.observer.Observable
import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl

/**
 * A RecyclerView ViewHolder implementation for "tab" items.
 * An abstract ViewHolder implementation for "tab" items.
 */
class TabViewHolder(
abstract class TabViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    abstract var tab: Tab?

    /**
     * Binds the ViewHolder to the `Tab`.
     * @param tab the `Tab` used to bind the viewHolder.
     * @param isSelected boolean to describe whether or not the `Tab` is selected.
     * @param observable message bus to pass events to Observers of the TabsTray.
     */
    abstract fun bind(tab: Tab, isSelected: Boolean, observable: Observable<TabsTray.Observer>)
}

/**
 * The default implementation of `TabViewHolder`
 */
class DefaultTabViewHolder(
    itemView: View,
    private val tabsTray: BrowserTabsTray
) : RecyclerView.ViewHolder(itemView) {
) : TabViewHolder(itemView) {
    private val iconView: ImageView? = itemView.findViewById(R.id.mozac_browser_tabstray_icon)
    private val titleView: TextView = itemView.findViewById(R.id.mozac_browser_tabstray_title)
    private val closeView: AppCompatImageButton = itemView.findViewById(R.id.mozac_browser_tabstray_close)
    private val thumbnailView: TabThumbnailView = itemView.findViewById(R.id.mozac_browser_tabstray_thumbnail)
    private val urlView: TextView? = itemView.findViewById(R.id.mozac_browser_tabstray_url)

    internal var tab: Tab? = null
    override var tab: Tab? = null

    /**
     * Displays the data of the given session and notifies the given observable about events.
     */
    fun bind(tab: Tab, isSelected: Boolean, observable: Observable<TabsTray.Observer>) {
    override fun bind(tab: Tab, isSelected: Boolean, observable: Observable<TabsTray.Observer>) {
        this.tab = tab

        val title = if (tab.title.isNotEmpty()) {
+17 −9
Original line number Diff line number Diff line
@@ -6,20 +6,34 @@ package mozilla.components.browser.tabstray

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
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

/**
 * Function responsible for creating a `TabViewHolder` in the `TabsAdapter`.
 */
typealias ViewHolderProvider = (ViewGroup, BrowserTabsTray) -> TabViewHolder

/**
 * RecyclerView adapter implementation to display a list/grid of tabs.
 * @param delegate TabsTray.Observer registry to allow `TabsAdapter` to conform to `Observable<TabsTray.Observer>`.
 * @param viewHolderProvider a function that creates a `TabViewHolder`.
 */
@Suppress("TooManyFunctions")
class TabsAdapter(
    delegate: Observable<TabsTray.Observer> = ObserverRegistry(),
    @LayoutRes private val layoutId: Int = R.layout.mozac_browser_tabstray_item
    private val viewHolderProvider: ViewHolderProvider = { parent, tabsTray ->
        DefaultTabViewHolder(
                LayoutInflater.from(parent.context).inflate(
                        R.layout.mozac_browser_tabstray_item,
                        parent,
                        false),
                tabsTray
        )
    }
) : RecyclerView.Adapter<TabViewHolder>(),
    TabsTray,
    Observable<TabsTray.Observer> by delegate {
@@ -29,13 +43,7 @@ class TabsAdapter(
    private var tabs: Tabs? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabViewHolder {
        return TabViewHolder(
            LayoutInflater.from(parent.context).inflate(
                layoutId,
                parent,
                false),
            tabsTray
        )
        return viewHolderProvider.invoke(parent, tabsTray)
    }

    override fun getItemCount() = tabs?.list?.size ?: 0
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ class TabTouchCallbackTest {
    @Test
    fun `onChildDraw alters alpha of ViewHolder on swipe gesture`() {
        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val holder = TabViewHolder(view, TabViewHolderTest.mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, TabViewHolderTest.mockTabsTrayWithStyles())
        val callback = TabTouchCallback(mock())

        holder.itemView.alpha = 0f
+7 −7
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ class TabViewHolderTest {
        val titleView = view.findViewById<TextView>(R.id.mozac_browser_tabstray_title)
        val urlView = view.findViewById<TextView>(R.id.mozac_browser_tabstray_url)

        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())

        assertEquals("", titleView.text)

@@ -47,7 +47,7 @@ class TabViewHolderTest {
    fun `URL text is set to tab URL when exception is thrown`() {
        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val urlView = view.findViewById<TextView>(R.id.mozac_browser_tabstray_url)
        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())
        val session = Tab("a", "about:home")

        holder.bind(session, isSelected = false, observable = mock())
@@ -63,7 +63,7 @@ class TabViewHolderTest {
        }

        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())

        val session = Tab("a", "https://www.mozilla.org")
        holder.bind(session, isSelected = false, observable = registry)
@@ -81,7 +81,7 @@ class TabViewHolderTest {
        }

        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())

        val session = Tab("a", "https://www.mozilla.org")
        holder.bind(session, isSelected = true, observable = registry)
@@ -99,7 +99,7 @@ class TabViewHolderTest {
        }

        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())

        val session = Tab("a", "https://www.mozilla.org")
        val titleView = holder.itemView.findViewById<TextView>(R.id.mozac_browser_tabstray_title)
@@ -119,7 +119,7 @@ class TabViewHolderTest {
        }

        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())

        val session = Tab("a", "https://www.mozilla.org", title = "Mozilla Firefox")
        val titleView = holder.itemView.findViewById<TextView>(R.id.mozac_browser_tabstray_title)
@@ -136,7 +136,7 @@ class TabViewHolderTest {
        val view = LayoutInflater.from(testContext).inflate(R.layout.mozac_browser_tabstray_item, null)
        val thumbnailView = view.findViewById<ImageView>(R.id.mozac_browser_tabstray_thumbnail)

        val holder = TabViewHolder(view, mockTabsTrayWithStyles())
        val holder = DefaultTabViewHolder(view, mockTabsTrayWithStyles())
        assertEquals(null, thumbnailView.drawable)

        val emptyBitmap = Bitmap.createBitmap(40, 40, Bitmap.Config.ARGB_8888)
+31 −0
Original line number Diff line number Diff line
@@ -4,19 +4,50 @@

package mozilla.components.browser.tabstray

import android.view.View
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
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.test.mock
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify

private class TestTabViewHolder(view: View) : TabViewHolder(view) {
    override var tab: Tab? = null
    override fun bind(tab: Tab, isSelected: Boolean, observable: Observable<TabsTray.Observer>) { /* noop */ }
}

@RunWith(AndroidJUnit4::class)
class TabsAdapterTest {

    @Test
    fun `onCreateViewHolder will create a DefaultTabViewHolder`() {
        val adapter = TabsAdapter()
        adapter.tabsTray = mock()

        val type = adapter.onCreateViewHolder(FrameLayout(testContext), 0)

        assertTrue(type is DefaultTabViewHolder)
    }

    @Test
    fun `onCreateViewHolder will create whatever TabViewHolder is provided`() {
        val adapter = TabsAdapter { _, _ -> TestTabViewHolder(View(testContext)) }
        adapter.tabsTray = mock()

        val type = adapter.onCreateViewHolder(FrameLayout(testContext), 0)

        assertTrue(type is TestTabViewHolder)
    }

    @Test
    fun `itemCount will reflect number of sessions`() {
        val adapter = TabsAdapter()
Loading