library tag searching and reordering downloads
This commit is contained in:
parent
bc6a1a1da6
commit
53402459f2
@ -5,6 +5,7 @@ import com.hippo.unifile.UniFile
|
|||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
@ -92,6 +93,18 @@ class DownloadManager(context: Context) {
|
|||||||
downloader.clearQueue(isNotification)
|
downloader.clearQueue(isNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorders the download queue.
|
||||||
|
*
|
||||||
|
* @param downloads value to set the download queue to
|
||||||
|
*/
|
||||||
|
fun reorderQueue(downloads: List<Download>) {
|
||||||
|
downloader.pause()
|
||||||
|
downloader.queue.clear()
|
||||||
|
downloader.queue.addAll(downloads)
|
||||||
|
downloader.start()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the downloader to enqueue the given list of chapters.
|
* Tells the downloader to enqueue the given list of chapters.
|
||||||
*
|
*
|
||||||
|
@ -95,7 +95,7 @@ class Downloader(
|
|||||||
fun start(): Boolean {
|
fun start(): Boolean {
|
||||||
if (isRunning || queue.isEmpty())
|
if (isRunning || queue.isEmpty())
|
||||||
return false
|
return false
|
||||||
|
notifier.paused = false
|
||||||
if (!subscriptions.hasSubscriptions())
|
if (!subscriptions.hasSubscriptions())
|
||||||
initializeSubscriptions()
|
initializeSubscriptions()
|
||||||
|
|
||||||
|
@ -1,72 +1,24 @@
|
|||||||
package eu.kanade.tachiyomi.ui.download
|
package eu.kanade.tachiyomi.ui.download
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import android.view.ViewGroup
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
|
||||||
import eu.kanade.tachiyomi.util.inflate
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter storing a list of downloads.
|
* Adapter storing a list of downloads.
|
||||||
*
|
*
|
||||||
* @param context the context of the fragment containing this adapter.
|
* @param context the context of the fragment containing this adapter.
|
||||||
*/
|
*/
|
||||||
class DownloadAdapter : RecyclerView.Adapter<DownloadHolder>() {
|
class DownloadAdapter(controller: DownloadController) : FlexibleAdapter<DownloadItem>(null, controller,
|
||||||
|
true) {
|
||||||
private var items = emptyList<Download>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
setHasStableIds(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a list of downloads in the adapter.
|
* Listener called when an item of the list is released.
|
||||||
*
|
|
||||||
* @param downloads the list to set.
|
|
||||||
*/
|
*/
|
||||||
fun setItems(downloads: List<Download>) {
|
val onItemReleaseListener: OnItemReleaseListener = controller
|
||||||
items = downloads
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface OnItemReleaseListener {
|
||||||
/**
|
/**
|
||||||
* Returns the number of downloads in the adapter
|
* Called when an item of the list is released.
|
||||||
*/
|
*/
|
||||||
override fun getItemCount(): Int {
|
fun onItemReleased(position: Int)
|
||||||
return items.size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the identifier for a download.
|
|
||||||
*
|
|
||||||
* @param position the position in the adapter.
|
|
||||||
* @return an identifier for the item.
|
|
||||||
*/
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
return items[position].chapter.id!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new view holder.
|
|
||||||
*
|
|
||||||
* @param parent the parent view.
|
|
||||||
* @param viewType the type of the holder.
|
|
||||||
* @return a new view holder for a manga.
|
|
||||||
*/
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadHolder {
|
|
||||||
val view = parent.inflate(R.layout.download_item)
|
|
||||||
return DownloadHolder(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds a holder with a new position.
|
|
||||||
*
|
|
||||||
* @param holder the holder to bind.
|
|
||||||
* @param position the position to bind.
|
|
||||||
*/
|
|
||||||
override fun onBindViewHolder(holder: DownloadHolder, position: Int) {
|
|
||||||
val download = items[position]
|
|
||||||
holder.onSetValues(download)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@ import java.util.concurrent.TimeUnit
|
|||||||
* Controller that shows the currently active downloads.
|
* Controller that shows the currently active downloads.
|
||||||
* Uses R.layout.fragment_download_queue.
|
* Uses R.layout.fragment_download_queue.
|
||||||
*/
|
*/
|
||||||
class DownloadController : NucleusController<DownloadPresenter>() {
|
class DownloadController : NucleusController<DownloadPresenter>(),
|
||||||
|
DownloadAdapter.OnItemReleaseListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter containing the active downloads.
|
* Adapter containing the active downloads.
|
||||||
@ -58,8 +59,9 @@ class DownloadController : NucleusController<DownloadPresenter>() {
|
|||||||
setInformationView()
|
setInformationView()
|
||||||
|
|
||||||
// Initialize adapter.
|
// Initialize adapter.
|
||||||
adapter = DownloadAdapter()
|
adapter = DownloadAdapter(this@DownloadController)
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
|
adapter?.isHandleDragEnabled = true
|
||||||
|
|
||||||
// Set the layout manager for the recycler and fixed size.
|
// Set the layout manager for the recycler and fixed size.
|
||||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
@ -168,7 +170,7 @@ class DownloadController : NucleusController<DownloadPresenter>() {
|
|||||||
// Avoid leaking subscriptions
|
// Avoid leaking subscriptions
|
||||||
progressSubscriptions.remove(download)?.unsubscribe()
|
progressSubscriptions.remove(download)?.unsubscribe()
|
||||||
|
|
||||||
progressSubscriptions.put(download, subscription)
|
progressSubscriptions[download] = subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,10 +200,10 @@ class DownloadController : NucleusController<DownloadPresenter>() {
|
|||||||
*
|
*
|
||||||
* @param downloads the downloads from the queue.
|
* @param downloads the downloads from the queue.
|
||||||
*/
|
*/
|
||||||
fun onNextDownloads(downloads: List<Download>) {
|
fun onNextDownloads(downloads: List<DownloadItem>) {
|
||||||
activity?.invalidateOptionsMenu()
|
activity?.invalidateOptionsMenu()
|
||||||
setInformationView()
|
setInformationView()
|
||||||
adapter?.setItems(downloads)
|
adapter?.updateDataSet(downloads)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,4 +246,15 @@ class DownloadController : NucleusController<DownloadPresenter>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an item is released from a drag.
|
||||||
|
*
|
||||||
|
* @param position The position of the released item.
|
||||||
|
*/
|
||||||
|
override fun onItemReleased(position: Int) {
|
||||||
|
val adapter = adapter ?: return
|
||||||
|
val downloads = (0 until adapter.itemCount).mapNotNull { adapter.getItem(it)?.download }
|
||||||
|
presenter.reorder(downloads)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.download
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
|
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||||
import kotlinx.android.synthetic.main.download_item.view.*
|
import kotlinx.android.synthetic.main.download_item.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to hold the data of a download.
|
* Class used to hold the data of a download.
|
||||||
@ -12,33 +12,36 @@ import kotlinx.android.synthetic.main.download_item.view.*
|
|||||||
* @param view the inflated view for this holder.
|
* @param view the inflated view for this holder.
|
||||||
* @constructor creates a new download holder.
|
* @constructor creates a new download holder.
|
||||||
*/
|
*/
|
||||||
class DownloadHolder(private val view: View) : BaseViewHolder(view) {
|
class DownloadHolder(view: View, val adapter: DownloadAdapter) : BaseFlexibleViewHolder(view, adapter) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
setDragHandleView(reorder)
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var download: Download
|
private lateinit var download: Download
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called from [DownloadAdapter.onBindViewHolder]. It updates the data for this
|
* Binds this holder with the given category
|
||||||
* holder with the given download.
|
|
||||||
*
|
*
|
||||||
* @param download the download to bind.
|
* @param category The category to bind
|
||||||
*/
|
*/
|
||||||
fun onSetValues(download: Download) {
|
fun bind(download: Download) {
|
||||||
this.download = download
|
this.download = download
|
||||||
|
|
||||||
// Update the chapter name.
|
// Update the chapter name.
|
||||||
view.chapter_title.text = download.chapter.name
|
chapter_title.text = download.chapter.name
|
||||||
|
|
||||||
// Update the manga title
|
// Update the manga title
|
||||||
view.manga_title.text = download.manga.title
|
manga_title.text = download.manga.title
|
||||||
|
|
||||||
// Update the progress bar and the number of downloaded pages
|
// Update the progress bar and the number of downloaded pages
|
||||||
val pages = download.pages
|
val pages = download.pages
|
||||||
if (pages == null) {
|
if (pages == null) {
|
||||||
view.download_progress.progress = 0
|
download_progress.progress = 0
|
||||||
view.download_progress.max = 1
|
download_progress.max = 1
|
||||||
view.download_progress_text.text = ""
|
download_progress_text.text = ""
|
||||||
} else {
|
} else {
|
||||||
view.download_progress.max = pages.size * 100
|
download_progress.max = pages.size * 100
|
||||||
notifyProgress()
|
notifyProgress()
|
||||||
notifyDownloadedPages()
|
notifyDownloadedPages()
|
||||||
}
|
}
|
||||||
@ -49,10 +52,10 @@ class DownloadHolder(private val view: View) : BaseViewHolder(view) {
|
|||||||
*/
|
*/
|
||||||
fun notifyProgress() {
|
fun notifyProgress() {
|
||||||
val pages = download.pages ?: return
|
val pages = download.pages ?: return
|
||||||
if (view.download_progress.max == 1) {
|
if (download_progress.max == 1) {
|
||||||
view.download_progress.max = pages.size * 100
|
download_progress.max = pages.size * 100
|
||||||
}
|
}
|
||||||
view.download_progress.progress = download.totalProgress
|
download_progress.progress = download.totalProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +63,12 @@ class DownloadHolder(private val view: View) : BaseViewHolder(view) {
|
|||||||
*/
|
*/
|
||||||
fun notifyDownloadedPages() {
|
fun notifyDownloadedPages() {
|
||||||
val pages = download.pages ?: return
|
val pages = download.pages ?: return
|
||||||
view.download_progress_text.text = "${download.downloadedImages}/${pages.size}"
|
download_progress_text.text = "${download.downloadedImages}/${pages.size}"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemReleased(position: Int) {
|
||||||
|
super.onItemReleased(position)
|
||||||
|
adapter.onItemReleaseListener.onItemReleased(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.download
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
|
||||||
|
class DownloadItem(val download: Download) : AbstractFlexibleItem<DownloadHolder>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this item is currently selected.
|
||||||
|
*/
|
||||||
|
var isSelected = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layout resource for this item.
|
||||||
|
*/
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.download_item
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new view holder for this item.
|
||||||
|
*
|
||||||
|
* @param view The view of this item.
|
||||||
|
* @param adapter The adapter of this item.
|
||||||
|
*/
|
||||||
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView
|
||||||
|
.ViewHolder>>): DownloadHolder {
|
||||||
|
return DownloadHolder(view, adapter as DownloadAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the given view holder with this item.
|
||||||
|
*
|
||||||
|
* @param adapter The adapter of this item.
|
||||||
|
* @param holder The holder to bind.
|
||||||
|
* @param position The position of this item in the adapter.
|
||||||
|
* @param payloads List of partial changes.
|
||||||
|
*/
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||||
|
holder: DownloadHolder, position: Int, payloads: MutableList<Any>) {
|
||||||
|
holder.bind(download)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this item is draggable.
|
||||||
|
*/
|
||||||
|
override fun isDraggable(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other is DownloadItem) {
|
||||||
|
return download.chapter.id == other.download.chapter.id
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return download.chapter.id!!.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,10 +32,10 @@ class DownloadPresenter : BasePresenter<DownloadController>() {
|
|||||||
|
|
||||||
downloadQueue.getUpdatedObservable()
|
downloadQueue.getUpdatedObservable()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.map { ArrayList(it) }
|
.map { it.map(::DownloadItem) }
|
||||||
.subscribeLatestCache(DownloadController::onNextDownloads, { _, error ->
|
.subscribeLatestCache(DownloadController::onNextDownloads) { _, error ->
|
||||||
Timber.e(error)
|
Timber.e(error)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDownloadStatusObservable(): Observable<Download> {
|
fun getDownloadStatusObservable(): Observable<Download> {
|
||||||
@ -62,4 +62,7 @@ class DownloadPresenter : BasePresenter<DownloadController>() {
|
|||||||
downloadManager.clearQueue()
|
downloadManager.clearQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reorder(downloads: List<Download>) {
|
||||||
|
downloadManager.reorderQueue(downloads)
|
||||||
|
}
|
||||||
}
|
}
|
@ -65,6 +65,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
*/
|
*/
|
||||||
private var subscriptions = CompositeSubscription()
|
private var subscriptions = CompositeSubscription()
|
||||||
|
|
||||||
|
private var lastClickPosition = -1
|
||||||
|
|
||||||
// EXH -->
|
// EXH -->
|
||||||
private var initialLoadHandle: LoadingHandle? = null
|
private var initialLoadHandle: LoadingHandle? = null
|
||||||
lateinit var scope: CoroutineScope
|
lateinit var scope: CoroutineScope
|
||||||
@ -233,6 +235,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
|
||||||
}
|
}
|
||||||
@ -240,6 +243,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,6 +271,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
// 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 {
|
||||||
@ -282,7 +287,16 @@ 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -305,5 +319,16 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
controller.setSelection(item.manga, !adapter.isSelected(position))
|
controller.setSelection(item.manga, !adapter.isSelected(position))
|
||||||
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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,42 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
|
|||||||
*/
|
*/
|
||||||
override fun filter(constraint: String): Boolean {
|
override fun filter(constraint: String): Boolean {
|
||||||
return manga.title.contains(constraint, true) ||
|
return manga.title.contains(constraint, true) ||
|
||||||
(manga.author?.contains(constraint, true) ?: false)
|
(manga.author?.contains(constraint, true) ?: false) ||
|
||||||
|
if (constraint.contains(" ") || constraint.contains("\"")) {
|
||||||
|
val genres = manga.genre?.split(", ")?.map {
|
||||||
|
it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces
|
||||||
|
}
|
||||||
|
var clean_constraint = ""
|
||||||
|
var ignorespace = false
|
||||||
|
for (i in constraint.trim().toLowerCase()) {
|
||||||
|
if (i==' ') {
|
||||||
|
if (!ignorespace) {
|
||||||
|
clean_constraint = clean_constraint + ","
|
||||||
|
} else {
|
||||||
|
clean_constraint = clean_constraint + " "
|
||||||
|
}
|
||||||
|
} else if (i=='"') {
|
||||||
|
ignorespace = !ignorespace
|
||||||
|
} else {
|
||||||
|
clean_constraint = clean_constraint + Character.toString(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clean_constraint.split(",").all { containsGenre(it.trim(), genres) }
|
||||||
|
}
|
||||||
|
else containsGenre(constraint, manga.genre?.split(", ")?.map {
|
||||||
|
it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
|
||||||
|
return if (tag.startsWith("-"))
|
||||||
|
genres?.find {
|
||||||
|
it.trim().toLowerCase() == tag.substringAfter("-").toLowerCase()
|
||||||
|
} == null
|
||||||
|
else
|
||||||
|
genres?.find {
|
||||||
|
it.trim().toLowerCase() == tag.toLowerCase()
|
||||||
|
} != null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
@ -60,6 +60,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)
|
||||||
@ -275,6 +277,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
|||||||
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 {
|
||||||
@ -285,7 +288,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
|
||||||
@ -294,6 +306,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 {
|
||||||
@ -302,6 +315,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) }
|
||||||
@ -314,6 +337,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun destroyActionModeIfNeeded() {
|
private fun destroyActionModeIfNeeded() {
|
||||||
|
lastClickPosition = -1
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,10 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
|
|||||||
})
|
})
|
||||||
recycler.tapListener = { event ->
|
recycler.tapListener = { event ->
|
||||||
val positionX = event.rawX
|
val positionX = event.rawX
|
||||||
|
val positionY = event.rawY
|
||||||
when {
|
when {
|
||||||
|
positionY < recycler.height * 0.25 -> if (config.tappingEnabled) scrollUp()
|
||||||
|
positionY > recycler.height * 0.75 -> if (config.tappingEnabled) scrollDown()
|
||||||
positionX < recycler.width * 0.33 -> if (config.tappingEnabled) scrollUp()
|
positionX < recycler.width * 0.33 -> if (config.tappingEnabled) scrollUp()
|
||||||
positionX > recycler.width * 0.66 -> if (config.tappingEnabled) scrollDown()
|
positionX > recycler.width * 0.66 -> if (config.tappingEnabled) scrollDown()
|
||||||
else -> activity.toggleMenu()
|
else -> activity.toggleMenu()
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingLeft="@dimen/material_layout_keylines_screen_edge_margin"
|
|
||||||
android:paddingRight="@dimen/material_layout_keylines_screen_edge_margin"
|
android:paddingRight="@dimen/material_layout_keylines_screen_edge_margin"
|
||||||
android:paddingTop="@dimen/material_component_lists_padding_above_list">
|
android:paddingTop="@dimen/material_component_lists_padding_above_list">
|
||||||
|
|
||||||
@ -25,6 +25,7 @@
|
|||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
android:layout_marginStart="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||||
android:textAppearance="@style/TextAppearance.Regular.Body1"
|
android:textAppearance="@style/TextAppearance.Regular.Body1"
|
||||||
tools:text="Manga title"/>
|
tools:text="Manga title"/>
|
||||||
|
|
||||||
@ -36,6 +37,7 @@
|
|||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:text="Chapter Title"
|
tools:text="Chapter Title"
|
||||||
|
android:layout_marginStart="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||||
android:textAppearance="@style/TextAppearance.Regular.Caption"/>
|
android:textAppearance="@style/TextAppearance.Regular.Caption"/>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@ -43,6 +45,16 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/chapter_title"
|
android:layout_below="@id/chapter_title"
|
||||||
|
android:layout_marginStart="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||||
style="?android:attr/progressBarStyleHorizontal"/>
|
style="?android:attr/progressBarStyleHorizontal"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/reorder"
|
||||||
|
android:layout_width="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||||
|
android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
app:srcCompat="@drawable/ic_reorder_grey_24dp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
Loading…
x
Reference in New Issue
Block a user