From 9410e9e0b5431f387a9dfc69bb832f018d97b1f4 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Tue, 14 Apr 2026 00:50:56 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 1 + .../main/java/io/legado/app/di/appModule.kt | 2 + .../app/help/source/BookSourceExtensions.kt | 16 ++++- .../ui/book/import/remote/RemoteBookScreen.kt | 1 + .../legado/app/ui/book/info/BookInfoScreen.kt | 7 ++- .../java/io/legado/app/ui/theme/AppTheme.kt | 38 +----------- .../java/io/legado/app/ui/theme/Typography.kt | 29 +++++---- .../app/ui/widget/components/AppTextField.kt | 59 ++++++++++++++++++- .../ui/widget/components/SearchBarSection.kt | 4 +- .../widget/components/SelectionBottomBar.kt | 3 +- .../ui/widget/components/list/ListScaffold.kt | 2 +- .../components/rules/RuleListScaffold.kt | 1 + .../java/io/legado/app/utils/UriExtensions.kt | 7 +++ 13 files changed, 114 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index df7b0cf57..ac136af40 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -134,6 +134,7 @@ object PreferKey { const val doublePageHorizontal = "doubleHorizontalPage" const val readUrlOpenInBrowser = "readUrlInBrowser" const val defaultBookTreeUri = "defaultBookTreeUri" + const val importBookPath = "importBookPath" const val checkSource = "checkSource" const val uploadRule = "uploadRule" const val tocUiUseReplace = "tocUiUseReplace" diff --git a/app/src/main/java/io/legado/app/di/appModule.kt b/app/src/main/java/io/legado/app/di/appModule.kt index 04e7bb6aa..fe6939991 100644 --- a/app/src/main/java/io/legado/app/di/appModule.kt +++ b/app/src/main/java/io/legado/app/di/appModule.kt @@ -15,6 +15,7 @@ import io.legado.app.data.repository.ReadRecordRepository import io.legado.app.data.repository.RemoteBookRepository import io.legado.app.data.repository.SearchContentRepository import io.legado.app.data.repository.UploadRepository +import io.legado.app.domain.usecase.ExploreKindUiUseCase import io.legado.app.help.coil.CoverFetcher import io.legado.app.help.coil.CoverInterceptor import io.legado.app.help.http.okHttpClient @@ -64,6 +65,7 @@ val appModule = module { singleOf(::BookGroupRepository) singleOf(::SearchContentRepository) singleOf(::RemoteBookRepository) + singleOf(::ExploreKindUiUseCase) single { DirectLinkUploadRepository() } single { ExploreRepositoryImpl(get()) } diff --git a/app/src/main/java/io/legado/app/help/source/BookSourceExtensions.kt b/app/src/main/java/io/legado/app/help/source/BookSourceExtensions.kt index 6d92062b2..a78e9eedb 100644 --- a/app/src/main/java/io/legado/app/help/source/BookSourceExtensions.kt +++ b/app/src/main/java/io/legado/app/help/source/BookSourceExtensions.kt @@ -1,6 +1,7 @@ package io.legado.app.help.source import com.script.rhino.runScriptWithContext +import androidx.collection.LruCache import io.legado.app.constant.BookSourceType import io.legado.app.constant.BookType import io.legado.app.data.entities.BookSource @@ -8,6 +9,7 @@ import io.legado.app.data.entities.BookSourcePart import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.utils.ACache import io.legado.app.utils.GSON +import io.legado.app.utils.InfoMap import io.legado.app.utils.MD5Utils import io.legado.app.utils.fromJsonArray import io.legado.app.utils.isJsonArray @@ -25,6 +27,15 @@ import java.util.concurrent.ConcurrentHashMap private val mutexMap by lazy { hashMapOf() } private val exploreKindsMap by lazy { ConcurrentHashMap>() } private val aCache by lazy { ACache.get("explore") } +private val exploreInfoMapList by lazy { LruCache(99) } + +fun getExploreInfoMap(sourceUrl: String): InfoMap { + return synchronized(exploreInfoMapList) { + exploreInfoMapList[sourceUrl] ?: InfoMap(sourceUrl).also { + exploreInfoMapList.put(sourceUrl, it) + } + } +} private fun BookSource.getExploreKindsKey(): String { return MD5Utils.md5Encode(bookSourceUrl + exploreUrl) @@ -57,13 +68,16 @@ suspend fun BookSource.exploreKinds(): List { ) { ruleStr = aCache.getAsString(exploreKindsKey) if (ruleStr.isNullOrBlank()) { + val exploreInfoMap = getExploreInfoMap(bookSourceUrl) val jsStr = if (exploreUrl.startsWith("@")) { exploreUrl.substring(4) } else { exploreUrl.substring(4, exploreUrl.lastIndexOf("<")) } ruleStr = runScriptWithContext { - evalJS(jsStr).toString().trim() + evalJS(jsStr) { + put("infoMap", exploreInfoMap) + }.toString().trim() } aCache.put(exploreKindsKey, ruleStr) } diff --git a/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt b/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt index 615d8f42e..7f9d30584 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt @@ -304,6 +304,7 @@ fun RemoteBookScreen( ) }, selectionActions = SelectionActions( + onClearSelection = { viewModel.clearSelection() }, onSelectAll = { viewModel.dispatch(RemoteBookIntent.SelectAll) }, onSelectInvert = { viewModel.dispatch(RemoteBookIntent.SelectInvert) }, primaryAction = ActionItem( diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt index 76fd3e59d..215bb4f3a 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt @@ -845,11 +845,14 @@ private fun BookInfoSummary( ) { AppText( text = stringResource(R.string.read_chapter_total, book.totalChapterNum), - style = LegadoTheme.typography.bodyMedium, + style = LegadoTheme.typography.labelMedium, color = LegadoTheme.colorScheme.primary, fontWeight = FontWeight.Bold, ) - AppText(text = "|", color = LegadoTheme.colorScheme.secondary) + AppText( + text = "|", + color = LegadoTheme.colorScheme.secondary + ) AppText( text = if (book.durChapterIndex + 1 == book.totalChapterNum && book.totalChapterNum > 0) "已读完" else stringResource(R.string.read_chapter_index, book.durChapterIndex + 1), style = LegadoTheme.typography.labelMedium, diff --git a/app/src/main/java/io/legado/app/ui/theme/AppTheme.kt b/app/src/main/java/io/legado/app/ui/theme/AppTheme.kt index 1199280b6..677b85bf5 100644 --- a/app/src/main/java/io/legado/app/ui/theme/AppTheme.kt +++ b/app/src/main/java/io/legado/app/ui/theme/AppTheme.kt @@ -13,8 +13,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight import io.legado.app.ui.config.themeConfig.ThemeConfig import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.ThemeController @@ -132,37 +130,7 @@ fun AppTheme( MiuixTheme(controller = controller) { val miuixStyles = MiuixTheme.textStyles val legadoTypography = remember(miuixStyles) { - LegadoTypography( - headlineLarge = miuixStyles.title1, - headlineLargeEmphasized = miuixStyles.title1.emphasized(), - headlineMedium = miuixStyles.title2, - headlineMediumEmphasized = miuixStyles.title2.emphasized(), - headlineSmall = miuixStyles.title3, - headlineSmallEmphasized = miuixStyles.title3.emphasized(), - - - titleLarge = miuixStyles.headline1, - titleLargeEmphasized = miuixStyles.headline1.emphasized(), - titleMedium = miuixStyles.headline2, - titleMediumEmphasized = miuixStyles.headline2.emphasized(), - titleSmall = miuixStyles.subtitle, - titleSmallEmphasized = miuixStyles.subtitle.emphasized(), - - - bodyLarge = miuixStyles.paragraph, - bodyLargeEmphasized = miuixStyles.paragraph.emphasized(), - bodyMedium = miuixStyles.body1, - bodyMediumEmphasized = miuixStyles.body1.emphasized(), - bodySmall = miuixStyles.body2, - bodySmallEmphasized = miuixStyles.body2.emphasized(), - - labelLarge = miuixStyles.button, - labelLargeEmphasized = miuixStyles.button.emphasized(), - labelMedium = miuixStyles.footnote1, - labelMediumEmphasized = miuixStyles.footnote1.emphasized(), - labelSmall = miuixStyles.footnote2, - labelSmallEmphasized = miuixStyles.footnote2.emphasized() - ) + miuixStylesToM3Typography(miuixStyles).toLegadoTypography() } val miuixColorScheme = MiuixTheme.colorScheme @@ -290,7 +258,3 @@ private fun Typography.toLegadoTypography(): LegadoTypography { labelSmallEmphasized = labelSmallEmphasized ) } - -private fun TextStyle.emphasized(): TextStyle { - return copy(fontWeight = FontWeight.Medium) -} diff --git a/app/src/main/java/io/legado/app/ui/theme/Typography.kt b/app/src/main/java/io/legado/app/ui/theme/Typography.kt index 5352a507d..5896878d5 100644 --- a/app/src/main/java/io/legado/app/ui/theme/Typography.kt +++ b/app/src/main/java/io/legado/app/ui/theme/Typography.kt @@ -3,6 +3,7 @@ package io.legado.app.ui.theme import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Typography import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp import top.yukonga.miuix.kmp.theme.TextStyles @@ -17,28 +18,34 @@ fun miuixStylesToM3Typography(miuixStyles: TextStyles): Typography { displayMedium = miuixStyles.title2, // 24.sp displaySmall = miuixStyles.title3, // 20.sp - headlineLarge = miuixStyles.title2, // 24.sp - headlineMedium = miuixStyles.title3, // 20.sp - headlineSmall = miuixStyles.title4, // 18.sp + headlineLarge = miuixStyles.title1, // 32.sp + headlineMedium = miuixStyles.title2, // 24.sp + headlineSmall = miuixStyles.title3, // 20.sp - titleLarge = miuixStyles.headline1, // 17.sp + titleLarge = miuixStyles.title4, // 18.sp titleMedium = miuixStyles.headline2, // 16.sp titleSmall = miuixStyles.subtitle, // 14.sp, Bold - bodyLarge = miuixStyles.paragraph, // 17.sp, lineHeight 1.2em (如果不需要行高也可以换成 main) + bodyLarge = miuixStyles.paragraph, // 17.sp bodyMedium = miuixStyles.body1, // 16.sp - bodySmall = miuixStyles.body2, // 14.sp + bodySmall = miuixStyles.body2.copy(fontSize = 12.sp), // 12.sp - labelLarge = miuixStyles.button, // 17.sp + labelLarge = miuixStyles.footnote1.copy(fontSize = 14.sp), // 14.sp labelMedium = miuixStyles.footnote1, // 13.sp - labelSmall = miuixStyles.footnote2, // 11.sp + labelSmall = miuixStyles.footnote2, // 11.sp bodyLargeEmphasized = miuixStyles.paragraph.copy(fontWeight = FontWeight.Medium), bodyMediumEmphasized = miuixStyles.body1.copy(fontWeight = FontWeight.Medium), - bodySmallEmphasized = miuixStyles.body2.copy(fontWeight = FontWeight.Medium), + bodySmallEmphasized = miuixStyles.body2.copy( + fontSize = 12.sp, + fontWeight = FontWeight.Medium + ), - labelLargeEmphasized = miuixStyles.button.copy(fontWeight = FontWeight.Medium), + labelLargeEmphasized = miuixStyles.button.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium + ), labelMediumEmphasized = miuixStyles.footnote1.copy(fontWeight = FontWeight.Medium), labelSmallEmphasized = miuixStyles.footnote2.copy(fontWeight = FontWeight.Medium) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt b/app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt index 9d018331d..92e5104b4 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt @@ -3,6 +3,7 @@ package io.legado.app.ui.widget.components import androidx.compose.foundation.ScrollState import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -22,6 +23,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.dp import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.ThemeResolver import io.legado.app.ui.widget.components.text.AppText @@ -272,6 +274,61 @@ fun AppTextFieldSurface( ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppDenseTextField( + state: TextFieldState, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + backgroundColor: Color = Color.Unspecified, + label: String? = null, + labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(), + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + prefix: @Composable (() -> Unit)? = null, + suffix: @Composable (() -> Unit)? = null, + supportingText: @Composable (() -> Unit)? = null, + isError: Boolean = false, + inputTransformation: InputTransformation? = null, + outputTransformation: OutputTransformation? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + onKeyboardAction: KeyboardActionHandler? = null, + lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default, + onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null, + scrollState: ScrollState = rememberScrollState(), + shape: Shape = TextFieldDefaults.shape, + interactionSource: MutableInteractionSource? = null, +) { + AppTextField( + state = state, + modifier = modifier.heightIn(min = 48.dp), + enabled = enabled, + readOnly = readOnly, + backgroundColor = backgroundColor, + label = label, + labelPosition = labelPosition, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + prefix = prefix, + suffix = suffix, + supportingText = supportingText, + isError = isError, + inputTransformation = inputTransformation, + outputTransformation = outputTransformation, + keyboardOptions = keyboardOptions, + onKeyboardAction = onKeyboardAction, + lineLimits = lineLimits, + onTextLayout = onTextLayout, + scrollState = scrollState, + shape = shape, + contentPadding = PaddingValues(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp), + interactionSource = interactionSource + ) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppTextFieldSurface( @@ -321,4 +378,4 @@ fun AppTextFieldSurface( shape = shape, interactionSource = interactionSource ) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt b/app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt index 92934955e..809e78bfd 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt @@ -79,7 +79,7 @@ fun SearchBarSection( ) val searchTextField = @Composable { - AppTextField( + AppDenseTextField( state = textFieldState, modifier = modifier, placeholder = { AppText(placeholder) }, @@ -106,4 +106,4 @@ fun SearchBarSection( searchTextField() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/SelectionBottomBar.kt b/app/src/main/java/io/legado/app/ui/widget/components/SelectionBottomBar.kt index 953ee3b0f..8a189abe1 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/SelectionBottomBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/SelectionBottomBar.kt @@ -31,6 +31,7 @@ import io.legado.app.ui.widget.components.text.AppText data class SelectionActions( val primaryAction: ActionItem, val secondaryActions: List = emptyList(), + val onClearSelection: (() -> Unit)? = null, val onSelectAll: () -> Unit, val onSelectInvert: () -> Unit, ) @@ -113,4 +114,4 @@ fun SelectionBottomBar( } } ) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/list/ListScaffold.kt b/app/src/main/java/io/legado/app/ui/widget/components/list/ListScaffold.kt index e1805234b..8a9449b17 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/list/ListScaffold.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/list/ListScaffold.kt @@ -88,7 +88,7 @@ fun ListScaffold( onSearchToggle = onSearchToggle, onSearchQueryChange = onSearchQueryChange, searchPlaceholder = searchPlaceholder, - onClearSelection = { selectionActions?.onSelectInvert?.invoke() }, + onClearSelection = { selectionActions?.onClearSelection?.invoke() }, topBarActions = topBarActions, dropDownMenuContent = dropDownMenuContent, bottomContent = bottomContent diff --git a/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleListScaffold.kt b/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleListScaffold.kt index c61035e24..75e65bed1 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleListScaffold.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleListScaffold.kt @@ -101,6 +101,7 @@ fun RuleListScaffold( floatingActionButton = floatingActionButton, snackbarHostState = snackbarHostState, selectionActions = SelectionActions( + onClearSelection = onClearSelection, onSelectAll = onSelectAll, onSelectInvert = onSelectInvert, primaryAction = ActionItem( diff --git a/app/src/main/java/io/legado/app/utils/UriExtensions.kt b/app/src/main/java/io/legado/app/utils/UriExtensions.kt index a90664b41..c07687c0c 100644 --- a/app/src/main/java/io/legado/app/utils/UriExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/UriExtensions.kt @@ -38,6 +38,13 @@ fun Uri.takePersistablePermissionSafely( modeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ): Boolean { if (!isContentScheme()) return false + kotlin.runCatching { + context.contentResolver.takePersistableUriPermission(this, modeFlags) + }.onSuccess { + return true + }.onFailure { + AppLog.put("持久化读写权限失败(组合模式): $this", it) + } var granted = false if (modeFlags and Intent.FLAG_GRANT_READ_URI_PERMISSION != 0) { kotlin.runCatching {