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:
1 parent
f7459886b2
commit
e8120e12d0
11 files changed
+101
-95
No files matched your search
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user