Database queries are now separated by table. Improve how the app creates downloads

This commit is contained in:
len
2016-05-02 22:33:36 +02:00
parent af2b886599
commit 5e24054a0b
20 changed files with 657 additions and 532 deletions
@@ -4,6 +4,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.view.ActionMode
import android.view.*
import com.afollestad.materialdialogs.MaterialDialog
@@ -11,7 +12,6 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
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.DownloadService
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.decoration.DividerItemDecoration
@@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.getCoordinates
import eu.kanade.tachiyomi.util.getResourceDrawable
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.widget.DeletingChaptersDialog
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
import kotlinx.android.synthetic.main.fragment_manga_chapters.*
import nucleus.factory.RequiresPresenter
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import timber.log.Timber
@RequiresPresenter(ChaptersPresenter::class)
class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
@@ -40,6 +39,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
fun newInstance(): ChaptersFragment {
return ChaptersFragment()
}
}
/**
@@ -73,7 +73,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
swipe_refresh.setOnRefreshListener { fetchChapters() }
fab.setOnClickListener { v ->
fab.setOnClickListener {
val chapter = presenter.getNextUnreadChapter()
if (chapter != null) {
// Create animation listener
@@ -252,7 +252,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
chapters = chapters.subList(0, 10)
}
}
onDownload(Observable.from(chapters))
downloadChapters(chapters)
}
}
.show()
@@ -278,11 +278,11 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_select_all -> onSelectAll()
R.id.action_mark_as_read -> onMarkAsRead(getSelectedChapters())
R.id.action_mark_as_unread -> onMarkAsUnread(getSelectedChapters())
R.id.action_download -> onDownload(getSelectedChapters())
R.id.action_delete -> onDelete(getSelectedChapters())
R.id.action_select_all -> selectAll()
R.id.action_mark_as_read -> markAsRead(getSelectedChapters())
R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters())
R.id.action_download -> downloadChapters(getSelectedChapters())
R.id.action_delete -> deleteChapters(getSelectedChapters())
else -> return false
}
return true
@@ -294,66 +294,57 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
actionMode = null
}
fun getSelectedChapters(): Observable<Chapter> {
val chapters = adapter.selectedItems.map { adapter.getItem(it) }
return Observable.from(chapters)
fun getSelectedChapters(): List<Chapter> {
return adapter.selectedItems.map { adapter.getItem(it) }
}
fun destroyActionModeIfNeeded() {
actionMode?.finish()
}
protected fun onSelectAll() {
fun selectAll() {
adapter.selectAll()
setContextTitle(adapter.selectedItemCount)
}
fun onMarkAsRead(chapters: Observable<Chapter>) {
fun markAsRead(chapters: List<Chapter>) {
presenter.markChaptersRead(chapters, true)
if (presenter.preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters)
}
}
fun onMarkAsUnread(chapters: Observable<Chapter>) {
fun markAsUnread(chapters: List<Chapter>) {
presenter.markChaptersRead(chapters, false)
}
fun onMarkPreviousAsRead(chapter: Chapter) {
fun markPreviousAsRead(chapter: Chapter) {
presenter.markPreviousChaptersAsRead(chapter)
}
fun onDownload(chapters: Observable<Chapter>) {
DownloadService.start(activity)
val observable = chapters.doOnCompleted { adapter.notifyDataSetChanged() }
presenter.downloadChapters(observable)
fun downloadChapters(chapters: List<Chapter>) {
destroyActionModeIfNeeded()
presenter.downloadChapters(chapters)
}
fun onDelete(chapters: Observable<Chapter>) {
val size = adapter.selectedItemCount
val dialog = MaterialDialog.Builder(activity)
.title(R.string.deleting)
.progress(false, size, true)
.cancelable(false)
.show()
val observable = chapters
.concatMap { chapter ->
presenter.deleteChapter(chapter)
Observable.just(chapter)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { chapter ->
dialog.incrementProgress(1)
chapter.status = Download.NOT_DOWNLOADED
}
.doOnCompleted { adapter.notifyDataSetChanged() }
.doAfterTerminate { dialog.dismiss() }
presenter.deleteChapters(observable)
fun deleteChapters(chapters: List<Chapter>) {
destroyActionModeIfNeeded()
DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
presenter.deleteChapters(chapters)
}
fun onChaptersDeleted() {
dismissDeletingDialog()
adapter.notifyDataSetChanged()
}
fun onChaptersDeletedError(error: Throwable) {
dismissDeletingDialog()
Timber.e(error, error.message)
}
fun dismissDeletingDialog() {
(childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)?.dismiss()
}
override fun onListItemClick(position: Int): Boolean {
@@ -10,14 +10,16 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.util.getResourceColor
import kotlinx.android.synthetic.main.item_chapter.view.*
import rx.Observable
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
class ChaptersHolder(private val view: View, private val adapter: ChaptersAdapter, listener: FlexibleViewHolder.OnListItemClickListener) :
FlexibleViewHolder(view, adapter, listener) {
class ChaptersHolder(
private val view: View,
private val adapter: ChaptersAdapter,
listener: FlexibleViewHolder.OnListItemClickListener)
: FlexibleViewHolder(view, adapter, listener) {
private val readColor = view.context.theme.getResourceColor(android.R.attr.textColorHint)
private val unreadColor = view.context.theme.getResourceColor(android.R.attr.textColorPrimary)
@@ -27,7 +29,10 @@ class ChaptersHolder(private val view: View, private val adapter: ChaptersAdapte
private var item: Chapter? = null
init {
view.chapter_menu.setOnClickListener { v -> v.post { showPopupMenu(v) } }
// 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
// PopupMenu is shown.
view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
}
fun onSetValues(chapter: Chapter, manga: Manga?) = with(view) {
@@ -101,14 +106,14 @@ class ChaptersHolder(private val view: View, private val adapter: ChaptersAdapte
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem ->
val chapter = Observable.just(item)
val chapter = listOf(item)
when (menuItem.itemId) {
R.id.action_download -> adapter.fragment.onDownload(chapter)
R.id.action_delete -> adapter.fragment.onDelete(chapter)
R.id.action_mark_as_read -> adapter.fragment.onMarkAsRead(chapter)
R.id.action_mark_as_unread -> adapter.fragment.onMarkAsUnread(chapter)
R.id.action_mark_previous_as_read -> adapter.fragment.onMarkPreviousAsRead(item)
R.id.action_download -> adapter.fragment.downloadChapters(chapter)
R.id.action_delete -> adapter.fragment.deleteChapters(chapter)
R.id.action_mark_as_read -> adapter.fragment.markAsRead(chapter)
R.id.action_mark_as_unread -> adapter.fragment.markAsUnread(chapter)
R.id.action_mark_previous_as_read -> adapter.fragment.markPreviousAsRead(item)
}
true
}
@@ -6,14 +6,13 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.source.SourceManager
import eu.kanade.tachiyomi.data.source.base.Source
import eu.kanade.tachiyomi.event.ChapterCountEvent
import eu.kanade.tachiyomi.event.DownloadChaptersEvent
import eu.kanade.tachiyomi.event.MangaEvent
import eu.kanade.tachiyomi.event.ReaderEvent
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.SharedData
import rx.Observable
@@ -163,50 +162,59 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
return db.getNextUnreadChapter(manga).executeAsBlocking()
}
fun markChaptersRead(selectedChapters: Observable<Chapter>, read: Boolean) {
add(selectedChapters.subscribeOn(Schedulers.io())
fun markChaptersRead(selectedChapters: List<Chapter>, read: Boolean) {
Observable.from(selectedChapters)
.doOnNext { chapter ->
chapter.read = read
if (!read) chapter.last_page_read = 0
// Delete chapter when marked as read if desired by user.
if (preferences.removeAfterMarkedAsRead() && read) {
deleteChapter(chapter)
if (!read) {
chapter.last_page_read = 0
}
}
.toList()
.flatMap { chapters -> db.insertChapters(chapters).asRxObservable() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe())
.flatMap { db.insertChapters(it).asRxObservable() }
.subscribeOn(Schedulers.io())
.subscribe()
}
fun markPreviousChaptersAsRead(selected: Chapter) {
Observable.from(chapters)
.filter { c -> c.chapter_number > -1 && c.chapter_number < selected.chapter_number }
.doOnNext { c -> c.read = true }
.filter { it.chapter_number > -1 && it.chapter_number < selected.chapter_number }
.doOnNext { it.read = true }
.toList()
.flatMap { chapters -> db.insertChapters(chapters).asRxObservable() }
.flatMap { db.insertChapters(it).asRxObservable() }
.subscribe()
}
fun downloadChapters(selectedChapters: Observable<Chapter>) {
add(selectedChapters.toList()
fun downloadChapters(chapters: List<Chapter>) {
DownloadService.start(context)
downloadManager.downloadChapters(manga, chapters)
}
fun deleteChapters(chapters: List<Chapter>) {
val wasRunning = downloadManager.isRunning
if (wasRunning) {
DownloadService.stop(context)
}
Observable.from(chapters)
.doOnNext { deleteChapter(it) }
.toList()
.doOnNext { if (onlyDownloaded()) refreshChapters() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { downloadManager.onDownloadChaptersEvent(DownloadChaptersEvent(manga, it)) })
.subscribeFirst({ view, result ->
view.onChaptersDeleted()
if (wasRunning) {
DownloadService.start(context)
}
}, { view, error ->
view.onChaptersDeletedError(error)
})
}
fun deleteChapters(selectedChapters: Observable<Chapter>) {
add(selectedChapters.subscribe(
{ chapter -> downloadManager.queue.del(chapter) },
{ error -> Timber.e(error.message) },
{
if (onlyDownloaded())
refreshChapters()
}))
}
fun deleteChapter(chapter: Chapter) {
private fun deleteChapter(chapter: Chapter) {
downloadManager.queue.del(chapter)
downloadManager.deleteChapter(source, manga, chapter)
chapter.status = Download.NOT_DOWNLOADED
}
fun revertSortOrder() {