mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Update chapters adapter
This commit is contained in:
parent
b512c67b5d
commit
112cdd54e3
7 changed files with 252 additions and 236 deletions
|
@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.ui.manga.chapter
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.Download
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
|
||||||
import eu.kanade.tachiyomi.util.getResourceColor
|
import eu.kanade.tachiyomi.util.getResourceColor
|
||||||
import kotlinx.android.synthetic.main.item_chapter.view.*
|
import kotlinx.android.synthetic.main.item_chapter.view.*
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
@ -13,11 +13,10 @@ import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ChaptersHolder(
|
class ChapterHolder(
|
||||||
private val view: View,
|
private val view: View,
|
||||||
private val adapter: ChaptersAdapter,
|
private val adapter: ChaptersAdapter)
|
||||||
listener: FlexibleViewHolder.OnListItemClickListener)
|
: FlexibleViewHolder(view, adapter) {
|
||||||
: FlexibleViewHolder(view, adapter, listener) {
|
|
||||||
|
|
||||||
private val readColor = view.context.getResourceColor(android.R.attr.textColorHint)
|
private val readColor = view.context.getResourceColor(android.R.attr.textColorHint)
|
||||||
private val unreadColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
|
private val unreadColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
|
||||||
|
@ -25,8 +24,6 @@ class ChaptersHolder(
|
||||||
private val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols().apply { decimalSeparator = '.' })
|
private val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols().apply { decimalSeparator = '.' })
|
||||||
private val df = DateFormat.getDateInstance(DateFormat.SHORT)
|
private val df = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||||
|
|
||||||
private var item: ChapterModel? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// We need to post a Runnable to show the popup to make sure that the PopupMenu is
|
// We need to post a Runnable to show the popup to make sure that the PopupMenu is
|
||||||
// correctly positioned. The reason being that the view may change position before the
|
// correctly positioned. The reason being that the view may change position before the
|
||||||
|
@ -34,10 +31,10 @@ class ChaptersHolder(
|
||||||
view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
|
view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSetValues(chapter: ChapterModel, manga: Manga?) = with(view) {
|
fun bind(item: ChapterItem, manga: Manga) = with(view) {
|
||||||
item = chapter
|
val chapter = item.chapter
|
||||||
|
|
||||||
chapter_title.text = when (manga?.displayMode) {
|
chapter_title.text = when (manga.displayMode) {
|
||||||
Manga.DISPLAY_NUMBER -> {
|
Manga.DISPLAY_NUMBER -> {
|
||||||
val formattedNumber = decimalFormat.format(chapter.chapter_number.toDouble())
|
val formattedNumber = decimalFormat.format(chapter.chapter_number.toDouble())
|
||||||
context.getString(R.string.display_mode_chapter, formattedNumber)
|
context.getString(R.string.display_mode_chapter, formattedNumber)
|
||||||
|
@ -62,7 +59,7 @@ class ChaptersHolder(
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyStatus(chapter.status)
|
notifyStatus(item.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyStatus(status: Int) = with(view.download_text) {
|
fun notifyStatus(status: Int) = with(view.download_text) {
|
||||||
|
@ -75,15 +72,19 @@ class ChaptersHolder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPopupMenu(view: View) = item?.let { chapter ->
|
private fun showPopupMenu(view: View) {
|
||||||
|
val item = adapter.getItem(adapterPosition) ?: return
|
||||||
|
|
||||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||||
val popup = PopupMenu(view.context, view)
|
val popup = PopupMenu(view.context, view)
|
||||||
|
|
||||||
// Inflate our menu resource into the PopupMenu's Menu
|
// Inflate our menu resource into the PopupMenu's Menu
|
||||||
popup.menuInflater.inflate(R.menu.chapter_single, popup.menu)
|
popup.menuInflater.inflate(R.menu.chapter_single, popup.menu)
|
||||||
|
|
||||||
|
val chapter = item.chapter
|
||||||
|
|
||||||
// Hide download and show delete if the chapter is downloaded
|
// Hide download and show delete if the chapter is downloaded
|
||||||
if (chapter.isDownloaded) {
|
if (item.isDownloaded) {
|
||||||
popup.menu.findItem(R.id.action_download).isVisible = false
|
popup.menu.findItem(R.id.action_download).isVisible = false
|
||||||
popup.menu.findItem(R.id.action_delete).isVisible = true
|
popup.menu.findItem(R.id.action_delete).isVisible = true
|
||||||
}
|
}
|
||||||
|
@ -104,20 +105,7 @@ class ChaptersHolder(
|
||||||
|
|
||||||
// Set a listener so we are notified if a menu item is clicked
|
// Set a listener so we are notified if a menu item is clicked
|
||||||
popup.setOnMenuItemClickListener { menuItem ->
|
popup.setOnMenuItemClickListener { menuItem ->
|
||||||
val chapterList = listOf(chapter)
|
adapter.menuItemListener(adapterPosition, menuItem)
|
||||||
|
|
||||||
with(adapter.fragment) {
|
|
||||||
when (menuItem.itemId) {
|
|
||||||
R.id.action_download -> downloadChapters(chapterList)
|
|
||||||
R.id.action_bookmark -> bookmarkChapters(chapterList, true)
|
|
||||||
R.id.action_remove_bookmark -> bookmarkChapters(chapterList, false)
|
|
||||||
R.id.action_delete -> deleteChapters(chapterList)
|
|
||||||
R.id.action_mark_as_read -> markAsRead(chapterList)
|
|
||||||
R.id.action_mark_as_unread -> markAsUnread(chapterList)
|
|
||||||
R.id.action_mark_previous_as_read -> markPreviousAsRead(chapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
|
||||||
|
class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem<ChapterHolder>(),
|
||||||
|
Chapter by chapter {
|
||||||
|
|
||||||
|
private var _status: Int = 0
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
get() = download?.status ?: _status
|
||||||
|
set(value) { _status = value }
|
||||||
|
|
||||||
|
@Transient var download: Download? = null
|
||||||
|
|
||||||
|
val isDownloaded: Boolean
|
||||||
|
get() = status == Download.DOWNLOADED
|
||||||
|
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.item_chapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createViewHolder(adapter: FlexibleAdapter<*>, inflater: LayoutInflater, parent: ViewGroup): ChapterHolder {
|
||||||
|
return ChapterHolder(inflater.inflate(layoutRes, parent, false), adapter as ChaptersAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<*>, holder: ChapterHolder, position: Int, payloads: List<Any?>?) {
|
||||||
|
holder.bind(this, manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other is ChapterItem) {
|
||||||
|
return chapter.id!! == other.chapter.id!!
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return chapter.id!!.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
|
||||||
|
|
||||||
class ChapterModel(c: Chapter) : Chapter by c {
|
|
||||||
|
|
||||||
private var _status: Int = 0
|
|
||||||
|
|
||||||
var status: Int
|
|
||||||
get() = download?.status ?: _status
|
|
||||||
set(value) { _status = value }
|
|
||||||
|
|
||||||
@Transient var download: Download? = null
|
|
||||||
|
|
||||||
val isDownloaded: Boolean
|
|
||||||
get() = status == Download.DOWNLOADED
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,42 +1,19 @@
|
||||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.MenuItem
|
||||||
import eu.davidea.flexibleadapter4.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.util.inflate
|
|
||||||
|
|
||||||
class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<ChaptersHolder, ChapterModel>() {
|
class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<ChapterItem>(null, fragment, true) {
|
||||||
|
|
||||||
init {
|
var items: List<ChapterItem> = emptyList()
|
||||||
setHasStableIds(true)
|
|
||||||
|
val menuItemListener: (Int, MenuItem) -> Unit = { position, item ->
|
||||||
|
fragment.onItemMenuClick(position, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
var items: List<ChapterModel>
|
override fun updateDataSet(items: List<ChapterItem>) {
|
||||||
get() = mItems
|
this.items = items
|
||||||
set(value) {
|
super.updateDataSet(items.toList())
|
||||||
mItems = value
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateDataSet(param: String) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersHolder {
|
|
||||||
val v = parent.inflate(R.layout.item_chapter)
|
|
||||||
return ChaptersHolder(v, this, fragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ChaptersHolder, position: Int) {
|
|
||||||
val chapter = getItem(position)
|
|
||||||
val manga = fragment.presenter.manga
|
|
||||||
holder.onSetValues(chapter, manga)
|
|
||||||
|
|
||||||
//When user scrolls this bind the correct selection status
|
|
||||||
holder.itemView.isActivated = isSelected(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
return mItems[position].id!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,11 @@ import android.support.v7.widget.DividerItemDecoration
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import eu.davidea.flexibleadapter4.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.Download
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
|
@ -30,7 +29,10 @@ import nucleus.factory.RequiresPresenter
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@RequiresPresenter(ChaptersPresenter::class)
|
@RequiresPresenter(ChaptersPresenter::class)
|
||||||
class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
|
class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(),
|
||||||
|
ActionMode.Callback,
|
||||||
|
FlexibleAdapter.OnItemClickListener,
|
||||||
|
FlexibleAdapter.OnItemLongClickListener {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
@ -71,38 +73,31 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
recycler.layoutManager = LinearLayoutManager(activity)
|
recycler.layoutManager = LinearLayoutManager(activity)
|
||||||
recycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
recycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||||
recycler.setHasFixedSize(true)
|
recycler.setHasFixedSize(true)
|
||||||
|
// TODO enable in a future commit
|
||||||
|
// adapter.setFastScroller(fast_scroller, context.getResourceColor(R.attr.colorAccent))
|
||||||
|
// adapter.toggleFastScroller()
|
||||||
|
|
||||||
swipe_refresh.setOnRefreshListener { fetchChapters() }
|
swipe_refresh.setOnRefreshListener { fetchChapters() }
|
||||||
|
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
val chapter = presenter.getNextUnreadChapter()
|
val item = presenter.getNextUnreadChapter()
|
||||||
if (chapter != null) {
|
if (item != null) {
|
||||||
// Create animation listener
|
// Create animation listener
|
||||||
val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
|
val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationStart(animation: Animator?) {
|
override fun onAnimationStart(animation: Animator?) {
|
||||||
openChapter(chapter, true)
|
openChapter(item.chapter, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get coordinates and start animation
|
// Get coordinates and start animation
|
||||||
val coordinates = fab.getCoordinates()
|
val coordinates = fab.getCoordinates()
|
||||||
if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) {
|
if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) {
|
||||||
openChapter(chapter)
|
openChapter(item.chapter)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.toast(R.string.no_next_chapter)
|
context.toast(R.string.no_next_chapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
// Stop recycler's scrolling when onPause is called. If the activity is finishing
|
|
||||||
// the presenter will be destroyed, and it could cause NPE
|
|
||||||
// https://github.com/inorichi/tachiyomi/issues/159
|
|
||||||
recycler.stopScroll()
|
|
||||||
|
|
||||||
super.onPause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -173,19 +168,20 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun onNextManga(manga: Manga) {
|
fun onNextManga(manga: Manga) {
|
||||||
// Set initial values
|
// Set initial values
|
||||||
activity.supportInvalidateOptionsMenu()
|
activity.supportInvalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNextChapters(chapters: List<ChapterModel>) {
|
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()
|
||||||
|
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
adapter.items = chapters
|
adapter.updateDataSet(chapters)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initialFetchChapters() {
|
private fun initialFetchChapters() {
|
||||||
|
@ -230,7 +226,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
.title(R.string.action_display_mode)
|
.title(R.string.action_display_mode)
|
||||||
.items(modes.map { getString(it) })
|
.items(modes.map { getString(it) })
|
||||||
.itemsIds(ids)
|
.itemsIds(ids)
|
||||||
.itemsCallbackSingleChoice(selectedIndex) { dialog, itemView, which, text ->
|
.itemsCallbackSingleChoice(selectedIndex) { _, itemView, _, _ ->
|
||||||
// Save the new display mode
|
// Save the new display mode
|
||||||
presenter.setDisplayMode(itemView.id)
|
presenter.setDisplayMode(itemView.id)
|
||||||
// Refresh ui
|
// Refresh ui
|
||||||
|
@ -250,7 +246,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
.title(R.string.sorting_mode)
|
.title(R.string.sorting_mode)
|
||||||
.items(modes.map { getString(it) })
|
.items(modes.map { getString(it) })
|
||||||
.itemsIds(ids)
|
.itemsIds(ids)
|
||||||
.itemsCallbackSingleChoice(selectedIndex) { dialog, itemView, which, text ->
|
.itemsCallbackSingleChoice(selectedIndex) { _, itemView, _, _ ->
|
||||||
// Save the new sorting mode
|
// Save the new sorting mode
|
||||||
presenter.setSorting(itemView.id)
|
presenter.setSorting(itemView.id)
|
||||||
true
|
true
|
||||||
|
@ -267,7 +263,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
.title(R.string.manga_download)
|
.title(R.string.manga_download)
|
||||||
.negativeText(android.R.string.cancel)
|
.negativeText(android.R.string.cancel)
|
||||||
.items(modes.map { getString(it) })
|
.items(modes.map { getString(it) })
|
||||||
.itemsCallback { dialog, view, i, charSequence ->
|
.itemsCallback { _, _, i, _ ->
|
||||||
|
|
||||||
fun getUnreadChaptersSorted() = presenter.chapters
|
fun getUnreadChaptersSorted() = presenter.chapters
|
||||||
.filter { !it.read && it.status == Download.NOT_DOWNLOADED }
|
.filter { !it.read && it.status == Download.NOT_DOWNLOADED }
|
||||||
|
@ -299,8 +295,8 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
getHolder(download.chapter)?.notifyStatus(download.status)
|
getHolder(download.chapter)?.notifyStatus(download.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHolder(chapter: Chapter): ChaptersHolder? {
|
private fun getHolder(chapter: Chapter): ChapterHolder? {
|
||||||
return recycler.findViewHolderForItemId(chapter.id!!) as? ChaptersHolder
|
return recycler.findViewHolderForItemId(chapter.id!!) as? ChapterHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
@ -324,7 +320,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
.content(R.string.confirm_delete_chapters)
|
.content(R.string.confirm_delete_chapters)
|
||||||
.positiveText(android.R.string.yes)
|
.positiveText(android.R.string.yes)
|
||||||
.negativeText(android.R.string.no)
|
.negativeText(android.R.string.no)
|
||||||
.onPositive { dialog, action -> deleteChapters(getSelectedChapters()) }
|
.onPositive { _, _ -> deleteChapters(getSelectedChapters()) }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
|
@ -338,8 +334,8 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
actionMode = null
|
actionMode = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSelectedChapters(): List<ChapterModel> {
|
fun getSelectedChapters(): List<ChapterItem> {
|
||||||
return adapter.selectedItems.map { adapter.getItem(it) }
|
return adapter.selectedPositions.map { adapter.getItem(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroyActionModeIfNeeded() {
|
fun destroyActionModeIfNeeded() {
|
||||||
|
@ -351,18 +347,18 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
setContextTitle(adapter.selectedItemCount)
|
setContextTitle(adapter.selectedItemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markAsRead(chapters: List<ChapterModel>) {
|
fun markAsRead(chapters: List<ChapterItem>) {
|
||||||
presenter.markChaptersRead(chapters, true)
|
presenter.markChaptersRead(chapters, true)
|
||||||
if (presenter.preferences.removeAfterMarkedAsRead()) {
|
if (presenter.preferences.removeAfterMarkedAsRead()) {
|
||||||
deleteChapters(chapters)
|
deleteChapters(chapters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markAsUnread(chapters: List<ChapterModel>) {
|
fun markAsUnread(chapters: List<ChapterItem>) {
|
||||||
presenter.markChaptersRead(chapters, false)
|
presenter.markChaptersRead(chapters, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markPreviousAsRead(chapter: ChapterModel) {
|
fun markPreviousAsRead(chapter: ChapterItem) {
|
||||||
val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items
|
val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items
|
||||||
val chapterPos = chapters.indexOf(chapter)
|
val chapterPos = chapters.indexOf(chapter)
|
||||||
if (chapterPos != -1) {
|
if (chapterPos != -1) {
|
||||||
|
@ -370,7 +366,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadChapters(chapters: List<ChapterModel>) {
|
fun downloadChapters(chapters: List<ChapterItem>) {
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
presenter.downloadChapters(chapters)
|
presenter.downloadChapters(chapters)
|
||||||
if (!presenter.manga.favorite){
|
if (!presenter.manga.favorite){
|
||||||
|
@ -382,12 +378,12 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bookmarkChapters(chapters: List<ChapterModel>, bookmarked: Boolean) {
|
fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
presenter.bookmarkChapters(chapters, bookmarked)
|
presenter.bookmarkChapters(chapters, bookmarked)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteChapters(chapters: List<ChapterModel>) {
|
fun deleteChapters(chapters: List<ChapterItem>) {
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
|
DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
|
||||||
presenter.deleteChapters(chapters)
|
presenter.deleteChapters(chapters)
|
||||||
|
@ -408,26 +404,40 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
|
||||||
?.dismissAllowingStateLoss()
|
?.dismissAllowingStateLoss()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onListItemClick(position: Int): Boolean {
|
override fun onItemClick(position: Int): Boolean {
|
||||||
val item = adapter.getItem(position) ?: return false
|
val item = adapter.getItem(position) ?: return false
|
||||||
if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
|
if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
|
||||||
toggleSelection(position)
|
toggleSelection(position)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
openChapter(item)
|
openChapter(item.chapter)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onListItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
if (actionMode == null)
|
if (actionMode == null)
|
||||||
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
|
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
|
||||||
|
|
||||||
toggleSelection(position)
|
toggleSelection(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onItemMenuClick(position: Int, item: MenuItem) {
|
||||||
|
val chapter = adapter.getItem(position)?.let { listOf(it) } ?: return
|
||||||
|
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_download -> downloadChapters(chapter)
|
||||||
|
R.id.action_bookmark -> bookmarkChapters(chapter, true)
|
||||||
|
R.id.action_remove_bookmark -> bookmarkChapters(chapter, false)
|
||||||
|
R.id.action_delete -> deleteChapters(chapter)
|
||||||
|
R.id.action_mark_as_read -> markAsRead(chapter)
|
||||||
|
R.id.action_mark_as_unread -> markAsUnread(chapter)
|
||||||
|
R.id.action_mark_previous_as_read -> markPreviousAsRead(chapter[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun toggleSelection(position: Int) {
|
private fun toggleSelection(position: Int) {
|
||||||
adapter.toggleSelection(position, false)
|
adapter.toggleSelection(position)
|
||||||
|
|
||||||
val count = adapter.selectedItemCount
|
val count = adapter.selectedItemCount
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
|
|
|
@ -65,14 +65,14 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
/**
|
/**
|
||||||
* List of chapters of the manga. It's always unfiltered and unsorted.
|
* List of chapters of the manga. It's always unfiltered and unsorted.
|
||||||
*/
|
*/
|
||||||
var chapters: List<ChapterModel> = emptyList()
|
var chapters: List<ChapterItem> = emptyList()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subject of list of chapters to allow updating the view without going to DB.
|
* Subject of list of chapters to allow updating the view without going to DB.
|
||||||
*/
|
*/
|
||||||
val chaptersRelay: PublishRelay<List<ChapterModel>>
|
val chaptersRelay: PublishRelay<List<ChapterItem>>
|
||||||
by lazy { PublishRelay.create<List<ChapterModel>>() }
|
by lazy { PublishRelay.create<List<ChapterItem>>() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the chapter list has been requested to the source.
|
* Whether the chapter list has been requested to the source.
|
||||||
|
@ -103,7 +103,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
chaptersRelay.flatMap { applyChapterFilters(it) }
|
chaptersRelay.flatMap { applyChapterFilters(it) }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeLatestCache(ChaptersFragment::onNextChapters,
|
.subscribeLatestCache(ChaptersFragment::onNextChapters,
|
||||||
{ view, error -> Timber.e(error) })
|
{ _, error -> Timber.e(error) })
|
||||||
|
|
||||||
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
|
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
|
||||||
// changes, and sends the list of chapters to the relay.
|
// changes, and sends the list of chapters to the relay.
|
||||||
|
@ -135,15 +135,15 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
.filter { download -> download.manga.id == manga.id }
|
.filter { download -> download.manga.id == manga.id }
|
||||||
.doOnNext { onDownloadStatusChange(it) }
|
.doOnNext { onDownloadStatusChange(it) }
|
||||||
.subscribeLatestCache(ChaptersFragment::onChapterStatusChange,
|
.subscribeLatestCache(ChaptersFragment::onChapterStatusChange,
|
||||||
{ view, error -> Timber.e(error) })
|
{ _, error -> Timber.e(error) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a chapter from the database to an extended model, allowing to store new fields.
|
* Converts a chapter from the database to an extended model, allowing to store new fields.
|
||||||
*/
|
*/
|
||||||
private fun Chapter.toModel(): ChapterModel {
|
private fun Chapter.toModel(): ChapterItem {
|
||||||
// Create the model object.
|
// Create the model object.
|
||||||
val model = ChapterModel(this)
|
val model = ChapterItem(this, manga)
|
||||||
|
|
||||||
// Find an active download for this chapter.
|
// Find an active download for this chapter.
|
||||||
val download = downloadManager.queue.find { it.chapter.id == id }
|
val download = downloadManager.queue.find { it.chapter.id == id }
|
||||||
|
@ -160,7 +160,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
*
|
*
|
||||||
* @param chapters the list of chapter from the database.
|
* @param chapters the list of chapter from the database.
|
||||||
*/
|
*/
|
||||||
private fun setDownloadedChapters(chapters: List<ChapterModel>) {
|
private fun setDownloadedChapters(chapters: List<ChapterItem>) {
|
||||||
val files = downloadManager.findMangaDir(source, manga)?.listFiles() ?: return
|
val files = downloadManager.findMangaDir(source, manga)?.listFiles() ?: return
|
||||||
val cached = mutableMapOf<Chapter, String>()
|
val cached = mutableMapOf<Chapter, String>()
|
||||||
files.mapNotNull { it.name }
|
files.mapNotNull { it.name }
|
||||||
|
@ -181,7 +181,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.map { syncChaptersWithSource(db, it, manga, source) }
|
.map { syncChaptersWithSource(db, it, manga, source) }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeFirst({ view, chapters ->
|
.subscribeFirst({ view, _ ->
|
||||||
view.onFetchChaptersDone()
|
view.onFetchChaptersDone()
|
||||||
}, ChaptersFragment::onFetchChaptersError)
|
}, ChaptersFragment::onFetchChaptersError)
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* @param chapters the list of chapters from the database
|
* @param chapters the list of chapters from the database
|
||||||
* @return an observable of the list of chapters filtered and sorted.
|
* @return an observable of the list of chapters filtered and sorted.
|
||||||
*/
|
*/
|
||||||
private fun applyChapterFilters(chapters: List<ChapterModel>): Observable<List<ChapterModel>> {
|
private fun applyChapterFilters(chapters: List<ChapterItem>): Observable<List<ChapterItem>> {
|
||||||
var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
|
var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
|
||||||
if (onlyUnread()) {
|
if (onlyUnread()) {
|
||||||
observable = observable.filter { !it.read }
|
observable = observable.filter { !it.read }
|
||||||
|
@ -248,7 +248,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
/**
|
/**
|
||||||
* Returns the next unread chapter or null if everything is read.
|
* Returns the next unread chapter or null if everything is read.
|
||||||
*/
|
*/
|
||||||
fun getNextUnreadChapter(): ChapterModel? {
|
fun getNextUnreadChapter(): ChapterItem? {
|
||||||
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* @param selectedChapters the list of selected chapters.
|
* @param selectedChapters the list of selected chapters.
|
||||||
* @param read whether to mark chapters as read or unread.
|
* @param read whether to mark chapters as read or unread.
|
||||||
*/
|
*/
|
||||||
fun markChaptersRead(selectedChapters: List<ChapterModel>, read: Boolean) {
|
fun markChaptersRead(selectedChapters: List<ChapterItem>, read: Boolean) {
|
||||||
Observable.from(selectedChapters)
|
Observable.from(selectedChapters)
|
||||||
.doOnNext { chapter ->
|
.doOnNext { chapter ->
|
||||||
chapter.read = read
|
chapter.read = read
|
||||||
|
@ -275,7 +275,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* Downloads the given list of chapters with the manager.
|
* Downloads the given list of chapters with the manager.
|
||||||
* @param chapters the list of chapters to download.
|
* @param chapters the list of chapters to download.
|
||||||
*/
|
*/
|
||||||
fun downloadChapters(chapters: List<ChapterModel>) {
|
fun downloadChapters(chapters: List<ChapterItem>) {
|
||||||
DownloadService.start(context)
|
DownloadService.start(context)
|
||||||
downloadManager.downloadChapters(manga, chapters)
|
downloadManager.downloadChapters(manga, chapters)
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* Bookmarks the given list of chapters.
|
* Bookmarks the given list of chapters.
|
||||||
* @param selectedChapters the list of chapters to bookmark.
|
* @param selectedChapters the list of chapters to bookmark.
|
||||||
*/
|
*/
|
||||||
fun bookmarkChapters(selectedChapters: List<ChapterModel>, bookmarked: Boolean) {
|
fun bookmarkChapters(selectedChapters: List<ChapterItem>, bookmarked: Boolean) {
|
||||||
Observable.from(selectedChapters)
|
Observable.from(selectedChapters)
|
||||||
.doOnNext { chapter ->
|
.doOnNext { chapter ->
|
||||||
chapter.bookmark = bookmarked
|
chapter.bookmark = bookmarked
|
||||||
|
@ -299,14 +299,14 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* Deletes the given list of chapter.
|
* Deletes the given list of chapter.
|
||||||
* @param chapters the list of chapters to delete.
|
* @param chapters the list of chapters to delete.
|
||||||
*/
|
*/
|
||||||
fun deleteChapters(chapters: List<ChapterModel>) {
|
fun deleteChapters(chapters: List<ChapterItem>) {
|
||||||
Observable.from(chapters)
|
Observable.from(chapters)
|
||||||
.doOnNext { deleteChapter(it) }
|
.doOnNext { deleteChapter(it) }
|
||||||
.toList()
|
.toList()
|
||||||
.doOnNext { if (onlyDownloaded()) refreshChapters() }
|
.doOnNext { if (onlyDownloaded()) refreshChapters() }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeFirst({ view, result ->
|
.subscribeFirst({ view, _ ->
|
||||||
view.onChaptersDeleted()
|
view.onChaptersDeleted()
|
||||||
}, ChaptersFragment::onChaptersDeletedError)
|
}, ChaptersFragment::onChaptersDeletedError)
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||||
* Deletes a chapter from disk. This method is called in a background thread.
|
* Deletes a chapter from disk. This method is called in a background thread.
|
||||||
* @param chapter the chapter to delete.
|
* @param chapter the chapter to delete.
|
||||||
*/
|
*/
|
||||||
private fun deleteChapter(chapter: ChapterModel) {
|
private fun deleteChapter(chapter: ChapterItem) {
|
||||||
downloadManager.queue.remove(chapter)
|
downloadManager.queue.remove(chapter)
|
||||||
downloadManager.deleteChapter(source, manga, chapter)
|
downloadManager.deleteChapter(source, manga, chapter)
|
||||||
chapter.status = Download.NOT_DOWNLOADED
|
chapter.status = Download.NOT_DOWNLOADED
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorAccent"
|
android:background="?attr/colorAccent"
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"/>
|
||||||
/>
|
|
||||||
|
|
||||||
<android.support.v4.widget.SwipeRefreshLayout
|
<android.support.v4.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipe_refresh"
|
android:id="@+id/swipe_refresh"
|
||||||
|
@ -36,6 +35,17 @@
|
||||||
|
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
</android.support.v4.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
<eu.davidea.fastscroller.FastScroller
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/fast_scroller"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
<android.support.design.widget.FloatingActionButton
|
<android.support.design.widget.FloatingActionButton
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
style="@style/Theme.Widget.FAB"
|
style="@style/Theme.Widget.FAB"
|
||||||
|
|
Loading…
Reference in a new issue