|
@@ -32,7 +32,7 @@ import timber.log.Timber
|
|
|
import uy.kohesive.injekt.Injekt
|
|
|
import uy.kohesive.injekt.api.get
|
|
|
import java.io.File
|
|
|
-import java.util.Date
|
|
|
+import java.util.*
|
|
|
import java.util.concurrent.TimeUnit
|
|
|
|
|
|
/**
|
|
@@ -84,12 +84,25 @@ class ReaderPresenter(
|
|
|
private val chapterList by lazy {
|
|
|
val manga = manga!!
|
|
|
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
|
|
+
|
|
|
val selectedChapter = dbChapters.find { it.id == chapterId }
|
|
|
- ?: error("Requested chapter of id $chapterId not found in chapter list")
|
|
|
+ ?: error("Requested chapter of id $chapterId not found in chapter list")
|
|
|
+
|
|
|
+ val chaptersForReader =
|
|
|
+ if (preferences.skipRead()) {
|
|
|
+ var list = dbChapters.filter { it -> !it.read }.toMutableList()
|
|
|
+ val find = list.find { it.id == chapterId }
|
|
|
+ if (find == null) {
|
|
|
+ list.add(selectedChapter)
|
|
|
+ }
|
|
|
+ list
|
|
|
+ } else {
|
|
|
+ dbChapters
|
|
|
+ }
|
|
|
|
|
|
when (manga.sorting) {
|
|
|
- Manga.SORTING_SOURCE -> ChapterLoadBySource().get(dbChapters)
|
|
|
- Manga.SORTING_NUMBER -> ChapterLoadByNumber().get(dbChapters, selectedChapter)
|
|
|
+ Manga.SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader)
|
|
|
+ Manga.SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter)
|
|
|
else -> error("Unknown sorting method")
|
|
|
}.map(::ReaderChapter)
|
|
|
}
|
|
@@ -165,12 +178,12 @@ class ReaderPresenter(
|
|
|
if (!needsInit()) return
|
|
|
|
|
|
db.getManga(mangaId).asRxObservable()
|
|
|
- .first()
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .doOnNext { init(it, initialChapterId) }
|
|
|
- .subscribeFirst({ _, _ ->
|
|
|
- // Ignore onNext event
|
|
|
- }, ReaderActivity::setInitialChapterError)
|
|
|
+ .first()
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .doOnNext { init(it, initialChapterId) }
|
|
|
+ .subscribeFirst({ _, _ ->
|
|
|
+ // Ignore onNext event
|
|
|
+ }, ReaderActivity::setInitialChapterError)
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -193,13 +206,13 @@ class ReaderPresenter(
|
|
|
// Read chapterList from an io thread because it's retrieved lazily and would block main.
|
|
|
activeChapterSubscription?.unsubscribe()
|
|
|
activeChapterSubscription = Observable
|
|
|
- .fromCallable { chapterList.first { chapterId == it.chapter.id } }
|
|
|
- .flatMap { getLoadObservable(loader!!, it) }
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .subscribeFirst({ _, _ ->
|
|
|
- // Ignore onNext event
|
|
|
- }, ReaderActivity::setInitialChapterError)
|
|
|
+ .fromCallable { chapterList.first { chapterId == it.chapter.id } }
|
|
|
+ .flatMap { getLoadObservable(loader!!, it) }
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .subscribeFirst({ _, _ ->
|
|
|
+ // Ignore onNext event
|
|
|
+ }, ReaderActivity::setInitialChapterError)
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -214,23 +227,23 @@ class ReaderPresenter(
|
|
|
chapter: ReaderChapter
|
|
|
): Observable<ViewerChapters> {
|
|
|
return loader.loadChapter(chapter)
|
|
|
- .andThen(Observable.fromCallable {
|
|
|
- val chapterPos = chapterList.indexOf(chapter)
|
|
|
-
|
|
|
- ViewerChapters(chapter,
|
|
|
- chapterList.getOrNull(chapterPos - 1),
|
|
|
- chapterList.getOrNull(chapterPos + 1))
|
|
|
- })
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .doOnNext { newChapters ->
|
|
|
- val oldChapters = viewerChaptersRelay.value
|
|
|
-
|
|
|
- // Add new references first to avoid unnecessary recycling
|
|
|
- newChapters.ref()
|
|
|
- oldChapters?.unref()
|
|
|
-
|
|
|
- viewerChaptersRelay.call(newChapters)
|
|
|
- }
|
|
|
+ .andThen(Observable.fromCallable {
|
|
|
+ val chapterPos = chapterList.indexOf(chapter)
|
|
|
+
|
|
|
+ ViewerChapters(chapter,
|
|
|
+ chapterList.getOrNull(chapterPos - 1),
|
|
|
+ chapterList.getOrNull(chapterPos + 1))
|
|
|
+ })
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .doOnNext { newChapters ->
|
|
|
+ val oldChapters = viewerChaptersRelay.value
|
|
|
+
|
|
|
+ // Add new references first to avoid unnecessary recycling
|
|
|
+ newChapters.ref()
|
|
|
+ oldChapters?.unref()
|
|
|
+
|
|
|
+ viewerChaptersRelay.call(newChapters)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -244,10 +257,10 @@ class ReaderPresenter(
|
|
|
|
|
|
activeChapterSubscription?.unsubscribe()
|
|
|
activeChapterSubscription = getLoadObservable(loader, chapter)
|
|
|
- .toCompletable()
|
|
|
- .onErrorComplete()
|
|
|
- .subscribe()
|
|
|
- .also(::add)
|
|
|
+ .toCompletable()
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribe()
|
|
|
+ .also(::add)
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -262,13 +275,13 @@ class ReaderPresenter(
|
|
|
|
|
|
activeChapterSubscription?.unsubscribe()
|
|
|
activeChapterSubscription = getLoadObservable(loader, chapter)
|
|
|
- .doOnSubscribe { isLoadingAdjacentChapterRelay.call(true) }
|
|
|
- .doOnUnsubscribe { isLoadingAdjacentChapterRelay.call(false) }
|
|
|
- .subscribeFirst({ view, _ ->
|
|
|
- view.moveToPageIndex(0)
|
|
|
- }, { _, _ ->
|
|
|
- // Ignore onError event, viewers handle that state
|
|
|
- })
|
|
|
+ .doOnSubscribe { isLoadingAdjacentChapterRelay.call(true) }
|
|
|
+ .doOnUnsubscribe { isLoadingAdjacentChapterRelay.call(false) }
|
|
|
+ .subscribeFirst({ view, _ ->
|
|
|
+ view.moveToPageIndex(0)
|
|
|
+ }, { _, _ ->
|
|
|
+ // Ignore onError event, viewers handle that state
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -285,12 +298,12 @@ class ReaderPresenter(
|
|
|
val loader = loader ?: return
|
|
|
|
|
|
loader.loadChapter(chapter)
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- // Update current chapters whenever a chapter is preloaded
|
|
|
- .doOnCompleted { viewerChaptersRelay.value?.let(viewerChaptersRelay::call) }
|
|
|
- .onErrorComplete()
|
|
|
- .subscribe()
|
|
|
- .also(::add)
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ // Update current chapters whenever a chapter is preloaded
|
|
|
+ .doOnCompleted { viewerChaptersRelay.value?.let(viewerChaptersRelay::call) }
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribe()
|
|
|
+ .also(::add)
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -331,9 +344,9 @@ class ReaderPresenter(
|
|
|
*/
|
|
|
private fun saveChapterProgress(chapter: ReaderChapter) {
|
|
|
db.updateChapterProgress(chapter.chapter).asRxCompletable()
|
|
|
- .onErrorComplete()
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .subscribe()
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .subscribe()
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -342,9 +355,9 @@ class ReaderPresenter(
|
|
|
private fun saveChapterHistory(chapter: ReaderChapter) {
|
|
|
val history = History.create(chapter.chapter).apply { last_read = Date().time }
|
|
|
db.updateHistoryLastRead(history).asRxCompletable()
|
|
|
- .onErrorComplete()
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .subscribe()
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .subscribe()
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -394,18 +407,18 @@ class ReaderPresenter(
|
|
|
db.updateMangaViewer(manga).executeAsBlocking()
|
|
|
|
|
|
Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
|
|
- .subscribeFirst({ view, _ ->
|
|
|
- val currChapters = viewerChaptersRelay.value
|
|
|
- if (currChapters != null) {
|
|
|
- // Save current page
|
|
|
- val currChapter = currChapters.currChapter
|
|
|
- currChapter.requestedPage = currChapter.chapter.last_page_read
|
|
|
-
|
|
|
- // Emit manga and chapters to the new viewer
|
|
|
- view.setManga(manga)
|
|
|
- view.setChapters(currChapters)
|
|
|
- }
|
|
|
- })
|
|
|
+ .subscribeFirst({ view, _ ->
|
|
|
+ val currChapters = viewerChaptersRelay.value
|
|
|
+ if (currChapters != null) {
|
|
|
+ // Save current page
|
|
|
+ val currChapter = currChapters.currChapter
|
|
|
+ currChapter.requestedPage = currChapter.chapter.last_page_read
|
|
|
+
|
|
|
+ // Emit manga and chapters to the new viewer
|
|
|
+ view.setManga(manga)
|
|
|
+ view.setChapters(currChapters)
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -446,22 +459,22 @@ class ReaderPresenter(
|
|
|
|
|
|
// Pictures directory.
|
|
|
val destDir = File(Environment.getExternalStorageDirectory().absolutePath +
|
|
|
- File.separator + Environment.DIRECTORY_PICTURES +
|
|
|
- File.separator + "Tachiyomi")
|
|
|
+ File.separator + Environment.DIRECTORY_PICTURES +
|
|
|
+ File.separator + "Tachiyomi")
|
|
|
|
|
|
// Copy file in background.
|
|
|
Observable.fromCallable { saveImage(page, destDir, manga) }
|
|
|
- .doOnNext { file ->
|
|
|
- DiskUtil.scanMedia(context, file)
|
|
|
- notifier.onComplete(file)
|
|
|
- }
|
|
|
- .doOnError { notifier.onError(it.message) }
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .subscribeFirst(
|
|
|
- { view, file -> view.onSaveImageResult(SaveImageResult.Success(file)) },
|
|
|
- { view, error -> view.onSaveImageResult(SaveImageResult.Error(error)) }
|
|
|
- )
|
|
|
+ .doOnNext { file ->
|
|
|
+ DiskUtil.scanMedia(context, file)
|
|
|
+ notifier.onComplete(file)
|
|
|
+ }
|
|
|
+ .doOnError { notifier.onError(it.message) }
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .subscribeFirst(
|
|
|
+ { view, file -> view.onSaveImageResult(SaveImageResult.Success(file)) },
|
|
|
+ { view, error -> view.onSaveImageResult(SaveImageResult.Error(error)) }
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -479,13 +492,13 @@ class ReaderPresenter(
|
|
|
val destDir = File(context.cacheDir, "shared_image")
|
|
|
|
|
|
Observable.fromCallable { destDir.delete() } // Keep only the last shared file
|
|
|
- .map { saveImage(page, destDir, manga) }
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .subscribeFirst(
|
|
|
- { view, file -> view.onShareImageResult(file) },
|
|
|
- { view, error -> /* Empty */ }
|
|
|
- )
|
|
|
+ .map { saveImage(page, destDir, manga) }
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .subscribeFirst(
|
|
|
+ { view, file -> view.onShareImageResult(file) },
|
|
|
+ { view, error -> /* Empty */ }
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -497,28 +510,28 @@ class ReaderPresenter(
|
|
|
val stream = page.stream ?: return
|
|
|
|
|
|
Observable
|
|
|
- .fromCallable {
|
|
|
- if (manga.source == LocalSource.ID) {
|
|
|
- val context = Injekt.get<Application>()
|
|
|
- LocalSource.updateCover(context, manga, stream())
|
|
|
- R.string.cover_updated
|
|
|
- SetAsCoverResult.Success
|
|
|
- } else {
|
|
|
- val thumbUrl = manga.thumbnail_url ?: throw Exception("Image url not found")
|
|
|
- if (manga.favorite) {
|
|
|
- coverCache.copyToCache(thumbUrl, stream())
|
|
|
+ .fromCallable {
|
|
|
+ if (manga.source == LocalSource.ID) {
|
|
|
+ val context = Injekt.get<Application>()
|
|
|
+ LocalSource.updateCover(context, manga, stream())
|
|
|
+ R.string.cover_updated
|
|
|
SetAsCoverResult.Success
|
|
|
} else {
|
|
|
- SetAsCoverResult.AddToLibraryFirst
|
|
|
+ val thumbUrl = manga.thumbnail_url ?: throw Exception("Image url not found")
|
|
|
+ if (manga.favorite) {
|
|
|
+ coverCache.copyToCache(thumbUrl, stream())
|
|
|
+ SetAsCoverResult.Success
|
|
|
+ } else {
|
|
|
+ SetAsCoverResult.AddToLibraryFirst
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .observeOn(AndroidSchedulers.mainThread())
|
|
|
- .subscribeFirst(
|
|
|
- { view, result -> view.onSetAsCoverResult(result) },
|
|
|
- { view, _ -> view.onSetAsCoverResult(SetAsCoverResult.Error) }
|
|
|
- )
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .observeOn(AndroidSchedulers.mainThread())
|
|
|
+ .subscribeFirst(
|
|
|
+ { view, result -> view.onSetAsCoverResult(result) },
|
|
|
+ { view, _ -> view.onSetAsCoverResult(SetAsCoverResult.Error) }
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -559,26 +572,26 @@ class ReaderPresenter(
|
|
|
val trackManager = Injekt.get<TrackManager>()
|
|
|
|
|
|
db.getTracks(manga).asRxSingle()
|
|
|
- .flatMapCompletable { trackList ->
|
|
|
- Completable.concat(trackList.map { track ->
|
|
|
- val service = trackManager.getService(track.sync_id)
|
|
|
- if (service != null && service.isLogged && lastChapterRead > track.last_chapter_read) {
|
|
|
- track.last_chapter_read = lastChapterRead
|
|
|
-
|
|
|
- // We wan't these to execute even if the presenter is destroyed and leaks
|
|
|
- // for a while. The view can still be garbage collected.
|
|
|
- Observable.defer { service.update(track) }
|
|
|
- .map { db.insertTrack(track).executeAsBlocking() }
|
|
|
- .toCompletable()
|
|
|
- .onErrorComplete()
|
|
|
- } else {
|
|
|
- Completable.complete()
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- .onErrorComplete()
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .subscribe()
|
|
|
+ .flatMapCompletable { trackList ->
|
|
|
+ Completable.concat(trackList.map { track ->
|
|
|
+ val service = trackManager.getService(track.sync_id)
|
|
|
+ if (service != null && service.isLogged && lastChapterRead > track.last_chapter_read) {
|
|
|
+ track.last_chapter_read = lastChapterRead
|
|
|
+
|
|
|
+ // We wan't these to execute even if the presenter is destroyed and leaks
|
|
|
+ // for a while. The view can still be garbage collected.
|
|
|
+ Observable.defer { service.update(track) }
|
|
|
+ .map { db.insertTrack(track).executeAsBlocking() }
|
|
|
+ .toCompletable()
|
|
|
+ .onErrorComplete()
|
|
|
+ } else {
|
|
|
+ Completable.complete()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .subscribe()
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -594,19 +607,19 @@ class ReaderPresenter(
|
|
|
if (removeAfterReadSlots == -1) return
|
|
|
|
|
|
Completable
|
|
|
- .fromCallable {
|
|
|
- // Position of the read chapter
|
|
|
- val position = chapterList.indexOf(chapter)
|
|
|
-
|
|
|
- // Retrieve chapter to delete according to preference
|
|
|
- val chapterToDelete = chapterList.getOrNull(position - removeAfterReadSlots)
|
|
|
- if (chapterToDelete != null) {
|
|
|
- downloadManager.enqueueDeleteChapters(listOf(chapterToDelete.chapter), manga)
|
|
|
+ .fromCallable {
|
|
|
+ // Position of the read chapter
|
|
|
+ val position = chapterList.indexOf(chapter)
|
|
|
+
|
|
|
+ // Retrieve chapter to delete according to preference
|
|
|
+ val chapterToDelete = chapterList.getOrNull(position - removeAfterReadSlots)
|
|
|
+ if (chapterToDelete != null) {
|
|
|
+ downloadManager.enqueueDeleteChapters(listOf(chapterToDelete.chapter), manga)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- .onErrorComplete()
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .subscribe()
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .subscribe()
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -615,9 +628,9 @@ class ReaderPresenter(
|
|
|
*/
|
|
|
private fun deletePendingChapters() {
|
|
|
Completable.fromCallable { downloadManager.deletePendingChapters() }
|
|
|
- .onErrorComplete()
|
|
|
- .subscribeOn(Schedulers.io())
|
|
|
- .subscribe()
|
|
|
+ .onErrorComplete()
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .subscribe()
|
|
|
}
|
|
|
|
|
|
}
|