For #24333 - Replace the xml based CollectionViewHolder with a composable

pull/543/head
Mugurell 2 years ago committed by mergify[bot]
parent 2126c7eeb4
commit 1e3319f0e8

@ -21,9 +21,10 @@ import mozilla.components.support.base.observer.Observable
import mozilla.components.support.base.observer.ObserverRegistry
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.perf.StrictModeManager
private const val COLLECTION_MAX_TITLE_LENGTH = 20
class TabCollectionStorage(
private val context: Context,
strictMode: StrictModeManager,
@ -106,10 +107,10 @@ fun TabCollection.description(context: Context): String {
return this.tabs
.map { it.url.toShortUrl(context.components.publicSuffixList) }
.map {
if (it.length > CollectionViewHolder.maxTitleLength) {
if (it.length > COLLECTION_MAX_TITLE_LENGTH) {
it.substring(
0,
CollectionViewHolder.maxTitleLength
COLLECTION_MAX_TITLE_LENGTH
) + ""
} else {
it

@ -0,0 +1,226 @@
/* 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.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
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.Icon
import androidx.compose.material.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
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 mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R.drawable
import org.mozilla.fenix.R.string
import org.mozilla.fenix.compose.list.ExpandableListHeader
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
/**
* Rectangular shape with all corners rounded used to display a collapsed collection.
*/
private val collapsedCollectionShape = RoundedCornerShape(8.dp)
/**
* Rectangular shape with only the top corners rounded used to display an expanded collection with other views
* placed immediately below this which can be shown immediately next to it, with no visible separation.
*/
private val expandedCollectionShape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)
/**
* Displays an individual [TabCollection].
*
* @param collection [TabCollection] to display.
* @param expanded Whether the collection is expanded to show it's containing tabs or not.
* @param menuItems List of [CollectionMenuItem] to be shown in a menu.
* @param onToggleCollectionExpanded Invoked when the user clicks on the collection.
* @param onCollectionShareTabsClicked Invoked when the user clicks to share the collection.
* @param onCollectionMenuOpened Invoked when the user clicks to open a menu for the collection.
*/
@Composable
@Suppress("LongParameterList")
fun Collection(
collection: TabCollection,
expanded: Boolean,
menuItems: List<CollectionMenuItem>,
onToggleCollectionExpanded: (TabCollection, Boolean) -> Unit,
onCollectionShareTabsClicked: (TabCollection) -> Unit,
onCollectionMenuOpened: () -> Unit,
) {
var isMenuExpanded by remember(collection) { mutableStateOf(false) }
val isExpanded by remember(collection) { mutableStateOf(expanded) }
Card(
modifier = Modifier
.semantics(mergeDescendants = true) {}
.clickable { onToggleCollectionExpanded(collection, !isExpanded) }
.height(48.dp),
shape = if (isExpanded) expandedCollectionShape else collapsedCollectionShape,
backgroundColor = FirefoxTheme.colors.layer2,
elevation = 5.dp, // This needs to match the elevation of TabInCollection for matching shadows.
) {
Row(
modifier = Modifier
.fillMaxHeight(),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(drawable.ic_tab_collection),
contentDescription = null,
modifier = Modifier.padding(
start = 16.dp,
end = 8.dp // (24.dp - 16.dp) hardcoded in ExpandableListHeader
),
tint = Paint().apply {
color = Color(collection.getIconColor(LocalContext.current))
blendMode = BlendMode.SrcIn
}.color,
)
ExpandableListHeader(
headerText = collection.title,
expanded = isExpanded,
) {
if (isExpanded) {
Row {
IconButton(
onClick = { onCollectionShareTabsClicked(collection) }
) {
Icon(
painter = painterResource(drawable.ic_share),
contentDescription = stringResource(string.share_button_content_description),
tint = FirefoxTheme.colors.iconPrimary,
)
}
IconButton(
onClick = {
isMenuExpanded = !isMenuExpanded
onCollectionMenuOpened()
}
) {
Icon(
painter = painterResource(drawable.ic_menu),
contentDescription = stringResource(
string.collection_menu_button_content_description
),
tint = FirefoxTheme.colors.iconPrimary,
)
CollectionMenu(
showMenu = isMenuExpanded,
menuItems = menuItems,
onDismissRequest = { isMenuExpanded = false },
)
}
}
}
}
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun CollectionDarkPreview() {
FirefoxTheme(Theme.Dark) {
Collection(
collection = collectionPreview,
expanded = false,
menuItems = emptyList(),
onToggleCollectionExpanded = { _, _ -> },
onCollectionShareTabsClicked = {},
onCollectionMenuOpened = {},
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun CollectionDarkExpandedPreview() {
FirefoxTheme(Theme.Dark) {
Collection(
collection = collectionPreview,
expanded = true,
menuItems = emptyList(),
onToggleCollectionExpanded = { _, _ -> },
onCollectionShareTabsClicked = {},
onCollectionMenuOpened = {},
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun CollectionLightPreview() {
FirefoxTheme(Theme.Light) {
Collection(
collection = collectionPreview,
expanded = false,
menuItems = emptyList(),
onToggleCollectionExpanded = { _, _ -> },
onCollectionShareTabsClicked = {},
onCollectionMenuOpened = {},
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun CollectionLightExpandedPreview() {
FirefoxTheme(Theme.Light) {
Collection(
collection = collectionPreview,
expanded = true,
menuItems = emptyList(),
onToggleCollectionExpanded = { _, _ -> },
onCollectionShareTabsClicked = {},
onCollectionMenuOpened = {},
)
}
}
private val collectionPreview = object : TabCollection {
override val id: Long = 1L
override val tabs: List<Tab> = emptyList()
override val title: String = "Collection 1"
override fun restore(
context: Context,
engine: Engine,
restoreSessionId: Boolean,
): List<RecoverableTab> = emptyList()
override fun restoreSubset(
context: Context,
engine: Engine,
tabs: List<Tab>,
restoreSessionId: Boolean,
): List<RecoverableTab> = emptyList()
}

@ -0,0 +1,210 @@
/* 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.background
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.state.recover.RecoverableTab
import mozilla.components.concept.engine.Engine
import mozilla.components.feature.tab.collections.Tab
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R.string
import org.mozilla.fenix.compose.inComposePreview
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
/**
* Menu shown for a [org.mozilla.fenix.home.collections.Collection].
*
* @see [DropdownMenu]
*
* @param showMenu Whether this is currently open and visible to the user.
* @param menuItems List of options shown.
* @param onDismissRequest Called when the user chooses a menu option or requests to dismiss the menu.
*/
@Composable
fun CollectionMenu(
showMenu: Boolean,
menuItems: List<CollectionMenuItem>,
onDismissRequest: () -> Unit,
) {
DisposableEffect(LocalConfiguration.current.orientation) {
onDispose { onDismissRequest() }
}
// DropdownMenu uses the medium shape from MaterialTheme.
// Override it's corner radius to be the same 8.dp as in mozac_browser_menu_corner_radius
MaterialTheme(shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(8.dp))) {
DropdownMenu(
expanded = showMenu,
onDismissRequest = { onDismissRequest() },
modifier = Modifier
.background(color = FirefoxTheme.colors.layer2),
) {
for (item in menuItems) {
DropdownMenuItem(
onClick = {
onDismissRequest()
item.onClick()
},
) {
Text(
text = item.title,
color = item.color,
maxLines = 1,
modifier = Modifier
.fillMaxHeight()
.align(Alignment.CenterVertically)
)
}
}
}
}
}
/**
* A menu item for collections.
*
* @property title The menu item title.
* @property color The color that should be set for the title.
* @property onClick Invoked when the user clicks on the menu item.
*/
@Immutable
data class CollectionMenuItem(
val title: String,
val color: Color,
val onClick: () -> Unit,
)
/**
* Constructs and returns the default list of menu options for a [TabCollection].
*
* @param collection [TabCollection] for which the menu will be shown.
* Might serve as an argument for the callbacks for when the user interacts with certain menu options.
* @param onOpenTabsTapped Invoked when the user chooses to open the tabs from [collection].
* @param onRenameCollectionTapped Invoked when the user chooses to rename the [collection].
* @param onAddTabTapped Invoked when the user chooses to add tabs to [collection].
* @param onDeleteCollectionTapped Invoked when the user chooses to delete [collection].
*/
@Composable
fun getMenuItems(
collection: TabCollection,
onOpenTabsTapped: (TabCollection) -> Unit,
onRenameCollectionTapped: (TabCollection) -> Unit,
onAddTabTapped: (TabCollection) -> Unit,
onDeleteCollectionTapped: (TabCollection) -> Unit,
): List<CollectionMenuItem> {
return listOfNotNull(
CollectionMenuItem(
title = stringResource(string.collection_open_tabs),
color = FirefoxTheme.colors.textPrimary
) {
onOpenTabsTapped(collection)
},
CollectionMenuItem(
title = stringResource(string.collection_rename),
color = FirefoxTheme.colors.textPrimary
) {
onRenameCollectionTapped(collection)
},
if (hasOpenTabs()) {
CollectionMenuItem(
title = stringResource(string.add_tab),
color = FirefoxTheme.colors.textPrimary
) {
onAddTabTapped(collection)
}
} else {
null
},
CollectionMenuItem(
title = stringResource(string.collection_delete),
color = FirefoxTheme.colors.textWarning
) {
onDeleteCollectionTapped(collection)
},
)
}
@Composable
private fun hasOpenTabs() = when (inComposePreview) {
true -> true
false -> LocalContext.current.components.core.store.state.normalTabs.isNotEmpty()
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun CollectionMenuDarkPreview() {
FirefoxTheme(Theme.Dark) {
CollectionMenu(
showMenu = true,
menuItems = getMenuItems(
collection = collectionPreview,
onOpenTabsTapped = {},
onRenameCollectionTapped = {},
onAddTabTapped = {},
onDeleteCollectionTapped = {}
),
) {}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun CollectionMenuLightPreview() {
FirefoxTheme(Theme.Light) {
CollectionMenu(
showMenu = true,
menuItems = getMenuItems(
collection = collectionPreview,
onOpenTabsTapped = {},
onRenameCollectionTapped = {},
onAddTabTapped = {},
onDeleteCollectionTapped = {}
),
) {}
}
}
private val collectionPreview = object : TabCollection {
override val id: Long = 1L
override val tabs: List<Tab> = emptyList()
override val title: String = "Collection 1"
override fun restore(
context: Context,
engine: Engine,
restoreSessionId: Boolean,
): List<RecoverableTab> = emptyList()
override fun restoreSubset(
context: Context,
engine: Engine,
tabs: List<Tab>,
restoreSessionId: Boolean,
): List<RecoverableTab> = emptyList()
}

@ -2,154 +2,103 @@
* 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
package org.mozilla.fenix.home.collections
import android.content.Context
import android.view.View
import androidx.core.graphics.BlendModeColorFilterCompat.createBlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.browser.state.selector.normalTabs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.CollectionHomeListRowBinding
import org.mozilla.fenix.utils.view.ViewHolder
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.removeAndDisable
import org.mozilla.fenix.ext.removeTouchDelegate
import org.mozilla.fenix.ext.showAndEnable
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
import org.mozilla.fenix.theme.ThemeManager
/**
* [RecyclerView.ViewHolder] for displaying an individual [TabCollection].
* Clients are expected to use [bindSession] to link a particular [TabCollection] 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 CollectionViewHolder(
view: View,
val interactor: CollectionInteractor
) : ViewHolder(view) {
private lateinit var collection: TabCollection
private var expanded = false
private var collectionMenu: CollectionItemMenu
private var binding: CollectionHomeListRowBinding
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val interactor: CollectionInteractor,
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
private var collectionData = CollectionInfo()
init {
binding = CollectionHomeListRowBinding.bind(view)
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
}
collectionMenu = CollectionItemMenu(
view.context,
{ view.context.components.core.store.state.normalTabs.isNotEmpty() }
) {
when (it) {
is CollectionItemMenu.Item.DeleteCollection -> interactor.onDeleteCollectionTapped(collection)
is CollectionItemMenu.Item.AddTab -> interactor.onCollectionAddTabTapped(collection)
is CollectionItemMenu.Item.RenameCollection -> interactor.onRenameCollectionTapped(collection)
is CollectionItemMenu.Item.OpenTabs -> interactor.onCollectionOpenTabsTapped(collection)
@Composable
override fun Content() {
val collectionInfo by remember { mutableStateOf(collectionData) }
collectionInfo.collection?.let { collection ->
val menuItems = getMenuItems(
collection = collection,
onOpenTabsTapped = interactor::onCollectionOpenTabsTapped,
onRenameCollectionTapped = interactor::onRenameCollectionTapped,
onAddTabTapped = interactor::onCollectionAddTabTapped,
onDeleteCollectionTapped = interactor::onDeleteCollectionTapped,
)
Column {
Spacer(Modifier.height(12.dp))
Collection(
collection = collection,
expanded = collectionInfo.isExpanded,
menuItems = menuItems,
onToggleCollectionExpanded = interactor::onToggleCollectionExpanded,
onCollectionShareTabsClicked = interactor::onCollectionShareTabsClicked,
onCollectionMenuOpened = interactor::onCollectionMenuOpened,
)
}
}
binding.collectionOverflowButton.setOnClickListener {
interactor.onCollectionMenuOpened()
collectionMenu.menuBuilder
.build(view.context)
.show(anchor = it)
}
binding.collectionShareButton.setOnClickListener {
interactor.onCollectionShareTabsClicked(collection)
}
view.clipToOutline = true
view.setOnClickListener {
interactor.onToggleCollectionExpanded(collection, !expanded)
}
}
/**
* Dynamically replace the current [TabCollection] shown in this `ViewHolder`.
*
* @param collection [TabCollection] to be shown
* @param expanded Whether to show the collection as expanded or collapsed.
*/
fun bindSession(collection: TabCollection, expanded: Boolean) {
this.collection = collection
this.expanded = expanded
updateCollectionUI()
}
private fun updateCollectionUI() {
binding.collectionTitle.text = collection.title
itemView.isActivated = expanded
if (expanded) {
binding.collectionShareButton.apply {
showAndEnable()
increaseTapArea(buttonIncreaseDps)
}
binding.collectionOverflowButton.apply {
showAndEnable()
increaseTapArea(buttonIncreaseDps)
}
} else {
binding.collectionShareButton.apply {
removeAndDisable()
removeTouchDelegate()
}
binding.collectionOverflowButton.apply {
removeAndDisable()
removeTouchDelegate()
}
}
binding.collectionIcon.colorFilter = createBlendModeColorFilterCompat(
collection.getIconColor(itemView.context),
SRC_IN
collectionData = CollectionInfo(
collection = collection,
isExpanded = expanded
)
}
companion object {
const val buttonIncreaseDps = 16
const val LAYOUT_ID = R.layout.collection_home_list_row
const val maxTitleLength = 20
val LAYOUT_ID = View.generateViewId()
}
}
class CollectionItemMenu(
private val context: Context,
private val shouldShowAddTab: () -> Boolean,
private val onItemTapped: (Item) -> Unit = {}
) {
sealed class Item {
object DeleteCollection : Item()
object AddTab : Item()
object RenameCollection : Item()
object OpenTabs : Item()
}
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
private val menuItems by lazy {
listOf(
SimpleBrowserMenuItem(
context.getString(R.string.collection_open_tabs)
) {
onItemTapped.invoke(Item.OpenTabs)
},
SimpleBrowserMenuItem(
context.getString(R.string.collection_rename)
) {
onItemTapped.invoke(Item.RenameCollection)
},
SimpleBrowserMenuItem(
context.getString(R.string.add_tab)
) {
onItemTapped.invoke(Item.AddTab)
}.apply { visible = shouldShowAddTab },
SimpleBrowserMenuItem(
context.getString(R.string.collection_delete),
textColorResource = ThemeManager.resolveAttribute(R.attr.textWarning, context)
) {
onItemTapped.invoke(Item.DeleteCollection)
}
)
}
}
/**
* Wrapper over a [TabCollection] adding information about whether it should be shown as expanded or collapsed.
*
* @property collection [TabCollection] to display.
* @property isExpanded Whether the collection is expanded to show it's containing tabs or not.
*/
@Stable
private data class CollectionInfo(
val collection: TabCollection? = null,
val isExpanded: Boolean = false,
)

@ -20,6 +20,7 @@ import org.mozilla.fenix.components.Components
import org.mozilla.fenix.gleanplumb.Message
import org.mozilla.fenix.home.BottomSpacerViewHolder
import org.mozilla.fenix.home.TopPlaceholderViewHolder
import org.mozilla.fenix.home.collections.CollectionViewHolder
import org.mozilla.fenix.home.pocket.PocketCategoriesViewHolder
import org.mozilla.fenix.home.pocket.PocketRecommendationsHeaderViewHolder
import org.mozilla.fenix.home.pocket.PocketStoriesViewHolder
@ -30,7 +31,6 @@ import org.mozilla.fenix.home.recenttabs.view.RecentTabsHeaderViewHolder
import org.mozilla.fenix.home.recentvisits.view.RecentVisitsHeaderViewHolder
import org.mozilla.fenix.home.recentvisits.view.RecentlyVisitedViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CustomizeHomeButtonViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
@ -278,6 +278,11 @@ class SessionControlAdapter(
viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor
)
CollectionViewHolder.LAYOUT_ID -> return CollectionViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
interactor = interactor
)
}
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
@ -288,7 +293,6 @@ class SessionControlAdapter(
viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor
)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, interactor)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(
view as WidgetSiteItemView,
interactor
@ -335,6 +339,11 @@ class SessionControlAdapter(
// This View already listens and maps store updates. Avoid creating and binding new Views.
// The composition will live until the ViewTreeLifecycleOwner to which it's attached to is destroyed.
}
is CollectionViewHolder -> {
// Dispose the underlying composition immediately.
// This ViewHolder can be removed / re-added and we need it to show a fresh new composition.
holder.composeView.disposeComposition()
}
else -> super.onViewRecycled(holder)
}
}

@ -1,98 +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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_collection"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
android:layout_marginTop="12dp"
android:background="@drawable/card_list_row_background"
android:clickable="true"
android:clipToPadding="false"
android:elevation="@dimen/home_item_elevation"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/collection_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_tab_collection"
app:tint="@null" />
<TextView
android:id="@+id/collection_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:minLines="1"
android:textAppearance="@style/Header14TextStyle"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@id/collection_icon"
app:layout_constraintEnd_toStartOf="@id/chevron"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@+id/collection_icon"
app:layout_constraintTop_toTopOf="@id/collection_icon"
tools:text="@tools:sample/lorem/random" />
<ImageView
android:id="@+id/chevron"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
app:srcCompat="@drawable/ic_chevron"
android:contentDescription="@string/tab_menu"
app:layout_constraintBottom_toBottomOf="@id/collection_icon"
app:layout_constraintEnd_toStartOf="@+id/collection_share_button"
app:layout_constraintStart_toEndOf="@+id/collection_title"
app:layout_constraintTop_toTopOf="@id/collection_icon" />
<ImageButton
android:id="@+id/collection_share_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/share_button_content_description"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/collection_icon"
app:layout_constraintEnd_toStartOf="@id/collection_overflow_button"
app:layout_constraintTop_toTopOf="@id/collection_icon"
app:srcCompat="@drawable/ic_share"
tools:visibility="visible" />
<ImageButton
android:id="@+id/collection_overflow_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/collection_menu_button_content_description"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/collection_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/collection_icon"
app:srcCompat="@drawable/ic_menu"
tools:visibility="visible" />
<View
android:id="@+id/selected_border"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
android:background="@drawable/session_border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save