From d9985d03ab87fbfdec865aeed457a69e45c9a041 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 4 Jul 2022 11:23:43 +0300 Subject: [PATCH] Save state of selection in lists --- .../koitharu/kotatsu/base/ui/BaseActivity.kt | 6 +- .../base/ui/list/ListSelectionController.kt | 156 ++++++++++++++++++ .../kotatsu/details/ui/ChaptersFragment.kt | 52 +++--- .../kotatsu/list/ui/MangaListFragment.kt | 55 +++--- .../koitharu/kotatsu/main/ui/MainActivity.kt | 30 +++- .../search/ui/multi/MultiSearchActivity.kt | 56 +++---- 6 files changed, 240 insertions(+), 115 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt index bd172d695..9cdce9654 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.ActionBarContextView import androidx.appcompat.widget.Toolbar +import androidx.core.app.ActivityCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -82,9 +83,8 @@ abstract class BaseActivity : override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // TODO remove - // ActivityCompat.recreate(this) - throw RuntimeException("Test crash") - // return true + ActivityCompat.recreate(this) + return true } return super.onKeyDown(keyCode, event) } 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 new file mode 100644 index 000000000..d0bea19b3 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/ListSelectionController.kt @@ -0,0 +1,156 @@ +package org.koitharu.kotatsu.base.ui.list + +import android.app.Activity +import android.os.Bundle +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 org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration + +private const val KEY_SELECTION = "selection" +private const val PROVIDER_NAME = "selection_decoration" + +class ListSelectionController( + private val activity: Activity, + private val decoration: AbstractSelectionItemDecoration, + private val registryOwner: SavedStateRegistryOwner, + private val callback: Callback, +) : ActionMode.Callback, SavedStateRegistry.SavedStateProvider { + + private var actionMode: ActionMode? = null + private val stateEventObserver = StateEventObserver() + + val count: Int + get() = decoration.checkedItemsCount + + fun snapshot(): Set { + return peekCheckedIds().toSet() + } + + fun peekCheckedIds(): Set { + return decoration.checkedItemsIds + } + + fun clear() { + decoration.clearSelection() + notifySelectionChanged() + } + + fun addAll(ids: Collection) { + if (ids.isEmpty()) { + return + } + decoration.checkAll(ids) + notifySelectionChanged() + } + + fun attachToRecyclerView(recyclerView: RecyclerView) { + recyclerView.addItemDecoration(decoration) + registryOwner.lifecycle.addObserver(stateEventObserver) + } + + override fun saveState(): Bundle { + val bundle = Bundle(1) + bundle.putLongArray(KEY_SELECTION, peekCheckedIds().toLongArray()) + return bundle + } + + fun onItemClick(id: Long): Boolean { + if (decoration.checkedItemsCount != 0) { + decoration.toggleItemChecked(id) + if (decoration.checkedItemsCount == 0) { + actionMode?.finish() + } else { + actionMode?.invalidate() + } + notifySelectionChanged() + return true + } + return false + } + + fun onItemLongClick(id: Long): Boolean { + 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 notifySelectionChanged() { + val count = decoration.checkedItemsCount + callback.onSelectionChanged(count) + if (count == 0) { + actionMode?.finish() + } else { + actionMode?.invalidate() + } + } + + private fun restoreState(ids: Collection) { + if (ids.isEmpty() || decoration.checkedItemsCount != 0) { + return + } + decoration.checkAll(ids) + startActionMode() + notifySelectionChanged() + } + + interface Callback : ActionMode.Callback { + + fun onSelectionChanged(count: Int) + + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean + + override fun onDestroyActionMode(mode: ActionMode) = Unit + } + + 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@ListSelectionController) + val state = registry.consumeRestoredStateForKey(PROVIDER_NAME) + if (state != null) { + restoreState(state.getLongArray(KEY_SELECTION)?.toList().orEmpty()) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt index 283a141d3..04e79d18d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.* import android.widget.AdapterView import android.widget.Spinner -import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.graphics.Insets @@ -16,6 +15,7 @@ import com.google.android.material.snackbar.Snackbar import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment +import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.FragmentChaptersBinding import org.koitharu.kotatsu.details.ui.adapter.BranchesAdapter @@ -34,16 +34,15 @@ import kotlin.math.roundToInt class ChaptersFragment : BaseFragment(), OnListItemClickListener, - ActionMode.Callback, AdapterView.OnItemSelectedListener, MenuItem.OnActionExpandListener, - SearchView.OnQueryTextListener { + SearchView.OnQueryTextListener, + ListSelectionController.Callback { private val viewModel by sharedViewModel() private var chaptersAdapter: ChaptersAdapter? = null - private var actionMode: ActionMode? = null - private var selectionDecoration: ChaptersSelectionDecoration? = null + private var selectionController: ListSelectionController? = null override fun onInflateView( inflater: LayoutInflater, @@ -53,9 +52,14 @@ class ChaptersFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) chaptersAdapter = ChaptersAdapter(this) - selectionDecoration = ChaptersSelectionDecoration(view.context) + selectionController = ListSelectionController( + activity = requireActivity(), + decoration = ChaptersSelectionDecoration(view.context), + registryOwner = this, + callback = this, + ) with(binding.recyclerViewChapters) { - addItemDecoration(selectionDecoration!!) + checkNotNull(selectionController).attachToRecyclerView(this) setHasFixedSize(true) adapter = chaptersAdapter } @@ -74,20 +78,13 @@ class ChaptersFragment : override fun onDestroyView() { chaptersAdapter = null - selectionDecoration = null + selectionController = null binding.spinnerBranches?.adapter = null super.onDestroyView() } override fun onItemClick(item: ChapterListItem, view: View) { - if (selectionDecoration?.checkedItemsCount != 0) { - selectionDecoration?.toggleItemChecked(item.chapter.id) - if (selectionDecoration?.checkedItemsCount == 0) { - actionMode?.finish() - } else { - actionMode?.invalidate() - binding.recyclerViewChapters.invalidateItemDecorations() - } + if (selectionController?.onItemClick(item.chapter.id) == true) { return } if (item.hasFlag(ChapterListItem.FLAG_MISSING)) { @@ -106,14 +103,7 @@ class ChaptersFragment : } override fun onItemLongClick(item: ChapterListItem, view: View): Boolean { - if (actionMode == null) { - actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this) - } - return actionMode?.also { - selectionDecoration?.setItemIsChecked(item.chapter.id, true) - binding.recyclerViewChapters.invalidateItemDecorations() - it.invalidate() - } != null + return selectionController?.onItemLongClick(item.chapter.id) ?: false } override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { @@ -122,13 +112,13 @@ class ChaptersFragment : DownloadService.start( context ?: return false, viewModel.getRemoteManga() ?: viewModel.manga.value ?: return false, - selectionDecoration?.checkedItemsIds?.toSet() + selectionController?.snapshot(), ) mode.finish() true } R.id.action_delete -> { - val ids = selectionDecoration?.checkedItemsIds + val ids = selectionController?.peekCheckedIds() val manga = viewModel.manga.value when { ids.isNullOrEmpty() || manga == null -> Unit @@ -147,9 +137,7 @@ class ChaptersFragment : } R.id.action_select_all -> { val ids = chaptersAdapter?.items?.map { it.chapter.id } ?: return false - selectionDecoration?.checkAll(ids) - binding.recyclerViewChapters.invalidateItemDecorations() - mode.invalidate() + selectionController?.addAll(ids) true } else -> false @@ -169,7 +157,7 @@ class ChaptersFragment : } override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - val selectedIds = selectionDecoration?.checkedItemsIds ?: return false + val selectedIds = selectionController?.peekCheckedIds() ?: return false val items = chaptersAdapter?.items?.filter { x -> x.chapter.id in selectedIds }.orEmpty() menu.findItem(R.id.action_save).isVisible = items.none { x -> x.chapter.source == MangaSource.LOCAL @@ -181,10 +169,8 @@ class ChaptersFragment : return true } - override fun onDestroyActionMode(mode: ActionMode?) { - selectionDecoration?.clearSelection() + override fun onSelectionChanged(count: Int) { binding.recyclerViewChapters.invalidateItemDecorations() - actionMode = null } override fun onMenuItemActionExpand(item: MenuItem?): Boolean = true diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 568bb6427..cf4465a8e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -3,7 +3,6 @@ package org.koitharu.kotatsu.list.ui import android.os.Bundle import android.view.* import androidx.annotation.CallSuper -import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.collection.ArraySet import androidx.core.graphics.Insets @@ -18,6 +17,7 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.list.FitHeightGridLayoutManager import org.koitharu.kotatsu.base.ui.list.FitHeightLinearLayoutManager +import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.base.ui.list.decor.TypedSpacingItemDecoration @@ -46,12 +46,11 @@ abstract class MangaListFragment : PaginationScrollListener.Callback, MangaListListener, SwipeRefreshLayout.OnRefreshListener, - ActionMode.Callback { + ListSelectionController.Callback { private var listAdapter: MangaListAdapter? = null private var paginationListener: PaginationScrollListener? = null - private var selectionDecoration: MangaSelectionDecoration? = null - private var actionMode: ActionMode? = null + private var selectionController: ListSelectionController? = null private val spanResolver = MangaListSpanResolver() private val spanSizeLookup = SpanSizeLookup() private val listCommitCallback = Runnable { @@ -62,7 +61,7 @@ abstract class MangaListFragment : protected abstract val viewModel: MangaListViewModel protected val selectedItemsIds: Set - get() = selectionDecoration?.checkedItemsIds?.toSet().orEmpty() + get() = selectionController?.snapshot().orEmpty() protected val selectedItems: Set get() = collectSelectedItems() @@ -79,12 +78,17 @@ abstract class MangaListFragment : lifecycleOwner = viewLifecycleOwner, listener = this, ) - selectionDecoration = MangaSelectionDecoration(view.context) + selectionController = ListSelectionController( + activity = requireActivity(), + decoration = MangaSelectionDecoration(view.context), + registryOwner = this, + callback = this, + ) paginationListener = PaginationScrollListener(4, this) with(binding.recyclerView) { setHasFixedSize(true) adapter = listAdapter - addItemDecoration(selectionDecoration!!) + checkNotNull(selectionController).attachToRecyclerView(binding.recyclerView) addOnScrollListener(paginationListener!!) } with(binding.swipeRefreshLayout) { @@ -105,34 +109,19 @@ abstract class MangaListFragment : override fun onDestroyView() { listAdapter = null paginationListener = null - selectionDecoration = null + selectionController = null spanSizeLookup.invalidateCache() super.onDestroyView() } override fun onItemClick(item: Manga, view: View) { - if (selectionDecoration?.checkedItemsCount != 0) { - selectionDecoration?.toggleItemChecked(item.id) - if (selectionDecoration?.checkedItemsCount == 0) { - actionMode?.finish() - } else { - actionMode?.invalidate() - binding.recyclerView.invalidateItemDecorations() - } - return + if (selectionController?.onItemClick(item.id) != true) { + startActivity(DetailsActivity.newIntent(context ?: return, item)) } - startActivity(DetailsActivity.newIntent(context ?: return, item)) } 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) - binding.recyclerView.invalidateItemDecorations() - it.invalidate() - } != null + return selectionController?.onItemLongClick(item.id) ?: false } @CallSuper @@ -249,7 +238,7 @@ abstract class MangaListFragment : addOnLayoutChangeListener(spanResolver) } } - selectionDecoration?.let { addItemDecoration(it) } + selectionController?.attachToRecyclerView(binding.recyclerView) } } @@ -259,7 +248,7 @@ abstract class MangaListFragment : @CallSuper override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.title = selectionDecoration?.checkedItemsCount?.toString() + mode.title = selectionController?.count?.toString() return true } @@ -269,9 +258,7 @@ abstract class MangaListFragment : val ids = listAdapter?.items?.mapNotNull { (it as? MangaItemModel)?.id } ?: return false - selectionDecoration?.checkAll(ids) - binding.recyclerView.invalidateItemDecorations() - mode.invalidate() + selectionController?.addAll(ids) true } R.id.action_share -> { @@ -293,14 +280,12 @@ abstract class MangaListFragment : } } - override fun onDestroyActionMode(mode: ActionMode) { - selectionDecoration?.clearSelection() + override fun onSelectionChanged(count: Int) { binding.recyclerView.invalidateItemDecorations() - actionMode = null } private fun collectSelectedItems(): Set { - val checkedIds = selectionDecoration?.checkedItemsIds ?: return emptySet() + val checkedIds = selectionController?.peekCheckedIds() ?: return emptySet() val items = listAdapter?.items ?: return emptySet() val result = ArraySet(checkedIds.size) for (item in items) { diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt index b327ae030..2c16cdb97 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -145,8 +145,15 @@ class MainActivity : override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) - drawerToggle?.isDrawerIndicatorEnabled = - drawer?.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_UNLOCKED + val isSearchOpened = isSearchOpened() + adjustDrawerLock(isSearchOpened) + if (isSearchOpened) { + binding.toolbarCard.updateLayoutParams { + scrollFlags = SCROLL_FLAG_NO_SCROLL + } + binding.appbar.setBackgroundColor(getThemeColor(materialR.attr.colorSurfaceVariant)) + binding.appbar.updatePadding(left = 0, right = 0) + } } override fun onPostCreate(savedInstanceState: Bundle?) { @@ -396,29 +403,31 @@ class MainActivity : private fun onSearchOpened() { TransitionManager.beginDelayedTransition(binding.appbar) - drawerToggle?.isDrawerIndicatorEnabled = false binding.toolbarCard.updateLayoutParams { scrollFlags = SCROLL_FLAG_NO_SCROLL } binding.appbar.setBackgroundColor(getThemeColor(materialR.attr.colorSurfaceVariant)) binding.appbar.updatePadding(left = 0, right = 0) - adjustDrawerLock() + adjustDrawerLock(isSearchOpened = true) adjustFabVisibility(isSearchOpened = true) } private fun onSearchClosed() { TransitionManager.beginDelayedTransition(binding.appbar) - drawerToggle?.isDrawerIndicatorEnabled = true binding.toolbarCard.updateLayoutParams { scrollFlags = SCROLL_FLAG_SCROLL or SCROLL_FLAG_ENTER_ALWAYS } binding.appbar.background = null val padding = resources.getDimensionPixelOffset(R.dimen.margin_normal) binding.appbar.updatePadding(left = padding, right = padding) - adjustDrawerLock() + adjustDrawerLock(isSearchOpened = false) adjustFabVisibility(isSearchOpened = false) } + private fun isSearchOpened(): Boolean { + return supportFragmentManager.findFragmentByTag(TAG_SEARCH)?.isVisible == true + } + private fun onFirstStart() { lifecycleScope.launchWhenResumed { val isUpdateSupported = withContext(Dispatchers.Default) { @@ -440,7 +449,7 @@ class MainActivity : private fun adjustFabVisibility( isResumeEnabled: Boolean = viewModel.isResumeEnabled.value == true, topFragment: Fragment? = supportFragmentManager.findFragmentByTag(TAG_PRIMARY), - isSearchOpened: Boolean = supportFragmentManager.findFragmentByTag(TAG_SEARCH)?.isVisible == true, + isSearchOpened: Boolean = isSearchOpened(), ) { val fab = binding.fab if (isResumeEnabled && !isSearchOpened && topFragment is HistoryListFragment) { @@ -454,12 +463,15 @@ class MainActivity : } } - private fun adjustDrawerLock() { + private fun adjustDrawerLock( + isSearchOpened: Boolean = isSearchOpened(), + ) { val drawer = drawer ?: return - val isLocked = actionModeDelegate.isActionModeStarted || (drawerToggle?.isDrawerIndicatorEnabled == false) + val isLocked = actionModeDelegate.isActionModeStarted || isSearchOpened drawer.setDrawerLockMode( if (isLocked) DrawerLayout.LOCK_MODE_LOCKED_CLOSED else DrawerLayout.LOCK_MODE_UNLOCKED ) + drawerToggle?.isDrawerIndicatorEnabled = !isLocked } private inner class VoiceInputCallback : ActivityResultCallback { diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt index 5bd119a83..369594eae 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt @@ -17,6 +17,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.base.ui.list.ListSelectionController import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.ActivitySearchMultiBinding import org.koitharu.kotatsu.details.ui.DetailsActivity @@ -32,14 +33,14 @@ import org.koitharu.kotatsu.search.ui.multi.adapter.MultiSearchAdapter import org.koitharu.kotatsu.utils.ShareHelper import org.koitharu.kotatsu.utils.ext.findViewsByType -class MultiSearchActivity : BaseActivity(), MangaListListener, ActionMode.Callback { +class MultiSearchActivity : BaseActivity(), MangaListListener, + ListSelectionController.Callback { private val viewModel by viewModel { parametersOf(intent.getStringExtra(EXTRA_QUERY).orEmpty()) } private lateinit var adapter: MultiSearchAdapter - private lateinit var selectionDecoration: MangaSelectionDecoration - private var actionMode: ActionMode? = null + private lateinit var selectionController: ListSelectionController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -51,7 +52,13 @@ class MultiSearchActivity : BaseActivity(), MangaLis } } val sizeResolver = ItemSizeResolver(resources, get()) - selectionDecoration = MangaSelectionDecoration(this) + val selectionDecoration = MangaSelectionDecoration(this) + selectionController = ListSelectionController( + activity = this, + decoration = selectionDecoration, + registryOwner = this, + callback = this, + ) adapter = MultiSearchAdapter( lifecycleOwner = this, coil = get(), @@ -90,29 +97,14 @@ class MultiSearchActivity : BaseActivity(), MangaLis } override fun onItemClick(item: Manga, view: View) { - if (selectionDecoration.checkedItemsCount != 0) { - selectionDecoration.toggleItemChecked(item.id) - if (selectionDecoration.checkedItemsCount == 0) { - actionMode?.finish() - } else { - actionMode?.invalidate() - invalidateItemDecorations() - } - return + if (!selectionController.onItemClick(item.id)) { + val intent = DetailsActivity.newIntent(this, item) + startActivity(intent) } - val intent = DetailsActivity.newIntent(this, item) - startActivity(intent) } override fun onItemLongClick(item: Manga, view: View): Boolean { - if (actionMode == null) { - actionMode = startSupportActionMode(this) - } - return actionMode?.also { - selectionDecoration.setItemIsChecked(item.id, true) - invalidateItemDecorations() - it.invalidate() - } != null + return selectionController.onItemLongClick(item.id) } override fun onRetryClick(error: Throwable) { @@ -131,7 +123,7 @@ class MultiSearchActivity : BaseActivity(), MangaLis } override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.title = selectionDecoration.checkedItemsCount.toString() + mode.title = selectionController.count.toString() return true } @@ -156,22 +148,16 @@ class MultiSearchActivity : BaseActivity(), MangaLis } } - override fun onDestroyActionMode(mode: ActionMode) { - selectionDecoration.clearSelection() - invalidateItemDecorations() - actionMode = null - } - - private fun collectSelectedItems(): Set { - return viewModel.getItems(selectionDecoration.checkedItemsIds) - } - - private fun invalidateItemDecorations() { + override fun onSelectionChanged(count: Int) { binding.recyclerView.findViewsByType(RecyclerView::class.java).forEach { it.invalidateItemDecorations() } } + private fun collectSelectedItems(): Set { + return viewModel.getItems(selectionController.peekCheckedIds()) + } + companion object { private const val EXTRA_QUERY = "query"