Implement long hold selection for Manga Chapters and library
Co-Authored-By: zhuoyang <zhuoyang@users.noreply.github.com> Co-Authored-By: Jays2Kings <Jays2Kings@users.noreply.github.com>
This commit is contained in:
parent
e414b9edf1
commit
2d3bfa9a89
2 changed files with 60 additions and 10 deletions
|
@ -1,11 +1,11 @@
|
||||||
package eu.kanade.tachiyomi.ui.library
|
package eu.kanade.tachiyomi.ui.library
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
@ -18,8 +18,8 @@ import eu.kanade.tachiyomi.util.inflate
|
||||||
import eu.kanade.tachiyomi.util.plusAssign
|
import eu.kanade.tachiyomi.util.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
import kotlinx.android.synthetic.main.chapters_controller.fast_scroller
|
import kotlinx.android.synthetic.main.library_category.view.fast_scroller
|
||||||
import kotlinx.android.synthetic.main.library_category.view.*
|
import kotlinx.android.synthetic.main.library_category.view.swipe_refresh
|
||||||
import rx.subscriptions.CompositeSubscription
|
import rx.subscriptions.CompositeSubscription
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
@ -62,6 +62,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
*/
|
*/
|
||||||
private var subscriptions = CompositeSubscription()
|
private var subscriptions = CompositeSubscription()
|
||||||
|
|
||||||
|
private var lastClickPosition = -1
|
||||||
|
|
||||||
fun onCreate(controller: LibraryController) {
|
fun onCreate(controller: LibraryController) {
|
||||||
this.controller = controller
|
this.controller = controller
|
||||||
|
|
||||||
|
@ -174,6 +176,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
}
|
}
|
||||||
is LibrarySelectionEvent.Unselected -> {
|
is LibrarySelectionEvent.Unselected -> {
|
||||||
findAndToggleSelection(event.manga)
|
findAndToggleSelection(event.manga)
|
||||||
|
if (adapter.indexOf(event.manga) != -1) lastClickPosition = -1
|
||||||
if (controller.selectedMangas.isEmpty()) {
|
if (controller.selectedMangas.isEmpty()) {
|
||||||
adapter.mode = SelectableAdapter.Mode.SINGLE
|
adapter.mode = SelectableAdapter.Mode.SINGLE
|
||||||
}
|
}
|
||||||
|
@ -181,6 +184,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
is LibrarySelectionEvent.Cleared -> {
|
is LibrarySelectionEvent.Cleared -> {
|
||||||
adapter.mode = SelectableAdapter.Mode.SINGLE
|
adapter.mode = SelectableAdapter.Mode.SINGLE
|
||||||
adapter.clearSelection()
|
adapter.clearSelection()
|
||||||
|
lastClickPosition = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,10 +208,11 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
* @param position the position of the element clicked.
|
* @param position the position of the element clicked.
|
||||||
* @return true if the item should be selected, false otherwise.
|
* @return true if the item should be selected, false otherwise.
|
||||||
*/
|
*/
|
||||||
override fun onItemClick(view: View, position: Int): Boolean {
|
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||||
// If the action mode is created and the position is valid, toggle the selection.
|
// If the action mode is created and the position is valid, toggle the selection.
|
||||||
val item = adapter.getItem(position) ?: return false
|
val item = adapter.getItem(position) ?: return false
|
||||||
if (adapter.mode == SelectableAdapter.Mode.MULTI) {
|
if (adapter.mode == SelectableAdapter.Mode.MULTI) {
|
||||||
|
lastClickPosition = position
|
||||||
toggleSelection(position)
|
toggleSelection(position)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -223,7 +228,15 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
*/
|
*/
|
||||||
override fun onItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
controller.createActionModeIfNeeded()
|
controller.createActionModeIfNeeded()
|
||||||
toggleSelection(position)
|
when {
|
||||||
|
lastClickPosition == -1 -> setSelection(position)
|
||||||
|
lastClickPosition > position -> for (i in position until lastClickPosition)
|
||||||
|
setSelection(i)
|
||||||
|
lastClickPosition < position -> for (i in lastClickPosition + 1..position)
|
||||||
|
setSelection(i)
|
||||||
|
else -> setSelection(position)
|
||||||
|
}
|
||||||
|
lastClickPosition = position
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -247,4 +260,17 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||||
controller.invalidateActionMode()
|
controller.invalidateActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the presenter to set the selection for the given position.
|
||||||
|
*
|
||||||
|
* @param position the position to toggle.
|
||||||
|
*/
|
||||||
|
private fun setSelection(position: Int) {
|
||||||
|
val item = adapter.getItem(position) ?: return
|
||||||
|
|
||||||
|
controller.setSelection(item.manga, true)
|
||||||
|
controller.invalidateActionMode()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
*/
|
*/
|
||||||
private val selectedItems = mutableSetOf<ChapterItem>()
|
private val selectedItems = mutableSetOf<ChapterItem>()
|
||||||
|
|
||||||
|
private var lastClickPosition = -1
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
setOptionsMenuHidden(true)
|
setOptionsMenuHidden(true)
|
||||||
|
@ -184,8 +186,9 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
fun onNextChapters(chapters: List<ChapterItem>) {
|
fun onNextChapters(chapters: List<ChapterItem>) {
|
||||||
// If the list is empty, fetch chapters from source if the conditions are met
|
// If the list is empty, fetch chapters from source if the conditions are met
|
||||||
// We use presenter chapters instead because they are always unfiltered
|
// We use presenter chapters instead because they are always unfiltered
|
||||||
if (presenter.chapters.isEmpty())
|
if (presenter.chapters.isEmpty()) {
|
||||||
initialFetchChapters()
|
initialFetchChapters()
|
||||||
|
}
|
||||||
|
|
||||||
val adapter = adapter ?: return
|
val adapter = adapter ?: return
|
||||||
adapter.updateDataSet(chapters)
|
adapter.updateDataSet(chapters)
|
||||||
|
@ -242,10 +245,11 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(view: View, position: Int): Boolean {
|
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||||
val adapter = adapter ?: return false
|
val adapter = adapter ?: return false
|
||||||
val item = adapter.getItem(position) ?: return false
|
val item = adapter.getItem(position) ?: return false
|
||||||
if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) {
|
if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) {
|
||||||
|
lastClickPosition = position
|
||||||
toggleSelection(position)
|
toggleSelection(position)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -256,7 +260,16 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
|
|
||||||
override fun onItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
createActionModeIfNeeded()
|
createActionModeIfNeeded()
|
||||||
toggleSelection(position)
|
when {
|
||||||
|
lastClickPosition == -1 -> setSelection(position)
|
||||||
|
lastClickPosition > position -> for (i in position until lastClickPosition)
|
||||||
|
setSelection(i)
|
||||||
|
lastClickPosition < position -> for (i in lastClickPosition + 1..position)
|
||||||
|
setSelection(i)
|
||||||
|
else -> setSelection(position)
|
||||||
|
}
|
||||||
|
lastClickPosition = position
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SELECTIONS & ACTION MODE
|
// SELECTIONS & ACTION MODE
|
||||||
|
@ -265,6 +278,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
val adapter = adapter ?: return
|
val adapter = adapter ?: return
|
||||||
val item = adapter.getItem(position) ?: return
|
val item = adapter.getItem(position) ?: return
|
||||||
adapter.toggleSelection(position)
|
adapter.toggleSelection(position)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
if (adapter.isSelected(position)) {
|
if (adapter.isSelected(position)) {
|
||||||
selectedItems.add(item)
|
selectedItems.add(item)
|
||||||
} else {
|
} else {
|
||||||
|
@ -273,6 +287,16 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
actionMode?.invalidate()
|
actionMode?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setSelection(position: Int) {
|
||||||
|
val adapter = adapter ?: return
|
||||||
|
val item = adapter.getItem(position) ?: return
|
||||||
|
if (!adapter.isSelected(position)) {
|
||||||
|
adapter.toggleSelection(position)
|
||||||
|
selectedItems.add(item)
|
||||||
|
actionMode?.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSelectedChapters(): List<ChapterItem> {
|
private fun getSelectedChapters(): List<ChapterItem> {
|
||||||
val adapter = adapter ?: return emptyList()
|
val adapter = adapter ?: return emptyList()
|
||||||
return adapter.selectedPositions.mapNotNull { adapter.getItem(it) }
|
return adapter.selectedPositions.mapNotNull { adapter.getItem(it) }
|
||||||
|
@ -285,6 +309,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun destroyActionModeIfNeeded() {
|
private fun destroyActionModeIfNeeded() {
|
||||||
|
lastClickPosition = -1
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +398,6 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun showDeleteChaptersConfirmationDialog() {
|
private fun showDeleteChaptersConfirmationDialog() {
|
||||||
DeleteChaptersDialog(this).showDialog(router)
|
DeleteChaptersDialog(this).showDialog(router)
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue