diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index a3d5297f6..1b2e6bb32 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -107,6 +107,8 @@ object PreferenceKeys { const val defaultCategory = "default_category" + const val skipRead = "skip_read" + const val downloadBadge = "display_download_badge" @Deprecated("Use the preferences of the source") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index c7b908f55..58ad2b0af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -167,6 +167,8 @@ class PreferencesHelper(val context: Context) { fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1) + fun skipRead() = prefs.getBoolean(Keys.skipRead, false) + fun migrateFlags() = rxPrefs.getInteger("migrate_flags", Int.MAX_VALUE) fun trustedSignatures() = rxPrefs.getStringSet("trusted_signatures", emptySet()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index ab8b56ee8..14739759d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -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 { return loader.loadChapter(chapter) - .andThen(Observable.fromCallable { - val chapterPos = chapterList.indexOf(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 + 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() + // Add new references first to avoid unnecessary recycling + newChapters.ref() + oldChapters?.unref() - viewerChaptersRelay.call(newChapters) - } + 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 + .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) - } - }) + // 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() - 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() + 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() 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 + .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() + // 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) + .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) + // 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() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt index ac413f270..ae59d13f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt @@ -63,6 +63,11 @@ class SettingsReaderController : SettingsController() { defaultValue = "500" summary = "%s" } + switchPreference { + key = Keys.skipRead + titleRes = R.string.pref_skip_read_chapters + defaultValue = false + } switchPreference { key = Keys.fullscreen titleRes = R.string.pref_fullscreen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 27befe0b9..f19427f62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -178,6 +178,7 @@ Use custom brightness Use custom color filter Keep screen on + Skip chapters marked read Navigation Volume keys Invert volume keys