Show all favorites on categories screen
This commit is contained in:
@@ -82,19 +82,35 @@ abstract class FavouritesDao {
|
|||||||
)
|
)
|
||||||
abstract suspend fun findAllManga(categoryId: Int): List<MangaEntity>
|
abstract suspend fun findAllManga(categoryId: Int): List<MangaEntity>
|
||||||
|
|
||||||
suspend fun findCovers(categoryId: Long, order: ListSortOrder): List<Cover> {
|
suspend fun findCovers(categoryId: Long, order: ListSortOrder, limit: Int): List<Cover> {
|
||||||
val orderBy = getOrderBy(order)
|
val orderBy = getOrderBy(order)
|
||||||
|
|
||||||
@Language("RoomSql")
|
@Language("RoomSql")
|
||||||
val query = SimpleSQLiteQuery(
|
val query = SimpleSQLiteQuery(
|
||||||
"SELECT manga.cover_url AS url, manga.source AS source FROM favourites " +
|
"SELECT manga.cover_url AS url, manga.source AS source FROM favourites " +
|
||||||
"LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
|
"LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
|
||||||
"WHERE favourites.category_id = ? AND deleted_at = 0 ORDER BY $orderBy",
|
"WHERE favourites.category_id = ? AND deleted_at = 0 ORDER BY $orderBy LIMIT ?",
|
||||||
arrayOf<Any>(categoryId),
|
arrayOf<Any>(categoryId, limit),
|
||||||
)
|
)
|
||||||
return findCoversImpl(query)
|
return findCoversImpl(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun findCovers(order: ListSortOrder, limit: Int): List<Cover> {
|
||||||
|
val orderBy = getOrderBy(order)
|
||||||
|
|
||||||
|
@Language("RoomSql")
|
||||||
|
val query = SimpleSQLiteQuery(
|
||||||
|
"SELECT manga.cover_url AS url, manga.source AS source FROM favourites " +
|
||||||
|
"LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
|
||||||
|
"WHERE deleted_at = 0 GROUP BY manga.manga_id ORDER BY $orderBy LIMIT ?",
|
||||||
|
arrayOf<Any>(limit),
|
||||||
|
)
|
||||||
|
return findCoversImpl(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(DISTINCT manga_id) FROM favourites WHERE deleted_at = 0")
|
||||||
|
abstract fun observeMangaCount(): Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM favourites WHERE deleted_at = 0)")
|
@Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM favourites WHERE deleted_at = 0)")
|
||||||
abstract suspend fun findAllManga(): List<MangaEntity>
|
abstract suspend fun findAllManga(): List<MangaEntity>
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ class FavouritesRepository @Inject constructor(
|
|||||||
.flatMapLatest { order -> observeAll(categoryId, order) }
|
.flatMapLatest { order -> observeAll(categoryId, order) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun observeMangaCount(): Flow<Int> {
|
||||||
|
return db.getFavouritesDao().observeMangaCount()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
}
|
||||||
|
|
||||||
fun observeCategories(): Flow<List<FavouriteCategory>> {
|
fun observeCategories(): Flow<List<FavouriteCategory>> {
|
||||||
return db.getFavouriteCategoriesDao().observeAll().mapItems {
|
return db.getFavouriteCategoriesDao().observeAll().mapItems {
|
||||||
it.toFavouriteCategory()
|
it.toFavouriteCategory()
|
||||||
@@ -72,7 +77,7 @@ class FavouritesRepository @Inject constructor(
|
|||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCategoriesWithCovers(): Flow<Map<FavouriteCategory, List<Cover>>> {
|
fun observeCategoriesWithCovers(coversLimit: Int): Flow<Map<FavouriteCategory, List<Cover>>> {
|
||||||
return db.getFavouriteCategoriesDao().observeAll()
|
return db.getFavouriteCategoriesDao().observeAll()
|
||||||
.map {
|
.map {
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
@@ -82,6 +87,7 @@ class FavouritesRepository @Inject constructor(
|
|||||||
res[cat] = db.getFavouritesDao().findCovers(
|
res[cat] = db.getFavouritesDao().findCovers(
|
||||||
categoryId = cat.id,
|
categoryId = cat.id,
|
||||||
order = cat.order,
|
order = cat.order,
|
||||||
|
limit = coversLimit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
@@ -89,6 +95,10 @@ class FavouritesRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getAllFavoritesCovers(order: ListSortOrder, limit: Int): List<Cover> {
|
||||||
|
return db.getFavouritesDao().findCovers(order, limit)
|
||||||
|
}
|
||||||
|
|
||||||
fun observeCategory(id: Long): Flow<FavouriteCategory?> {
|
fun observeCategory(id: Long): Flow<FavouriteCategory?> {
|
||||||
return db.getFavouriteCategoriesDao().observe(id)
|
return db.getFavouriteCategoriesDao().observe(id)
|
||||||
.map { it?.toFavouriteCategory() }
|
.map { it?.toFavouriteCategory() }
|
||||||
|
|||||||
@@ -76,7 +76,13 @@ class FavouriteCategoriesActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(item: FavouriteCategory, view: View) {
|
override fun onItemClick(item: FavouriteCategory?, view: View) {
|
||||||
|
if (item == null) {
|
||||||
|
if (selectionController.count == 0) {
|
||||||
|
startActivity(FavouritesActivity.newIntent(view.context))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (selectionController.onItemClick(item.id)) {
|
if (selectionController.onItemClick(item.id)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -92,8 +98,12 @@ class FavouriteCategoriesActivity :
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean {
|
override fun onItemLongClick(item: FavouriteCategory?, view: View): Boolean {
|
||||||
return selectionController.onItemLongClick(item.id)
|
return item != null && selectionController.onItemLongClick(item.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShowAllClick(isChecked: Boolean) {
|
||||||
|
viewModel.setAllCategoriesVisible(isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean {
|
override fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean {
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||||
|
|
||||||
interface FavouriteCategoriesListListener : OnListItemClickListener<FavouriteCategory> {
|
interface FavouriteCategoriesListListener : OnListItemClickListener<FavouriteCategory?> {
|
||||||
|
|
||||||
fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean
|
fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean
|
||||||
|
|
||||||
fun onEditClick(item: FavouriteCategory, view: View)
|
fun onEditClick(item: FavouriteCategory, view: View)
|
||||||
|
|
||||||
|
fun onShowAllClick(isChecked: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,21 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.util.ext.requireValue
|
import org.koitharu.kotatsu.core.util.ext.requireValue
|
||||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||||
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
||||||
|
import org.koitharu.kotatsu.favourites.ui.categories.adapter.AllCategoriesListModel
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
@@ -30,9 +34,13 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
private var commitJob: Job? = null
|
private var commitJob: Job? = null
|
||||||
|
|
||||||
val content = repository.observeCategoriesWithCovers()
|
val content = combine(
|
||||||
.map { it.toUiList() }
|
repository.observeCategoriesWithCovers(coversLimit = 3),
|
||||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
observeAllCategories(),
|
||||||
|
settings.observeAsFlow(AppSettings.KEY_ALL_FAVOURITES_VISIBLE) { isAllFavouritesVisible },
|
||||||
|
) { cats, all, showAll ->
|
||||||
|
cats.toUiList(all, showAll)
|
||||||
|
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
||||||
|
|
||||||
fun deleteCategories(ids: Set<Long>) {
|
fun deleteCategories(ids: Set<Long>) {
|
||||||
launchJob(Dispatchers.Default) {
|
launchJob(Dispatchers.Default) {
|
||||||
@@ -74,21 +82,46 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Map<FavouriteCategory, List<Cover>>.toUiList(): List<ListModel> = map { (category, covers) ->
|
private fun Map<FavouriteCategory, List<Cover>>.toUiList(
|
||||||
CategoryListModel(
|
allFavorites: Pair<Int, List<Cover>>,
|
||||||
mangaCount = covers.size,
|
showAll: Boolean
|
||||||
covers = covers.take(3),
|
): List<ListModel> {
|
||||||
category = category,
|
if (isEmpty()) {
|
||||||
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
return listOf(
|
||||||
)
|
EmptyState(
|
||||||
}.ifEmpty {
|
icon = R.drawable.ic_empty_favourites,
|
||||||
listOf(
|
textPrimary = R.string.text_empty_holder_primary,
|
||||||
EmptyState(
|
textSecondary = R.string.empty_favourite_categories,
|
||||||
icon = R.drawable.ic_empty_favourites,
|
actionStringRes = 0,
|
||||||
textPrimary = R.string.text_empty_holder_primary,
|
),
|
||||||
textSecondary = R.string.empty_favourite_categories,
|
)
|
||||||
actionStringRes = 0,
|
}
|
||||||
|
val result = ArrayList<ListModel>(size + 1)
|
||||||
|
result.add(
|
||||||
|
AllCategoriesListModel(
|
||||||
|
mangaCount = allFavorites.first,
|
||||||
|
covers = allFavorites.second,
|
||||||
|
isVisible = showAll,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
mapTo(result) { (category, covers) ->
|
||||||
|
CategoryListModel(
|
||||||
|
mangaCount = covers.size,
|
||||||
|
covers = covers.take(3),
|
||||||
|
category = category,
|
||||||
|
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeAllCategories(): Flow<Pair<Int, List<Cover>>> {
|
||||||
|
return settings.observeAsFlow(AppSettings.KEY_FAVORITES_ORDER) {
|
||||||
|
allFavoritesSortOrder
|
||||||
|
}.mapLatest { order ->
|
||||||
|
repository.getAllFavoritesCovers(order, limit = 3)
|
||||||
|
}.combine(repository.observeMangaCount()) { covers, count ->
|
||||||
|
count to covers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
|
data class AllCategoriesListModel(
|
||||||
|
val mangaCount: Int,
|
||||||
|
val covers: List<Cover>,
|
||||||
|
val isVisible: Boolean,
|
||||||
|
) : ListModel {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||||
|
return other is AllCategoriesListModel
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ class CategoriesAdapter(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
addDelegate(ListItemType.CATEGORY_LARGE, categoryAD(coil, lifecycleOwner, onItemClickListener))
|
addDelegate(ListItemType.CATEGORY_LARGE, categoryAD(coil, lifecycleOwner, onItemClickListener))
|
||||||
|
addDelegate(ListItemType.NAV_ITEM, allCategoriesAD(coil, lifecycleOwner, onItemClickListener))
|
||||||
addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listListener))
|
addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listListener))
|
||||||
addDelegate(ListItemType.STATE_LOADING, loadingStateAD())
|
addDelegate(ListItemType.STATE_LOADING, loadingStateAD())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
|||||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||||
import org.koitharu.kotatsu.core.util.ext.source
|
import org.koitharu.kotatsu.core.util.ext.source
|
||||||
|
import org.koitharu.kotatsu.databinding.ItemCategoriesAllBinding
|
||||||
import org.koitharu.kotatsu.databinding.ItemCategoryBinding
|
import org.koitharu.kotatsu.databinding.ItemCategoryBinding
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener
|
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
@@ -92,3 +93,68 @@ fun categoryAD(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allCategoriesAD(
|
||||||
|
coil: ImageLoader,
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
clickListener: FavouriteCategoriesListListener,
|
||||||
|
) = adapterDelegateViewBinding<AllCategoriesListModel, ListModel, ItemCategoriesAllBinding>(
|
||||||
|
{ inflater, parent -> ItemCategoriesAllBinding.inflate(inflater, parent, false) },
|
||||||
|
) {
|
||||||
|
val eventListener = OnClickListener { v ->
|
||||||
|
if (v.id == R.id.imageView_visible) {
|
||||||
|
clickListener.onShowAllClick(!item.isVisible)
|
||||||
|
} else {
|
||||||
|
clickListener.onItemClick(null, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val backgroundColor = context.getThemeColor(android.R.attr.colorBackground)
|
||||||
|
ImageViewCompat.setImageTintList(
|
||||||
|
binding.imageViewCover3,
|
||||||
|
ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153)),
|
||||||
|
)
|
||||||
|
ImageViewCompat.setImageTintList(
|
||||||
|
binding.imageViewCover2,
|
||||||
|
ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76)),
|
||||||
|
)
|
||||||
|
binding.imageViewCover2.backgroundTintList =
|
||||||
|
ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76))
|
||||||
|
binding.imageViewCover3.backgroundTintList =
|
||||||
|
ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153))
|
||||||
|
val fallback = ColorDrawable(Color.TRANSPARENT)
|
||||||
|
val coverViews = arrayOf(binding.imageViewCover1, binding.imageViewCover2, binding.imageViewCover3)
|
||||||
|
val crossFadeDuration = context.getAnimationDuration(R.integer.config_defaultAnimTime).toInt()
|
||||||
|
itemView.setOnClickListener(eventListener)
|
||||||
|
binding.imageViewVisible.setOnClickListener(eventListener)
|
||||||
|
|
||||||
|
bind {
|
||||||
|
binding.textViewSubtitle.text = if (item.mangaCount == 0) {
|
||||||
|
getString(R.string.empty)
|
||||||
|
} else {
|
||||||
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.items,
|
||||||
|
item.mangaCount,
|
||||||
|
item.mangaCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
binding.imageViewVisible.setImageResource(
|
||||||
|
if (item.isVisible) {
|
||||||
|
R.drawable.ic_eye
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_eye_off
|
||||||
|
},
|
||||||
|
)
|
||||||
|
repeat(coverViews.size) { i ->
|
||||||
|
val cover = item.covers.getOrNull(i)
|
||||||
|
coverViews[i].newImageRequest(lifecycleOwner, cover?.url)?.run {
|
||||||
|
placeholder(R.drawable.ic_placeholder)
|
||||||
|
fallback(fallback)
|
||||||
|
source(cover?.mangaSource)
|
||||||
|
crossfade(crossFadeDuration * (i + 1))
|
||||||
|
error(R.drawable.ic_error_placeholder)
|
||||||
|
allowRgb565(true)
|
||||||
|
enqueueWith(coil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
111
app/src/main/res/layout/item_categories_all.xml
Normal file
111
app/src/main/res/layout/item_categories_all.xml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/list_selector"
|
||||||
|
android:minHeight="98dp"
|
||||||
|
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||||
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/imageView_cover3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:backgroundTintMode="src_atop"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="W,13:18"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||||
|
app:tintMode="src_atop"
|
||||||
|
tools:backgroundTint="#99FFFFFF"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic"
|
||||||
|
tools:tint="#99FFFFFF" />
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/imageView_cover2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:backgroundTintMode="src_atop"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="W,13:18"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||||
|
app:tintMode="src_atop"
|
||||||
|
tools:backgroundTint="#4DFFFFFF"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic"
|
||||||
|
tools:tint="#4DFFFFFF" />
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/imageView_cover1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:backgroundTintMode="src_atop"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="W,13:18"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_normal"
|
||||||
|
android:layout_marginEnd="?listPreferredItemPaddingEnd"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/all_favourites"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textView_subtitle"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/imageView_visible"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover3"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_normal"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/imageView_visible"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover3"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
app:layout_constraintWidth_default="wrap"
|
||||||
|
tools:text="@tools:sample/lorem[1]" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView_visible"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/show_all"
|
||||||
|
android:padding="@dimen/margin_normal"
|
||||||
|
android:src="@drawable/ic_eye"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user