[Reader] Show gallery title & fix status bar color

This commit is contained in:
FooIbar committed 2025-10-19 12:41:17 +08:00
1 parent 371103908b
commit c1ac592855
5 files changed
+71 -50

No files matched your search

@@ -17,8 +17,10 @@ package com.hippo.ehviewer.gallery
import arrow.autoCloseScope
import com.ehviewer.core.files.openFileDescriptor
import com.ehviewer.core.model.GalleryInfo
import com.ehviewer.core.util.logcat
import com.hippo.ehviewer.Settings.archivePasswds
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.image.ImageSource
import com.hippo.ehviewer.image.byteBufferSource
import com.hippo.ehviewer.jni.closeArchive
@@ -40,7 +42,7 @@ typealias PasswdProvider = suspend (PasswdInvalidator) -> String
suspend inline fun <T> useArchivePageLoader(
file: Path,
gid: Long = 0,
info: GalleryInfo? = null,
startPage: Int = 0,
hasAds: Boolean = false,
crossinline passwdProvider: PasswdProvider,
@@ -49,7 +51,7 @@ suspend inline fun <T> useArchivePageLoader(
coroutineScope {
val pfd = install(file.openFileDescriptor("r"))
val size = install(
{ openArchive(pfd.fd, pfd.statSize, gid == 0L || file.name.endsWith(".zip")) },
{ openArchive(pfd.fd, pfd.statSize, info == null || file.name.endsWith(".zip")) },
{ _, _ -> closeArchive() },
)
check(size > 0) { "Archive have no content!" }
@@ -57,8 +59,14 @@ suspend inline fun <T> useArchivePageLoader(
archivePasswds += passwdProvider(::providePassword)
}
val loader = install(
object : PageLoader(this, gid, startPage, size, hasAds) {
override val title by lazy { FileUtils.getNameFromFilename(file.displayName)!! }
object : PageLoader(this, info, startPage, size, hasAds) {
override val title by lazy {
if (info != null) {
EhUtils.getSuitableTitle(info)
} else {
FileUtils.getNameFromFilename(file.displayName)!!
}
}
override fun getImageExtension(index: Int) = getExtension(index)
@@ -24,7 +24,7 @@ suspend inline fun <T> useEhPageLoader(
)
queen.awaitReady()
val loader = install(
object : PageLoader(this, info.gid, startPage, queen.size, info.hasAds) {
object : PageLoader(this, info, startPage, queen.size, info.hasAds) {
override val title by lazy { EhUtils.getSuitableTitle(info) }
override fun getImageExtension(index: Int) = queen.getExtension(index)
@@ -4,6 +4,7 @@ import androidx.collection.SieveCache
import androidx.collection.mutableIntObjectMapOf
import arrow.fx.coroutines.ExitCase
import arrow.fx.coroutines.bracketCase
import com.ehviewer.core.model.GalleryInfo
import com.ehviewer.core.util.isAtLeastO
import com.ehviewer.core.util.withNonCancellableContext
import com.hippo.ehviewer.EhDB
@@ -35,7 +36,7 @@ private val progressScope = CoroutineScope(Dispatchers.IO)
private const val MAX_CACHE_SIZE = 512 * 1024 * 1024
private const val MIN_CACHE_SIZE = 256 * 1024 * 1024
abstract class PageLoader(val scope: CoroutineScope, val gid: Long, startPage: Int, val size: Int, val hasAds: Boolean = false) : AutoCloseable {
abstract class PageLoader(val scope: CoroutineScope, val info: GalleryInfo?, startPage: Int, val size: Int, val hasAds: Boolean = false) : AutoCloseable {
var startPage = startPage.coerceIn(0, size - 1)
private val jobs = mutableIntObjectMapOf<Job>()
@@ -111,14 +112,14 @@ abstract class PageLoader(val scope: CoroutineScope, val gid: Long, startPage: I
override fun close() {
lock.write { cache.evictAll() }
if (gid != 0L) {
info?.run {
progressScope.launch {
EhDB.putReadProgress(gid, startPage)
}
}
}
protected abstract val title: String
abstract val title: String
protected abstract fun getImageExtension(index: Int): String?
@@ -13,14 +13,11 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.windowInsetsTopHeight
import androidx.compose.foundation.lazy.layout.LazyLayoutCacheWindow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.rememberPagerState
@@ -53,7 +50,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.keepScreenOn
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.zIndex
import arrow.core.Either
import arrow.core.Either.Companion.catch
import arrow.core.raise.ensure
@@ -82,7 +78,6 @@ import com.hippo.ehviewer.gallery.useEhPageLoader
import com.hippo.ehviewer.ui.MainActivity
import com.hippo.ehviewer.ui.Screen
import com.hippo.ehviewer.ui.theme.EhTheme
import com.hippo.ehviewer.ui.theme.scrim
import com.hippo.ehviewer.ui.tools.DialogState
import com.hippo.ehviewer.ui.tools.awaitInputText
import com.hippo.ehviewer.ui.tools.dialog
@@ -131,15 +126,11 @@ fun AnimatedVisibilityScope.ReaderScreen(args: ReaderScreenArgs, navigator: Dest
val uiController = rememberSystemUiController()
DisposableEffect(uiController) {
val lightStatusBar = uiController.statusBarDarkContentEnabled
uiController.statusBarDarkContentEnabled = bgColor == Color.White
onDispose {
uiController.statusBarDarkContentEnabled = lightStatusBar
}
}
LaunchedEffect(uiController) {
snapshotFlow { bgColor }.collect {
uiController.statusBarDarkContentEnabled = it == Color.White
}
}
Await(
block = asyncInVM(args) { alive ->
@@ -184,7 +175,7 @@ fun AnimatedVisibilityScope.ReaderScreen(args: ReaderScreenArgs, navigator: Dest
}
@Composable
context(activity: MainActivity, _: SnackbarHostState, _: DialogState, _: CoroutineScope)
context(activity: MainActivity, _: SnackbarHostState, _: DialogState, _: CoroutineScope, _: DestinationsNavigator)
fun ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) {
LaunchedEffect(Unit) {
val orientation = activity.requestedOrientation
@@ -240,18 +231,14 @@ fun ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) {
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
val bgColor by collectBackgroundColorAsState()
syncState.Sync(isWebtoon) { appbarVisible = false }
if (fullscreen) {
LaunchedEffect(Unit) {
snapshotFlow { appbarVisible }.collect {
uiController.isSystemBarsVisible = it
}
val bgColor by collectBackgroundColorAsState()
val isDarkTheme = isSystemInDarkTheme()
LaunchedEffect(isDarkTheme) {
snapshotFlow { appbarVisible }.collect {
uiController.isSystemBarsVisible = it || !fullscreen
uiController.statusBarDarkContentEnabled = if (it) !isDarkTheme else bgColor == Color.White
}
Box(
Modifier.windowInsetsTopHeight(WindowInsets.statusBars).fillMaxWidth()
.align(Alignment.TopCenter).background(bgColor.scrim()).zIndex(1f),
)
}
var showNavigationOverlay by remember {
val showOnStart = Settings.showNavigationOverlayNewUser.value || Settings.showNavigationOverlayOnStart.value
@@ -353,6 +340,7 @@ fun ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) {
}
ReaderAppBars(
visible = appbarVisible,
title = pageLoader.title,
isRtl = readingMode == ReadingModeType.RIGHT_TO_LEFT,
showSeekBar = showSeekbar,
currentPage = syncState.sliderValue,
@@ -383,7 +371,6 @@ fun ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) {
}
}
},
modifier = Modifier.align(Alignment.BottomCenter),
)
}
}
@@ -395,7 +382,7 @@ suspend inline fun <T> usePageLoader(args: ReaderScreenArgs, crossinline block:
val page = args.page.takeUnless { it == -1 } ?: EhDB.getReadProgress(info.gid)
val archive = DownloadManager.getDownloadInfo(info.gid)?.archiveFile
if (archive != null) {
useArchivePageLoader(archive, info.gid, page, info.hasAds, { error("Managed Archive have password???") }, block)
useArchivePageLoader(archive, info, page, info.hasAds, { error("Managed Archive have password???") }, block)
} else {
useEhPageLoader(info, page, block)
}
@@ -6,47 +6,72 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.ui.main.NavigationIcon
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
private val animationSpec = tween<IntOffset>(200)
@Composable
fun ReaderAppBars(
context(navigator: DestinationsNavigator)
fun BoxScope.ReaderAppBars(
visible: Boolean,
title: String,
isRtl: Boolean,
showSeekBar: Boolean,
currentPage: Int,
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
onClickSettings: () -> Unit,
modifier: Modifier = Modifier,
) = AnimatedVisibility(
visible = visible,
modifier = modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)),
enter = slideInVertically(initialOffsetY = { it }, animationSpec = animationSpec),
exit = slideOutVertically(targetOffsetY = { it }, animationSpec = animationSpec),
) {
val toolbarColor = BottomAppBarDefaults.containerColor.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
if (showSeekBar && totalPages > 1) {
ChapterNavigator(
isRtl = isRtl,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
containerColor = toolbarColor,
)
val backgroundColor = BottomAppBarDefaults.containerColor.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
AnimatedVisibility(
visible = visible,
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBarsIgnoringVisibility.only(WindowInsetsSides.Horizontal)).align(Alignment.TopStart),
enter = slideInVertically(initialOffsetY = { -it }, animationSpec = animationSpec),
exit = slideOutVertically(targetOffsetY = { -it }, animationSpec = animationSpec),
) {
TopAppBar(
title = { Text(text = title, maxLines = 2, overflow = TextOverflow.Ellipsis) },
colors = TopAppBarDefaults.topAppBarColors(containerColor = backgroundColor),
navigationIcon = { NavigationIcon() },
)
}
AnimatedVisibility(
visible = visible,
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBarsIgnoringVisibility.only(WindowInsetsSides.Horizontal)).align(Alignment.BottomStart),
enter = slideInVertically(initialOffsetY = { it }, animationSpec = animationSpec),
exit = slideOutVertically(targetOffsetY = { it }, animationSpec = animationSpec),
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
if (showSeekBar && totalPages > 1) {
ChapterNavigator(
isRtl = isRtl,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
containerColor = backgroundColor,
)
}
BottomReaderBar(onClickSettings = onClickSettings, containerColor = backgroundColor)
}
BottomReaderBar(onClickSettings = onClickSettings, containerColor = toolbarColor)
}
}