refactor: Change Page.State to sealed interface
Some checks failed
Build app / Build app (push) Has been cancelled
Mirror Repository / mirror (push) Has been cancelled

This commit is contained in:
AwkwardPeak7 2025-05-28 09:19:42 +07:00 committed by Ahmad Ansori Palembani
parent 18528fbd92
commit 370bb62ef9
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
14 changed files with 63 additions and 66 deletions

View file

@ -21,8 +21,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.injectLazy
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.download.DownloadPreferences import yokai.domain.download.DownloadPreferences
import yokai.i18n.MR import yokai.i18n.MR
import yokai.util.lang.getString import yokai.util.lang.getString
@ -171,7 +171,7 @@ class DownloadManager(
return files.sortedBy { it.name } return files.sortedBy { it.name }
.mapIndexed { i, file -> .mapIndexed { i, file ->
Page(i, uri = file.uri).apply { status = Page.State.READY } Page(i, uri = file.uri).apply { status = Page.State.Ready }
} }
} }

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import android.os.Looper
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
@ -55,8 +54,8 @@ import kotlinx.coroutines.supervisorScope
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.injectLazy
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.core.archive.ZipWriter import yokai.core.archive.ZipWriter
import yokai.core.metadata.COMIC_INFO_FILE import yokai.core.metadata.COMIC_INFO_FILE
import yokai.core.metadata.ComicInfo import yokai.core.metadata.ComicInfo
@ -365,11 +364,11 @@ class Downloader(
// Get all the URLs to the source images, fetch pages if necessary // Get all the URLs to the source images, fetch pages if necessary
pageList.filter { it.imageUrl.isNullOrEmpty() }.forEach { page -> pageList.filter { it.imageUrl.isNullOrEmpty() }.forEach { page ->
page.status = Page.State.LOAD_PAGE page.status = Page.State.LoadPage
try { try {
page.imageUrl = download.source.getImageUrl(page) page.imageUrl = download.source.getImageUrl(page)
} catch (e: Throwable) { } catch (e: Throwable) {
page.status = Page.State.ERROR page.status = Page.State.Error
} }
} }
@ -494,12 +493,12 @@ class Downloader(
page.uri = file.uri page.uri = file.uri
page.progress = 100 page.progress = 100
page.status = Page.State.READY page.status = Page.State.Ready
} catch (e: Throwable) { } catch (e: Throwable) {
if (e is CancellationException) throw e if (e is CancellationException) throw e
// Mark this page as error and allow to download the remaining // Mark this page as error and allow to download the remaining
page.progress = 0 page.progress = 0
page.status = Page.State.ERROR page.status = Page.State.Error
notifier.onError(e.message, chapName, download.manga.title) notifier.onError(e.message, chapName, download.manga.title)
} }
} }
@ -518,7 +517,7 @@ class Downloader(
tmpDir: UniFile, tmpDir: UniFile,
filename: String, filename: String,
): UniFile { ): UniFile {
page.status = Page.State.DOWNLOAD_IMAGE page.status = Page.State.DownloadImage
page.progress = 0 page.progress = 0
return flow { return flow {
val response = source.getImage(page) val response = source.getImage(page)

View file

@ -22,7 +22,7 @@ class Download(val source: HttpSource, val manga: Manga, val chapter: Chapter) {
get() = pages?.sumOf(Page::progress) ?: 0 get() = pages?.sumOf(Page::progress) ?: 0
val downloadedImages: Int val downloadedImages: Int
get() = pages?.count { it.status == Page.State.READY } ?: 0 get() = pages?.count { it.status is Page.State.Ready } ?: 0
@Transient @Transient
private val _statusFlow = MutableStateFlow(State.NOT_DOWNLOADED) private val _statusFlow = MutableStateFlow(State.NOT_DOWNLOADED)

View file

@ -1655,7 +1655,7 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
} }
private fun showSetCoverPrompt(page: ReaderPage) { private fun showSetCoverPrompt(page: ReaderPage) {
if (page.status != Page.State.READY) return if (page.status !is Page.State.Ready) return
materialAlertDialog() materialAlertDialog()
.setMessage(MR.strings.use_image_as_cover) .setMessage(MR.strings.use_image_as_cover)

View file

@ -613,7 +613,7 @@ class ReaderViewModel(
} }
val shouldTrack = !preferences.incognitoMode().get() || hasTrackers val shouldTrack = !preferences.incognitoMode().get() || hasTrackers
if (shouldTrack && page.status != Page.State.ERROR) { if (shouldTrack && page.status !is Page.State.Error) {
readerChapter.chapter.last_page_read = page.index readerChapter.chapter.last_page_read = page.index
readerChapter.chapter.pages_left = (readerChapter.pages?.size ?: page.index) - page.index readerChapter.chapter.pages_left = (readerChapter.pages?.size ?: page.index) - page.index
// For double pages, check if the second to last page is doubled up // For double pages, check if the second to last page is doubled up
@ -860,7 +860,7 @@ class ReaderViewModel(
* There's also a notification to allow sharing the image somewhere else or deleting it. * There's also a notification to allow sharing the image somewhere else or deleting it.
*/ */
fun saveImage(page: ReaderPage) { fun saveImage(page: ReaderPage) {
if (page.status != Page.State.READY) return if (page.status !is Page.State.Ready) return
val manga = manga ?: return val manga = manga ?: return
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
@ -891,8 +891,8 @@ class ReaderViewModel(
fun saveImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) { fun saveImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
viewModelScope.launch { viewModelScope.launch {
if (firstPage.status != Page.State.READY) return@launch if (firstPage.status !is Page.State.Ready) return@launch
if (secondPage.status != Page.State.READY) return@launch if (secondPage.status !is Page.State.Ready) return@launch
val manga = manga ?: return@launch val manga = manga ?: return@launch
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
@ -926,7 +926,7 @@ class ReaderViewModel(
* image will be kept so it won't be taking lots of internal disk space. * image will be kept so it won't be taking lots of internal disk space.
*/ */
fun shareImage(page: ReaderPage) { fun shareImage(page: ReaderPage) {
if (page.status != Page.State.READY) return if (page.status !is Page.State.Ready) return
val manga = manga ?: return val manga = manga ?: return
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
@ -940,8 +940,8 @@ class ReaderViewModel(
fun shareImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) { fun shareImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
viewModelScope.launch { viewModelScope.launch {
if (firstPage.status != Page.State.READY) return@launch if (firstPage.status !is Page.State.Ready) return@launch
if (secondPage.status != Page.State.READY) return@launch if (secondPage.status !is Page.State.Ready) return@launch
val manga = manga ?: return@launch val manga = manga ?: return@launch
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
@ -958,7 +958,7 @@ class ReaderViewModel(
* Sets the image of this [page] as cover and notifies the UI of the result. * Sets the image of this [page] as cover and notifies the UI of the result.
*/ */
fun setAsCover(page: ReaderPage) { fun setAsCover(page: ReaderPage) {
if (page.status != Page.State.READY) return if (page.status !is Page.State.Ready) return
val manga = manga ?: return val manga = manga ?: return
val stream = page.stream ?: return val stream = page.stream ?: return

View file

@ -31,7 +31,7 @@ internal class ArchivePageLoader(private val reader: ArchiveReader) : PageLoader
.mapIndexed { i, entry -> .mapIndexed { i, entry ->
ReaderPage(i).apply { ReaderPage(i).apply {
stream = { reader.getInputStream(entry.name)!! } stream = { reader.getInputStream(entry.name)!! }
status = Page.State.READY status = Page.State.Ready
} }
} }
.toList() .toList()

View file

@ -24,7 +24,7 @@ class DirectoryPageLoader(val file: UniFile) : PageLoader() {
val streamFn = { file.openInputStream() } val streamFn = { file.openInputStream() }
ReaderPage(i).apply { ReaderPage(i).apply {
stream = streamFn stream = streamFn
status = Page.State.READY status = Page.State.Ready
} }
} ?: emptyList() } ?: emptyList()
} }

View file

@ -60,7 +60,7 @@ class DownloadPageLoader(
ReaderPage(page.index, page.url, page.imageUrl, stream = { ReaderPage(page.index, page.url, page.imageUrl, stream = {
context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!! context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!!
},).apply { },).apply {
status = Page.State.READY status = Page.State.Ready
} }
} }
} }

View file

@ -28,7 +28,7 @@ class EpubPageLoader(private val epub: EpubReader) : PageLoader() {
val streamFn = { epub.getInputStream(path)!! } val streamFn = { epub.getInputStream(path)!! }
ReaderPage(i).apply { ReaderPage(i).apply {
stream = streamFn stream = streamFn
status = Page.State.READY status = Page.State.Ready
} }
} }
} }

View file

@ -51,7 +51,7 @@ class HttpPageLoader(
emit(runInterruptible { queue.take() }.page) emit(runInterruptible { queue.take() }.page)
} }
} }
.filter { it.status == Page.State.QUEUE } .filter { it.status is Page.State.Queue }
.collect { .collect {
_loadPage(it) _loadPage(it)
} }
@ -108,17 +108,17 @@ class HttpPageLoader(
val imageUrl = page.imageUrl val imageUrl = page.imageUrl
// Check if the image has been deleted // Check if the image has been deleted
if (page.status == Page.State.READY && imageUrl != null && !chapterCache.isImageInCache(imageUrl)) { if (page.status is Page.State.Ready && imageUrl != null && !chapterCache.isImageInCache(imageUrl)) {
page.status = Page.State.QUEUE page.status = Page.State.Queue
} }
// Automatically retry failed pages when subscribed to this page // Automatically retry failed pages when subscribed to this page
if (page.status == Page.State.ERROR) { if (page.status is Page.State.Error) {
page.status = Page.State.QUEUE page.status = Page.State.Queue
} }
val queuedPages = mutableListOf<PriorityPage>() val queuedPages = mutableListOf<PriorityPage>()
if (page.status == Page.State.QUEUE) { if (page.status is Page.State.Queue) {
queuedPages += PriorityPage(page, 1).also { queue.offer(it) } queuedPages += PriorityPage(page, 1).also { queue.offer(it) }
} }
queuedPages += preloadNextPages(page, preloadSize) queuedPages += preloadNextPages(page, preloadSize)
@ -126,7 +126,7 @@ class HttpPageLoader(
suspendCancellableCoroutine<Nothing> { continuation -> suspendCancellableCoroutine<Nothing> { continuation ->
continuation.invokeOnCancellation { continuation.invokeOnCancellation {
queuedPages.forEach { queuedPages.forEach {
if (it.page.status == Page.State.QUEUE) { if (it.page.status is Page.State.Queue) {
queue.remove(it) queue.remove(it)
} }
} }
@ -146,7 +146,7 @@ class HttpPageLoader(
return pages return pages
.subList(pageIndex + 1, min(pageIndex + 1 + amount, pages.size)) .subList(pageIndex + 1, min(pageIndex + 1 + amount, pages.size))
.mapNotNull { .mapNotNull {
if (it.status == Page.State.QUEUE) { if (it.status is Page.State.Queue) {
PriorityPage(it, 0).apply { queue.offer(this) } PriorityPage(it, 0).apply { queue.offer(this) }
} else { } else {
null null
@ -158,8 +158,8 @@ class HttpPageLoader(
* Retries a page. This method is only called from user interaction on the viewer. * Retries a page. This method is only called from user interaction on the viewer.
*/ */
override fun retryPage(page: ReaderPage) { override fun retryPage(page: ReaderPage) {
if (page.status == Page.State.ERROR) { if (page.status is Page.State.Error) {
page.status = Page.State.QUEUE page.status = Page.State.Queue
} }
queue.offer(PriorityPage(page, 2)) queue.offer(PriorityPage(page, 2))
} }
@ -192,21 +192,21 @@ class HttpPageLoader(
private suspend fun _loadPage(page: ReaderPage) { private suspend fun _loadPage(page: ReaderPage) {
try { try {
if (page.imageUrl.isNullOrEmpty()) { if (page.imageUrl.isNullOrEmpty()) {
page.status = Page.State.LOAD_PAGE page.status = Page.State.LoadPage
page.imageUrl = source.getImageUrl(page) page.imageUrl = source.getImageUrl(page)
} }
val imageUrl = page.imageUrl!! val imageUrl = page.imageUrl!!
if (!chapterCache.isImageInCache(imageUrl)) { if (!chapterCache.isImageInCache(imageUrl)) {
page.status = Page.State.DOWNLOAD_IMAGE page.status = Page.State.DownloadImage
val imageResponse = source.getImage(page) val imageResponse = source.getImage(page)
chapterCache.putImageToCache(imageUrl, imageResponse) chapterCache.putImageToCache(imageUrl, imageResponse)
} }
page.stream = { chapterCache.getImageFile(imageUrl).inputStream() } page.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
page.status = Page.State.READY page.status = Page.State.Ready
} catch (e: Throwable) { } catch (e: Throwable) {
page.status = Page.State.ERROR page.status = Page.State.Error
if (e is CancellationException) { if (e is CancellationException) {
throw e throw e
} }

View file

@ -12,6 +12,6 @@ class InsertPage(parent: ReaderPage) : ReaderPage(
fullPage = true fullPage = true
firstHalf = false firstHalf = false
stream = parent.stream stream = parent.stream
status = State.READY status = State.Ready
} }
} }

View file

@ -109,8 +109,8 @@ class PagerPageHolder(
*/ */
private var extraProgressJob: Job? = null private var extraProgressJob: Job? = null
private var status = Page.State.READY private var status = Page.State.Ready
private var extraStatus = Page.State.READY private var extraStatus = Page.State.Ready
private var progress: Int = 0 private var progress: Int = 0
private var extraProgress: Int = 0 private var extraProgress: Int = 0
@ -337,19 +337,19 @@ class PagerPageHolder(
*/ */
private suspend fun processStatus(status: Page.State) { private suspend fun processStatus(status: Page.State) {
when (status) { when (status) {
Page.State.QUEUE -> setQueued() is Page.State.Queue -> setQueued()
Page.State.LOAD_PAGE -> setLoading() is Page.State.LoadPage -> setLoading()
Page.State.DOWNLOAD_IMAGE -> { is Page.State.DownloadImage -> {
launchProgressJob() launchProgressJob()
setDownloading() setDownloading()
} }
Page.State.READY -> { is Page.State.Ready -> {
if (extraStatus == Page.State.READY || extraPage == null) { if (extraPage == null) {
setImage() setImage()
} }
cancelProgressJob(1) cancelProgressJob(1)
} }
Page.State.ERROR -> { is Page.State.Error -> {
setError() setError()
cancelProgressJob(1) cancelProgressJob(1)
} }
@ -363,19 +363,17 @@ class PagerPageHolder(
*/ */
private suspend fun processStatus2(status: Page.State) { private suspend fun processStatus2(status: Page.State) {
when (status) { when (status) {
Page.State.QUEUE -> setQueued() is Page.State.Queue -> setQueued()
Page.State.LOAD_PAGE -> setLoading() is Page.State.LoadPage -> setLoading()
Page.State.DOWNLOAD_IMAGE -> { is Page.State.DownloadImage -> {
launchProgressJob2() launchProgressJob2()
setDownloading() setDownloading()
} }
Page.State.READY -> { is Page.State.Ready -> {
if (this.status == Page.State.READY) { setImage()
setImage()
}
cancelProgressJob(2) cancelProgressJob(2)
} }
Page.State.ERROR -> { is Page.State.Error -> {
setError() setError()
cancelProgressJob(2) cancelProgressJob(2)
} }

View file

@ -128,9 +128,9 @@ class WebtoonPageHolder(
launchIO { loader.loadPage(page) } launchIO { loader.loadPage(page) }
page.statusFlow.collectLatest { status -> page.statusFlow.collectLatest { status ->
when (status) { when (status) {
Page.State.QUEUE -> setQueued() is Page.State.Queue -> setQueued()
Page.State.LOAD_PAGE -> setLoading() is Page.State.LoadPage -> setLoading()
Page.State.DOWNLOAD_IMAGE -> { is Page.State.DownloadImage -> {
setDownloading() setDownloading()
scope.launch { scope.launch {
page.progressFlow.collectLatest { value -> page.progressFlow.collectLatest { value ->
@ -138,8 +138,8 @@ class WebtoonPageHolder(
} }
} }
} }
Page.State.READY -> setImage() is Page.State.Ready -> setImage()
Page.State.ERROR -> setError() is Page.State.Error -> setError()
} }
} }
} }

View file

@ -19,7 +19,7 @@ open class Page(
get() = index + 1 get() = index + 1
@Transient @Transient
private val _statusFlow = MutableStateFlow(State.QUEUE) private val _statusFlow = MutableStateFlow<State>(State.Queue)
@Transient @Transient
val statusFlow = _statusFlow.asStateFlow() val statusFlow = _statusFlow.asStateFlow()
@ -48,11 +48,11 @@ open class Page(
} }
} }
enum class State { sealed interface State {
QUEUE, data object Queue : State
LOAD_PAGE, data object LoadPage : State
DOWNLOAD_IMAGE, data object DownloadImage : State
READY, data object Ready : State
ERROR, data object Error : State
} }
} }