diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt index b46da465f..5cadc9c6f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt @@ -12,9 +12,9 @@ import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryOwner +import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration -import kotlin.coroutines.EmptyCoroutineContext private const val KEY_SELECTION = "selection" private const val PROVIDER_NAME = "selection_decoration" @@ -159,7 +159,7 @@ class ListSelectionController( override fun onActionItemClicked( controller: ListSelectionController, mode: ActionMode, - item: MenuItem + item: MenuItem, ): Boolean = onActionItemClicked(mode, item) override fun onDestroyActionMode(controller: ListSelectionController, mode: ActionMode) { @@ -173,7 +173,10 @@ class ListSelectionController( fun onCreateActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean - fun onPrepareActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean + fun onPrepareActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean { + mode.title = controller.count.toString() + return true + } fun onActionItemClicked(controller: ListSelectionController, mode: ActionMode, item: MenuItem): Boolean @@ -197,4 +200,4 @@ class ListSelectionController( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/SectionedSelectionController.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/SectionedSelectionController.kt index d514bd4fd..5e96cd066 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/SectionedSelectionController.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/SectionedSelectionController.kt @@ -13,15 +13,15 @@ import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryOwner +import kotlin.coroutines.EmptyCoroutineContext 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( private val activity: Activity, - private val registryOwner: SavedStateRegistryOwner, + private val owner: SavedStateRegistryOwner, private val callback: Callback, ) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider { @@ -34,7 +34,7 @@ class SectionedSelectionController( get() = decorations.values.sumOf { it.checkedItemsCount } init { - registryOwner.lifecycle.addObserver(StateEventObserver()) + owner.lifecycle.addObserver(StateEventObserver()) } fun snapshot(): Map> { @@ -117,19 +117,19 @@ class SectionedSelectionController( } override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - return callback.onCreateActionMode(mode, menu) + return callback.onCreateActionMode(this, mode, menu) } override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - return callback.onPrepareActionMode(mode, menu) + return callback.onPrepareActionMode(this, mode, menu) } override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return callback.onActionItemClicked(mode, item) + return callback.onActionItemClicked(this, mode, item) } override fun onDestroyActionMode(mode: ActionMode) { - callback.onDestroyActionMode(mode) + callback.onDestroyActionMode(this, mode) clear() actionMode = null } @@ -146,7 +146,7 @@ class SectionedSelectionController( private fun notifySelectionChanged() { val count = this.count - callback.onSelectionChanged(count) + callback.onSelectionChanged(this, count) if (count == 0) { actionMode?.finish() } else { @@ -173,27 +173,48 @@ class SectionedSelectionController( private fun getDecoration(section: T): AbstractSelectionItemDecoration { return decorations.getOrPut(section) { - callback.onCreateItemDecoration(section) + callback.onCreateItemDecoration(this, section) } } - interface Callback : ListSelectionController.Callback { + interface Callback { - fun onCreateItemDecoration(section: T): AbstractSelectionItemDecoration + fun onSelectionChanged(controller: SectionedSelectionController, count: Int) + + fun onCreateActionMode(controller: SectionedSelectionController, mode: ActionMode, menu: Menu): Boolean + + fun onPrepareActionMode(controller: SectionedSelectionController, mode: ActionMode, menu: Menu): Boolean { + mode.title = controller.count.toString() + return true + } + + fun onDestroyActionMode(controller: SectionedSelectionController, mode: ActionMode) = Unit + + fun onActionItemClicked( + controller: SectionedSelectionController, + mode: ActionMode, + item: MenuItem, + ): Boolean + + fun onCreateItemDecoration( + controller: SectionedSelectionController, + 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 + val registry = owner.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() } + state.keySet() + .associateWithTo(HashMap()) { state.getLongArray(it)?.toList().orEmpty() }, ) } } @@ -201,4 +222,4 @@ class SectionedSelectionController( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt index 73d2079c5..b5a55e328 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/BookmarksFragment.kt @@ -30,8 +30,11 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf -class BookmarksFragment : BaseFragment(), ListStateHolderListener, - OnListItemClickListener, SectionedSelectionController.Callback { +class BookmarksFragment : + BaseFragment(), + ListStateHolderListener, + OnListItemClickListener, + SectionedSelectionController.Callback { private val viewModel by viewModel() private var adapter: BookmarksGroupAdapter? = null @@ -45,7 +48,7 @@ class BookmarksFragment : BaseFragment(), ListStateHo super.onViewCreated(view, savedInstanceState) selectionController = SectionedSelectionController( activity = requireActivity(), - registryOwner = this, + owner = this, callback = this, ) adapter = BookmarksGroupAdapter( @@ -87,21 +90,24 @@ class BookmarksFragment : BaseFragment(), ListStateHo override fun onEmptyActionClick() = Unit - override fun onSelectionChanged(count: Int) { + override fun onSelectionChanged(controller: SectionedSelectionController, count: Int) { binding.recyclerView.invalidateNestedItemDecorations() } - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + override fun onCreateActionMode( + controller: SectionedSelectionController, + mode: ActionMode, + menu: Menu, + ): Boolean { mode.menuInflater.inflate(R.menu.mode_bookmarks, menu) return true } - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.title = selectionController?.count?.toString() - return true - } - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + override fun onActionItemClicked( + controller: SectionedSelectionController, + mode: ActionMode, + item: MenuItem, + ): Boolean { return when (item.itemId) { R.id.action_remove -> { val ids = selectionController?.snapshot() ?: return false @@ -113,9 +119,10 @@ class BookmarksFragment : BaseFragment(), ListStateHo } } - override fun onCreateItemDecoration(section: Manga): AbstractSelectionItemDecoration { - return BookmarksSelectionDecoration(requireContext()) - } + override fun onCreateItemDecoration( + controller: SectionedSelectionController, + section: Manga, + ): AbstractSelectionItemDecoration = BookmarksSelectionDecoration(requireContext()) override fun onWindowInsetsChanged(insets: Insets) { binding.root.updatePadding( @@ -135,7 +142,7 @@ class BookmarksFragment : BaseFragment(), ListStateHo Snackbar.make( binding.recyclerView, e.getDisplayMessage(resources), - Snackbar.LENGTH_SHORT + Snackbar.LENGTH_SHORT, ).show() } @@ -174,4 +181,4 @@ class BookmarksFragment : BaseFragment(), ListStateHo fun newInstance() = BookmarksFragment() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt index 562d289a5..18bfef6cf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt @@ -106,15 +106,32 @@ abstract class FavouritesDao { /** DELETE **/ - suspend fun delete(mangaId: Long) = setDeletedAt(mangaId, System.currentTimeMillis()) + suspend fun delete(mangaId: Long) = setDeletedAt( + mangaId = mangaId, + deletedAt = System.currentTimeMillis(), + ) - suspend fun delete(mangaId: Long, categoryId: Long) = setDeletedAt(mangaId, categoryId, System.currentTimeMillis()) + suspend fun delete(mangaId: Long, categoryId: Long) = setDeletedAt( + categoryId = categoryId, + mangaId = mangaId, + deletedAt = System.currentTimeMillis(), + ) - suspend fun deleteAll(categoryId: Long) = setDeletedAtAll(categoryId, System.currentTimeMillis()) + suspend fun deleteAll(categoryId: Long) = setDeletedAtAll( + categoryId = categoryId, + deletedAt = System.currentTimeMillis(), + ) - suspend fun recover(mangaId: Long) = setDeletedAt(mangaId, 0L) + suspend fun recover(mangaId: Long) = setDeletedAt( + mangaId = mangaId, + deletedAt = 0L, + ) - suspend fun recover(mangaId: Long, categoryId: Long) = setDeletedAt(categoryId, mangaId, 0L) + suspend fun recover(categoryId: Long, mangaId: Long) = setDeletedAt( + categoryId = categoryId, + mangaId = mangaId, + deletedAt = 0L, + ) @Query("DELETE FROM favourites WHERE deleted_at != 0 AND deleted_at < :maxDeletionTime") abstract suspend fun gc(maxDeletionTime: Long) @@ -139,7 +156,7 @@ abstract class FavouritesDao { protected abstract suspend fun setDeletedAt(mangaId: Long, deletedAt: Long) @Query("UPDATE favourites SET deleted_at = :deletedAt WHERE manga_id = :mangaId AND category_id = :categoryId") - abstract suspend fun setDeletedAt(mangaId: Long, categoryId: Long, deletedAt: Long) + abstract suspend fun setDeletedAt(categoryId: Long, mangaId: Long, deletedAt: Long) @Query("UPDATE favourites SET deleted_at = :deletedAt WHERE category_id = :categoryId AND deleted_at = 0") protected abstract suspend fun setDeletedAtAll(categoryId: Long, deletedAt: Long) diff --git a/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt b/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt index bac79d44b..972752aa7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/LibraryModule.kt @@ -11,6 +11,6 @@ val libraryModule factory { LibraryRepository(get()) } - viewModel { LibraryViewModel(get(), get(), get(), get()) } + viewModel { LibraryViewModel(get(), get(), get(), get(), get()) } viewModel { LibraryCategoriesConfigViewModel(get()) } - } \ No newline at end of file + } diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt index e722d70a2..726d7fb4a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryFragment.kt @@ -1,8 +1,9 @@ package org.koitharu.kotatsu.library.ui import android.os.Bundle -import android.view.* -import androidx.appcompat.view.ActionMode +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding import com.google.android.material.snackbar.Snackbar @@ -12,32 +13,24 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.reverseAsync import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController -import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration import org.koitharu.kotatsu.base.ui.util.ReversibleAction import org.koitharu.kotatsu.databinding.FragmentLibraryBinding import org.koitharu.kotatsu.details.ui.DetailsActivity -import org.koitharu.kotatsu.download.ui.service.DownloadService import org.koitharu.kotatsu.favourites.ui.FavouritesActivity -import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet import org.koitharu.kotatsu.history.ui.HistoryActivity import org.koitharu.kotatsu.library.ui.adapter.LibraryAdapter 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.MangaSelectionDecoration import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.main.ui.BottomNavOwner import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.util.flattenTo -import org.koitharu.kotatsu.utils.ShareHelper import org.koitharu.kotatsu.utils.ext.addMenuProvider import org.koitharu.kotatsu.utils.ext.getDisplayMessage -import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations class LibraryFragment : BaseFragment(), - LibraryListEventListener, - SectionedSelectionController.Callback { + LibraryListEventListener { private val viewModel by viewModel() private var adapter: LibraryAdapter? = null @@ -52,8 +45,8 @@ class LibraryFragment : val sizeResolver = ItemSizeResolver(resources, get()) selectionController = SectionedSelectionController( activity = requireActivity(), - registryOwner = this, - callback = this, + owner = this, + callback = LibrarySelectionCallback(binding.recyclerView, childFragmentManager, viewModel), ) adapter = LibraryAdapter( lifecycleOwner = viewLifecycleOwner, @@ -111,62 +104,6 @@ class LibraryFragment : ) } - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.menuInflater.inflate(R.menu.mode_library, menu) - return true - } - - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.title = selectionController?.count?.toString() - return true - } - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - val ctx = context ?: return false - return when (item.itemId) { - R.id.action_share -> { - ShareHelper(ctx).shareMangaLinks(collectSelectedItems()) - mode.finish() - true - } - R.id.action_favourite -> { - FavouriteCategoriesBottomSheet.show(childFragmentManager, collectSelectedItems()) - mode.finish() - true - } - R.id.action_save -> { - DownloadService.confirmAndStart(ctx, collectSelectedItems()) - mode.finish() - true - } - else -> false - } - } - - override fun onSelectionChanged(count: Int) { - binding.recyclerView.invalidateNestedItemDecorations() - } - - override fun onCreateItemDecoration(section: LibrarySectionModel): AbstractSelectionItemDecoration { - return MangaSelectionDecoration(requireContext()) - } - - private fun collectSelectedItemsMap(): Map> { - val snapshot = selectionController?.snapshot() - if (snapshot.isNullOrEmpty()) { - return emptyMap() - } - return snapshot.mapValues { (_, ids) -> viewModel.getManga(ids) } - } - - private fun collectSelectedItems(): Set { - val snapshot = selectionController?.snapshot() - if (snapshot.isNullOrEmpty()) { - return emptySet() - } - return viewModel.getManga(snapshot.values.flattenTo(HashSet())) - } - private fun onListChanged(list: List) { adapter?.items = list } diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibrarySelectionCallback.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibrarySelectionCallback.kt new file mode 100644 index 000000000..3eb0c0ec4 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibrarySelectionCallback.kt @@ -0,0 +1,111 @@ +package org.koitharu.kotatsu.library.ui + +import android.content.Context +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.view.ActionMode +import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.RecyclerView +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController +import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration +import org.koitharu.kotatsu.download.ui.service.DownloadService +import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet +import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel +import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.util.flattenTo +import org.koitharu.kotatsu.utils.ShareHelper +import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations + +class LibrarySelectionCallback( + private val recyclerView: RecyclerView, + private val fragmentManager: FragmentManager, + private val viewModel: LibraryViewModel, +) : SectionedSelectionController.Callback { + + private val context: Context + get() = recyclerView.context + + override fun onCreateActionMode( + controller: SectionedSelectionController, + mode: ActionMode, + menu: Menu, + ): Boolean { + mode.menuInflater.inflate(R.menu.mode_library, menu) + return true + } + + override fun onPrepareActionMode( + controller: SectionedSelectionController, + mode: ActionMode, + menu: Menu, + ): Boolean { + menu.findItem(R.id.action_remove).isVisible = + controller.peekCheckedIds().count { (_, v) -> v.isNotEmpty() } == 1 + return super.onPrepareActionMode(controller, mode, menu) + } + + override fun onActionItemClicked( + controller: SectionedSelectionController, + mode: ActionMode, + item: MenuItem, + ): Boolean { + return when (item.itemId) { + R.id.action_share -> { + ShareHelper(context).shareMangaLinks(collectSelectedItems(controller)) + mode.finish() + true + } + R.id.action_favourite -> { + FavouriteCategoriesBottomSheet.show(fragmentManager, collectSelectedItems(controller)) + mode.finish() + true + } + R.id.action_save -> { + DownloadService.confirmAndStart(context, collectSelectedItems(controller)) + mode.finish() + true + } + R.id.action_remove -> { + val (group, ids) = controller.snapshot().entries.singleOrNull { it.value.isNotEmpty() } ?: return false + when (group) { + is LibrarySectionModel.Favourites -> viewModel.removeFromFavourites(group.category, ids) + is LibrarySectionModel.History -> viewModel.removeFromHistory(ids) + } + mode.finish() + true + } + else -> false + } + } + + override fun onSelectionChanged(controller: SectionedSelectionController, count: Int) { + recyclerView.invalidateNestedItemDecorations() + } + + override fun onCreateItemDecoration( + controller: SectionedSelectionController, + section: LibrarySectionModel, + ): AbstractSelectionItemDecoration = MangaSelectionDecoration(context) + + private fun collectSelectedItemsMap( + controller: SectionedSelectionController, + ): Map> { + val snapshot = controller.peekCheckedIds() + if (snapshot.isEmpty()) { + return emptyMap() + } + return snapshot.mapValues { (_, ids) -> viewModel.getManga(ids) } + } + + private fun collectSelectedItems( + controller: SectionedSelectionController, + ): Set { + val snapshot = controller.peekCheckedIds() + if (snapshot.isEmpty()) { + return emptySet() + } + return viewModel.getManga(snapshot.values.flattenTo(HashSet())) + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt index 0a6aa24db..b31818c37 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryViewModel.kt @@ -14,6 +14,7 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.DateTimeAgo +import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.history.domain.MangaWithHistory import org.koitharu.kotatsu.history.domain.PROGRESS_NONE @@ -32,6 +33,7 @@ private const val HISTORY_MAX_SEGMENTS = 2 class LibraryViewModel( repository: LibraryRepository, private val historyRepository: HistoryRepository, + private val favouritesRepository: FavouritesRepository, private val trackingRepository: TrackingRepository, private val settings: AppSettings, ) : BaseViewModel(), ListExtraProvider { @@ -59,6 +61,16 @@ class LibraryViewModel( } } + fun removeFromFavourites(category: FavouriteCategory, ids: Set) { + if (ids.isEmpty()) { + return + } + launchJob(Dispatchers.Default) { + val handle = favouritesRepository.removeFromCategory(category.id, ids) + onActionDone.postCall(ReversibleAction(R.string.removed_from_favourites, handle)) + } + } + fun removeFromHistory(ids: Set) { if (ids.isEmpty()) { return diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/adapter/LibraryGroupAD.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/adapter/LibraryGroupAD.kt index 846eb21c5..bc28dcb4e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/adapter/LibraryGroupAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/adapter/LibraryGroupAD.kt @@ -9,6 +9,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R 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.base.ui.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.databinding.ItemListGroupBinding import org.koitharu.kotatsu.library.ui.model.LibrarySectionModel @@ -16,7 +17,7 @@ import org.koitharu.kotatsu.list.ui.ItemSizeResolver import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.clearItemDecorations +import org.koitharu.kotatsu.utils.ext.removeItemDecoration import org.koitharu.kotatsu.utils.ext.setTextAndVisible fun libraryGroupAD( @@ -55,8 +56,7 @@ fun libraryGroupAD( bind { payloads -> if (payloads.isEmpty()) { - binding.recyclerView.clearItemDecorations() - binding.recyclerView.addItemDecoration(spacingDecoration) + binding.recyclerView.removeItemDecoration(AbstractSelectionItemDecoration::class.java) selectionController.attachToRecyclerView(item, binding.recyclerView) } binding.textViewTitle.text = item.getTitle(context.resources) @@ -66,5 +66,6 @@ fun libraryGroupAD( onViewRecycled { adapter.items = emptyList() + binding.recyclerView.removeItemDecoration(AbstractSelectionItemDecoration::class.java) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index a62abab53..d8174c8d5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -10,6 +10,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.slider.Slider import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewBindingViewHolder @@ -34,6 +35,15 @@ fun RecyclerView.clearItemDecorations() { suppressLayout(false) } +fun RecyclerView.removeItemDecoration(cls: Class) { + repeat(itemDecorationCount) { i -> + if (cls.isInstance(getItemDecorationAt(i))) { + removeItemDecorationAt(i) + return + } + } +} + var RecyclerView.firstVisibleItemPosition: Int get() = (layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition() ?: RecyclerView.NO_POSITION @@ -69,13 +79,15 @@ fun View.measureWidth(): Int { } inline fun ViewPager2.doOnPageChanged(crossinline callback: (Int) -> Unit) { - registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + registerOnPageChangeCallback( + object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - callback(position) - } - }) + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + callback(position) + } + }, + ) } val ViewPager2.recyclerView: RecyclerView? @@ -157,4 +169,4 @@ val View.parents: Sequence yield(p) p = p.parent } - } \ No newline at end of file + }