Multiple selection in library
This commit is contained in:
@@ -27,11 +27,14 @@ class ListSelectionController(
|
|||||||
) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider {
|
) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider {
|
||||||
|
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
private val stateEventObserver = StateEventObserver()
|
|
||||||
|
|
||||||
val count: Int
|
val count: Int
|
||||||
get() = decoration.checkedItemsCount
|
get() = decoration.checkedItemsCount
|
||||||
|
|
||||||
|
init {
|
||||||
|
registryOwner.lifecycle.addObserver(StateEventObserver())
|
||||||
|
}
|
||||||
|
|
||||||
fun snapshot(): Set<Long> {
|
fun snapshot(): Set<Long> {
|
||||||
return peekCheckedIds().toSet()
|
return peekCheckedIds().toSet()
|
||||||
}
|
}
|
||||||
@@ -55,7 +58,6 @@ class ListSelectionController(
|
|||||||
|
|
||||||
fun attachToRecyclerView(recyclerView: RecyclerView) {
|
fun attachToRecyclerView(recyclerView: RecyclerView) {
|
||||||
recyclerView.addItemDecoration(decoration)
|
recyclerView.addItemDecoration(decoration)
|
||||||
registryOwner.lifecycle.addObserver(stateEventObserver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveState(): Bundle {
|
override fun saveState(): Bundle {
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
package org.koitharu.kotatsu.base.ui.list
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.ArrayMap
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.savedstate.SavedStateRegistry
|
||||||
|
import androidx.savedstate.SavedStateRegistryOwner
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
private const val PROVIDER_NAME = "selection_decoration_sectioned"
|
||||||
|
|
||||||
|
class SectionedSelectionController<T : Any>(
|
||||||
|
private val activity: Activity,
|
||||||
|
private val registryOwner: SavedStateRegistryOwner,
|
||||||
|
private val callback: Callback<T>,
|
||||||
|
) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider {
|
||||||
|
|
||||||
|
private var actionMode: ActionMode? = null
|
||||||
|
|
||||||
|
private var pendingData: MutableMap<String, Collection<Long>>? = null
|
||||||
|
private val decorations = ArrayMap<T, AbstractSelectionItemDecoration>()
|
||||||
|
|
||||||
|
val count: Int
|
||||||
|
get() = decorations.values.sumOf { it.checkedItemsCount }
|
||||||
|
|
||||||
|
init {
|
||||||
|
registryOwner.lifecycle.addObserver(StateEventObserver())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun snapshot(): Map<T, Set<Long>> {
|
||||||
|
return decorations.mapValues { it.value.checkedItemsIds.toSet() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun peekCheckedIds(): Map<T, Set<Long>> {
|
||||||
|
return decorations.mapValues { it.value.checkedItemsIds }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
decorations.values.forEach {
|
||||||
|
it.clearSelection()
|
||||||
|
}
|
||||||
|
notifySelectionChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attachToRecyclerView(section: T, recyclerView: RecyclerView) {
|
||||||
|
val decoration = getDecoration(section)
|
||||||
|
val pendingIds = pendingData?.remove(section.toString())
|
||||||
|
if (!pendingIds.isNullOrEmpty()) {
|
||||||
|
decoration.checkAll(pendingIds)
|
||||||
|
startActionMode()
|
||||||
|
notifySelectionChanged()
|
||||||
|
}
|
||||||
|
recyclerView.addItemDecoration(decoration)
|
||||||
|
if (pendingData?.isEmpty() == true) {
|
||||||
|
pendingData = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveState(): Bundle {
|
||||||
|
val bundle = Bundle(decorations.size)
|
||||||
|
for ((k, v) in decorations) {
|
||||||
|
bundle.putLongArray(k.toString(), v.checkedItemsIds.toLongArray())
|
||||||
|
}
|
||||||
|
return bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onItemClick(section: T, id: Long): Boolean {
|
||||||
|
val decoration = getDecoration(section)
|
||||||
|
if (isInSelectionMode()) {
|
||||||
|
decoration.toggleItemChecked(id)
|
||||||
|
if (isInSelectionMode()) {
|
||||||
|
actionMode?.invalidate()
|
||||||
|
} else {
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
notifySelectionChanged()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onItemLongClick(section: T, id: Long): Boolean {
|
||||||
|
val decoration = getDecoration(section)
|
||||||
|
startActionMode()
|
||||||
|
return actionMode?.also {
|
||||||
|
decoration.setItemIsChecked(id, true)
|
||||||
|
notifySelectionChanged()
|
||||||
|
} != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
return callback.onCreateActionMode(mode, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
return callback.onPrepareActionMode(mode, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||||
|
return callback.onActionItemClicked(mode, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode) {
|
||||||
|
callback.onDestroyActionMode(mode)
|
||||||
|
clear()
|
||||||
|
actionMode = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startActionMode() {
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInSelectionMode(): Boolean {
|
||||||
|
return decorations.values.any { x -> x.checkedItemsCount > 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifySelectionChanged() {
|
||||||
|
val count = this.count
|
||||||
|
callback.onSelectionChanged(count)
|
||||||
|
if (count == 0) {
|
||||||
|
actionMode?.finish()
|
||||||
|
} else {
|
||||||
|
actionMode?.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restoreState(ids: MutableMap<String, Collection<Long>>) {
|
||||||
|
if (ids.isEmpty() || isInSelectionMode()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for ((k, v) in decorations) {
|
||||||
|
val items = ids.remove(k.toString())
|
||||||
|
if (!items.isNullOrEmpty()) {
|
||||||
|
v.checkAll(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pendingData = ids
|
||||||
|
if (isInSelectionMode()) {
|
||||||
|
startActionMode()
|
||||||
|
notifySelectionChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDecoration(section: T): AbstractSelectionItemDecoration {
|
||||||
|
return decorations.getOrPut(section) {
|
||||||
|
callback.onCreateItemDecoration(section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback<T> : ListSelectionController.Callback {
|
||||||
|
|
||||||
|
fun onCreateItemDecoration(section: T): AbstractSelectionItemDecoration
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class StateEventObserver : LifecycleEventObserver {
|
||||||
|
|
||||||
|
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||||
|
if (event == Lifecycle.Event.ON_CREATE) {
|
||||||
|
val registry = registryOwner.savedStateRegistry
|
||||||
|
registry.registerSavedStateProvider(PROVIDER_NAME, this@SectionedSelectionController)
|
||||||
|
val state = registry.consumeRestoredStateForKey(PROVIDER_NAME)
|
||||||
|
if (state != null) {
|
||||||
|
Dispatchers.Main.dispatch(EmptyCoroutineContext) { // == Handler.post
|
||||||
|
if (source.lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
|
||||||
|
restoreState(
|
||||||
|
state.keySet().associateWithTo(HashMap()) { state.getLongArray(it)?.toList().orEmpty() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ sealed class DateTimeAgo : ListModel {
|
|||||||
override fun format(resources: Resources): String {
|
override fun format(resources: Resources): String {
|
||||||
return resources.getString(R.string.just_now)
|
return resources.getString(R.string.just_now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "just_now"
|
||||||
}
|
}
|
||||||
|
|
||||||
class MinutesAgo(val minutes: Int) : DateTimeAgo() {
|
class MinutesAgo(val minutes: Int) : DateTimeAgo() {
|
||||||
@@ -31,6 +33,8 @@ sealed class DateTimeAgo : ListModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int = minutes
|
override fun hashCode(): Int = minutes
|
||||||
|
|
||||||
|
override fun toString() = "minutes_ago_$minutes"
|
||||||
}
|
}
|
||||||
|
|
||||||
class HoursAgo(val hours: Int) : DateTimeAgo() {
|
class HoursAgo(val hours: Int) : DateTimeAgo() {
|
||||||
@@ -46,18 +50,24 @@ sealed class DateTimeAgo : ListModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int = hours
|
override fun hashCode(): Int = hours
|
||||||
|
|
||||||
|
override fun toString() = "hours_ago_$hours"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Today : DateTimeAgo() {
|
object Today : DateTimeAgo() {
|
||||||
override fun format(resources: Resources): String {
|
override fun format(resources: Resources): String {
|
||||||
return resources.getString(R.string.today)
|
return resources.getString(R.string.today)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "today"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Yesterday : DateTimeAgo() {
|
object Yesterday : DateTimeAgo() {
|
||||||
override fun format(resources: Resources): String {
|
override fun format(resources: Resources): String {
|
||||||
return resources.getString(R.string.yesterday)
|
return resources.getString(R.string.yesterday)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "yesterday"
|
||||||
}
|
}
|
||||||
|
|
||||||
class DaysAgo(val days: Int) : DateTimeAgo() {
|
class DaysAgo(val days: Int) : DateTimeAgo() {
|
||||||
@@ -73,6 +83,8 @@ sealed class DateTimeAgo : ListModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int = days
|
override fun hashCode(): Int = days
|
||||||
|
|
||||||
|
override fun toString() = "days_ago_$days"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Absolute(private val date: Date) : DateTimeAgo() {
|
class Absolute(private val date: Date) : DateTimeAgo() {
|
||||||
@@ -97,11 +109,15 @@ sealed class DateTimeAgo : ListModel {
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return day
|
return day
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "abs_$day"
|
||||||
}
|
}
|
||||||
|
|
||||||
object LongAgo : DateTimeAgo() {
|
object LongAgo : DateTimeAgo() {
|
||||||
override fun format(resources: Resources): String {
|
override fun format(resources: Resources): String {
|
||||||
return resources.getString(R.string.long_ago)
|
return resources.getString(R.string.long_ago)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "long_ago"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.library.ui
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
@@ -12,30 +11,31 @@ import org.koin.android.ext.android.get
|
|||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration
|
||||||
import org.koitharu.kotatsu.databinding.FragmentLibraryBinding
|
import org.koitharu.kotatsu.databinding.FragmentLibraryBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
|
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
|
||||||
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
||||||
import org.koitharu.kotatsu.library.ui.adapter.LibraryAdapter
|
import org.koitharu.kotatsu.library.ui.adapter.LibraryAdapter
|
||||||
import org.koitharu.kotatsu.library.ui.model.LibraryGroupModel
|
import org.koitharu.kotatsu.library.ui.adapter.LibraryListEventListener
|
||||||
|
import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel
|
||||||
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
||||||
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.util.flattenTo
|
||||||
import org.koitharu.kotatsu.utils.ShareHelper
|
import org.koitharu.kotatsu.utils.ShareHelper
|
||||||
import org.koitharu.kotatsu.utils.ext.findViewsByType
|
import org.koitharu.kotatsu.utils.ext.findViewsByType
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|
||||||
class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), MangaListListener, ActionMode.Callback {
|
class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), LibraryListEventListener,
|
||||||
|
SectionedSelectionController.Callback<LibrarySectionModel> {
|
||||||
|
|
||||||
private val viewModel by viewModel<LibraryViewModel>()
|
private val viewModel by viewModel<LibraryViewModel>()
|
||||||
private var adapter: LibraryAdapter? = null
|
private var adapter: LibraryAdapter? = null
|
||||||
private var selectionDecoration: MangaSelectionDecoration? = null
|
private var selectionController: SectionedSelectionController<LibrarySectionModel>? = null
|
||||||
private var actionMode: ActionMode? = null
|
|
||||||
|
|
||||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): FragmentLibraryBinding {
|
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): FragmentLibraryBinding {
|
||||||
return FragmentLibraryBinding.inflate(inflater, container, false)
|
return FragmentLibraryBinding.inflate(inflater, container, false)
|
||||||
@@ -44,19 +44,17 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), MangaListListene
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val sizeResolver = ItemSizeResolver(resources, get())
|
val sizeResolver = ItemSizeResolver(resources, get())
|
||||||
val itemCLickListener = object : OnListItemClickListener<LibraryGroupModel> {
|
selectionController = SectionedSelectionController(
|
||||||
override fun onItemClick(item: LibraryGroupModel, view: View) {
|
activity = requireActivity(),
|
||||||
onGroupClick(item, view)
|
registryOwner = this,
|
||||||
}
|
callback = this,
|
||||||
}
|
)
|
||||||
selectionDecoration = MangaSelectionDecoration(view.context)
|
|
||||||
adapter = LibraryAdapter(
|
adapter = LibraryAdapter(
|
||||||
lifecycleOwner = viewLifecycleOwner,
|
lifecycleOwner = viewLifecycleOwner,
|
||||||
coil = get(),
|
coil = get(),
|
||||||
listener = this,
|
listener = this,
|
||||||
itemClickListener = itemCLickListener,
|
|
||||||
sizeResolver = sizeResolver,
|
sizeResolver = sizeResolver,
|
||||||
selectionDecoration = checkNotNull(selectionDecoration),
|
selectionController = checkNotNull(selectionController),
|
||||||
)
|
)
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
@@ -68,42 +66,30 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), MangaListListene
|
|||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
adapter = null
|
adapter = null
|
||||||
selectionDecoration = null
|
selectionController = null
|
||||||
actionMode = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(item: Manga, view: View) {
|
override fun onItemClick(item: Manga, section: LibrarySectionModel, view: View) {
|
||||||
if (selectionDecoration?.checkedItemsCount != 0) {
|
if (selectionController?.onItemClick(section, item.id) != true) {
|
||||||
selectionDecoration?.toggleItemChecked(item.id)
|
val intent = DetailsActivity.newIntent(view.context, item)
|
||||||
if (selectionDecoration?.checkedItemsCount == 0) {
|
startActivity(intent)
|
||||||
actionMode?.finish()
|
}
|
||||||
} else {
|
}
|
||||||
actionMode?.invalidate()
|
|
||||||
invalidateItemDecorations()
|
override fun onItemLongClick(item: Manga, section: LibrarySectionModel, view: View): Boolean {
|
||||||
}
|
return selectionController?.onItemLongClick(section, item.id) ?: false
|
||||||
return
|
}
|
||||||
|
|
||||||
|
override fun onSectionClick(section: LibrarySectionModel, view: View) {
|
||||||
|
val intent = when (section) {
|
||||||
|
is LibrarySectionModel.History -> HistoryActivity.newIntent(view.context)
|
||||||
|
is LibrarySectionModel.Favourites -> TODO()
|
||||||
}
|
}
|
||||||
val intent = DetailsActivity.newIntent(view.context, item)
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemLongClick(item: Manga, view: View): Boolean {
|
|
||||||
if (actionMode == null) {
|
|
||||||
actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this)
|
|
||||||
}
|
|
||||||
return actionMode?.also {
|
|
||||||
selectionDecoration?.setItemIsChecked(item.id, true)
|
|
||||||
invalidateItemDecorations()
|
|
||||||
it.invalidate()
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRetryClick(error: Throwable) = Unit
|
override fun onRetryClick(error: Throwable) = Unit
|
||||||
|
|
||||||
override fun onTagRemoveClick(tag: MangaTag) = Unit
|
|
||||||
|
|
||||||
override fun onFilterClick() = Unit
|
|
||||||
|
|
||||||
override fun onEmptyActionClick() = Unit
|
override fun onEmptyActionClick() = Unit
|
||||||
|
|
||||||
override fun onWindowInsetsChanged(insets: Insets) {
|
override fun onWindowInsetsChanged(insets: Insets) {
|
||||||
@@ -121,7 +107,7 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), MangaListListene
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
mode.title = selectionDecoration?.checkedItemsCount?.toString()
|
mode.title = selectionController?.count?.toString()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,26 +133,28 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), MangaListListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode) {
|
override fun onSelectionChanged(count: Int) {
|
||||||
selectionDecoration?.clearSelection()
|
|
||||||
invalidateItemDecorations()
|
invalidateItemDecorations()
|
||||||
actionMode = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onGroupClick(item: LibraryGroupModel, view: View) {
|
override fun onCreateItemDecoration(section: LibrarySectionModel): AbstractSelectionItemDecoration {
|
||||||
val intent = when (item) {
|
return MangaSelectionDecoration(requireContext())
|
||||||
is LibraryGroupModel.History -> HistoryActivity.newIntent(view.context)
|
}
|
||||||
is LibraryGroupModel.Favourites -> TODO()
|
|
||||||
|
private fun collectSelectedItemsMap(): Map<LibrarySectionModel, Set<Manga>> {
|
||||||
|
val snapshot = selectionController?.snapshot()
|
||||||
|
if (snapshot.isNullOrEmpty()) {
|
||||||
|
return emptyMap()
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
return snapshot.mapValues { (_, ids) -> viewModel.getManga(ids) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectSelectedItems(): Set<Manga> {
|
private fun collectSelectedItems(): Set<Manga> {
|
||||||
val ids = selectionDecoration?.checkedItemsIds
|
val snapshot = selectionController?.snapshot()
|
||||||
if (ids.isNullOrEmpty()) {
|
if (snapshot.isNullOrEmpty()) {
|
||||||
return emptySet()
|
return emptySet()
|
||||||
}
|
}
|
||||||
return emptySet()//viewModel.getItems(ids)
|
return viewModel.getManga(snapshot.values.flattenTo(HashSet()))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun invalidateItemDecorations() {
|
private fun invalidateItemDecorations() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.koitharu.kotatsu.library.ui
|
package org.koitharu.kotatsu.library.ui
|
||||||
|
|
||||||
|
import androidx.collection.ArraySet
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -16,7 +17,7 @@ import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
|||||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||||
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
||||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||||
import org.koitharu.kotatsu.library.ui.model.LibraryGroupModel
|
import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel
|
||||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||||
import org.koitharu.kotatsu.list.ui.model.*
|
import org.koitharu.kotatsu.list.ui.model.*
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
@@ -57,6 +58,25 @@ class LibraryViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getManga(ids: Set<Long>): Set<Manga> {
|
||||||
|
val snapshot = content.value ?: return emptySet()
|
||||||
|
val result = ArraySet<Manga>(ids.size)
|
||||||
|
for (section in snapshot) {
|
||||||
|
if (section !is LibrarySectionModel) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (item in section.items) {
|
||||||
|
if (item.id in ids) {
|
||||||
|
result.add(item.manga)
|
||||||
|
if (result.size == ids.size) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun mapList(
|
private suspend fun mapList(
|
||||||
history: List<MangaWithHistory>,
|
history: List<MangaWithHistory>,
|
||||||
favourites: Map<FavouriteCategory, List<Manga>>,
|
favourites: Map<FavouriteCategory, List<Manga>>,
|
||||||
@@ -66,12 +86,12 @@ class LibraryViewModel(
|
|||||||
result += mapHistory(history)
|
result += mapHistory(history)
|
||||||
}
|
}
|
||||||
for ((category, list) in favourites) {
|
for ((category, list) in favourites) {
|
||||||
result += LibraryGroupModel.Favourites(list.toUi(ListMode.GRID, this), category, R.string.show_all)
|
result += LibrarySectionModel.Favourites(list.toUi(ListMode.GRID, this), category, R.string.show_all)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun mapHistory(list: List<MangaWithHistory>): List<LibraryGroupModel.History> {
|
private suspend fun mapHistory(list: List<MangaWithHistory>): List<LibrarySectionModel.History> {
|
||||||
val showPercent = settings.isReadingIndicatorsEnabled
|
val showPercent = settings.isReadingIndicatorsEnabled
|
||||||
val groups = ArrayList<DateTimeAgo>()
|
val groups = ArrayList<DateTimeAgo>()
|
||||||
val map = HashMap<DateTimeAgo, ArrayList<MangaItemModel>>()
|
val map = HashMap<DateTimeAgo, ArrayList<MangaItemModel>>()
|
||||||
@@ -84,12 +104,12 @@ class LibraryViewModel(
|
|||||||
}
|
}
|
||||||
map.getOrPut(date) { ArrayList() }.add(manga.toGridModel(counter, percent))
|
map.getOrPut(date) { ArrayList() }.add(manga.toGridModel(counter, percent))
|
||||||
}
|
}
|
||||||
val result = ArrayList<LibraryGroupModel.History>(HISTORY_MAX_SEGMENTS)
|
val result = ArrayList<LibrarySectionModel.History>(HISTORY_MAX_SEGMENTS)
|
||||||
repeat(minOf(HISTORY_MAX_SEGMENTS - 1, groups.size - 1)) { i ->
|
repeat(minOf(HISTORY_MAX_SEGMENTS - 1, groups.size - 1)) { i ->
|
||||||
val key = groups[i]
|
val key = groups[i]
|
||||||
val values = map.remove(key)
|
val values = map.remove(key)
|
||||||
if (!values.isNullOrEmpty()) {
|
if (!values.isNullOrEmpty()) {
|
||||||
result.add(LibraryGroupModel.History(values, key, 0))
|
result.add(LibrarySectionModel.History(values, key, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val values = map.values.flatten()
|
val values = map.values.flatten()
|
||||||
@@ -99,7 +119,7 @@ class LibraryViewModel(
|
|||||||
} else {
|
} else {
|
||||||
map.keys.singleOrNull() ?: DateTimeAgo.LongAgo
|
map.keys.singleOrNull() ?: DateTimeAgo.LongAgo
|
||||||
}
|
}
|
||||||
result.add(LibraryGroupModel.History(values, key, R.string.show_all))
|
result.add(LibrarySectionModel.History(values, key, R.string.show_all))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,22 @@ import androidx.recyclerview.widget.DiffUtil
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController
|
||||||
import org.koitharu.kotatsu.library.ui.model.LibraryGroupModel
|
import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel
|
||||||
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
||||||
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.*
|
import org.koitharu.kotatsu.list.ui.adapter.errorStateListAD
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import kotlin.jvm.internal.Intrinsics
|
import kotlin.jvm.internal.Intrinsics
|
||||||
|
|
||||||
class LibraryAdapter(
|
class LibraryAdapter(
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
listener: MangaListListener,
|
listener: LibraryListEventListener,
|
||||||
sizeResolver: ItemSizeResolver,
|
sizeResolver: ItemSizeResolver,
|
||||||
selectionDecoration: MangaSelectionDecoration,
|
selectionController: SectionedSelectionController<LibrarySectionModel>,
|
||||||
itemClickListener: OnListItemClickListener<LibraryGroupModel>,
|
|
||||||
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -31,9 +32,8 @@ class LibraryAdapter(
|
|||||||
lifecycleOwner = lifecycleOwner,
|
lifecycleOwner = lifecycleOwner,
|
||||||
coil = coil,
|
coil = coil,
|
||||||
sizeResolver = sizeResolver,
|
sizeResolver = sizeResolver,
|
||||||
selectionDecoration = selectionDecoration,
|
selectionController = selectionController,
|
||||||
listener = listener,
|
listener = listener,
|
||||||
itemClickListener = itemClickListener,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.addDelegate(loadingStateAD())
|
.addDelegate(loadingStateAD())
|
||||||
@@ -46,7 +46,7 @@ class LibraryAdapter(
|
|||||||
|
|
||||||
override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel): Boolean {
|
override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel): Boolean {
|
||||||
return when {
|
return when {
|
||||||
oldItem is LibraryGroupModel && newItem is LibraryGroupModel -> {
|
oldItem is LibrarySectionModel && newItem is LibrarySectionModel -> {
|
||||||
oldItem.key == newItem.key
|
oldItem.key == newItem.key
|
||||||
}
|
}
|
||||||
else -> oldItem.javaClass == newItem.javaClass
|
else -> oldItem.javaClass == newItem.javaClass
|
||||||
@@ -59,7 +59,7 @@ class LibraryAdapter(
|
|||||||
|
|
||||||
override fun getChangePayload(oldItem: ListModel, newItem: ListModel): Any? {
|
override fun getChangePayload(oldItem: ListModel, newItem: ListModel): Any? {
|
||||||
return when {
|
return when {
|
||||||
oldItem is LibraryGroupModel && newItem is LibraryGroupModel -> Unit
|
oldItem is LibrarySectionModel && newItem is LibrarySectionModel -> Unit
|
||||||
else -> super.getChangePayload(oldItem, newItem)
|
else -> super.getChangePayload(oldItem, newItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
package org.koitharu.kotatsu.library.ui.adapter
|
package org.koitharu.kotatsu.library.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter
|
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController
|
||||||
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||||
import org.koitharu.kotatsu.databinding.ItemListGroupBinding
|
import org.koitharu.kotatsu.databinding.ItemListGroupBinding
|
||||||
import org.koitharu.kotatsu.library.ui.model.LibraryGroupModel
|
import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel
|
||||||
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
||||||
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
|
import org.koitharu.kotatsu.utils.ext.clearItemDecorations
|
||||||
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
||||||
|
|
||||||
fun libraryGroupAD(
|
fun libraryGroupAD(
|
||||||
@@ -23,26 +24,42 @@ fun libraryGroupAD(
|
|||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
sizeResolver: ItemSizeResolver,
|
sizeResolver: ItemSizeResolver,
|
||||||
selectionDecoration: MangaSelectionDecoration,
|
selectionController: SectionedSelectionController<LibrarySectionModel>,
|
||||||
listener: OnListItemClickListener<Manga>,
|
listener: LibraryListEventListener,
|
||||||
itemClickListener: OnListItemClickListener<LibraryGroupModel>,
|
) = adapterDelegateViewBinding<LibrarySectionModel, ListModel, ItemListGroupBinding>(
|
||||||
) = adapterDelegateViewBinding<LibraryGroupModel, ListModel, ItemListGroupBinding>(
|
|
||||||
{ layoutInflater, parent -> ItemListGroupBinding.inflate(layoutInflater, parent, false) }
|
{ layoutInflater, parent -> ItemListGroupBinding.inflate(layoutInflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.recyclerView.setRecycledViewPool(sharedPool)
|
val listenerAdapter = object : OnListItemClickListener<Manga>, View.OnClickListener {
|
||||||
val adapter = AsyncListDifferDelegationAdapter<ListModel>(
|
override fun onItemClick(item: Manga, view: View) {
|
||||||
MangaItemDiffCallback(),
|
listener.onItemClick(item, this@adapterDelegateViewBinding.item, view)
|
||||||
mangaGridItemAD(coil, lifecycleOwner, listener, sizeResolver)
|
}
|
||||||
)
|
|
||||||
binding.recyclerView.addItemDecoration(selectionDecoration)
|
|
||||||
binding.recyclerView.adapter = adapter
|
|
||||||
val spacing = context.resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
|
||||||
binding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
|
||||||
val eventListener = AdapterDelegateClickListenerAdapter(this, itemClickListener)
|
|
||||||
binding.buttonMore.setOnClickListener(eventListener)
|
|
||||||
|
|
||||||
bind {
|
override fun onItemLongClick(item: Manga, view: View): Boolean {
|
||||||
|
return listener.onItemLongClick(item, this@adapterDelegateViewBinding.item, view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
listener.onSectionClick(item, itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val adapter = AsyncListDifferDelegationAdapter(
|
||||||
|
MangaItemDiffCallback(),
|
||||||
|
mangaGridItemAD(coil, lifecycleOwner, listenerAdapter, sizeResolver)
|
||||||
|
)
|
||||||
|
binding.recyclerView.setRecycledViewPool(sharedPool)
|
||||||
|
binding.recyclerView.adapter = adapter
|
||||||
|
val spacingDecoration = SpacingItemDecoration(context.resources.getDimensionPixelOffset(R.dimen.grid_spacing))
|
||||||
|
binding.recyclerView.addItemDecoration(spacingDecoration)
|
||||||
|
binding.buttonMore.setOnClickListener(listenerAdapter)
|
||||||
|
|
||||||
|
bind { payloads ->
|
||||||
|
if (payloads.isEmpty()) {
|
||||||
|
binding.recyclerView.clearItemDecorations()
|
||||||
|
binding.recyclerView.addItemDecoration(spacingDecoration)
|
||||||
|
selectionController.attachToRecyclerView(item, binding.recyclerView)
|
||||||
|
}
|
||||||
binding.textViewTitle.text = item.getTitle(context.resources)
|
binding.textViewTitle.text = item.getTitle(context.resources)
|
||||||
binding.buttonMore.setTextAndVisible(item.showAllButtonText)
|
binding.buttonMore.setTextAndVisible(item.showAllButtonText)
|
||||||
adapter.items = item.items
|
adapter.items = item.items
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.library.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||||
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
|
|
||||||
|
interface LibraryListEventListener : ListStateHolderListener {
|
||||||
|
|
||||||
|
fun onItemClick(item: Manga, section: LibrarySectionModel, view: View)
|
||||||
|
|
||||||
|
fun onItemLongClick(item: Manga, section: LibrarySectionModel, view: View): Boolean
|
||||||
|
|
||||||
|
fun onSectionClick(section: LibrarySectionModel, view: View)
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
|||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
||||||
|
|
||||||
sealed class LibraryGroupModel(
|
sealed class LibrarySectionModel(
|
||||||
val items: List<MangaItemModel>,
|
val items: List<MangaItemModel>,
|
||||||
@StringRes val showAllButtonText: Int,
|
@StringRes val showAllButtonText: Int,
|
||||||
) : ListModel {
|
) : ListModel {
|
||||||
@@ -20,7 +20,7 @@ sealed class LibraryGroupModel(
|
|||||||
items: List<MangaItemModel>,
|
items: List<MangaItemModel>,
|
||||||
val timeAgo: DateTimeAgo?,
|
val timeAgo: DateTimeAgo?,
|
||||||
showAllButtonText: Int,
|
showAllButtonText: Int,
|
||||||
) : LibraryGroupModel(items, showAllButtonText) {
|
) : LibrarySectionModel(items, showAllButtonText) {
|
||||||
|
|
||||||
override val key: Any
|
override val key: Any
|
||||||
get() = timeAgo?.javaClass ?: this::class.java
|
get() = timeAgo?.javaClass ?: this::class.java
|
||||||
@@ -48,13 +48,17 @@ sealed class LibraryGroupModel(
|
|||||||
result = 31 * result + showAllButtonText.hashCode()
|
result = 31 * result + showAllButtonText.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "hist_$timeAgo"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Favourites(
|
class Favourites(
|
||||||
items: List<MangaItemModel>,
|
items: List<MangaItemModel>,
|
||||||
val category: FavouriteCategory,
|
val category: FavouriteCategory,
|
||||||
showAllButtonText: Int,
|
showAllButtonText: Int,
|
||||||
) : LibraryGroupModel(items, showAllButtonText) {
|
) : LibrarySectionModel(items, showAllButtonText) {
|
||||||
|
|
||||||
override val key: Any
|
override val key: Any
|
||||||
get() = category.id
|
get() = category.id
|
||||||
@@ -82,5 +86,9 @@ sealed class LibraryGroupModel(
|
|||||||
result = 31 * result + showAllButtonText.hashCode()
|
result = 31 * result + showAllButtonText.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "fav_${category.id}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
|||||||
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
||||||
|
|
||||||
fun emptyStateListAD(
|
fun emptyStateListAD(
|
||||||
listener: MangaListListener,
|
listener: ListStateHolderListener,
|
||||||
) = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
|
) = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
|
||||||
{ inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }
|
{ inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
|||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|
||||||
fun errorStateListAD(
|
fun errorStateListAD(
|
||||||
listener: MangaListListener,
|
listener: ListStateHolderListener,
|
||||||
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
|
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
|
||||||
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
|
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.koitharu.kotatsu.list.ui.adapter
|
||||||
|
|
||||||
|
interface ListStateHolderListener {
|
||||||
|
|
||||||
|
fun onRetryClick(error: Throwable)
|
||||||
|
|
||||||
|
fun onEmptyActionClick()
|
||||||
|
}
|
||||||
@@ -4,10 +4,9 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
|||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
|
|
||||||
interface MangaListListener : OnListItemClickListener<Manga> {
|
interface MangaListListener : OnListItemClickListener<Manga>, ListStateHolderListener {
|
||||||
|
|
||||||
fun onRetryClick(error: Throwable)
|
|
||||||
fun onTagRemoveClick(tag: MangaTag)
|
fun onTagRemoveClick(tag: MangaTag)
|
||||||
|
|
||||||
fun onFilterClick()
|
fun onFilterClick()
|
||||||
fun onEmptyActionClick()
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user