From e8120e12d0331809a0b6351a4dd3859695c2a880 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <99822064+revonateB0T@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:19:59 +0800 Subject: [PATCH] ADT Page model (#1655) * Enable when guards * ADT Page model * Refine * Refine * Remove type conversion * Cleanup --------- Co-authored-by: Dude so hot --- app/build.gradle.kts | 1 + .../kotlin/com/hippo/ehviewer/gallery/Page.kt | 43 +++++++++++++++++++ .../hippo/ehviewer/ui/reader/GalleryPager.kt | 4 +- .../hippo/ehviewer/ui/reader/PageActions.kt | 10 ++--- .../com/hippo/ehviewer/ui/reader/PagerItem.kt | 30 ++++++------- .../hippo/ehviewer/ui/reader/PagerViewer.kt | 17 ++++---- .../hippo/ehviewer/ui/reader/ReaderScreen.kt | 12 +++--- .../hippo/ehviewer/ui/reader/WebtoonViewer.kt | 4 +- .../eu/kanade/tachiyomi/source/model/Page.kt | 32 -------------- .../tachiyomi/ui/reader/loader/PageLoader.kt | 35 ++++++++------- .../tachiyomi/ui/reader/model/ReaderPage.kt | 8 ---- 11 files changed, 101 insertions(+), 95 deletions(-) create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/gallery/Page.kt delete mode 100644 app/src/main/kotlin/eu/kanade/tachiyomi/source/model/Page.kt delete mode 100644 app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/model/ReaderPage.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 06be1032f..e4242a7d7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -273,6 +273,7 @@ kotlin { "-progressive", "-Xjvm-default=all", "-Xcontext-receivers", + "-Xwhen-guards", "-Xsuppress-warning=CONTEXT_RECEIVERS_DEPRECATED", "-opt-in=coil3.annotation.ExperimentalCoilApi", diff --git a/app/src/main/kotlin/com/hippo/ehviewer/gallery/Page.kt b/app/src/main/kotlin/com/hippo/ehviewer/gallery/Page.kt new file mode 100644 index 000000000..4bfac339e --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/gallery/Page.kt @@ -0,0 +1,43 @@ +package com.hippo.ehviewer.gallery + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import com.hippo.ehviewer.image.Image +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update + +class Page( + val index: Int, + val statusFlow: MutableStateFlow = MutableStateFlow(PageStatus.Queued), +) + +fun Page.unblock() = statusFlow.update { status -> + when (status) { + is PageStatus.Blocked -> PageStatus.Ready(status.ad) + else -> error("Call unblock on page not blocked!!!") + } +} + +fun Page.reset() = statusFlow.update { PageStatus.Queued } + +val Page.status + get() = statusFlow.value + +val Page.statusObserved + @Composable + get() = statusFlow.collectAsState().value + +val PageStatus.progressObserved + @Composable + get() = when (this) { + is PageStatus.Loading -> progress.collectAsState().value + else -> 0f + } + +sealed interface PageStatus { + data object Queued : PageStatus + data class Blocked(val ad: Image) : PageStatus + data class Ready(val image: Image) : PageStatus + data class Error(val message: String?) : PageStatus + data class Loading(val progress: MutableStateFlow) : PageStatus +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt index 604f4d8fe..a8a68195b 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt @@ -20,8 +20,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastFold import com.hippo.ehviewer.Settings import com.hippo.ehviewer.collectAsState +import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader2 -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType.CONTINUOUS_VERTICAL import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType.RIGHT_TO_LEFT @@ -40,7 +40,7 @@ fun GalleryPager( pageLoader: PageLoader2, showNavigationOverlay: Boolean, onNavigationModeChange: () -> Unit, - onSelectPage: (ReaderPage) -> Unit, + onSelectPage: (Page) -> Unit, onMenuRegionClick: () -> Unit, modifier: Modifier = Modifier, ) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt index 6d2902d49..fe18a69d6 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt @@ -16,6 +16,7 @@ import com.hippo.ehviewer.BuildConfig.APPLICATION_ID import com.hippo.ehviewer.R import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.data.GalleryInfo +import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader2 import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.FileUtils @@ -25,7 +26,6 @@ import com.hippo.ehviewer.util.isAtLeastQ import com.hippo.ehviewer.util.isAtLeastT import com.hippo.ehviewer.util.requestPermission import com.hippo.files.toOkioPath -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.system.logcat import java.io.File import kotlinx.datetime.Clock @@ -42,7 +42,7 @@ private fun Context.provideImage(index: Int): Uri? { } context(SnackbarHostState, Context, PageLoader2) -suspend fun shareImage(page: ReaderPage, info: GalleryInfo? = null) { +suspend fun shareImage(page: Page, info: GalleryInfo? = null) { val error = getString(R.string.error_cant_save_image) val share = getString(R.string.share_image) val noActivity = getString(R.string.error_cant_find_activity) @@ -67,7 +67,7 @@ suspend fun shareImage(page: ReaderPage, info: GalleryInfo? = null) { } context(SnackbarHostState, Context, PageLoader2) -suspend fun copy(page: ReaderPage) { +suspend fun copy(page: Page) { val error = getString(R.string.error_cant_save_image) val copied = getString(R.string.copied_to_clipboard) val uri = provideImage(page.index) @@ -83,7 +83,7 @@ suspend fun copy(page: ReaderPage) { } context(SnackbarHostState, Context, PageLoader2) -suspend fun save(page: ReaderPage) { +suspend fun save(page: Page) { val granted = isAtLeastQ || requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) val cannotSave = getString(R.string.error_cant_save_image) if (granted) { @@ -137,7 +137,7 @@ suspend fun save(page: ReaderPage) { } context(SnackbarHostState, Context, PageLoader2) -suspend fun saveTo(page: ReaderPage) { +suspend fun saveTo(page: Page) { val filename = getImageFilename(page.index) if (filename == null) { showSnackbar(getString(R.string.error_cant_save_image)) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt index 6789ff8af..d69289703 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt @@ -34,18 +34,20 @@ import com.google.accompanist.drawablepainter.DrawablePainter import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.collectAsState +import com.hippo.ehviewer.gallery.Page +import com.hippo.ehviewer.gallery.PageStatus +import com.hippo.ehviewer.gallery.progressObserved +import com.hippo.ehviewer.gallery.statusObserved import com.hippo.ehviewer.image.Image import com.hippo.ehviewer.ui.settings.AdsPlaceholderFile -import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.loader.PageLoader -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.viewer.CombinedCircularProgressIndicator import kotlinx.coroutines.flow.drop import moe.tarsin.kt.unreachable @Composable fun PagerItem( - page: ReaderPage, + page: Page, pageLoader: PageLoader, contentScale: ContentScale, modifier: Modifier = Modifier, @@ -53,26 +55,24 @@ fun PagerItem( ) { LaunchedEffect(Unit) { pageLoader.request(page.index) - page.status.drop(1).collect { - if (page.status.value == Page.State.QUEUE) { + page.statusFlow.drop(1).collect { + if (page.statusFlow.value == PageStatus.Queued) { pageLoader.request(page.index) } } } val defaultError = stringResource(id = R.string.decode_image_error) - val state by page.status.collectAsState() - when (state) { - Page.State.QUEUE, Page.State.LOAD_PAGE, Page.State.DOWNLOAD_IMAGE -> { + when (val state = page.statusObserved) { + is PageStatus.Queued, is PageStatus.Loading -> { Box( modifier = modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT), contentAlignment = Alignment.Center, ) { - val progress by page.progressFlow.collectAsState() - CombinedCircularProgressIndicator(progress = progress / 100f) + CombinedCircularProgressIndicator(progress = state.progressObserved) } } - Page.State.READY -> { - val image = page.image!! + is PageStatus.Ready -> { + val image = state.image val painter = remember(image) { image.toPainter() } val grayScale by Settings.grayScale.collectAsState() val invert by Settings.invertedColors.collectAsState() @@ -100,7 +100,7 @@ fun PagerItem( }, ) } - Page.State.BLOCKED -> { + is PageStatus.Blocked -> { SubcomposeAsyncImage( model = AdsPlaceholderFile, contentDescription = null, @@ -122,14 +122,14 @@ fun PagerItem( } } } - Page.State.ERROR -> { + is PageStatus.Error -> { Box(modifier = modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) { Column( modifier = Modifier.align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( - text = page.errorMsg ?: defaultError, + text = state.message ?: defaultError, modifier = Modifier.padding(8.dp), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodyMedium, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerViewer.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerViewer.kt index ed54c95d6..672fc9500 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerViewer.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerViewer.kt @@ -31,9 +31,10 @@ import androidx.compose.ui.unit.toSize import arrow.core.partially1 import com.hippo.ehviewer.Settings import com.hippo.ehviewer.collectAsState +import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader2 -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage +import com.hippo.ehviewer.gallery.PageStatus +import com.hippo.ehviewer.gallery.statusObserved import eu.kanade.tachiyomi.ui.reader.viewer.NavigationRegions import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.reader.viewer.getAction @@ -55,7 +56,7 @@ fun PagerViewer( isVertical: Boolean, pageLoader: PageLoader2, navigator: () -> NavigationRegions, - onSelectPage: (ReaderPage) -> Unit, + onSelectPage: (Page) -> Unit, onMenuRegionClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -121,7 +122,7 @@ fun PagerViewer( @Composable private fun PageContainer( - page: ReaderPage, + page: Page, pageLoader: PageLoader2, isRtl: Boolean, scaleType: Int, @@ -130,16 +131,16 @@ private fun PageContainer( layoutSize: Size, navigator: () -> NavigationRegions, pagerState: PagerState, - onSelectPage: (ReaderPage) -> Unit, + onSelectPage: (Page) -> Unit, onMenuRegionClick: () -> Unit, scope: CoroutineScope, ) { @Suppress("NAME_SHADOWING") val isRtl by rememberUpdatedState(isRtl) val zoomableState = rememberZoomableState(zoomSpec = PagerZoomSpec) - val status by page.status.collectAsState() - if (status == Page.State.READY && layoutSize != Size.Zero) { - val size = page.image!!.intrinsicSize.toSize() + val status = page.statusObserved + if (status is PageStatus.Ready && layoutSize != Size.Zero) { + val size = status.image.intrinsicSize.toSize() val contentScale = ContentScale.fromPreferences(scaleType, size, layoutSize) zoomableState.contentScale = contentScale LaunchedEffect(size, contentScale, alignment) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt index aa3c3da99..eb7baa4a5 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt @@ -60,7 +60,11 @@ import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.download.archiveFile import com.hippo.ehviewer.gallery.ArchivePageLoader import com.hippo.ehviewer.gallery.EhPageLoader +import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader2 +import com.hippo.ehviewer.gallery.PageStatus +import com.hippo.ehviewer.gallery.status +import com.hippo.ehviewer.gallery.unblock import com.hippo.ehviewer.ui.composing import com.hippo.ehviewer.ui.theme.EhTheme import com.hippo.ehviewer.ui.tools.Await @@ -71,12 +75,10 @@ import com.hippo.files.toOkioPath import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.PageIndicatorText import eu.kanade.tachiyomi.ui.reader.ReaderAppBars import eu.kanade.tachiyomi.ui.reader.ReaderContentOverlay import eu.kanade.tachiyomi.ui.reader.ReaderPageSheetMeta -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withIOContext @@ -216,10 +218,10 @@ fun AnimatedVisibilityScope.ReaderScreen(pageLoader: PageLoader2, info: BaseGall Settings.showNavigationOverlayNewUser.value = false mutableStateOf(showOnStart) } - val onSelectPage = { page: ReaderPage -> + val onSelectPage = { page: Page -> if (Settings.readerLongTapAction.value) { launch { - val pageBlocked = page.status.value == Page.State.BLOCKED + val blocked = page.status is PageStatus.Blocked dialog { cont -> fun dispose() = cont.resume(Unit) val state = rememberModalBottomSheetState() @@ -236,7 +238,7 @@ fun AnimatedVisibilityScope.ReaderScreen(pageLoader: PageLoader2, info: BaseGall copy = { launchIO { with(pageLoader) { copy(page) } } }, save = { launchIO { with(pageLoader) { save(page) } } }, saveTo = { launchIO { with(pageLoader) { saveTo(page) } } }, - showAds = { page.status.value = Page.State.READY }.takeIf { pageBlocked }, + showAds = { page.unblock() }.takeIf { blocked }, dismiss = { launch { state.hide().also { dispose() } } }, ) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/WebtoonViewer.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/WebtoonViewer.kt index 27ee62189..fae99def7 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/WebtoonViewer.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/WebtoonViewer.kt @@ -17,8 +17,8 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import com.hippo.ehviewer.Settings import com.hippo.ehviewer.collectAsState +import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader2 -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.viewer.NavigationRegions import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.reader.viewer.getAction @@ -33,7 +33,7 @@ fun WebtoonViewer( withGaps: Boolean, pageLoader: PageLoader2, navigator: () -> NavigationRegions, - onSelectPage: (ReaderPage) -> Unit, + onSelectPage: (Page) -> Unit, onMenuRegionClick: () -> Unit, modifier: Modifier = Modifier, ) { diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/source/model/Page.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/source/model/Page.kt deleted file mode 100644 index b610dab48..000000000 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/source/model/Page.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.kanade.tachiyomi.source.model - -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -open class Page(val index: Int) { - - val number: Int - get() = index + 1 - - var status = MutableStateFlow(State.QUEUE) - - var errorMsg: String? = null - - private val _progressFlow = MutableStateFlow(0) - - val progressFlow = _progressFlow.asStateFlow() - var progress: Int - get() = _progressFlow.value - set(value) { - _progressFlow.value = value - } - - enum class State { - QUEUE, - LOAD_PAGE, - DOWNLOAD_IMAGE, - READY, - BLOCKED, - ERROR, - } -} diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt index 830573073..c51a83f9d 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt @@ -3,12 +3,16 @@ package eu.kanade.tachiyomi.ui.reader.loader import androidx.annotation.CallSuper import androidx.collection.lruCache import com.hippo.ehviewer.Settings +import com.hippo.ehviewer.gallery.Page +import com.hippo.ehviewer.gallery.PageStatus +import com.hippo.ehviewer.gallery.reset +import com.hippo.ehviewer.gallery.status import com.hippo.ehviewer.image.Image import com.hippo.ehviewer.util.OSUtils import com.hippo.ehviewer.util.isAtLeastO -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.withIOContext +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update private const val MAX_CACHE_SIZE = 512 * 1024 * 1024 private const val MIN_CACHE_SIZE = 128 * 1024 * 1024 @@ -35,7 +39,7 @@ abstract class PageLoader { val pages by lazy { check(size > 0) - (0 until size).map { ReaderPage(it) } + (0 until size).map { Page(it) } } private val prefetchPageCount = Settings.preloadImage @@ -58,7 +62,7 @@ abstract class PageLoader { fun restart() { cache.evictAll() - pages.forEach(ReaderPage::reset) + pages.forEach(Page::reset) } abstract val size: Int @@ -80,7 +84,7 @@ abstract class PageLoader { } else { index - 1 downTo (index - prefetchPageCount).coerceAtLeast(0) } - val pagesAbsent = prefetchRange.filter { pages[it].status.value == Page.State.QUEUE } + val pagesAbsent = prefetchRange.filter { pages[it].status == PageStatus.Queued } val start = if (prefetchRange.step > 0) prefetchRange.first else prefetchRange.last val end = if (prefetchRange.step > 0) prefetchRange.last else prefetchRange.first prefetchPages(pagesAbsent, start - 5 to end + 5) @@ -110,27 +114,22 @@ abstract class PageLoader { } fun notifyPagePercent(index: Int, percent: Float) { - pages[index].progress = (percent * 100).toInt() - pages[index].status.compareAndSet(Page.State.QUEUE, Page.State.DOWNLOAD_IMAGE) + pages[index].statusFlow.update { + when (it) { + is PageStatus.Loading -> it.apply { progress.update { percent } } + else -> PageStatus.Loading(MutableStateFlow(percent)) + } + } } fun notifyPageSucceed(index: Int, image: Image, replaceCache: Boolean = true) { if (replaceCache) { cache.put(index, image) } - pages[index].image = image - pages[index].status.value = if (image.hasQrCode) Page.State.BLOCKED else Page.State.READY + pages[index].statusFlow.update { if (image.hasQrCode) PageStatus.Blocked(image) else PageStatus.Ready(image) } } fun notifyPageFailed(index: Int, error: String?) { - pages[index].errorMsg = error - pages[index].status.value = Page.State.ERROR + pages[index].statusFlow.update { PageStatus.Error(error) } } } - -private fun ReaderPage.reset() { - image = null - errorMsg = null - progress = 0 - status.value = Page.State.QUEUE -} diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/model/ReaderPage.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/model/ReaderPage.kt deleted file mode 100644 index a56b6545e..000000000 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/model/ReaderPage.kt +++ /dev/null @@ -1,8 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.model - -import com.hippo.ehviewer.image.Image -import eu.kanade.tachiyomi.source.model.Page - -class ReaderPage(index: Int) : Page(index) { - var image: Image? = null -}