代码优化

This commit is contained in:
HapeLee
2026-05-13 01:31:50 +08:00
parent 14ac91246d
commit dda4a27730
13 changed files with 110 additions and 417 deletions
+10 -1
View File
@@ -55,7 +55,16 @@
"PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 40)", "PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 40)",
"PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 100)", "PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 100)",
"PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 50)", "PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 50)",
"Bash(python3 *)" "Bash(python3 *)",
"WebFetch(domain:raw.githubusercontent.com)",
"Bash(xargs -I{} jar -tf {})",
"Bash(gh api *)",
"Bash(rustup show *)",
"PowerShell(rustup show *)",
"PowerShell(npm *)",
"PowerShell(gcc *)",
"Bash(gh repo *)",
"Bash(Test-Path *)"
] ]
} }
} }
@@ -210,11 +210,6 @@ object PreferKey {
const val secondaryTextColor = "colorMD3OnPrimaryContainer" const val secondaryTextColor = "colorMD3OnPrimaryContainer"
const val themeBackgroundColor = "colorMD3Background" const val themeBackgroundColor = "colorMD3Background"
const val labelContainerColor = "colorMD3SurfaceContainerLow" const val labelContainerColor = "colorMD3SurfaceContainerLow"
const val enableContainerBorder = "enableContainerBorder"
const val containerBorderWidth = "containerBorderWidth"
const val containerBorderStyle = "containerBorderStyle"
const val containerBorderColor = "containerBorderColor"
const val containerBorderDashWidth = "containerBorderDashWidth"
// 中间单线间隔设置 // 中间单线间隔设置
const val enableItemDivider = "enableItemDivider" const val enableItemDivider = "enableItemDivider"
const val itemDividerWidth = "itemDividerWidth" const val itemDividerWidth = "itemDividerWidth"
@@ -82,6 +82,7 @@ import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel
import io.legado.app.ui.config.backupConfig.BackupConfigViewModel import io.legado.app.ui.config.backupConfig.BackupConfigViewModel
import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig
import io.legado.app.ui.config.coverConfig.CoverConfigViewModel import io.legado.app.ui.config.coverConfig.CoverConfigViewModel
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigViewModel
import io.legado.app.ui.config.otherConfig.OtherConfigViewModel import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
import io.legado.app.ui.config.readConfig.ReadConfigViewModel import io.legado.app.ui.config.readConfig.ReadConfigViewModel
import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel
@@ -189,6 +190,7 @@ val appModule = module {
viewModelOf(::OtherConfigViewModel) viewModelOf(::OtherConfigViewModel)
viewModelOf(::ReadConfigViewModel) viewModelOf(::ReadConfigViewModel)
viewModelOf(::CoverConfigViewModel) viewModelOf(::CoverConfigViewModel)
viewModelOf(::DownloadCacheConfigViewModel)
viewModelOf(::ThemeConfigViewModel) viewModelOf(::ThemeConfigViewModel)
viewModelOf(::BackupConfigViewModel) viewModelOf(::BackupConfigViewModel)
viewModelOf(::TocViewModel) viewModelOf(::TocViewModel)
@@ -52,16 +52,12 @@ val cookieJar by lazy {
} }
/** /**
* 强制缓存拦截器,对于图片资源,如果没有缓存头,则强制缓存30天 * 强制缓存拦截器,对于封面请求(由CoverFetcher标记),强制缓存30天
*/ */
private val cacheControlInterceptor = Interceptor { chain -> private val cacheControlInterceptor = Interceptor { chain ->
val request = chain.request() val request = chain.request()
val response = chain.proceed(request) val response = chain.proceed(request)
val url = request.url.toString() if (request.tag() === io.legado.app.help.coil.CoverFetcher.COVER_REQUEST_TAG) {
if (url.contains("image", true) || url.endsWith(".jpg") || url.endsWith(".png") || url.endsWith(
".webp"
) || url.endsWith(".jpeg")
) {
response.newBuilder() response.newBuilder()
.header("Cache-Control", "public, max-age=2592000") .header("Cache-Control", "public, max-age=2592000")
.removeHeader("Pragma") .removeHeader("Pragma")
@@ -149,6 +145,7 @@ val okHttpClient: OkHttpClient by lazy {
val okHttpClientManga by lazy { val okHttpClientManga by lazy {
okHttpClient.newBuilder().run { okHttpClient.newBuilder().run {
cache(Cache(File(appCtx.cacheDir, "manga_cache"), 100L * 1024L * 1024L))
val interceptors = interceptors() val interceptors = interceptors()
interceptors.add(1) { chain -> interceptors.add(1) { chain ->
val request = chain.request() val request = chain.request()
@@ -167,6 +164,24 @@ val okHttpClientManga by lazy {
} }
} }
enum class HttpCacheType(val dirName: String, val maxSize: Long) {
COVER("http_cache", 100L * 1024 * 1024),
MANGA("manga_cache", 100L * 1024 * 1024),
}
fun getHttpCacheSize(type: HttpCacheType): Long {
val dir = File(appCtx.cacheDir, type.dirName)
if (!dir.exists()) return 0
return dir.walkTopDown().filter { it.isFile }.sumOf { it.length() }
}
fun clearHttpCache(type: HttpCacheType) {
when (type) {
HttpCacheType.COVER -> okHttpClient.cache?.delete()
HttpCacheType.MANGA -> okHttpClientManga.cache?.delete()
}
}
/** /**
* 缓存代理okHttp * 缓存代理okHttp
*/ */
@@ -1,7 +1,7 @@
package io.legado.app.ui.config.otherConfig package io.legado.app.ui.config.otherConfig
import io.legado.app.BuildConfig
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfig
import io.legado.app.ui.config.prefDelegate import io.legado.app.ui.config.prefDelegate
object OtherConfig { object OtherConfig {
@@ -56,21 +56,6 @@ object OtherConfig {
false false
) )
var bitmapCacheSize by prefDelegate(
PreferKey.bitmapCacheSize,
50
)
var imageRetainNum by prefDelegate(
PreferKey.imageRetainNum,
0
)
var preDownloadNum by prefDelegate(
PreferKey.preDownloadNum,
10
)
var replaceEnableDefault by prefDelegate( var replaceEnableDefault by prefDelegate(
PreferKey.replaceEnableDefault, PreferKey.replaceEnableDefault,
true true
@@ -106,6 +91,18 @@ object OtherConfig {
true true
) )
var userAgent: String
get() = DownloadCacheConfig.userAgent
set(value) {
DownloadCacheConfig.userAgent = value
}
var cronetEnable: Boolean
get() = DownloadCacheConfig.cronetEnable
set(value) {
DownloadCacheConfig.cronetEnable = value
}
var sharedElementEnterTransitionEnable by prefDelegate( var sharedElementEnterTransitionEnable by prefDelegate(
PreferKey.sharedElementEnterTransitionEnable, PreferKey.sharedElementEnterTransitionEnable,
false false
@@ -116,19 +113,6 @@ object OtherConfig {
true true
) )
private var _userAgent by prefDelegate(
PreferKey.userAgent,
""
)
var userAgent: String
get() = _userAgent.ifBlank {
defaultUserAgent
}
set(value) {
_userAgent = value
}
var webServiceWakeLock by prefDelegate( var webServiceWakeLock by prefDelegate(
PreferKey.webServiceWakeLock, PreferKey.webServiceWakeLock,
false false
@@ -145,25 +129,40 @@ object OtherConfig {
_sourceEditMaxLine = value _sourceEditMaxLine = value
} }
var cronetEnable by prefDelegate(
PreferKey.cronet,
false
)
var webPort by prefDelegate( var webPort by prefDelegate(
PreferKey.webPort, PreferKey.webPort,
1122 1122
) )
var threadCount by prefDelegate( var threadCount: Int
PreferKey.threadCount, get() = DownloadCacheConfig.threadCount
16 set(value) {
) DownloadCacheConfig.threadCount = value
}
var cacheBookThreadCount by prefDelegate( var cacheBookThreadCount: Int
PreferKey.cacheBookThreadCount, get() = DownloadCacheConfig.cacheBookThreadCount
16 set(value) {
) DownloadCacheConfig.cacheBookThreadCount = value
}
var preDownloadNum: Int
get() = DownloadCacheConfig.preDownloadNum
set(value) {
DownloadCacheConfig.preDownloadNum = value
}
var bitmapCacheSize: Int
get() = DownloadCacheConfig.bitmapCacheSize
set(value) {
DownloadCacheConfig.bitmapCacheSize = value
}
var imageRetainNum: Int
get() = DownloadCacheConfig.imageRetainNum
set(value) {
DownloadCacheConfig.imageRetainNum = value
}
var processText by prefDelegate( var processText by prefDelegate(
PreferKey.processText, PreferKey.processText,
@@ -180,9 +179,4 @@ object OtherConfig {
false false
) )
private val defaultUserAgent: String
get() = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/${BuildConfig.Cronet_Main_Version} Safari/537.36"
} }
@@ -21,7 +21,6 @@ import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.R import io.legado.app.R
import io.legado.app.model.CacheBook
import io.legado.app.service.WebService import io.legado.app.service.WebService
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPadding import io.legado.app.ui.theme.adaptiveContentPadding
@@ -34,7 +33,6 @@ import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
import io.legado.app.ui.widget.components.settingItem.InputSettingItem import io.legado.app.ui.widget.components.settingItem.InputSettingItem
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
@@ -58,8 +56,6 @@ fun OtherConfigScreen(
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior() val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
var showClearCacheDialog by remember { mutableStateOf(false) }
var showShrinkDbDialog by remember { mutableStateOf(false) }
var showClearWebViewDialog by remember { mutableStateOf(false) } var showClearWebViewDialog by remember { mutableStateOf(false) }
var showPasswordDialog by remember { mutableStateOf(false) } var showPasswordDialog by remember { mutableStateOf(false) }
var showCheckSourceSheet by remember { mutableStateOf(false) } var showCheckSourceSheet by remember { mutableStateOf(false) }
@@ -194,44 +190,6 @@ fun OtherConfigScreen(
onCheckedChange = { OtherConfig.antiAlias = it } onCheckedChange = { OtherConfig.antiAlias = it }
) )
SliderSettingItem(
title = stringResource(R.string.bitmap_cache_size),
description = stringResource(
R.string.bitmap_cache_size_summary,
OtherConfig.bitmapCacheSize
),
value = OtherConfig.bitmapCacheSize.toFloat(),
defaultValue = 32f,
valueRange = 1f..2047f,
onValueChange = {
viewModel.updateBitmapCacheSize(it.toInt())
}
)
SliderSettingItem(
title = stringResource(R.string.image_retain_number),
description = stringResource(
R.string.image_retain_number_summary,
OtherConfig.imageRetainNum
),
value = OtherConfig.imageRetainNum.toFloat(),
defaultValue = 10f,
valueRange = 0f..100f,
onValueChange = { OtherConfig.imageRetainNum = it.toInt() }
)
SliderSettingItem(
title = stringResource(R.string.pre_download),
description = stringResource(
R.string.pre_download_s,
OtherConfig.preDownloadNum
),
value = OtherConfig.preDownloadNum.toFloat(),
defaultValue = 10f,
valueRange = 0f..100f,
onValueChange = { OtherConfig.preDownloadNum = it.toInt() }
)
SwitchSettingItem( SwitchSettingItem(
title = stringResource(R.string.replace_enable_default_t), title = stringResource(R.string.replace_enable_default_t),
description = stringResource(R.string.replace_enable_default_s), description = stringResource(R.string.replace_enable_default_s),
@@ -297,12 +255,6 @@ fun OtherConfigScreen(
onCheckedChange = { OtherConfig.delayBookLoadEnable = it } onCheckedChange = { OtherConfig.delayBookLoadEnable = it }
) )
InputSettingItem(
title = stringResource(R.string.user_agent),
value = OtherConfig.userAgent,
onConfirm = { viewModel.saveUserAgent(it) }
)
SwitchSettingItem( SwitchSettingItem(
title = stringResource(R.string.web_service_wake_lock), title = stringResource(R.string.web_service_wake_lock),
description = stringResource(R.string.web_service_wake_lock_summary), description = stringResource(R.string.web_service_wake_lock_summary),
@@ -328,13 +280,6 @@ fun OtherConfigScreen(
onClick = { showDirectLinkUploadSheet = true } onClick = { showDirectLinkUploadSheet = true }
) )
SwitchSettingItem(
title = "Cronet",
description = stringResource(R.string.pref_cronet_summary),
checked = OtherConfig.cronetEnable,
onCheckedChange = { OtherConfig.cronetEnable = it }
)
InputSettingItem( InputSettingItem(
title = stringResource(R.string.web_port_title), title = stringResource(R.string.web_port_title),
value = OtherConfig.webPort.toString(), value = OtherConfig.webPort.toString(),
@@ -347,47 +292,12 @@ fun OtherConfigScreen(
} }
) )
ClickableSettingItem(
title = stringResource(R.string.clear_cache),
description = stringResource(R.string.clear_cache_summary),
onClick = { showClearCacheDialog = true }
)
ClickableSettingItem( ClickableSettingItem(
title = stringResource(R.string.clear_webview_data), title = stringResource(R.string.clear_webview_data),
description = stringResource(R.string.clear_webview_data_summary), description = stringResource(R.string.clear_webview_data_summary),
onClick = { showClearWebViewDialog = true } onClick = { showClearWebViewDialog = true }
) )
ClickableSettingItem(
title = stringResource(R.string.shrink_database),
description = stringResource(R.string.shrink_database_summary),
onClick = { showShrinkDbDialog = true }
)
SliderSettingItem(
title = stringResource(R.string.threads_num_title),
description = stringResource(R.string.threads_num_summary),
value = OtherConfig.threadCount.toFloat(),
defaultValue = 8f,
valueRange = 1f..256f,
onValueChange = { OtherConfig.threadCount = it.toInt() }
)
SliderSettingItem(
title = stringResource(R.string.cache_book_threads_num_title),
description = stringResource(R.string.cache_book_threads_num_summary),
value = OtherConfig.cacheBookThreadCount
.coerceIn(1, CacheBook.maxDownloadConcurrency)
.toFloat(),
defaultValue = CacheBook.maxDownloadConcurrency.toFloat(),
valueRange = 1f..CacheBook.maxDownloadConcurrency.toFloat(),
onValueChange = {
OtherConfig.cacheBookThreadCount =
it.toInt().coerceIn(1, CacheBook.maxDownloadConcurrency)
}
)
SwitchSettingItem( SwitchSettingItem(
title = stringResource(R.string.add_to_text_context_menu_t), title = stringResource(R.string.add_to_text_context_menu_t),
description = stringResource(R.string.add_to_text_context_menu_s), description = stringResource(R.string.add_to_text_context_menu_s),
@@ -437,30 +347,6 @@ fun OtherConfigScreen(
onDismiss = { showDirectLinkUploadSheet = false } onDismiss = { showDirectLinkUploadSheet = false }
) )
AppAlertDialog(
show = showClearCacheDialog,
onDismissRequest = { showClearCacheDialog = false },
title = stringResource(R.string.clear_cache),
text = stringResource(R.string.sure_del),
onConfirm = {
viewModel.clearCache(context)
showClearCacheDialog = false
},
onDismiss = { showClearCacheDialog = false }
)
AppAlertDialog(
show = showShrinkDbDialog,
onDismissRequest = { showShrinkDbDialog = false },
title = stringResource(R.string.shrink_database),
text = stringResource(R.string.sure),
onConfirm = {
viewModel.shrinkDatabase()
showShrinkDbDialog = false
},
onDismiss = { showShrinkDbDialog = false }
)
AppAlertDialog( AppAlertDialog(
show = showClearWebViewDialog, show = showClearWebViewDialog,
onDismissRequest = { showClearWebViewDialog = false }, onDismissRequest = { showClearWebViewDialog = false },
@@ -9,14 +9,12 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.domain.usecase.ClearBookCacheUseCase
import io.legado.app.domain.usecase.ShrinkDatabaseUseCase
import io.legado.app.help.DirectLinkUpload import io.legado.app.help.DirectLinkUpload
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.model.CheckSource import io.legado.app.model.CheckSource
import io.legado.app.model.ImageProvider
import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.receiver.SharedReceiverActivity
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfig
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.putPrefString import io.legado.app.utils.putPrefString
import io.legado.app.utils.restart import io.legado.app.utils.restart
@@ -25,10 +23,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import splitties.init.appCtx import splitties.init.appCtx
class OtherConfigViewModel( class OtherConfigViewModel : ViewModel() {
private val clearBookCacheUseCase: ClearBookCacheUseCase,
private val shrinkDatabaseUseCase: ShrinkDatabaseUseCase
) : ViewModel() {
private val packageManager = appCtx.packageManager private val packageManager = appCtx.packageManager
private val componentName = ComponentName( private val componentName = ComponentName(
@@ -53,21 +48,6 @@ class OtherConfigViewModel(
) )
} }
fun clearCache(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
clearBookCacheUseCase.executeAll()
FileUtils.delete(context.cacheDir.absolutePath)
context.externalCacheDir?.deleteRecursively()
context.cacheDir.deleteRecursively()
}
}
fun shrinkDatabase() {
viewModelScope.launch(Dispatchers.IO) {
shrinkDatabaseUseCase.execute()
}
}
fun clearWebViewData(context: Context) { fun clearWebViewData(context: Context) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
FileUtils.delete(context.getDir("webview", Context.MODE_PRIVATE)) FileUtils.delete(context.getDir("webview", Context.MODE_PRIVATE))
@@ -82,13 +62,8 @@ class OtherConfigViewModel(
} }
fun saveUserAgent(input: String) { fun saveUserAgent(input: String) {
OtherConfig.userAgent = input DownloadCacheConfig.userAgent = input
AppConfig.userAgent = OtherConfig.userAgent AppConfig.userAgent = DownloadCacheConfig.userAgent
}
fun updateBitmapCacheSize(size: Int) {
AppConfig.bitmapCacheSize = size
ImageProvider.bitmapLruCache.resize(ImageProvider.cacheSize)
} }
fun updateLocalBookDir(path: String) { fun updateLocalBookDir(path: String) {
@@ -1,185 +0,0 @@
package io.legado.app.ui.config.personalizationConfig
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.ResourcesCompat
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.loadFontFiles
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.adaptiveContentPadding
import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import io.legado.app.utils.FileDoc
import io.legado.app.utils.getPrefString
import io.legado.app.utils.putPrefString
import io.legado.app.utils.takePersistablePermissionSafely
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FontSelectScreen(
onBackClick: () -> Unit
) {
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
val context = LocalContext.current
var fontItems by remember { mutableStateOf<List<FileDoc>>(emptyList()) }
var selectedFolderUri by remember { mutableStateOf<Uri?>(null) }
// 加载字体文件
fun loadFonts() {
fontItems = loadFontFiles(context, selectedFolderUri)
}
// 从偏好设置中加载保存的字体文件夹路径
remember {
val savedFontPath = context.getPrefString(PreferKey.fontFolder)
if (!savedFontPath.isNullOrEmpty()) {
selectedFolderUri = Uri.parse(savedFontPath)
}
loadFonts()
}
// 文件夹选择启动器
val selectFolderLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocumentTree()
) { uri ->
if (uri != null) {
selectedFolderUri = uri
// 获取持久化权限
uri.takePersistablePermissionSafely(context, Intent.FLAG_GRANT_READ_URI_PERMISSION)
// 保存选择的文件夹路径到偏好设置
context.putPrefString(PreferKey.fontFolder, uri.toString())
loadFonts()
}
}
AppScaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
GlassMediumFlexibleTopAppBar(
title = stringResource(R.string.font_setting),
scrollBehavior = scrollBehavior,
navigationIcon = {
TopBarNavigationButton(onClick = onBackClick)
}
)
}
) { paddingValues ->
if (fontItems.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "没有字体文件",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(bottom = 16.dp)
)
Button(onClick = { selectFolderLauncher.launch(null) }) {
Text(text = stringResource(R.string.select_font))
}
}
}
} else {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
contentPadding = adaptiveContentPadding(
top = paddingValues.calculateTopPadding(),
bottom = 120.dp
),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
items(fontItems) {
FontItem(it) {
// 选择字体的逻辑
ThemeConfig.appFontPath = it.uri.toString()
}
}
}
}
}
}
@Composable
fun FontItem(fontDoc: FileDoc, onFontSelected: (FileDoc) -> Unit) {
val context = LocalContext.current
Card(
modifier = Modifier.fillMaxWidth().height(120.dp),
onClick = {
onFontSelected(fontDoc)
},
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
// 使用AndroidView显示使用对应字体的文本
AndroidView(
factory = { ctx ->
android.widget.TextView(ctx).apply {
text = fontDoc.name
textSize = 16f
// 设置垂直和水平居中
gravity = android.view.Gravity.CENTER
// 设置最大行数为2,超过省略
maxLines = 2
ellipsize = android.text.TextUtils.TruncateAt.END
// 加载并设置字体
runCatching {
val typeface: Typeface? = if (fontDoc.uri.scheme == "content") {
ctx.contentResolver.openFileDescriptor(fontDoc.uri, "r")?.use {
Typeface.Builder(it.fileDescriptor).build()
}
} else {
Typeface.createFromFile(fontDoc.uri.path!!)
}
this.typeface = typeface
}.onFailure {
// 字体加载失败,使用默认字体
}
}
},
modifier = Modifier.fillMaxSize()
)
}
}
@@ -276,11 +276,6 @@ fun EditThemeSheet(
valueRange = 0f..100f, valueRange = 0f..100f,
onValueChange = { data = data.copy(containerOpacity = it.toInt()) } onValueChange = { data = data.copy(containerOpacity = it.toInt()) }
) )
CompactSwitchSettingItem(
title = "容器边框",
checked = data.enableContainerBorder,
onCheckedChange = { data = data.copy(enableContainerBorder = it) }
)
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
@@ -62,9 +62,9 @@ import io.legado.app.ui.config.ConfigNavScreen
import io.legado.app.ui.config.ConfigTag import io.legado.app.ui.config.ConfigTag
import io.legado.app.ui.config.backupConfig.BackupConfigScreen import io.legado.app.ui.config.backupConfig.BackupConfigScreen
import io.legado.app.ui.config.coverConfig.CoverConfigScreen import io.legado.app.ui.config.coverConfig.CoverConfigScreen
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
import io.legado.app.ui.config.themeConfig.ThemeConfig import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.config.otherConfig.OtherConfigScreen import io.legado.app.ui.config.otherConfig.OtherConfigScreen
import io.legado.app.ui.config.personalizationConfig.FontSelectScreen
import io.legado.app.ui.config.customTheme.CustomThemeScreen import io.legado.app.ui.config.customTheme.CustomThemeScreen
import io.legado.app.ui.config.readConfig.ReadConfigScreen import io.legado.app.ui.config.readConfig.ReadConfigScreen
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
@@ -105,6 +105,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
private const val ROUTE_SETTINGS_THEME = "settings/theme" private const val ROUTE_SETTINGS_THEME = "settings/theme"
private const val ROUTE_SETTINGS_BACKUP = "settings/backup" private const val ROUTE_SETTINGS_BACKUP = "settings/backup"
private const val ROUTE_SETTINGS_CUSTOM_THEME = "settings/custom_theme" private const val ROUTE_SETTINGS_CUSTOM_THEME = "settings/custom_theme"
private const val ROUTE_SETTINGS_DOWNLOAD_CACHE = "settings/download_cache"
private const val ROUTE_IMPORT_LOCAL = "import/local" private const val ROUTE_IMPORT_LOCAL = "import/local"
private const val ROUTE_IMPORT_REMOTE = "import/remote" private const val ROUTE_IMPORT_REMOTE = "import/remote"
private const val ROUTE_CACHE = "cache" private const val ROUTE_CACHE = "cache"
@@ -255,6 +256,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
ConfigTag.COVER_CONFIG -> ROUTE_SETTINGS_COVER ConfigTag.COVER_CONFIG -> ROUTE_SETTINGS_COVER
ConfigTag.THEME_CONFIG -> ROUTE_SETTINGS_THEME ConfigTag.THEME_CONFIG -> ROUTE_SETTINGS_THEME
ConfigTag.BACKUP_CONFIG -> ROUTE_SETTINGS_BACKUP ConfigTag.BACKUP_CONFIG -> ROUTE_SETTINGS_BACKUP
ConfigTag.DOWNLOAD_CACHE_CONFIG -> ROUTE_SETTINGS_DOWNLOAD_CACHE
else -> ROUTE_SETTINGS else -> ROUTE_SETTINGS
} }
} }
@@ -294,6 +296,9 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
@Serializable @Serializable
private data object MainRouteSettingsThemeManage : MainRoute private data object MainRouteSettingsThemeManage : MainRoute
@Serializable
private data object MainRouteSettingsDownloadCache : MainRoute
@Serializable @Serializable
private data object MainRouteImportLocal : MainRoute private data object MainRouteImportLocal : MainRoute
@@ -551,7 +556,8 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
onNavigateToRead = { backStack.add(MainRouteSettingsRead) }, onNavigateToRead = { backStack.add(MainRouteSettingsRead) },
onNavigateToCover = { backStack.add(MainRouteSettingsCover) }, onNavigateToCover = { backStack.add(MainRouteSettingsCover) },
onNavigateToTheme = { backStack.add(MainRouteSettingsTheme) }, onNavigateToTheme = { backStack.add(MainRouteSettingsTheme) },
onNavigateToBackup = { backStack.add(MainRouteSettingsBackup) } onNavigateToBackup = { backStack.add(MainRouteSettingsBackup) },
onNavigateToDownloadCache = { backStack.add(MainRouteSettingsDownloadCache) }
) )
} }
@@ -579,6 +585,10 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
BackupConfigScreen(onBackClick = { navigateBack(backStack) }) BackupConfigScreen(onBackClick = { navigateBack(backStack) })
} }
entry<MainRouteSettingsDownloadCache> {
DownloadCacheConfigScreen(onBackClick = { navigateBack(backStack) })
}
entry<MainRouteSettingsCustomTheme> { entry<MainRouteSettingsCustomTheme> {
CustomThemeScreen( CustomThemeScreen(
onBackClick = { navigateBack(backStack) } onBackClick = { navigateBack(backStack) }
@@ -832,7 +842,8 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
MainRouteSettingsTheme, MainRouteSettingsTheme,
MainRouteSettingsBackup, MainRouteSettingsBackup,
MainRouteSettingsCustomTheme, MainRouteSettingsCustomTheme,
MainRouteSettingsThemeManage -> { MainRouteSettingsThemeManage,
MainRouteSettingsDownloadCache -> {
backStack.clear() backStack.clear()
backStack.add(MainRouteHome) backStack.add(MainRouteHome)
backStack.add(MainRouteSettings) backStack.add(MainRouteSettings)
@@ -1095,6 +1106,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
"settings/theme" -> MainRouteSettingsTheme "settings/theme" -> MainRouteSettingsTheme
"settings/backup" -> MainRouteSettingsBackup "settings/backup" -> MainRouteSettingsBackup
"settings/custom_theme" -> MainRouteSettingsCustomTheme "settings/custom_theme" -> MainRouteSettingsCustomTheme
"settings/download_cache" -> MainRouteSettingsDownloadCache
"import/local" -> MainRouteImportLocal "import/local" -> MainRouteImportLocal
"import/remote" -> MainRouteImportRemote "import/remote" -> MainRouteImportRemote
ROUTE_MAIN -> MainRouteHome ROUTE_MAIN -> MainRouteHome
@@ -1104,6 +1116,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
ROUTE_SETTINGS_COVER -> MainRouteSettingsCover ROUTE_SETTINGS_COVER -> MainRouteSettingsCover
ROUTE_SETTINGS_THEME -> MainRouteSettingsTheme ROUTE_SETTINGS_THEME -> MainRouteSettingsTheme
ROUTE_SETTINGS_BACKUP -> MainRouteSettingsBackup ROUTE_SETTINGS_BACKUP -> MainRouteSettingsBackup
ROUTE_SETTINGS_DOWNLOAD_CACHE -> MainRouteSettingsDownloadCache
ROUTE_IMPORT_LOCAL -> MainRouteImportLocal ROUTE_IMPORT_LOCAL -> MainRouteImportLocal
ROUTE_IMPORT_REMOTE -> MainRouteImportRemote ROUTE_IMPORT_REMOTE -> MainRouteImportRemote
ROUTE_CACHE -> MainRouteCache(intent?.getLongExtra(EXTRA_CACHE_GROUP_ID, -1L) ?: -1L) ROUTE_CACHE -> MainRouteCache(intent?.getLongExtra(EXTRA_CACHE_GROUP_ID, -1L) ?: -1L)
@@ -5,7 +5,6 @@ import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -285,22 +284,12 @@ fun ExploreScreen(
.align(Alignment.TopStart) .align(Alignment.TopStart)
.padding(top = paddingValues.calculateTopPadding() + 4.dp, start = 8.dp) .padding(top = paddingValues.calculateTopPadding() + 4.dp, start = 8.dp)
) { item -> ) { item ->
val enableBorder = ThemeConfig.enableDeepPersonalization && ThemeConfig.enableContainerBorder
val borderWidth = ThemeConfig.containerBorderWidth.dp
val borderColor = if (ThemeConfig.containerBorderColor != 0) {
Color(ThemeConfig.containerBorderColor)
} else {
LegadoTheme.colorScheme.outline
}
TextCard( TextCard(
text = item.bookSourceName, text = item.bookSourceName,
textStyle = LegadoTheme.typography.labelMediumEmphasized, textStyle = LegadoTheme.typography.labelMediumEmphasized,
cornerRadius = 12.dp, cornerRadius = 12.dp,
horizontalPadding = 12.dp, horizontalPadding = 12.dp,
verticalPadding = 8.dp, verticalPadding = 8.dp,
border = if (enableBorder) {
BorderStroke(borderWidth, borderColor)
} else null,
onClick = { onClick = {
scope.launch { scope.launch {
val index = val index =
@@ -371,23 +360,12 @@ fun ExploreSourceHeader(
label = "CardColor" label = "CardColor"
) )
val enableBorder = ThemeConfig.enableDeepPersonalization && ThemeConfig.enableContainerBorder
val borderWidth = ThemeConfig.containerBorderWidth.dp
val borderColor = if (ThemeConfig.containerBorderColor != 0) {
Color(ThemeConfig.containerBorderColor)
} else {
LegadoTheme.colorScheme.outline
}
GlassCard( GlassCard(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 4.dp), .padding(vertical = 4.dp),
cornerRadius = 12.dp, cornerRadius = 12.dp,
containerColor = containerColor, containerColor = containerColor,
border = if (enableBorder) {
BorderStroke(borderWidth, borderColor)
} else null
) { ) {
ListItem( ListItem(
modifier = Modifier modifier = Modifier
@@ -1483,4 +1483,12 @@
<string name="search_source_progress"> · 进度 %1$d/%2$d</string> <string name="search_source_progress"> · 进度 %1$d/%2$d</string>
<string name="search_empty_scope_disable_precision">%1$s分组搜索结果为空,是否关闭精准搜索?</string> <string name="search_empty_scope_disable_precision">%1$s分组搜索结果为空,是否关闭精准搜索?</string>
<string name="search_empty_scope_switch_all">%1$s分组搜索结果为空,是否切换到全部分组?</string> <string name="search_empty_scope_switch_all">%1$s分组搜索结果为空,是否切换到全部分组?</string>
<string name="download_cache_config">下载与缓存</string>
<string name="http_cache">HTTP缓存</string>
<string name="cover_cache">封面缓存</string>
<string name="manga_cache">漫画缓存</string>
<string name="cache_size_mb">%1$.1f MB</string>
<string name="download_setting">下载</string>
<string name="image_cache">图片缓存</string>
<string name="network">网络</string>
</resources> </resources>
+8
View File
@@ -1527,4 +1527,12 @@
<string name="search_source_progress"> · Progress %1$d/%2$d</string> <string name="search_source_progress"> · Progress %1$d/%2$d</string>
<string name="search_empty_scope_disable_precision">%1$s group search returned no results. Disable precise search?</string> <string name="search_empty_scope_disable_precision">%1$s group search returned no results. Disable precise search?</string>
<string name="search_empty_scope_switch_all">%1$s group search returned no results. Switch to all groups?</string> <string name="search_empty_scope_switch_all">%1$s group search returned no results. Switch to all groups?</string>
<string name="download_cache_config">Download &amp; Cache</string>
<string name="http_cache">HTTP Cache</string>
<string name="cover_cache">Cover Cache</string>
<string name="manga_cache">Manga Cache</string>
<string name="cache_size_mb">%1$.1f MB</string>
<string name="download_setting">Download</string>
<string name="image_cache">Image Cache</string>
<string name="network">Network</string>
</resources> </resources>