[修复] 修复无法打开除文本之外的问题、修复在有在线封面时依旧显示文字的问题、修复主页面底部间距等界面问题

This commit is contained in:
HapeLee
2026-03-18 01:52:02 +08:00
parent c469cab5d6
commit 1f4a5fb01c
9 changed files with 169 additions and 112 deletions
@@ -8,12 +8,16 @@ import io.legado.app.help.glide.progress.ProgressResponseBody
import io.legado.app.help.http.CookieManager.cookieJarHeader
import io.legado.app.model.ReadManga
import io.legado.app.utils.NetworkUtils
import okhttp3.Cache
import okhttp3.ConnectionSpec
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.Credentials
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import splitties.init.appCtx
import java.io.File
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.concurrent.ConcurrentHashMap
@@ -47,6 +51,26 @@ val cookieJar by lazy {
}
}
/**
* 强制缓存拦截器,对于图片资源,如果没有缓存头,则强制缓存30天
*/
private val cacheControlInterceptor = Interceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
val url = request.url.toString()
if (url.contains("image", true) || url.endsWith(".jpg") || url.endsWith(".png") || url.endsWith(
".webp"
) || url.endsWith(".jpeg")
) {
response.newBuilder()
.header("Cache-Control", "public, max-age=2592000")
.removeHeader("Pragma")
.build()
} else {
response
}
}
val okHttpClient: OkHttpClient by lazy {
val specs = arrayListOf(
ConnectionSpec.MODERN_TLS,
@@ -54,7 +78,11 @@ val okHttpClient: OkHttpClient by lazy {
ConnectionSpec.CLEARTEXT
)
//TODO:自定义缓存大小
val cache = Cache(File(appCtx.cacheDir, "http_cache"), 100L * 1024L * 1024L)
val builder = OkHttpClient.Builder()
.cache(cache)
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
@@ -77,9 +105,9 @@ val okHttpClient: OkHttpClient by lazy {
}
builder.addHeader("Keep-Alive", "300")
builder.addHeader("Connection", "Keep-Alive")
builder.addHeader("Cache-Control", "no-cache")
chain.proceed(builder.build())
}
.addNetworkInterceptor(cacheControlInterceptor) // 添加强制缓存拦截器
.addNetworkInterceptor { chain ->
var request = chain.request()
val enableCookieJar = request.header(cookieJarHeader) != null
@@ -1,9 +1,13 @@
package io.legado.app.ui.main
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
@@ -22,23 +26,26 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.WideNavigationRail
import androidx.compose.material3.WideNavigationRailItem
import androidx.compose.material3.WideNavigationRailValue
import androidx.compose.material3.rememberWideNavigationRailState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource
import io.legado.app.R
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.search.SearchActivity
import io.legado.app.ui.config.mainConfig.MainConfig
import io.legado.app.ui.main.bookshelf.BookshelfScreen
@@ -48,6 +55,7 @@ import io.legado.app.ui.main.rss.RssScreen
import io.legado.app.ui.theme.regularHazeEffect
import io.legado.app.ui.widget.components.GlassDefaults
import io.legado.app.utils.startActivity
import io.legado.app.utils.startActivityForBook
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
@@ -78,27 +86,60 @@ fun MainScreen(
}
val pagerState = rememberPagerState(initialPage = initialPage) { destinations.size }
val navState = rememberWideNavigationRailState(
initialValue = if (MainConfig.navExtended)
WideNavigationRailValue.Expanded
else
WideNavigationRailValue.Collapsed
)
LaunchedEffect(navState.currentValue) {
MainConfig.navExtended =
navState.currentValue == WideNavigationRailValue.Expanded
}
Row(modifier = Modifier.fillMaxSize()) {
if (useRail && MainConfig.showBottomView) {
NavigationRail(
WideNavigationRail(
state = navState,
header = {
IconButton(onClick = { MainConfig.navExtended = !MainConfig.navExtended }) {
Icon(
if (MainConfig.navExtended) Icons.AutoMirrored.Filled.MenuOpen else Icons.Default.Menu,
contentDescription = null
val expanded = navState.targetValue == WideNavigationRailValue.Expanded
Column {
IconButton(
modifier = Modifier.padding(start = 24.dp),
onClick = {
coroutineScope.launch {
if (expanded) navState.collapse()
else navState.expand()
}
}
) {
Icon(
if (expanded)
Icons.AutoMirrored.Filled.MenuOpen
else
Icons.Default.Menu,
contentDescription = null
)
}
Spacer(modifier = Modifier.height(8.dp))
ExtendedFloatingActionButton(
modifier = Modifier.padding(start = 20.dp),
onClick = { context.startActivity<SearchActivity>() },
expanded = expanded,
icon = { Icon(Icons.Default.Search, contentDescription = null) },
text = { Text(stringResource(R.string.search)) }
)
}
ExtendedFloatingActionButton(
onClick = { context.startActivity<SearchActivity>() },
expanded = MainConfig.navExtended,
icon = { Icon(Icons.Default.Search, contentDescription = null) },
text = { Text(text = stringResource(R.string.search)) }
)
}
) {
destinations.forEachIndexed { index, destination ->
val selected = pagerState.currentPage == index
NavigationRailItem(
WideNavigationRailItem(
railExpanded = navState.targetValue == WideNavigationRailValue.Expanded,
selected = selected,
onClick = {
coroutineScope.launch {
@@ -157,14 +198,12 @@ fun MainScreen(
state = pagerState,
modifier = Modifier.fillMaxSize(),
userScrollEnabled = true,
beyondViewportPageCount = 1
beyondViewportPageCount = 3
) { page ->
when (destinations[page]) {
MainDestination.Bookshelf -> BookshelfScreen(
onBookClick = { book ->
context.startActivity<ReadBookActivity> {
putExtra("bookUrl", book.bookUrl)
}
context.startActivityForBook(book)
},
onBookLongClick = { book ->
context.startActivity<BookInfoActivity> {
@@ -71,12 +71,14 @@ fun BookshelfItem(
val coverModifier = if (coverShadow) {
Modifier
.fillMaxWidth()
.shadow(4.dp, RoundedCornerShape(4.dp))
.shadow(4.dp, MaterialTheme.shapes.extraSmall)
} else {
Modifier.fillMaxWidth()
}
Box {
Box(
modifier = Modifier.clip(MaterialTheme.shapes.extraSmall)
) {
cover(coverModifier)
if (isCompact) {
Text(
@@ -135,7 +137,7 @@ fun BookshelfItem(
) {
cover(
Modifier
.width(if (!isCompact) 70.dp else 55.dp)
.width(if (!isCompact) 80.dp else 56.dp)
.padding(end = 12.dp)
)
Column(
@@ -8,11 +8,9 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
@@ -254,7 +252,7 @@ fun BookshelfScreen(
text = group.groupName,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 4.dp),
modifier = Modifier.padding(horizontal = 8.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
@@ -291,16 +289,20 @@ fun BookshelfScreen(
}
) {
if (bookGroupStyle == 2 && isInFolderRoot) {
if (uiState.bookshelfLayoutMode == 0) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 56.dp
)
) {
items(uiState.groups) { group ->
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 120.dp,
start = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp
),
verticalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp)
) {
items(uiState.groups) { group ->
if (uiState.bookshelfLayoutMode == 0) {
BookGroupItemList(
group = group,
previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(),
@@ -317,23 +319,7 @@ fun BookshelfScreen(
},
onLongClick = { showGroupManageSheet = true }
)
}
}
} else {
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding() + 16.dp,
start = 12.dp,
end = 12.dp
),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(uiState.groups) { group ->
} else {
BookGroupItemGrid(
group = group,
previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(),
@@ -358,7 +344,7 @@ fun BookshelfScreen(
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize(),
beyondViewportPageCount = 1,
beyondViewportPageCount = 3,
key = { if (it < uiState.groups.size) uiState.groups[it].groupId else it }
) { pageIndex ->
val group = uiState.groups.getOrNull(pageIndex)
@@ -447,52 +433,31 @@ fun BookshelfPage(
onBookClick: (Book) -> Unit,
onBookLongClick: (Book) -> Unit
) {
if (uiState.bookshelfLayoutMode == 0) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 56.dp
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 120.dp,
start = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp
),
verticalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp)
) {
items(books, key = { it.bookUrl }) { book ->
BookItem(
book = book,
layoutMode = uiState.bookshelfLayoutMode,
isCompact = uiState.bookshelfLayoutCompact,
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
titleSmallFont = uiState.titleSmallFont,
titleCenter = uiState.titleCenter,
titleMaxLines = uiState.titleMaxLines,
coverShadow = uiState.coverShadow,
onClick = { onBookClick(book) },
onLongClick = { onBookLongClick(book) }
)
) {
items(books, key = { it.bookUrl }) { book ->
BookItem(
book = book,
layoutMode = uiState.bookshelfLayoutMode,
isCompact = uiState.bookshelfLayoutCompact,
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
onClick = { onBookClick(book) },
onLongClick = { onBookLongClick(book) }
)
}
}
} else {
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding() + 16.dp,
start = 12.dp,
end = 12.dp
),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(books, key = { it.bookUrl }) { book ->
BookItem(
book = book,
layoutMode = uiState.bookshelfLayoutMode,
isCompact = uiState.bookshelfLayoutCompact,
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
titleSmallFont = uiState.titleSmallFont,
titleCenter = uiState.titleCenter,
titleMaxLines = uiState.titleMaxLines,
coverShadow = uiState.coverShadow,
onClick = { onBookClick(book) },
onLongClick = { onBookLongClick(book) }
)
}
}
}
}
@@ -105,7 +105,7 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
val scrollTrigger = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val groupsFlow: StateFlow<List<BookGroup>> = appDb.bookGroupDao.show.asFlow()
.map { it ?: emptyList() }
.map { it }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
private val allBooksFlow = appDb.bookDao.flowAll().flowOn(Dispatchers.Default)
@@ -166,7 +166,7 @@ fun ExploreScreen(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding() + 16.dp
bottom = 120.dp
)
) {
uiState.items.forEach { item ->
@@ -1,8 +1,10 @@
package io.legado.app.ui.main.my
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
@@ -90,9 +92,13 @@ fun MyScreen(
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.verticalScroll(rememberScrollState())
.padding(
top = padding.calculateTopPadding()
)
.verticalScroll(rememberScrollState()),
//TODO:暂时这样解决,改天加模糊
) {
SplicedColumnGroup(
modifier = Modifier.padding(horizontal = 16.dp),
title = ""
@@ -271,6 +277,8 @@ fun MyScreen(
}
)
}
Spacer(Modifier.height(120.dp))
}
}
}
@@ -165,7 +165,7 @@ fun RssScreen(
start = 12.dp,
end = 12.dp,
top = paddingValues.calculateTopPadding() + 8.dp,
bottom = paddingValues.calculateBottomPadding() + 12.dp
bottom = 120.dp
),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
@@ -22,7 +22,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -64,6 +67,8 @@ fun BookCover(
BookCover.getRandomDefaultDrawable(seed = name ?: author ?: path ?: "")
}
var isOnlineCoverLoaded by remember(path) { mutableStateOf(false) }
Box(
modifier = modifier
.aspectRatio(5f / 7f)
@@ -98,16 +103,26 @@ fun BookCover(
imageLoader = koinInject(),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
onSuccess = { onLoadFinish?.invoke() },
onError = { onLoadFinish?.invoke() }
onSuccess = {
if (finalPath != null) {
isOnlineCoverLoaded = true
}
onLoadFinish?.invoke()
},
onError = {
isOnlineCoverLoaded = false
onLoadFinish?.invoke()
}
)
// 2. 文字叠加层(始终尝试渲染,由 CoverConfig 内部控制具体显示逻辑
CoverTextOverlay(
name = name,
author = author,
isNight = isNight
)
// 2. 文字叠加层(当没有在线封面或者加载失败时显示
if (!isOnlineCoverLoaded) {
CoverTextOverlay(
name = name,
author = author,
isNight = isNight
)
}
// 3. 角标 (Badge)
if (badgeContent != null) {