Save state of selection in lists
This commit is contained in:
@@ -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<B : ViewBinding> :
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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<Long> {
|
||||
return peekCheckedIds().toSet()
|
||||
}
|
||||
|
||||
fun peekCheckedIds(): Set<Long> {
|
||||
return decoration.checkedItemsIds
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
decoration.clearSelection()
|
||||
notifySelectionChanged()
|
||||
}
|
||||
|
||||
fun addAll(ids: Collection<Long>) {
|
||||
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<Long>) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<FragmentChaptersBinding>(),
|
||||
OnListItemClickListener<ChapterListItem>,
|
||||
ActionMode.Callback,
|
||||
AdapterView.OnItemSelectedListener,
|
||||
MenuItem.OnActionExpandListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
SearchView.OnQueryTextListener,
|
||||
ListSelectionController.Callback {
|
||||
|
||||
private val viewModel by sharedViewModel<DetailsViewModel>()
|
||||
|
||||
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
|
||||
|
||||
@@ -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<Long>
|
||||
get() = selectionDecoration?.checkedItemsIds?.toSet().orEmpty()
|
||||
get() = selectionController?.snapshot().orEmpty()
|
||||
|
||||
protected val selectedItems: Set<Manga>
|
||||
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<Manga> {
|
||||
val checkedIds = selectionDecoration?.checkedItemsIds ?: return emptySet()
|
||||
val checkedIds = selectionController?.peekCheckedIds() ?: return emptySet()
|
||||
val items = listAdapter?.items ?: return emptySet()
|
||||
val result = ArraySet<Manga>(checkedIds.size)
|
||||
for (item in items) {
|
||||
|
||||
@@ -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<AppBarLayout.LayoutParams> {
|
||||
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<AppBarLayout.LayoutParams> {
|
||||
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<AppBarLayout.LayoutParams> {
|
||||
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<String?> {
|
||||
|
||||
@@ -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<ActivitySearchMultiBinding>(), MangaListListener, ActionMode.Callback {
|
||||
class MultiSearchActivity : BaseActivity<ActivitySearchMultiBinding>(), MangaListListener,
|
||||
ListSelectionController.Callback {
|
||||
|
||||
private val viewModel by viewModel<MultiSearchViewModel> {
|
||||
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<ActivitySearchMultiBinding>(), 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<ActivitySearchMultiBinding>(), 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<ActivitySearchMultiBinding>(), 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<ActivitySearchMultiBinding>(), MangaLis
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
selectionDecoration.clearSelection()
|
||||
invalidateItemDecorations()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
private fun collectSelectedItems(): Set<Manga> {
|
||||
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<Manga> {
|
||||
return viewModel.getItems(selectionController.peekCheckedIds())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_QUERY = "query"
|
||||
|
||||
Reference in New Issue
Block a user