代码优化
This commit is contained in:
@@ -210,11 +210,6 @@ object PreferKey {
|
||||
const val secondaryTextColor = "colorMD3OnPrimaryContainer"
|
||||
const val themeBackgroundColor = "colorMD3Background"
|
||||
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 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.bookshelfConfig.BookshelfManageScreenConfig
|
||||
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.readConfig.ReadConfigViewModel
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel
|
||||
@@ -189,6 +190,7 @@ val appModule = module {
|
||||
viewModelOf(::OtherConfigViewModel)
|
||||
viewModelOf(::ReadConfigViewModel)
|
||||
viewModelOf(::CoverConfigViewModel)
|
||||
viewModelOf(::DownloadCacheConfigViewModel)
|
||||
viewModelOf(::ThemeConfigViewModel)
|
||||
viewModelOf(::BackupConfigViewModel)
|
||||
viewModelOf(::TocViewModel)
|
||||
|
||||
@@ -52,16 +52,12 @@ val cookieJar by lazy {
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制缓存拦截器,对于图片资源,如果没有缓存头,则强制缓存30天
|
||||
* 强制缓存拦截器,对于封面请求(由CoverFetcher标记),强制缓存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")
|
||||
) {
|
||||
if (request.tag() === io.legado.app.help.coil.CoverFetcher.COVER_REQUEST_TAG) {
|
||||
response.newBuilder()
|
||||
.header("Cache-Control", "public, max-age=2592000")
|
||||
.removeHeader("Pragma")
|
||||
@@ -149,6 +145,7 @@ val okHttpClient: OkHttpClient by lazy {
|
||||
|
||||
val okHttpClientManga by lazy {
|
||||
okHttpClient.newBuilder().run {
|
||||
cache(Cache(File(appCtx.cacheDir, "manga_cache"), 100L * 1024L * 1024L))
|
||||
val interceptors = interceptors()
|
||||
interceptors.add(1) { chain ->
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.legado.app.ui.config.otherConfig
|
||||
|
||||
import io.legado.app.BuildConfig
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfig
|
||||
import io.legado.app.ui.config.prefDelegate
|
||||
|
||||
object OtherConfig {
|
||||
@@ -56,21 +56,6 @@ object OtherConfig {
|
||||
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(
|
||||
PreferKey.replaceEnableDefault,
|
||||
true
|
||||
@@ -106,6 +91,18 @@ object OtherConfig {
|
||||
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(
|
||||
PreferKey.sharedElementEnterTransitionEnable,
|
||||
false
|
||||
@@ -116,19 +113,6 @@ object OtherConfig {
|
||||
true
|
||||
)
|
||||
|
||||
private var _userAgent by prefDelegate(
|
||||
PreferKey.userAgent,
|
||||
""
|
||||
)
|
||||
|
||||
var userAgent: String
|
||||
get() = _userAgent.ifBlank {
|
||||
defaultUserAgent
|
||||
}
|
||||
set(value) {
|
||||
_userAgent = value
|
||||
}
|
||||
|
||||
var webServiceWakeLock by prefDelegate(
|
||||
PreferKey.webServiceWakeLock,
|
||||
false
|
||||
@@ -145,25 +129,40 @@ object OtherConfig {
|
||||
_sourceEditMaxLine = value
|
||||
}
|
||||
|
||||
var cronetEnable by prefDelegate(
|
||||
PreferKey.cronet,
|
||||
false
|
||||
)
|
||||
|
||||
var webPort by prefDelegate(
|
||||
PreferKey.webPort,
|
||||
1122
|
||||
)
|
||||
|
||||
var threadCount by prefDelegate(
|
||||
PreferKey.threadCount,
|
||||
16
|
||||
)
|
||||
var threadCount: Int
|
||||
get() = DownloadCacheConfig.threadCount
|
||||
set(value) {
|
||||
DownloadCacheConfig.threadCount = value
|
||||
}
|
||||
|
||||
var cacheBookThreadCount by prefDelegate(
|
||||
PreferKey.cacheBookThreadCount,
|
||||
16
|
||||
)
|
||||
var cacheBookThreadCount: Int
|
||||
get() = DownloadCacheConfig.cacheBookThreadCount
|
||||
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(
|
||||
PreferKey.processText,
|
||||
@@ -180,9 +179,4 @@ object OtherConfig {
|
||||
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.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.model.CacheBook
|
||||
import io.legado.app.service.WebService
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
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.DropdownListSettingItem
|
||||
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.topbar.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
@@ -58,8 +56,6 @@ fun OtherConfigScreen(
|
||||
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
|
||||
var showClearCacheDialog by remember { mutableStateOf(false) }
|
||||
var showShrinkDbDialog by remember { mutableStateOf(false) }
|
||||
var showClearWebViewDialog by remember { mutableStateOf(false) }
|
||||
var showPasswordDialog by remember { mutableStateOf(false) }
|
||||
var showCheckSourceSheet by remember { mutableStateOf(false) }
|
||||
@@ -194,44 +190,6 @@ fun OtherConfigScreen(
|
||||
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(
|
||||
title = stringResource(R.string.replace_enable_default_t),
|
||||
description = stringResource(R.string.replace_enable_default_s),
|
||||
@@ -297,12 +255,6 @@ fun OtherConfigScreen(
|
||||
onCheckedChange = { OtherConfig.delayBookLoadEnable = it }
|
||||
)
|
||||
|
||||
InputSettingItem(
|
||||
title = stringResource(R.string.user_agent),
|
||||
value = OtherConfig.userAgent,
|
||||
onConfirm = { viewModel.saveUserAgent(it) }
|
||||
)
|
||||
|
||||
SwitchSettingItem(
|
||||
title = stringResource(R.string.web_service_wake_lock),
|
||||
description = stringResource(R.string.web_service_wake_lock_summary),
|
||||
@@ -328,13 +280,6 @@ fun OtherConfigScreen(
|
||||
onClick = { showDirectLinkUploadSheet = true }
|
||||
)
|
||||
|
||||
SwitchSettingItem(
|
||||
title = "Cronet",
|
||||
description = stringResource(R.string.pref_cronet_summary),
|
||||
checked = OtherConfig.cronetEnable,
|
||||
onCheckedChange = { OtherConfig.cronetEnable = it }
|
||||
)
|
||||
|
||||
InputSettingItem(
|
||||
title = stringResource(R.string.web_port_title),
|
||||
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(
|
||||
title = stringResource(R.string.clear_webview_data),
|
||||
description = stringResource(R.string.clear_webview_data_summary),
|
||||
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(
|
||||
title = stringResource(R.string.add_to_text_context_menu_t),
|
||||
description = stringResource(R.string.add_to_text_context_menu_s),
|
||||
@@ -437,30 +347,6 @@ fun OtherConfigScreen(
|
||||
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(
|
||||
show = showClearWebViewDialog,
|
||||
onDismissRequest = { showClearWebViewDialog = false },
|
||||
|
||||
@@ -9,14 +9,12 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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.config.AppConfig
|
||||
import io.legado.app.help.config.LocalConfig
|
||||
import io.legado.app.model.CheckSource
|
||||
import io.legado.app.model.ImageProvider
|
||||
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.putPrefString
|
||||
import io.legado.app.utils.restart
|
||||
@@ -25,10 +23,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
|
||||
class OtherConfigViewModel(
|
||||
private val clearBookCacheUseCase: ClearBookCacheUseCase,
|
||||
private val shrinkDatabaseUseCase: ShrinkDatabaseUseCase
|
||||
) : ViewModel() {
|
||||
class OtherConfigViewModel : ViewModel() {
|
||||
|
||||
private val packageManager = appCtx.packageManager
|
||||
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) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
FileUtils.delete(context.getDir("webview", Context.MODE_PRIVATE))
|
||||
@@ -82,13 +62,8 @@ class OtherConfigViewModel(
|
||||
}
|
||||
|
||||
fun saveUserAgent(input: String) {
|
||||
OtherConfig.userAgent = input
|
||||
AppConfig.userAgent = OtherConfig.userAgent
|
||||
}
|
||||
|
||||
fun updateBitmapCacheSize(size: Int) {
|
||||
AppConfig.bitmapCacheSize = size
|
||||
ImageProvider.bitmapLruCache.resize(ImageProvider.cacheSize)
|
||||
DownloadCacheConfig.userAgent = input
|
||||
AppConfig.userAgent = DownloadCacheConfig.userAgent
|
||||
}
|
||||
|
||||
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,
|
||||
onValueChange = { data = data.copy(containerOpacity = it.toInt()) }
|
||||
)
|
||||
CompactSwitchSettingItem(
|
||||
title = "容器边框",
|
||||
checked = data.enableContainerBorder,
|
||||
onCheckedChange = { data = data.copy(enableContainerBorder = it) }
|
||||
)
|
||||
|
||||
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.backupConfig.BackupConfigScreen
|
||||
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.otherConfig.OtherConfigScreen
|
||||
import io.legado.app.ui.config.personalizationConfig.FontSelectScreen
|
||||
import io.legado.app.ui.config.customTheme.CustomThemeScreen
|
||||
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
||||
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_BACKUP = "settings/backup"
|
||||
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_REMOTE = "import/remote"
|
||||
private const val ROUTE_CACHE = "cache"
|
||||
@@ -255,6 +256,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
ConfigTag.COVER_CONFIG -> ROUTE_SETTINGS_COVER
|
||||
ConfigTag.THEME_CONFIG -> ROUTE_SETTINGS_THEME
|
||||
ConfigTag.BACKUP_CONFIG -> ROUTE_SETTINGS_BACKUP
|
||||
ConfigTag.DOWNLOAD_CACHE_CONFIG -> ROUTE_SETTINGS_DOWNLOAD_CACHE
|
||||
else -> ROUTE_SETTINGS
|
||||
}
|
||||
}
|
||||
@@ -294,6 +296,9 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
@Serializable
|
||||
private data object MainRouteSettingsThemeManage : MainRoute
|
||||
|
||||
@Serializable
|
||||
private data object MainRouteSettingsDownloadCache : MainRoute
|
||||
|
||||
@Serializable
|
||||
private data object MainRouteImportLocal : MainRoute
|
||||
|
||||
@@ -551,7 +556,8 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
onNavigateToRead = { backStack.add(MainRouteSettingsRead) },
|
||||
onNavigateToCover = { backStack.add(MainRouteSettingsCover) },
|
||||
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) })
|
||||
}
|
||||
|
||||
entry<MainRouteSettingsDownloadCache> {
|
||||
DownloadCacheConfigScreen(onBackClick = { navigateBack(backStack) })
|
||||
}
|
||||
|
||||
entry<MainRouteSettingsCustomTheme> {
|
||||
CustomThemeScreen(
|
||||
onBackClick = { navigateBack(backStack) }
|
||||
@@ -832,7 +842,8 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
MainRouteSettingsTheme,
|
||||
MainRouteSettingsBackup,
|
||||
MainRouteSettingsCustomTheme,
|
||||
MainRouteSettingsThemeManage -> {
|
||||
MainRouteSettingsThemeManage,
|
||||
MainRouteSettingsDownloadCache -> {
|
||||
backStack.clear()
|
||||
backStack.add(MainRouteHome)
|
||||
backStack.add(MainRouteSettings)
|
||||
@@ -1095,6 +1106,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
"settings/theme" -> MainRouteSettingsTheme
|
||||
"settings/backup" -> MainRouteSettingsBackup
|
||||
"settings/custom_theme" -> MainRouteSettingsCustomTheme
|
||||
"settings/download_cache" -> MainRouteSettingsDownloadCache
|
||||
"import/local" -> MainRouteImportLocal
|
||||
"import/remote" -> MainRouteImportRemote
|
||||
ROUTE_MAIN -> MainRouteHome
|
||||
@@ -1104,6 +1116,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
ROUTE_SETTINGS_COVER -> MainRouteSettingsCover
|
||||
ROUTE_SETTINGS_THEME -> MainRouteSettingsTheme
|
||||
ROUTE_SETTINGS_BACKUP -> MainRouteSettingsBackup
|
||||
ROUTE_SETTINGS_DOWNLOAD_CACHE -> MainRouteSettingsDownloadCache
|
||||
ROUTE_IMPORT_LOCAL -> MainRouteImportLocal
|
||||
ROUTE_IMPORT_REMOTE -> MainRouteImportRemote
|
||||
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.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -285,22 +284,12 @@ fun ExploreScreen(
|
||||
.align(Alignment.TopStart)
|
||||
.padding(top = paddingValues.calculateTopPadding() + 4.dp, start = 8.dp)
|
||||
) { 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(
|
||||
text = item.bookSourceName,
|
||||
textStyle = LegadoTheme.typography.labelMediumEmphasized,
|
||||
cornerRadius = 12.dp,
|
||||
horizontalPadding = 12.dp,
|
||||
verticalPadding = 8.dp,
|
||||
border = if (enableBorder) {
|
||||
BorderStroke(borderWidth, borderColor)
|
||||
} else null,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val index =
|
||||
@@ -371,23 +360,12 @@ fun ExploreSourceHeader(
|
||||
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(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = containerColor,
|
||||
border = if (enableBorder) {
|
||||
BorderStroke(borderWidth, borderColor)
|
||||
} else null
|
||||
) {
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
|
||||
Reference in New Issue
Block a user