ADT Page model (#1655)

* Enable when guards

* ADT Page model

* Refine

* Refine

* Remove type conversion

* Cleanup

---------

Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
renovate[bot]andDude so hot authored and GitHub committed 2024-09-20 16:19:59 +08:00
1 parent f7459886b2
commit e8120e12d0
11 files changed
+101 -95

No files matched your search

+1
View File
@@ -273,6 +273,7 @@ kotlin {
"-progressive",
"-Xjvm-default=all",
"-Xcontext-receivers",
"-Xwhen-guards",
"-Xsuppress-warning=CONTEXT_RECEIVERS_DEPRECATED",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
@@ -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<PageStatus> = 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<Float>) : PageStatus
}
@@ -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,
) {
@@ -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))
@@ -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,
@@ -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) {
@@ -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() } } },
)
}
@@ -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,
) {
@@ -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,
}
}
@@ -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
}
@@ -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
}