diff --git a/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt b/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt index b2ac9734f..5d6afd311 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/ChapterTransition.kt @@ -28,7 +28,6 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.download.DownloadManager -import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import tachiyomi.domain.chapter.service.calculateChapterGap import tachiyomi.domain.manga.model.Manga @@ -43,7 +42,7 @@ fun ChapterTransition( manga ?: return val currChapter = transition.from.chapter - val currChapterDownloaded = transition.from.pageLoader is DownloadPageLoader + val currChapterDownloaded = transition.from.pageLoader?.isLocal == true val goingToChapter = transition.to?.chapter val goingToChapterDownloaded = if (goingToChapter != null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 32e74f43d..d1f6760b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -25,8 +25,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader -import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader -import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage @@ -365,7 +363,7 @@ class ReaderViewModel( return } - if (chapter.pageLoader is HttpPageLoader) { + if (chapter.pageLoader?.isLocal == false) { val manga = manga ?: return val dbChapter = chapter.chapter val isDownloaded = downloadManager.isChapterDownloaded( @@ -440,7 +438,7 @@ class ReaderViewModel( if (amount == 0 || !manga.favorite) return // Only download ahead if current + next chapter is already downloaded too to avoid jank - if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return + if (getCurrentChapter()?.pageLoader?.isLocal == true) return val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return viewModelScope.launchIO { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt index c55ebddb6..837986b28 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt @@ -10,11 +10,10 @@ import java.io.FileInputStream /** * Loader used to load a chapter from a directory given on [file]. */ -class DirectoryPageLoader(val file: File) : PageLoader() { +internal class DirectoryPageLoader(val file: File) : PageLoader() { + + override var isLocal: Boolean = true - /** - * Returns the pages found on this directory ordered with a natural comparator. - */ override suspend fun getPages(): List { return file.listFiles() ?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } @@ -28,9 +27,4 @@ class DirectoryPageLoader(val file: File) : PageLoader() { } .orEmpty() } - - /** - * No additional action required to load the page - */ - override suspend fun loadPage(page: ReaderPage) {} } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index bd47b0915..64b6a73f5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -17,7 +17,7 @@ import java.io.File /** * Loader used to load a chapter from the downloaded chapters. */ -class DownloadPageLoader( +internal class DownloadPageLoader( private val chapter: ReaderChapter, private val manga: Manga, private val source: Source, @@ -25,19 +25,12 @@ class DownloadPageLoader( private val downloadProvider: DownloadProvider, ) : PageLoader() { - // Needed to open input streams private val context: Application by injectLazy() private var zipPageLoader: ZipPageLoader? = null - override fun recycle() { - super.recycle() - zipPageLoader?.recycle() - } + override var isLocal: Boolean = true - /** - * Returns the pages found on this downloaded chapter. - */ override suspend fun getPages(): List { val dbChapter = chapter.chapter val chapterPath = downloadProvider.findChapterDir(dbChapter.name, dbChapter.scanlator, manga.title, source) @@ -48,6 +41,11 @@ class DownloadPageLoader( } } + override fun recycle() { + super.recycle() + zipPageLoader?.recycle() + } + private suspend fun getPagesFromArchive(chapterPath: UniFile): List { val loader = ZipPageLoader(File(chapterPath.filePath!!)).also { zipPageLoader = it } return loader.getPages() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt index 6d581f2ba..324af51bf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt @@ -8,24 +8,12 @@ import java.io.File /** * Loader used to load a chapter from a .epub file. */ -class EpubPageLoader(file: File) : PageLoader() { +internal class EpubPageLoader(file: File) : PageLoader() { - /** - * The epub file. - */ private val epub = EpubFile(file) - /** - * Recycles this loader and the open zip. - */ - override fun recycle() { - super.recycle() - epub.close() - } + override var isLocal: Boolean = true - /** - * Returns the pages found on this zip archive ordered with a natural comparator. - */ override suspend fun getPages(): List { return epub.getImagesFromPages() .mapIndexed { i, path -> @@ -37,10 +25,12 @@ class EpubPageLoader(file: File) : PageLoader() { } } - /** - * No additional action required to load the page - */ override suspend fun loadPage(page: ReaderPage) { check(!isRecycled) } + + override fun recycle() { + super.recycle() + epub.close() + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt index 55bf83f3a..0f55c2241 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt @@ -27,7 +27,7 @@ import kotlin.math.min /** * Loader used to load chapters from an online source. */ -class HttpPageLoader( +internal class HttpPageLoader( private val chapter: ReaderChapter, private val source: HttpSource, private val chapterCache: ChapterCache = Injekt.get(), @@ -56,30 +56,7 @@ class HttpPageLoader( } } - /** - * Recycles this loader and the active subscriptions and queue. - */ - override fun recycle() { - super.recycle() - scope.cancel() - queue.clear() - - // Cache current page list progress for online chapters to allow a faster reopen - val pages = chapter.pages - if (pages != null) { - launchIO { - try { - // Convert to pages without reader information - val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) } - chapterCache.putPageListToCache(chapter.chapter.toDomainChapter()!!, pagesToSave) - } catch (e: Throwable) { - if (e is CancellationException) { - throw e - } - } - } - } - } + override var isLocal: Boolean = false /** * Returns the page list for a chapter. It tries to return the page list from the local cache, @@ -135,8 +112,41 @@ class HttpPageLoader( } } + /** + * Retries a page. This method is only called from user interaction on the viewer. + */ + override fun retryPage(page: ReaderPage) { + if (page.status == Page.State.ERROR) { + page.status = Page.State.QUEUE + } + queue.offer(PriorityPage(page, 2)) + } + + override fun recycle() { + super.recycle() + scope.cancel() + queue.clear() + + // Cache current page list progress for online chapters to allow a faster reopen + val pages = chapter.pages + if (pages != null) { + launchIO { + try { + // Convert to pages without reader information + val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) } + chapterCache.putPageListToCache(chapter.chapter.toDomainChapter()!!, pagesToSave) + } catch (e: Throwable) { + if (e is CancellationException) { + throw e + } + } + } + } + } + /** * Preloads the given [amount] of pages after the [currentPage] with a lower priority. + * * @return a list of [PriorityPage] that were added to the [queue] */ private fun preloadNextPages(currentPage: ReaderPage, amount: Int): List { @@ -155,35 +165,6 @@ class HttpPageLoader( } } - /** - * Retries a page. This method is only called from user interaction on the viewer. - */ - override fun retryPage(page: ReaderPage) { - if (page.status == Page.State.ERROR) { - page.status = Page.State.QUEUE - } - queue.offer(PriorityPage(page, 2)) - } - - /** - * Data class used to keep ordering of pages in order to maintain priority. - */ - private class PriorityPage( - val page: ReaderPage, - val priority: Int, - ) : Comparable { - companion object { - private val idGenerator = AtomicInteger() - } - - private val identifier = idGenerator.incrementAndGet() - - override fun compareTo(other: PriorityPage): Int { - val p = other.priority.compareTo(priority) - return if (p != 0) p else identifier.compareTo(other.identifier) - } - } - /** * Loads the page, retrieving the image URL and downloading the image if necessary. * Downloaded images are stored in the chapter cache. @@ -214,3 +195,22 @@ class HttpPageLoader( } } } + +/** + * Data class used to keep ordering of pages in order to maintain priority. + */ +private class PriorityPage( + val page: ReaderPage, + val priority: Int, +) : Comparable { + companion object { + private val idGenerator = AtomicInteger() + } + + private val identifier = idGenerator.incrementAndGet() + + override fun compareTo(other: PriorityPage): Int { + val p = other.priority.compareTo(priority) + return if (p != 0) p else identifier.compareTo(other.identifier) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt index 720e81a43..164de6bda 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt @@ -15,14 +15,7 @@ abstract class PageLoader { var isRecycled = false private set - /** - * Recycles this loader. Implementations must override this method to clean up any active - * resources. - */ - @CallSuper - open fun recycle() { - isRecycled = true - } + abstract var isLocal: Boolean /** * Returns the list of pages of a chapter. @@ -34,11 +27,20 @@ abstract class PageLoader { * Progress of the page loading should be followed via [page.statusFlow]. * [loadPage] is not currently guaranteed to complete, so it should be launched asynchronously. */ - abstract suspend fun loadPage(page: ReaderPage) + open suspend fun loadPage(page: ReaderPage) {} /** * Retries the given [page] in case it failed to load. This method only makes sense when an * online source is used. */ open fun retryPage(page: ReaderPage) {} + + /** + * Recycles this loader. Implementations must override this method to clean up any active + * resources. + */ + @CallSuper + open fun recycle() { + isRecycled = true + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt index 6cd804dda..5374216ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt @@ -15,34 +15,20 @@ import java.util.concurrent.Executors /** * Loader used to load a chapter from a .rar or .cbr file. */ -class RarPageLoader(file: File) : PageLoader() { +internal class RarPageLoader(file: File) : PageLoader() { - /** - * The rar archive to load pages from. - */ - private val archive = Archive(file) + private val rar = Archive(file) /** * Pool for copying compressed files to an input stream. */ private val pool = Executors.newFixedThreadPool(1) - /** - * Recycles this loader and the open archive. - */ - override fun recycle() { - super.recycle() - archive.close() - pool.shutdown() - } + override var isLocal: Boolean = true - /** - * Returns an RxJava Single containing the pages found on this rar archive ordered with a natural - * comparator. - */ override suspend fun getPages(): List { - return archive.fileHeaders.asSequence() - .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } + return rar.fileHeaders.asSequence() + .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } } .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .mapIndexed { i, header -> ReaderPage(i).apply { @@ -53,13 +39,16 @@ class RarPageLoader(file: File) : PageLoader() { .toList() } - /** - * No additional action required to load the page - */ override suspend fun loadPage(page: ReaderPage) { check(!isRecycled) } + override fun recycle() { + super.recycle() + rar.close() + pool.shutdown() + } + /** * Returns an input stream for the given [header]. */ @@ -69,7 +58,7 @@ class RarPageLoader(file: File) : PageLoader() { pool.execute { try { pipeOut.use { - archive.extractFile(header, it) + rar.extractFile(header, it) } } catch (e: Exception) { } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index 5022fb45e..e04fe78e6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -12,28 +12,16 @@ import java.util.zip.ZipFile /** * Loader used to load a chapter from a .zip or .cbz file. */ -class ZipPageLoader(file: File) : PageLoader() { +internal class ZipPageLoader(file: File) : PageLoader() { - /** - * The zip file to load pages from. - */ private val zip = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ZipFile(file, StandardCharsets.ISO_8859_1) } else { ZipFile(file) } - /** - * Recycles this loader and the open zip. - */ - override fun recycle() { - super.recycle() - zip.close() - } + override var isLocal: Boolean = true - /** - * Returns the pages found on this zip archive ordered with a natural comparator. - */ override suspend fun getPages(): List { return zip.entries().asSequence() .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } @@ -47,10 +35,12 @@ class ZipPageLoader(file: File) : PageLoader() { .toList() } - /** - * No additional action required to load the page - */ override suspend fun loadPage(page: ReaderPage) { check(!isRecycled) } + + override fun recycle() { + super.recycle() + zip.close() + } }