Remove from library action

This commit is contained in:
Koitharu
2022-07-22 13:13:00 +03:00
parent 83886362be
commit 7c659371a9
10 changed files with 242 additions and 121 deletions

View File

@@ -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(
}
}
}
}
}

View File

@@ -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<T : Any>(
private val activity: Activity,
private val registryOwner: SavedStateRegistryOwner,
private val owner: SavedStateRegistryOwner,
private val callback: Callback<T>,
) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider {
@@ -34,7 +34,7 @@ class SectionedSelectionController<T : Any>(
get() = decorations.values.sumOf { it.checkedItemsCount }
init {
registryOwner.lifecycle.addObserver(StateEventObserver())
owner.lifecycle.addObserver(StateEventObserver())
}
fun snapshot(): Map<T, Set<Long>> {
@@ -117,19 +117,19 @@ class SectionedSelectionController<T : Any>(
}
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<T : Any>(
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<T : Any>(
private fun getDecoration(section: T): AbstractSelectionItemDecoration {
return decorations.getOrPut(section) {
callback.onCreateItemDecoration(section)
callback.onCreateItemDecoration(this, section)
}
}
interface Callback<T> : ListSelectionController.Callback {
interface Callback<T : Any> {
fun onCreateItemDecoration(section: T): AbstractSelectionItemDecoration
fun onSelectionChanged(controller: SectionedSelectionController<T>, count: Int)
fun onCreateActionMode(controller: SectionedSelectionController<T>, mode: ActionMode, menu: Menu): Boolean
fun onPrepareActionMode(controller: SectionedSelectionController<T>, mode: ActionMode, menu: Menu): Boolean {
mode.title = controller.count.toString()
return true
}
fun onDestroyActionMode(controller: SectionedSelectionController<T>, mode: ActionMode) = Unit
fun onActionItemClicked(
controller: SectionedSelectionController<T>,
mode: ActionMode,
item: MenuItem,
): Boolean
fun onCreateItemDecoration(
controller: SectionedSelectionController<T>,
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<T : Any>(
}
}
}
}
}

View File

@@ -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<FragmentListSimpleBinding>(), ListStateHolderListener,
OnListItemClickListener<Bookmark>, SectionedSelectionController.Callback<Manga> {
class BookmarksFragment :
BaseFragment<FragmentListSimpleBinding>(),
ListStateHolderListener,
OnListItemClickListener<Bookmark>,
SectionedSelectionController.Callback<Manga> {
private val viewModel by viewModel<BookmarksViewModel>()
private var adapter: BookmarksGroupAdapter? = null
@@ -45,7 +48,7 @@ class BookmarksFragment : BaseFragment<FragmentListSimpleBinding>(), ListStateHo
super.onViewCreated(view, savedInstanceState)
selectionController = SectionedSelectionController(
activity = requireActivity(),
registryOwner = this,
owner = this,
callback = this,
)
adapter = BookmarksGroupAdapter(
@@ -87,21 +90,24 @@ class BookmarksFragment : BaseFragment<FragmentListSimpleBinding>(), ListStateHo
override fun onEmptyActionClick() = Unit
override fun onSelectionChanged(count: Int) {
override fun onSelectionChanged(controller: SectionedSelectionController<Manga>, count: Int) {
binding.recyclerView.invalidateNestedItemDecorations()
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
override fun onCreateActionMode(
controller: SectionedSelectionController<Manga>,
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<Manga>,
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<FragmentListSimpleBinding>(), ListStateHo
}
}
override fun onCreateItemDecoration(section: Manga): AbstractSelectionItemDecoration {
return BookmarksSelectionDecoration(requireContext())
}
override fun onCreateItemDecoration(
controller: SectionedSelectionController<Manga>,
section: Manga,
): AbstractSelectionItemDecoration = BookmarksSelectionDecoration(requireContext())
override fun onWindowInsetsChanged(insets: Insets) {
binding.root.updatePadding(
@@ -135,7 +142,7 @@ class BookmarksFragment : BaseFragment<FragmentListSimpleBinding>(), ListStateHo
Snackbar.make(
binding.recyclerView,
e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT
Snackbar.LENGTH_SHORT,
).show()
}
@@ -174,4 +181,4 @@ class BookmarksFragment : BaseFragment<FragmentListSimpleBinding>(), ListStateHo
fun newInstance() = BookmarksFragment()
}
}
}

View File

@@ -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)

View File

@@ -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()) }
}
}

View File

@@ -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<FragmentLibraryBinding>(),
LibraryListEventListener,
SectionedSelectionController.Callback<LibrarySectionModel> {
LibraryListEventListener {
private val viewModel by viewModel<LibraryViewModel>()
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<LibrarySectionModel, Set<Manga>> {
val snapshot = selectionController?.snapshot()
if (snapshot.isNullOrEmpty()) {
return emptyMap()
}
return snapshot.mapValues { (_, ids) -> viewModel.getManga(ids) }
}
private fun collectSelectedItems(): Set<Manga> {
val snapshot = selectionController?.snapshot()
if (snapshot.isNullOrEmpty()) {
return emptySet()
}
return viewModel.getManga(snapshot.values.flattenTo(HashSet()))
}
private fun onListChanged(list: List<ListModel>) {
adapter?.items = list
}

View File

@@ -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<LibrarySectionModel> {
private val context: Context
get() = recyclerView.context
override fun onCreateActionMode(
controller: SectionedSelectionController<LibrarySectionModel>,
mode: ActionMode,
menu: Menu,
): Boolean {
mode.menuInflater.inflate(R.menu.mode_library, menu)
return true
}
override fun onPrepareActionMode(
controller: SectionedSelectionController<LibrarySectionModel>,
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<LibrarySectionModel>,
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<LibrarySectionModel>, count: Int) {
recyclerView.invalidateNestedItemDecorations()
}
override fun onCreateItemDecoration(
controller: SectionedSelectionController<LibrarySectionModel>,
section: LibrarySectionModel,
): AbstractSelectionItemDecoration = MangaSelectionDecoration(context)
private fun collectSelectedItemsMap(
controller: SectionedSelectionController<LibrarySectionModel>,
): Map<LibrarySectionModel, Set<Manga>> {
val snapshot = controller.peekCheckedIds()
if (snapshot.isEmpty()) {
return emptyMap()
}
return snapshot.mapValues { (_, ids) -> viewModel.getManga(ids) }
}
private fun collectSelectedItems(
controller: SectionedSelectionController<LibrarySectionModel>,
): Set<Manga> {
val snapshot = controller.peekCheckedIds()
if (snapshot.isEmpty()) {
return emptySet()
}
return viewModel.getManga(snapshot.values.flattenTo(HashSet()))
}
}

View File

@@ -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<Long>) {
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<Long>) {
if (ids.isEmpty()) {
return

View File

@@ -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)
}
}

View File

@@ -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<out ItemDecoration>) {
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<ViewParent>
yield(p)
p = p.parent
}
}
}