For #24316 - Migrate TabInCollectionViewHolder to Compose
parent
30fa80151d
commit
a93e434f0d
@ -0,0 +1,261 @@
|
||||
/* 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.home.collections
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.DismissDirection
|
||||
import androidx.compose.material.DismissDirection.EndToStart
|
||||
import androidx.compose.material.DismissDirection.StartToEnd
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.SwipeToDismiss
|
||||
import androidx.compose.material.rememberDismissState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import mozilla.components.browser.state.state.recover.RecoverableTab
|
||||
import mozilla.components.concept.engine.Engine
|
||||
import mozilla.components.feature.tab.collections.Tab
|
||||
import org.mozilla.fenix.R.drawable
|
||||
import org.mozilla.fenix.R.string
|
||||
import org.mozilla.fenix.compose.inComposePreview
|
||||
import org.mozilla.fenix.compose.list.FaviconListItem
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.toShortUrl
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.theme.Theme
|
||||
|
||||
/**
|
||||
* Rectangular shape with only right angles used to display a middle tab.
|
||||
*/
|
||||
private val MIDDLE_TAB_SHAPE = RoundedCornerShape(0.dp)
|
||||
|
||||
/**
|
||||
* Rectangular shape with only the bottom corners rounded used to display the last tab in a collection.
|
||||
*/
|
||||
private val BOTTOM_TAB_SHAPE = RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp)
|
||||
|
||||
/**
|
||||
* Display an individual [Tab] as part of a collection.
|
||||
*
|
||||
* @param tab [Tab] to display.
|
||||
* @param isLastInCollection Whether the tab is to be shown between others or as the last one in collection.
|
||||
* @param onClick Invoked when the user click on the tab.
|
||||
* @param onRemove Invoked when the user removes the tab informing also if the tab was swiped to be removed.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun CollectionItem(
|
||||
tab: Tab,
|
||||
isLastInCollection: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onRemove: (Boolean) -> Unit,
|
||||
) {
|
||||
val dismissState = rememberDismissState()
|
||||
|
||||
if (dismissState.isDismissed(StartToEnd) || dismissState.isDismissed(EndToStart)) {
|
||||
onRemove(true)
|
||||
}
|
||||
|
||||
SwipeToDismiss(
|
||||
state = dismissState,
|
||||
background = {
|
||||
DismissedTabBackground(
|
||||
dismissDirection = dismissState.dismissDirection,
|
||||
isLastInCollection = isLastInCollection,
|
||||
)
|
||||
}
|
||||
) {
|
||||
// We need to clip the top bounds to avoid this item drawing shadows over the above item.
|
||||
// But we need to add this shadows back to have a clearer separation between tabs
|
||||
// when one is being swiped away.
|
||||
val clippingModifier by remember {
|
||||
derivedStateOf {
|
||||
try {
|
||||
if (dismissState.progress.fraction != 1f) Modifier else Modifier.clipTop()
|
||||
} catch (e: NoSuchElementException) {
|
||||
// `androidx.compose.material.Swipeable.findBounds` couldn't find anchors.
|
||||
// Happened once in testing when deleting a tab. Could not reproduce afterwards.
|
||||
Modifier.clipTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier = clippingModifier
|
||||
.fillMaxWidth(),
|
||||
shape = if (isLastInCollection) BOTTOM_TAB_SHAPE else MIDDLE_TAB_SHAPE,
|
||||
backgroundColor = FirefoxTheme.colors.layer2,
|
||||
elevation = 5.dp,
|
||||
) {
|
||||
FaviconListItem(
|
||||
label = tab.title,
|
||||
description = shortenUrl(tab.url),
|
||||
onClick = onClick,
|
||||
url = tab.url,
|
||||
iconPainter = painterResource(drawable.ic_close),
|
||||
iconDescription = stringResource(string.remove_tab_from_collection),
|
||||
onIconClick = { onRemove(false) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable used to display the background of a [Tab] shown in collections that is being swiped left or right.
|
||||
*
|
||||
* @param dismissDirection [DismissDirection] of the tab being swiped depending on which this composable
|
||||
* will also indicate the swipe direction by placing a warning icon at the start of the swipe gesture.
|
||||
* If `null` the warning icon will be shown at both ends.
|
||||
* @param isLastInCollection Whether the tab is to be shown between others or as the last one in collection.
|
||||
*/
|
||||
@Composable
|
||||
private fun DismissedTabBackground(
|
||||
dismissDirection: DismissDirection?,
|
||||
isLastInCollection: Boolean,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
backgroundColor = FirefoxTheme.colors.layer3,
|
||||
shape = if (isLastInCollection) BOTTOM_TAB_SHAPE else MIDDLE_TAB_SHAPE,
|
||||
elevation = 0.dp,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(drawable.ic_delete),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp)
|
||||
// Only show the delete icon for where the swipe starts.
|
||||
.alpha(
|
||||
if (dismissDirection == StartToEnd) 1f else 0f
|
||||
),
|
||||
tint = FirefoxTheme.colors.iconWarning,
|
||||
)
|
||||
|
||||
Icon(
|
||||
painter = painterResource(drawable.ic_delete),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp)
|
||||
// Only show the delete icon for where the swipe starts.
|
||||
.alpha(
|
||||
if (dismissDirection == EndToStart) 1f else 0f
|
||||
),
|
||||
tint = FirefoxTheme.colors.iconWarning,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clips the Composable this applies to such that it cannot draw content / shadows outside it's top bound.
|
||||
*/
|
||||
private fun Modifier.clipTop() = this.then(
|
||||
Modifier.drawWithContent {
|
||||
val paddingPx = Constraints.Infinity.toFloat()
|
||||
clipRect(
|
||||
left = 0f - paddingPx,
|
||||
top = 0f,
|
||||
right = size.width + paddingPx,
|
||||
bottom = size.height + paddingPx
|
||||
) {
|
||||
this@drawWithContent.drawContent()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Get a friendlier short url for [url].
|
||||
*
|
||||
* @param url Full url to be shortened.
|
||||
*
|
||||
* @see toShortUrl
|
||||
*/
|
||||
@Composable
|
||||
private fun shortenUrl(url: String): String {
|
||||
return if (inComposePreview) {
|
||||
url
|
||||
} else {
|
||||
url.toShortUrl(LocalContext.current.components.publicSuffixList)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun TabInCollectionPreview() {
|
||||
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
|
||||
Column {
|
||||
Box(modifier = Modifier.height(56.dp)) {
|
||||
DismissedTabBackground(
|
||||
dismissDirection = StartToEnd,
|
||||
isLastInCollection = false,
|
||||
)
|
||||
}
|
||||
CollectionItem(
|
||||
tab = tabPreview,
|
||||
isLastInCollection = false,
|
||||
onClick = {},
|
||||
onRemove = {},
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Box(modifier = Modifier.height(56.dp)) {
|
||||
DismissedTabBackground(
|
||||
dismissDirection = EndToStart,
|
||||
isLastInCollection = true,
|
||||
)
|
||||
}
|
||||
CollectionItem(
|
||||
tab = tabPreview,
|
||||
isLastInCollection = true,
|
||||
onClick = {},
|
||||
onRemove = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val tabPreview = object : Tab {
|
||||
override val id = 2L
|
||||
override val title = "Mozilla-Firefox"
|
||||
override val url = "https://www.mozilla.org/en-US/firefox/whats-new-in-last-version"
|
||||
|
||||
override fun restore(
|
||||
context: Context,
|
||||
engine: Engine,
|
||||
restoreSessionId: Boolean
|
||||
): RecoverableTab? = null
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/* 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.home.collections
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.feature.tab.collections.Tab
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.ComposeViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||
|
||||
/**
|
||||
* [RecyclerView.ViewHolder] for displaying an individual [Tab].
|
||||
* Clients are expected to use [bindSession] to link a particular [Tab] to be displayed
|
||||
* otherwise this will be an empty, 0 size View.
|
||||
*
|
||||
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
|
||||
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
|
||||
* @param interactor [CollectionInteractor] callback for user interactions.
|
||||
*/
|
||||
class TabInCollectionViewHolder(
|
||||
composeView: ComposeView,
|
||||
viewLifecycleOwner: LifecycleOwner,
|
||||
private val interactor: CollectionInteractor,
|
||||
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
|
||||
private var tabData = TabInfo()
|
||||
|
||||
init {
|
||||
val horizontalPadding =
|
||||
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
|
||||
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val tabInfo = remember { mutableStateOf(tabData) }
|
||||
|
||||
tabInfo.value.tab?.let { tab ->
|
||||
tabInfo.value.collection?.let { collection ->
|
||||
|
||||
CollectionItem(
|
||||
tab = tab,
|
||||
isLastInCollection = tabInfo.value.isLastInCollection,
|
||||
onClick = { interactor.onCollectionOpenTabClicked(tab) },
|
||||
onRemove = { wasSwiped ->
|
||||
interactor.onCollectionRemoveTab(
|
||||
collection = collection,
|
||||
tab = tab,
|
||||
wasSwiped = wasSwiped,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically replace the current [Tab] shown in this `ViewHolder`.
|
||||
*
|
||||
* @param collection [TabCollection] containing [tab].
|
||||
* @param tab [Tab] to display.
|
||||
* @param isLastInCollection Whether [tab] is to be shown as the last item in [collection].
|
||||
*/
|
||||
fun bindSession(collection: TabCollection, tab: Tab, isLastInCollection: Boolean) {
|
||||
tabData = TabInfo(collection, tab, isLastInCollection)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LAYOUT_ID = View.generateViewId()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper over a [Tab] adding information about the collection it is part of and the position in this collection.
|
||||
*
|
||||
* @property collection [TabCollection] which contains this tab.
|
||||
* @property tab [Tab] to display.
|
||||
* @property isLastInCollection Whether the tab is to be shown between others or as the last one in collection.
|
||||
*/
|
||||
@Stable
|
||||
private data class TabInfo(
|
||||
val collection: TabCollection? = null,
|
||||
val tab: Tab? = null,
|
||||
val isLastInCollection: Boolean = false,
|
||||
)
|
@ -1,135 +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.home.sessioncontrol
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
|
||||
|
||||
class SwipeToDeleteCallback(
|
||||
val interactor: SessionControlInteractor
|
||||
) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
// We don't support drag and drop so this method will never be called
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
when (viewHolder) {
|
||||
is TabInCollectionViewHolder -> {
|
||||
interactor.onCollectionRemoveTab(viewHolder.collection, viewHolder.tab, wasSwiped = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChildDraw(
|
||||
c: Canvas,
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
dX: Float,
|
||||
dY: Float,
|
||||
actionState: Int,
|
||||
isCurrentlyActive: Boolean
|
||||
) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
val icon = AppCompatResources.getDrawable(recyclerView.context, R.drawable.ic_delete)
|
||||
icon?.setTint(recyclerView.context.getColorFromAttr(R.attr.textWarning))
|
||||
|
||||
val backgroundDrawable = when {
|
||||
viewHolder is TabInCollectionViewHolder && viewHolder.isLastItem -> {
|
||||
R.drawable.tab_in_collection_last_swipe_background
|
||||
}
|
||||
viewHolder is TabInCollectionViewHolder -> {
|
||||
R.drawable.tab_in_collection_swipe_background
|
||||
}
|
||||
else -> R.drawable.session_background
|
||||
}
|
||||
|
||||
val background = AppCompatResources.getDrawable(recyclerView.context, backgroundDrawable)
|
||||
background?.let {
|
||||
icon?.let {
|
||||
val itemView = viewHolder.itemView
|
||||
val iconLeft: Int
|
||||
val iconRight: Int
|
||||
val margin = convertDpToPixel(MARGIN.toFloat())
|
||||
val iconWidth = icon.intrinsicWidth
|
||||
val iconHeight = icon.intrinsicHeight
|
||||
val cellHeight = itemView.bottom - itemView.top
|
||||
val iconTop = itemView.top + (cellHeight - iconHeight) / 2
|
||||
val iconBottom = iconTop + iconHeight
|
||||
|
||||
when {
|
||||
dX > 0 -> { // Swiping to the right
|
||||
iconLeft = itemView.left + margin
|
||||
iconRight = itemView.left + margin + iconWidth
|
||||
background.setBounds(
|
||||
itemView.left, itemView.top,
|
||||
(itemView.left + dX).toInt() + BACKGROUND_CORNER_OFFSET,
|
||||
itemView.bottom
|
||||
)
|
||||
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom)
|
||||
draw(background, icon, c)
|
||||
}
|
||||
dX < 0 -> { // Swiping to the left
|
||||
iconLeft = itemView.right - margin - iconWidth
|
||||
iconRight = itemView.right - margin
|
||||
background.setBounds(
|
||||
(itemView.right + dX).toInt() - BACKGROUND_CORNER_OFFSET,
|
||||
itemView.top, itemView.right, itemView.bottom
|
||||
)
|
||||
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom)
|
||||
draw(background, icon, c)
|
||||
}
|
||||
else -> { // View not swiped
|
||||
background.setBounds(0, 0, 0, 0)
|
||||
icon.setBounds(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSwipeDirs(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
return if (recyclerView.hasWindowFocus() &&
|
||||
viewHolder is TabInCollectionViewHolder
|
||||
) {
|
||||
super.getSwipeDirs(recyclerView, viewHolder)
|
||||
} else 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val BACKGROUND_CORNER_OFFSET = 40
|
||||
const val MARGIN = 32
|
||||
private const val DENSITY_CONVERSION = 160f
|
||||
|
||||
private fun draw(
|
||||
background: Drawable,
|
||||
icon: Drawable,
|
||||
c: Canvas
|
||||
) {
|
||||
background.draw(c)
|
||||
icon.draw(c)
|
||||
}
|
||||
|
||||
private fun convertDpToPixel(dp: Float): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
val px = dp * (metrics.densityDpi / DENSITY_CONVERSION)
|
||||
return Math.round(px)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import mozilla.components.browser.icons.BrowserIcons
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.ui.widgets.WidgetSiteItemView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.ext.toShortUrl
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
|
||||
class TabInCollectionViewHolder(
|
||||
private val view: WidgetSiteItemView,
|
||||
val interactor: CollectionInteractor,
|
||||
private val icons: BrowserIcons = view.context.components.core.icons,
|
||||
private val publicSuffixList: PublicSuffixList = view.context.components.publicSuffixList
|
||||
) : ViewHolder(view) {
|
||||
|
||||
lateinit var collection: TabCollection
|
||||
private set
|
||||
lateinit var tab: ComponentTab
|
||||
private set
|
||||
var isLastItem = false
|
||||
private set
|
||||
|
||||
init {
|
||||
if (SDK_INT >= Build.VERSION_CODES.M) {
|
||||
view.foreground = AppCompatResources.getDrawable(
|
||||
view.context,
|
||||
view.context.theme.resolveAttribute(R.attr.selectableItemBackground)
|
||||
)
|
||||
}
|
||||
|
||||
// This needs to match the elevation of the CollectionViewHolder for the shadow
|
||||
view.elevation = view.resources.getDimension(R.dimen.home_item_elevation)
|
||||
|
||||
view.setOnClickListener {
|
||||
interactor.onCollectionOpenTabClicked(tab)
|
||||
}
|
||||
|
||||
view.setSecondaryButton(
|
||||
icon = R.drawable.ic_close,
|
||||
contentDescription = R.string.remove_tab_from_collection
|
||||
) {
|
||||
interactor.onCollectionRemoveTab(collection, tab, wasSwiped = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun bindSession(collection: TabCollection, tab: ComponentTab, isLastTab: Boolean) {
|
||||
this.collection = collection
|
||||
this.tab = tab
|
||||
this.isLastItem = isLastTab
|
||||
updateTabUI()
|
||||
}
|
||||
|
||||
private fun updateTabUI() {
|
||||
view.setText(
|
||||
label = tab.title,
|
||||
caption = tab.url.toShortUrl(publicSuffixList)
|
||||
)
|
||||
|
||||
icons.loadIntoView(view.iconView, tab.url)
|
||||
|
||||
// If last item and we want to change UI for it
|
||||
val context = view.context
|
||||
if (isLastItem) {
|
||||
view.background = AppCompatResources.getDrawable(context, R.drawable.rounded_bottom_corners)
|
||||
} else {
|
||||
view.setBackgroundColor(context.getColorFromAttr(R.attr.layer2))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.site_list_item
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="@dimen/tab_corner_radius" />
|
||||
<solid android:color="?layer3" />
|
||||
</shape>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:bottomLeftRadius="@dimen/tab_corner_radius" android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
<solid android:color="?layer3" />
|
||||
</shape>
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?layer3" />
|
||||
</shape>
|
Loading…
Reference in New Issue