Fully migrate to AdapterDelegates and cleanup code
parent
53e36d23b1
commit
5ed4d0b6b7
@ -1,15 +1,9 @@
|
||||
language: android
|
||||
dist: trusty
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools-30.0.3
|
||||
- build-tools-30.0.2
|
||||
- android-30
|
||||
licenses:
|
||||
- android-sdk-preview-license-.+
|
||||
- android-sdk-license-.+
|
||||
- google-gdk-license-.+
|
||||
- build-tools-30.0.2
|
||||
- platform-tools-30.0.3
|
||||
- tools
|
||||
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug
|
||||
@ -1,110 +0,0 @@
|
||||
package org.koitharu.kotatsu.base.ui.list
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koitharu.kotatsu.utils.ext.replaceWith
|
||||
|
||||
@Deprecated("", replaceWith = ReplaceWith("AsyncListDifferDelegationAdapter"))
|
||||
abstract class BaseRecyclerAdapter<T, E>(private val onItemClickListener: OnRecyclerItemClickListener<T>? = null) :
|
||||
RecyclerView.Adapter<BaseViewHolder<T, E>>(),
|
||||
KoinComponent {
|
||||
|
||||
protected val dataSet = ArrayList<T>() //TODO make private
|
||||
|
||||
val items get() = dataSet as List<T>
|
||||
|
||||
val hasItems get() = dataSet.isNotEmpty()
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BaseViewHolder<T, E>, position: Int) {
|
||||
val item = dataSet[position]
|
||||
holder.bind(item, getExtra(item, position))
|
||||
}
|
||||
|
||||
fun getItem(position: Int) = dataSet[position]
|
||||
|
||||
override fun getItemId(position: Int) = onGetItemId(dataSet[position])
|
||||
|
||||
protected fun findItemById(id: Long) = dataSet.find { x -> onGetItemId(x) == id }
|
||||
|
||||
protected fun findItemPositionById(id: Long) =
|
||||
dataSet.indexOfFirst { x -> onGetItemId(x) == id }
|
||||
|
||||
fun replaceData(newData: List<T>) {
|
||||
val updater = AdapterUpdater(dataSet, newData, this::onGetItemId)
|
||||
dataSet.replaceWith(newData)
|
||||
updater(this)
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun appendData(newData: List<T>) {
|
||||
val pos = dataSet.size
|
||||
dataSet.addAll(newData)
|
||||
notifyItemRangeInserted(pos, newData.size)
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun prependData(newData: List<T>) {
|
||||
dataSet.addAll(0, newData)
|
||||
notifyItemRangeInserted(0, newData.size)
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun appendItem(newItem: T) {
|
||||
dataSet.add(newItem)
|
||||
notifyItemInserted(dataSet.lastIndex)
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun removeItem(item: T) {
|
||||
removeItemAt(dataSet.indexOf(item))
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun removeItemAt(position: Int) {
|
||||
if (position in dataSet.indices) {
|
||||
dataSet.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun clearData() {
|
||||
dataSet.clear()
|
||||
notifyDataSetChanged()
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: BaseViewHolder<T, E>) {
|
||||
holder.onRecycled()
|
||||
}
|
||||
|
||||
final override fun getItemCount() = dataSet.size
|
||||
|
||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<T, E> {
|
||||
return onCreateViewHolder(parent)
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: BaseViewHolder<T, E>) {
|
||||
holder.setOnItemClickListener(null)
|
||||
super.onViewDetachedFromWindow(holder)
|
||||
}
|
||||
|
||||
override fun onViewAttachedToWindow(holder: BaseViewHolder<T, E>) {
|
||||
super.onViewAttachedToWindow(holder)
|
||||
holder.setOnItemClickListener(onItemClickListener)
|
||||
}
|
||||
|
||||
protected open fun onDataSetChanged() = Unit
|
||||
|
||||
protected abstract fun getExtra(item: T, position: Int): E
|
||||
|
||||
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<T, E>
|
||||
|
||||
protected abstract fun onGetItemId(item: T): Long
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package org.koitharu.kotatsu.base.ui.list
|
||||
|
||||
import android.view.View
|
||||
|
||||
interface OnRecyclerItemClickListener<I> {
|
||||
|
||||
fun onItemClick(item: I, position: Int, view: View)
|
||||
|
||||
fun onItemLongClick(item: I, position: Int, view: View) = false
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package org.koitharu.kotatsu.base.ui.list
|
||||
|
||||
import android.view.ViewGroup
|
||||
|
||||
class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
|
||||
|
||||
var isProgressVisible: Boolean
|
||||
get() = dataSet.isNotEmpty()
|
||||
set(value) {
|
||||
if (value == dataSet.isEmpty()) {
|
||||
if (value) {
|
||||
appendItem(true)
|
||||
} else {
|
||||
removeItemAt(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExtra(item: Boolean, position: Int) = Unit
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent)
|
||||
|
||||
override fun onGetItemId(item: Boolean) = -1L
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Checkable
|
||||
import androidx.collection.ArraySet
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
class CategoriesSelectAdapter(private val listener: OnCategoryCheckListener) :
|
||||
BaseRecyclerAdapter<FavouriteCategory, Boolean>() {
|
||||
|
||||
private val checkedIds = ArraySet<Long>()
|
||||
|
||||
fun setCheckedIds(ids: Iterable<Long>) {
|
||||
checkedIds.clear()
|
||||
checkedIds.addAll(ids)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getExtra(item: FavouriteCategory, position: Int) = item.id in checkedIds
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) =
|
||||
CategoryCheckableHolder(
|
||||
parent
|
||||
)
|
||||
|
||||
override fun onGetItemId(item: FavouriteCategory) = item.id
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: BaseViewHolder<FavouriteCategory, Boolean>) {
|
||||
holder.itemView.setOnClickListener(null)
|
||||
}
|
||||
|
||||
override fun onViewAttachedToWindow(holder: BaseViewHolder<FavouriteCategory, Boolean>) {
|
||||
holder.itemView.setOnClickListener {
|
||||
if (it !is Checkable) return@setOnClickListener
|
||||
it.toggle()
|
||||
if (it.isChecked) {
|
||||
listener.onCategoryChecked(holder.requireData())
|
||||
} else {
|
||||
listener.onCategoryUnchecked(holder.requireData())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select
|
||||
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.synthetic.main.item_category_checkable.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
class CategoryCheckableHolder(parent: ViewGroup) :
|
||||
BaseViewHolder<FavouriteCategory, Boolean>(parent, R.layout.item_category_checkable) {
|
||||
|
||||
override fun onBind(data: FavouriteCategory, extra: Boolean) {
|
||||
checkedTextView.text = data.title
|
||||
checkedTextView.isChecked = extra
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select
|
||||
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
|
||||
|
||||
class MangaCategoriesViewModel(
|
||||
private val manga: Manga,
|
||||
private val favouritesRepository: FavouritesRepository
|
||||
) : BaseViewModel() {
|
||||
|
||||
val content = combine(
|
||||
favouritesRepository.observeCategories(),
|
||||
favouritesRepository.observeCategoriesIds(manga.id)
|
||||
) { all, checked ->
|
||||
all.map {
|
||||
MangaCategoryItem(
|
||||
id = it.id,
|
||||
name = it.title,
|
||||
isChecked = it.id in checked
|
||||
)
|
||||
}
|
||||
}.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||
|
||||
fun setChecked(categoryId: Long, isChecked: Boolean) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
if (isChecked) {
|
||||
favouritesRepository.addToCategory(manga, categoryId)
|
||||
} else {
|
||||
favouritesRepository.removeFromCategory(manga, categoryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createCategory(name: String) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
favouritesRepository.addCategory(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select
|
||||
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
interface OnCategoryCheckListener {
|
||||
|
||||
fun onCategoryChecked(category: FavouriteCategory)
|
||||
|
||||
fun onCategoryUnchecked(category: FavouriteCategory)
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_category_checkable.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
|
||||
|
||||
fun mangaCategoryAD(
|
||||
clickListener: OnListItemClickListener<MangaCategoryItem>
|
||||
) = adapterDelegateLayoutContainer<MangaCategoryItem, MangaCategoryItem>(
|
||||
R.layout.item_category_checkable
|
||||
) {
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickListener.onItemClick(item, itemView)
|
||||
}
|
||||
|
||||
bind {
|
||||
checkedTextView.text = item.name
|
||||
checkedTextView.isChecked = item.isChecked
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
|
||||
|
||||
class MangaCategoriesAdapter(
|
||||
clickListener: OnListItemClickListener<MangaCategoryItem>
|
||||
) : AsyncListDifferDelegationAdapter<MangaCategoryItem>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(mangaCategoryAD(clickListener))
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<MangaCategoryItem>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: MangaCategoryItem,
|
||||
newItem: MangaCategoryItem
|
||||
): Boolean = oldItem.id == newItem.id
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: MangaCategoryItem,
|
||||
newItem: MangaCategoryItem
|
||||
): Boolean = oldItem == newItem
|
||||
|
||||
override fun getChangePayload(
|
||||
oldItem: MangaCategoryItem,
|
||||
newItem: MangaCategoryItem
|
||||
): Any? {
|
||||
if (oldItem.isChecked != newItem.isChecked) {
|
||||
return newItem.isChecked
|
||||
}
|
||||
return super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.select.model
|
||||
|
||||
data class MangaCategoryItem(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val isChecked: Boolean
|
||||
)
|
||||
@ -1,34 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
||||
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.DisposableHandle
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.koin.core.component.inject
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnRecyclerItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.local.data.PagesCache
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class PagesThumbnailsAdapter(onItemClickListener: OnRecyclerItemClickListener<MangaPage>?) :
|
||||
BaseRecyclerAdapter<MangaPage, PagesCache>(onItemClickListener), CoroutineScope,
|
||||
DisposableHandle {
|
||||
|
||||
private val job = SupervisorJob()
|
||||
private val cache by inject<PagesCache>()
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.Main.immediate + job
|
||||
|
||||
override fun dispose() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
override fun getExtra(item: MangaPage, position: Int) = cache
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = PageThumbnailHolder(parent, this)
|
||||
|
||||
override fun onGetItemId(item: MangaPage) = item.id
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package org.koitharu.kotatsu.reader.ui.thumbnails.adapter
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import coil.ImageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.PixelSize
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_page_thumb.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.local.data.PagesCache
|
||||
import org.koitharu.kotatsu.utils.ext.IgnoreErrors
|
||||
|
||||
fun pageThumbnailAD(
|
||||
coil: ImageLoader,
|
||||
scope: CoroutineScope,
|
||||
cache: PagesCache,
|
||||
clickListener: OnListItemClickListener<MangaPage>
|
||||
) = adapterDelegateLayoutContainer<MangaPage, MangaPage>(R.layout.item_page_thumb) {
|
||||
|
||||
var job: Job? = null
|
||||
val gridWidth = itemView.context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width)
|
||||
val thumbSize = PixelSize(
|
||||
width = gridWidth,
|
||||
height = (gridWidth * 13f / 18f).toInt()
|
||||
)
|
||||
|
||||
handle.setOnClickListener {
|
||||
clickListener.onItemClick(item, itemView)
|
||||
}
|
||||
|
||||
bind {
|
||||
job?.cancel()
|
||||
imageView_thumb.setImageDrawable(null)
|
||||
textView_number.text = (bindingAdapterPosition + 1).toString()
|
||||
job = scope.launch(Dispatchers.Default + IgnoreErrors) {
|
||||
val url = item.preview ?: item.url.let {
|
||||
val pageUrl = item.source.repository.getPageFullUrl(item)
|
||||
cache[pageUrl]?.toUri()?.toString() ?: pageUrl
|
||||
}
|
||||
val drawable = coil.execute(
|
||||
ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.size(thumbSize)
|
||||
.build()
|
||||
).drawable
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView_thumb.setImageDrawable(drawable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
job?.cancel()
|
||||
imageView_thumb.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.reader.ui.thumbnails.adapter
|
||||
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.local.data.PagesCache
|
||||
|
||||
class PageThumbnailAdapter(
|
||||
coil: ImageLoader,
|
||||
scope: CoroutineScope,
|
||||
cache: PagesCache,
|
||||
clickListener: OnListItemClickListener<MangaPage>
|
||||
) : ListDelegationAdapter<List<MangaPage>>() {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(pageThumbnailAD(coil, scope, cache, clickListener))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package org.koitharu.kotatsu.widget
|
||||
|
||||
import org.koin.android.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.widget.shelf.ShelfConfigViewModel
|
||||
|
||||
val appWidgetModule
|
||||
get() = module {
|
||||
viewModel { ShelfConfigViewModel(get()) }
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package org.koitharu.kotatsu.widget.shelf
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnRecyclerItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
class CategorySelectAdapter(onItemClickListener: OnRecyclerItemClickListener<FavouriteCategory>? = null) :
|
||||
BaseRecyclerAdapter<FavouriteCategory, Boolean>(onItemClickListener) {
|
||||
|
||||
var checkedItemId = 0L
|
||||
private set
|
||||
|
||||
fun setCheckedId(id: Long) {
|
||||
val oldId = checkedItemId
|
||||
checkedItemId = id
|
||||
val oldPos = findItemPositionById(oldId)
|
||||
val newPos = findItemPositionById(id)
|
||||
if (newPos != -1) {
|
||||
notifyItemChanged(newPos)
|
||||
}
|
||||
if (oldPos != -1) {
|
||||
notifyItemChanged(oldPos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExtra(item: FavouriteCategory, position: Int) =
|
||||
checkedItemId == item.id
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = CategorySelectHolder(
|
||||
parent
|
||||
)
|
||||
|
||||
override fun onGetItemId(item: FavouriteCategory) = item.id
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package org.koitharu.kotatsu.widget.shelf
|
||||
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.synthetic.main.item_category_checkable.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
class CategorySelectHolder(parent: ViewGroup) :
|
||||
BaseViewHolder<FavouriteCategory, Boolean>(parent, R.layout.item_category_checkable_single) {
|
||||
|
||||
override fun onBind(data: FavouriteCategory, extra: Boolean) {
|
||||
checkedTextView.text = data.title
|
||||
checkedTextView.isChecked = extra
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package org.koitharu.kotatsu.widget.shelf
|
||||
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.widget.shelf.model.CategoryItem
|
||||
import java.util.*
|
||||
|
||||
class ShelfConfigViewModel(
|
||||
favouritesRepository: FavouritesRepository
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val selectedCategoryId = MutableStateFlow(0L)
|
||||
|
||||
val content = combine(
|
||||
favouritesRepository.observeCategories(),
|
||||
selectedCategoryId
|
||||
) { categories, selectedId ->
|
||||
val list = ArrayList<CategoryItem>(categories.size + 1)
|
||||
list += CategoryItem(0L, null, selectedId == 0L)
|
||||
categories.mapTo(list) {
|
||||
CategoryItem(it.id, it.title, selectedId == it.id)
|
||||
}
|
||||
list
|
||||
}.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||
|
||||
var checkedId: Long by selectedCategoryId::value
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package org.koitharu.kotatsu.widget.shelf.adapter
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.widget.shelf.model.CategoryItem
|
||||
|
||||
class CategorySelectAdapter(
|
||||
clickListener: OnListItemClickListener<CategoryItem>
|
||||
) : AsyncListDifferDelegationAdapter<CategoryItem>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(categorySelectItemAD(clickListener))
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<CategoryItem>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: CategoryItem, newItem: CategoryItem): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: CategoryItem, newItem: CategoryItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: CategoryItem, newItem: CategoryItem): Any? {
|
||||
if (oldItem.isSelected != newItem.isSelected) {
|
||||
return newItem.isSelected
|
||||
}
|
||||
return super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package org.koitharu.kotatsu.widget.shelf.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_category_checkable.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.widget.shelf.model.CategoryItem
|
||||
|
||||
fun categorySelectItemAD(
|
||||
clickListener: OnListItemClickListener<CategoryItem>
|
||||
) = adapterDelegateLayoutContainer<CategoryItem, CategoryItem>(
|
||||
R.layout.item_category_checkable_single
|
||||
) {
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickListener.onItemClick(item, it)
|
||||
}
|
||||
|
||||
bind {
|
||||
checkedTextView.text = item.name ?: getString(R.string.all_favourites)
|
||||
checkedTextView.isChecked = item.isSelected
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package org.koitharu.kotatsu.widget.shelf.model
|
||||
|
||||
data class CategoryItem(
|
||||
val id: Long,
|
||||
val name: String?,
|
||||
val isSelected: Boolean
|
||||
)
|
||||
Loading…
Reference in New Issue