Fully migrate to AdapterDelegates and cleanup code
parent
53e36d23b1
commit
5ed4d0b6b7
@ -1,15 +1,9 @@
|
|||||||
language: android
|
language: android
|
||||||
dist: trusty
|
dist: trusty
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
android:
|
android:
|
||||||
components:
|
components:
|
||||||
- tools
|
|
||||||
- platform-tools-30.0.3
|
|
||||||
- build-tools-30.0.2
|
|
||||||
- android-30
|
- android-30
|
||||||
licenses:
|
- build-tools-30.0.2
|
||||||
- android-sdk-preview-license-.+
|
- platform-tools-30.0.3
|
||||||
- android-sdk-license-.+
|
- tools
|
||||||
- google-gdk-license-.+
|
|
||||||
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug
|
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