From 7fda394d74c1a3fa1bd814a1667870aa9fdbbb5b Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Thu, 14 May 2026 15:37:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/help/rhino/NativeBaseSource.kt | 3 +- .../io/legado/app/ui/config/PrefDelegate.kt | 17 ++ .../config/bookshelfConfig/BookshelfConfig.kt | 167 +++++++++++++----- .../java/io/legado/app/ui/main/MainScreen.kt | 17 +- .../app/ui/main/bookshelf/BookshelfScreen.kt | 125 ++++++++----- baselineProfile/build.gradle.kts | 3 +- .../BaselineProfileGenerator.kt | 49 ++--- .../baselineprofile/StartupBenchmarks.kt | 18 +- gradle/libs.versions.toml | 14 +- .../com/script/rhino/ReadOnlyJavaObject.kt | 3 +- .../java/com/script/rhino/VMBridgeReflect.kt | 31 ++-- settings.gradle | 1 + 12 files changed, 301 insertions(+), 147 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/rhino/NativeBaseSource.kt b/app/src/main/java/io/legado/app/help/rhino/NativeBaseSource.kt index ed50b4d88..ea9432cd4 100644 --- a/app/src/main/java/io/legado/app/help/rhino/NativeBaseSource.kt +++ b/app/src/main/java/io/legado/app/help/rhino/NativeBaseSource.kt @@ -3,9 +3,10 @@ package io.legado.app.help.rhino import com.script.rhino.JavaObjectWrapFactory import org.mozilla.javascript.NativeJavaObject import org.mozilla.javascript.Scriptable +import org.mozilla.javascript.lc.type.TypeInfoFactory class NativeBaseSource(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) : - NativeJavaObject(scope, javaObject, staticType) { + NativeJavaObject(scope, javaObject, staticType?.let { TypeInfoFactory.GLOBAL.create(it) }) { override fun has(name: String, start: Scriptable): Boolean { if (name != "setVariable" && name.length > 3 && name.startsWith("set")) { diff --git a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt index 0827dd6c4..495fc67d5 100644 --- a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.config import android.content.SharedPreferences import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -21,9 +22,14 @@ import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty interface PrefDelegate : ReadWriteProperty { + val state: State fun dispose() } +class PrefStateDelegate(private val delegate: PrefDelegate) : ReadWriteProperty by delegate { + val state: State get() = delegate.state +} + fun prefDelegate( key: String, defaultValue: T, @@ -32,6 +38,7 @@ fun prefDelegate( ): PrefDelegate { return object : PrefDelegate, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver { private var _value: MutableState = mutableStateOf(readInitialValue()) + override val state: State get() = _value init { if (lifecycleOwner != null) { @@ -98,4 +105,14 @@ fun prefDelegate( } } } +} + +fun prefStateDelegate( + key: String, + defaultValue: T, + lifecycleOwner: LifecycleOwner? = null, + onValueChange: ((T) -> Unit)? = null +): PrefStateDelegate { + val delegate = prefDelegate(key, defaultValue, lifecycleOwner, onValueChange) + return PrefStateDelegate(delegate) } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt b/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt index c1b8a35bb..98efa72b1 100644 --- a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt +++ b/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt @@ -1,9 +1,10 @@ package io.legado.app.ui.config.bookshelfConfig +import androidx.compose.runtime.State import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.data.entities.BookGroup -import io.legado.app.ui.config.prefDelegate +import io.legado.app.ui.config.prefStateDelegate import io.legado.app.utils.postEvent /** @@ -14,213 +15,295 @@ object BookshelfConfig { /** * 书架分组样式: 0: Tab, 1:隐藏Tab, 2:文件夹 */ - var bookGroupStyle by prefDelegate(PreferKey.bookGroupStyle, 0) { + private val _bookGroupStyle = prefStateDelegate(PreferKey.bookGroupStyle, 0) { postEvent(EventBus.NOTIFY_MAIN, false) } + var bookGroupStyle by _bookGroupStyle + val bookGroupStyleState: State get() = _bookGroupStyle.state /** * 书架排序方式 */ - var bookshelfSort by prefDelegate(PreferKey.bookshelfSort, 0) + private val _bookshelfSort = prefStateDelegate(PreferKey.bookshelfSort, 0) + var bookshelfSort by _bookshelfSort + val bookshelfSortState: State get() = _bookshelfSort.state /** * 书架排序顺序 */ - var bookshelfSortOrder by prefDelegate(PreferKey.bookshelfSortOrder, 1) + private val _bookshelfSortOrder = prefStateDelegate(PreferKey.bookshelfSortOrder, 1) + var bookshelfSortOrder by _bookshelfSortOrder + val bookshelfSortOrderState: State get() = _bookshelfSortOrder.state /** * 是否显示未读标志 */ - var showUnread by prefDelegate(PreferKey.showUnread, true) + private val _showUnread = prefStateDelegate(PreferKey.showUnread, true) + var showUnread by _showUnread + val showUnreadState: State get() = _showUnread.state /** * 是否显示新未读标志(小圆点) */ - var showUnreadNew by prefDelegate(PreferKey.showUnreadNew, true) + private val _showUnreadNew = prefStateDelegate(PreferKey.showUnreadNew, true) + var showUnreadNew by _showUnreadNew + val showUnreadNewState: State get() = _showUnreadNew.state /** * 是否显示更新提示 */ - var showTip by prefDelegate(PreferKey.showTip, false) + private val _showTip = prefStateDelegate(PreferKey.showTip, false) + var showTip by _showTip + val showTipState: State get() = _showTip.state /** * 鏄惁鏄剧ず鍒嗙粍鍐呬功绫嶆暟閲? */ - var showBookCount by prefDelegate(PreferKey.showBookCount, true) + private val _showBookCount = prefStateDelegate(PreferKey.showBookCount, true) + var showBookCount by _showBookCount + val showBookCountState: State get() = _showBookCount.state /** * 是否在列表中显示最后更新时间 */ - var showLastUpdateTime by prefDelegate(PreferKey.showLastUpdateTime, false) + private val _showLastUpdateTime = prefStateDelegate(PreferKey.showLastUpdateTime, false) + var showLastUpdateTime by _showLastUpdateTime + val showLastUpdateTimeState: State get() = _showLastUpdateTime.state /** * 是否在列表中显示书籍简介 */ - var showBookIntro by prefDelegate(PreferKey.showBookIntro, false) + private val _showBookIntro = prefStateDelegate(PreferKey.showBookIntro, false) + var showBookIntro by _showBookIntro + val showBookIntroState: State get() = _showBookIntro.state /** * 是否显示简介 */ - var bookshelfShowIntro by prefDelegate(PreferKey.bookshelfShowIntro, true) + private val _bookshelfShowIntro = prefStateDelegate(PreferKey.bookshelfShowIntro, true) + var bookshelfShowIntro by _bookshelfShowIntro + val bookshelfShowIntroState: State get() = _bookshelfShowIntro.state /** * 是否显示标签 */ - var bookshelfShowTag by prefDelegate(PreferKey.bookshelfShowTag, true) + private val _bookshelfShowTag = prefStateDelegate(PreferKey.bookshelfShowTag, true) + var bookshelfShowTag by _bookshelfShowTag + val bookshelfShowTagState: State get() = _bookshelfShowTag.state /** * 列表模式下简介显示行数 (0为显示全部) */ - var bookshelfIntroMaxLines by prefDelegate(PreferKey.bookshelfIntroMaxLines, 0) + private val _bookshelfIntroMaxLines = prefStateDelegate(PreferKey.bookshelfIntroMaxLines, 0) + var bookshelfIntroMaxLines by _bookshelfIntroMaxLines + val bookshelfIntroMaxLinesState: State get() = _bookshelfIntroMaxLines.state /** * 是否显示等待更新的书籍数量 */ - var showWaitUpCount by prefDelegate(PreferKey.showWaitUpCount, false) + private val _showWaitUpCount = prefStateDelegate(PreferKey.showWaitUpCount, false) + var showWaitUpCount by _showWaitUpCount + val showWaitUpCountState: State get() = _showWaitUpCount.state /** * 是否显示书架快速滚动条 */ - var showBookshelfFastScroller by prefDelegate(PreferKey.showBookshelfFastScroller, false) + private val _showBookshelfFastScroller = prefStateDelegate(PreferKey.showBookshelfFastScroller, false) + var showBookshelfFastScroller by _showBookshelfFastScroller + val showBookshelfFastScrollerState: State get() = _showBookshelfFastScroller.state /** * 是否在书架Tab处显示分类展开按钮 */ - var shouldShowExpandButton by prefDelegate(PreferKey.shouldShowExpandButton, false) + private val _shouldShowExpandButton = prefStateDelegate(PreferKey.shouldShowExpandButton, false) + var shouldShowExpandButton by _shouldShowExpandButton + val shouldShowExpandButtonState: State get() = _shouldShowExpandButton.state /** * 书架同时更新书籍数量限制 (0为无限制) */ - var bookshelfRefreshingLimit by prefDelegate(PreferKey.bookshelfRefreshingLimit, 0) + private val _bookshelfRefreshingLimit = prefStateDelegate(PreferKey.bookshelfRefreshingLimit, 0) + var bookshelfRefreshingLimit by _bookshelfRefreshingLimit + val bookshelfRefreshingLimitState: State get() = _bookshelfRefreshingLimit.state /** * 竖屏书架布局模式: 0:列表, 1:网格 */ - var bookshelfLayoutModePortrait by prefDelegate(PreferKey.bookshelfLayoutModePortrait, 1) + private val _bookshelfLayoutModePortrait = prefStateDelegate(PreferKey.bookshelfLayoutModePortrait, 1) + var bookshelfLayoutModePortrait by _bookshelfLayoutModePortrait + val bookshelfLayoutModePortraitState: State get() = _bookshelfLayoutModePortrait.state /** * 竖屏书架网格列数 */ - var bookshelfLayoutGridPortrait by prefDelegate(PreferKey.bookshelfLayoutGridPortrait, 3) + private val _bookshelfLayoutGridPortrait = prefStateDelegate(PreferKey.bookshelfLayoutGridPortrait, 3) + var bookshelfLayoutGridPortrait by _bookshelfLayoutGridPortrait + val bookshelfLayoutGridPortraitState: State get() = _bookshelfLayoutGridPortrait.state /** * 横屏书架布局模式: 0:列表, 1:网格 */ - var bookshelfLayoutModeLandscape by prefDelegate(PreferKey.bookshelfLayoutModeLandscape, 1) + private val _bookshelfLayoutModeLandscape = prefStateDelegate(PreferKey.bookshelfLayoutModeLandscape, 1) + var bookshelfLayoutModeLandscape by _bookshelfLayoutModeLandscape + val bookshelfLayoutModeLandscapeState: State get() = _bookshelfLayoutModeLandscape.state /** * 横屏书架网格列数 */ - var bookshelfLayoutGridLandscape by prefDelegate(PreferKey.bookshelfLayoutGridLandscape, 7) + private val _bookshelfLayoutGridLandscape = prefStateDelegate(PreferKey.bookshelfLayoutGridLandscape, 7) + var bookshelfLayoutGridLandscape by _bookshelfLayoutGridLandscape + val bookshelfLayoutGridLandscapeState: State get() = _bookshelfLayoutGridLandscape.state /** * 竖屏书架列表列数 */ - var bookshelfLayoutListPortrait by prefDelegate(PreferKey.bookshelfLayoutListPortrait, 1) + private val _bookshelfLayoutListPortrait = prefStateDelegate(PreferKey.bookshelfLayoutListPortrait, 1) + var bookshelfLayoutListPortrait by _bookshelfLayoutListPortrait + val bookshelfLayoutListPortraitState: State get() = _bookshelfLayoutListPortrait.state /** * 横屏书架列表列数 */ - var bookshelfLayoutListLandscape by prefDelegate(PreferKey.bookshelfLayoutListLandscape, 1) + private val _bookshelfLayoutListLandscape = prefStateDelegate(PreferKey.bookshelfLayoutListLandscape, 1) + var bookshelfLayoutListLandscape by _bookshelfLayoutListLandscape + val bookshelfLayoutListLandscapeState: State get() = _bookshelfLayoutListLandscape.state /** * 文件夹竖屏布局模式: 0:列表, 1:网格 */ - var bookshelfFolderLayoutModePortrait by prefDelegate(PreferKey.bookshelfFolderLayoutModePortrait, 1) + private val _bookshelfFolderLayoutModePortrait = prefStateDelegate(PreferKey.bookshelfFolderLayoutModePortrait, 1) + var bookshelfFolderLayoutModePortrait by _bookshelfFolderLayoutModePortrait + val bookshelfFolderLayoutModePortraitState: State get() = _bookshelfFolderLayoutModePortrait.state /** * 文件夹竖屏网格列数 */ - var bookshelfFolderLayoutGridPortrait by prefDelegate(PreferKey.bookshelfFolderLayoutGridPortrait, 3) + private val _bookshelfFolderLayoutGridPortrait = prefStateDelegate(PreferKey.bookshelfFolderLayoutGridPortrait, 3) + var bookshelfFolderLayoutGridPortrait by _bookshelfFolderLayoutGridPortrait + val bookshelfFolderLayoutGridPortraitState: State get() = _bookshelfFolderLayoutGridPortrait.state /** * 文件夹横屏布局模式: 0:列表, 1:网格 */ - var bookshelfFolderLayoutModeLandscape by prefDelegate(PreferKey.bookshelfFolderLayoutModeLandscape, 1) + private val _bookshelfFolderLayoutModeLandscape = prefStateDelegate(PreferKey.bookshelfFolderLayoutModeLandscape, 1) + var bookshelfFolderLayoutModeLandscape by _bookshelfFolderLayoutModeLandscape + val bookshelfFolderLayoutModeLandscapeState: State get() = _bookshelfFolderLayoutModeLandscape.state /** * 文件夹横屏网格列数 */ - var bookshelfFolderLayoutGridLandscape by prefDelegate(PreferKey.bookshelfFolderLayoutGridLandscape, 7) + private val _bookshelfFolderLayoutGridLandscape = prefStateDelegate(PreferKey.bookshelfFolderLayoutGridLandscape, 7) + var bookshelfFolderLayoutGridLandscape by _bookshelfFolderLayoutGridLandscape + val bookshelfFolderLayoutGridLandscapeState: State get() = _bookshelfFolderLayoutGridLandscape.state /** * 文件夹竖屏列表列数 */ - var bookshelfFolderLayoutListPortrait by prefDelegate(PreferKey.bookshelfFolderLayoutListPortrait, 1) + private val _bookshelfFolderLayoutListPortrait = prefStateDelegate(PreferKey.bookshelfFolderLayoutListPortrait, 1) + var bookshelfFolderLayoutListPortrait by _bookshelfFolderLayoutListPortrait + val bookshelfFolderLayoutListPortraitState: State get() = _bookshelfFolderLayoutListPortrait.state /** * 文件夹横屏列表列数 */ - var bookshelfFolderLayoutListLandscape by prefDelegate(PreferKey.bookshelfFolderLayoutListLandscape, 1) + private val _bookshelfFolderLayoutListLandscape = prefStateDelegate(PreferKey.bookshelfFolderLayoutListLandscape, 1) + var bookshelfFolderLayoutListLandscape by _bookshelfFolderLayoutListLandscape + val bookshelfFolderLayoutListLandscapeState: State get() = _bookshelfFolderLayoutListLandscape.state /** * 网格模式布局样式: 0:标准, 1:紧凑, 2:仅封面 */ - var bookshelfGridLayout by prefDelegate(PreferKey.bookshelfGridLayout, 0) + private val _bookshelfGridLayout = prefStateDelegate(PreferKey.bookshelfGridLayout, 0) + var bookshelfGridLayout by _bookshelfGridLayout + val bookshelfGridLayoutState: State get() = _bookshelfGridLayout.state /** * 紧凑模式 */ - var bookshelfLayoutCompact by prefDelegate(PreferKey.bookshelfLayoutCompact, false) + private val _bookshelfLayoutCompact = prefStateDelegate(PreferKey.bookshelfLayoutCompact, false) + var bookshelfLayoutCompact by _bookshelfLayoutCompact + val bookshelfLayoutCompactState: State get() = _bookshelfLayoutCompact.state /** * 是否显示书架分隔线 */ - var bookshelfShowDivider by prefDelegate(PreferKey.bookshelfShowDivider, true) + private val _bookshelfShowDivider = prefStateDelegate(PreferKey.bookshelfShowDivider, true) + var bookshelfShowDivider by _bookshelfShowDivider + val bookshelfShowDividerState: State get() = _bookshelfShowDivider.state /** * 书架标题小字体 */ - var bookshelfTitleSmallFont by prefDelegate(PreferKey.bookshelfTitleSmallFont, false) + private val _bookshelfTitleSmallFont = prefStateDelegate(PreferKey.bookshelfTitleSmallFont, false) + var bookshelfTitleSmallFont by _bookshelfTitleSmallFont + val bookshelfTitleSmallFontState: State get() = _bookshelfTitleSmallFont.state /** * 书架标题居中 */ - var bookshelfTitleCenter by prefDelegate(PreferKey.bookshelfTitleCenter, true) + private val _bookshelfTitleCenter = prefStateDelegate(PreferKey.bookshelfTitleCenter, true) + var bookshelfTitleCenter by _bookshelfTitleCenter + val bookshelfTitleCenterState: State get() = _bookshelfTitleCenter.state /** * 书架标题最大行数 */ - var bookshelfTitleMaxLines by prefDelegate(PreferKey.bookshelfTitleMaxLines, 2) + private val _bookshelfTitleMaxLines = prefStateDelegate(PreferKey.bookshelfTitleMaxLines, 2) + var bookshelfTitleMaxLines by _bookshelfTitleMaxLines + val bookshelfTitleMaxLinesState: State get() = _bookshelfTitleMaxLines.state /** * 书架封面阴影 */ - var bookshelfCoverShadow by prefDelegate(PreferKey.bookshelfCoverShadow, false) + private val _bookshelfCoverShadow = prefStateDelegate(PreferKey.bookshelfCoverShadow, false) + var bookshelfCoverShadow by _bookshelfCoverShadow + val bookshelfCoverShadowState: State get() = _bookshelfCoverShadow.state /** * 书架卡片背景颜色 */ - var bookshelfCardColor by prefDelegate(PreferKey.bookshelfCardColor, 0) { + private val _bookshelfCardColor = prefStateDelegate(PreferKey.bookshelfCardColor, 0) { postEvent(EventBus.NOTIFY_MAIN, false) } + var bookshelfCardColor by _bookshelfCardColor + val bookshelfCardColorState: State get() = _bookshelfCardColor.state /** * 文件夹在列表模式下的样式: 0: 默认, 2: 横排封面 */ - var bookshelfGroupListStyle by prefDelegate(PreferKey.bookshelfGroupListStyle, 0) + private val _bookshelfGroupListStyle = prefStateDelegate(PreferKey.bookshelfGroupListStyle, 0) + var bookshelfGroupListStyle by _bookshelfGroupListStyle + val bookshelfGroupListStyleState: State get() = _bookshelfGroupListStyle.state /** * 文件夹在列表模式下横排封面的数量 */ - var bookshelfGroupCoverCount by prefDelegate(PreferKey.bookshelfGroupCoverCount, 4) + private val _bookshelfGroupCoverCount = prefStateDelegate(PreferKey.bookshelfGroupCoverCount, 4) + var bookshelfGroupCoverCount by _bookshelfGroupCoverCount + val bookshelfGroupCoverCountState: State get() = _bookshelfGroupCoverCount.state /** * 书架搜索按钮是否直接跳转搜索页 */ - var bookshelfSearchActionDirectToSearch by prefDelegate( + private val _bookshelfSearchActionDirectToSearch = prefStateDelegate( PreferKey.bookshelfSearchActionDirectToSearch, true ) + var bookshelfSearchActionDirectToSearch by _bookshelfSearchActionDirectToSearch + val bookshelfSearchActionDirectToSearchState: State get() = _bookshelfSearchActionDirectToSearch.state /** * 启动时自动刷新书架 */ - var autoRefreshBook by prefDelegate(PreferKey.autoRefresh, false) + private val _autoRefreshBook = prefStateDelegate(PreferKey.autoRefresh, false) + var autoRefreshBook by _autoRefreshBook + val autoRefreshBookState: State get() = _autoRefreshBook.state /** * 保存上次停留的分组Id */ - var saveTabPosition by prefDelegate(PreferKey.saveTabPosition, BookGroup.IdAll) + private val _saveTabPosition = prefStateDelegate(PreferKey.saveTabPosition, BookGroup.IdAll) + var saveTabPosition by _saveTabPosition + val saveTabPositionState: State get() = _saveTabPosition.state } diff --git a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt index 9becff411..9bbe91b8f 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt @@ -56,6 +56,9 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -241,7 +244,9 @@ fun MainScreen( val haptic = LocalHapticFeedback.current WideNavigationRailItem( - modifier = Modifier.testTag("nav_${destination.route}"), + modifier = Modifier.semantics(mergeDescendants = true) { + contentDescription = "nav_${destination.route}" + }, railExpanded = navState.targetValue == WideNavigationRailValue.Expanded, selected = selected, onClick = { @@ -310,7 +315,9 @@ fun MainScreen( MainDestination.My -> ThemeConfig.navIconMy } AppNavigationBarItem( - modifier = Modifier.testTag("nav_${destination.route}"), + modifier = Modifier.semantics(mergeDescendants = true) { + contentDescription = "nav_${destination.route}" + }, selected = selected, onClick = { coroutineScope.launch { pagerState.animateScrollToPage(index) } @@ -457,7 +464,11 @@ fun MainScreen( pagerState.animateScrollToPage(index) } }, - modifier = Modifier.defaultMinSize(minWidth = 76.dp) + modifier = Modifier + .defaultMinSize(minWidth = 76.dp) + .semantics(mergeDescendants = true) { + contentDescription = "nav_${destination.route}" + } ) { NavigationIcon( destination = destination, diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt index ff2cf7b82..160b773ce 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt @@ -83,7 +83,11 @@ import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -123,6 +127,9 @@ import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.tabRow.AppTabRow import io.legado.app.ui.widget.components.text.AppText +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel @@ -321,32 +328,38 @@ fun BookshelfScreen( val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE val bookshelfLayoutMode by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape else BookshelfConfig.bookshelfLayoutModePortrait + if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscapeState.value + else BookshelfConfig.bookshelfLayoutModePortraitState.value } } val bookshelfLayoutGrid by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait + if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscapeState.value + else BookshelfConfig.bookshelfLayoutGridPortraitState.value } } val bookshelfLayoutList by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait + if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscapeState.value + else BookshelfConfig.bookshelfLayoutListPortraitState.value } } val bookshelfFolderLayoutMode by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfFolderLayoutModeLandscape else BookshelfConfig.bookshelfFolderLayoutModePortrait + if (isLandscape) BookshelfConfig.bookshelfFolderLayoutModeLandscapeState.value + else BookshelfConfig.bookshelfFolderLayoutModePortraitState.value } } val bookshelfFolderLayoutGrid by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfFolderLayoutGridLandscape else BookshelfConfig.bookshelfFolderLayoutGridPortrait + if (isLandscape) BookshelfConfig.bookshelfFolderLayoutGridLandscapeState.value + else BookshelfConfig.bookshelfFolderLayoutGridPortraitState.value } } val bookshelfFolderLayoutList by remember { derivedStateOf { - if (isLandscape) BookshelfConfig.bookshelfFolderLayoutListLandscape else BookshelfConfig.bookshelfFolderLayoutListPortrait + if (isLandscape) BookshelfConfig.bookshelfFolderLayoutListLandscapeState.value + else BookshelfConfig.bookshelfFolderLayoutListPortraitState.value } } val currentMenuGroupId by remember { @@ -388,7 +401,7 @@ fun BookshelfScreen( backNavigationIcon = AppIcons.Close, showSearchAction = true, onSearchToggle = { active -> - if (BookshelfConfig.bookshelfSearchActionDirectToSearch) { + if (BookshelfConfig.bookshelfSearchActionDirectToSearchState.value) { onNavigateToSearch(uiState.searchKey.trim()) } else { viewModel.setSearchMode(active) @@ -561,7 +574,8 @@ fun BookshelfScreen( modifier = Modifier.weight(1f) ) - if (BookshelfConfig.shouldShowExpandButton) { + val showExpandButton by BookshelfConfig.shouldShowExpandButtonState + if (showExpandButton) { Box(modifier = Modifier) { SmallOutlinedIconToggleButton( checked = showGroupMenu, @@ -716,12 +730,12 @@ fun BookshelfScreen( ), verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), - showFastScroll = BookshelfConfig.showBookshelfFastScroller + showFastScroll = BookshelfConfig.showBookshelfFastScrollerState.value ) { itemsIndexed( uiState.groups, key = { _, it -> it.groupId }) { index, group -> - val countText = if (BookshelfConfig.showBookCount) { + val countText = if (BookshelfConfig.showBookCountState.value) { uiState.groupBookCounts[group.groupId]?.let { stringResource(R.string.book_count, it) } @@ -734,11 +748,11 @@ fun BookshelfScreen( previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(), countText = countText, - isCompact = BookshelfConfig.bookshelfLayoutCompact, - titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont, - titleCenter = BookshelfConfig.bookshelfTitleCenter, - titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines, - coverShadow = BookshelfConfig.bookshelfCoverShadow, + isCompact = BookshelfConfig.bookshelfLayoutCompactState.value, + titleSmallFont = BookshelfConfig.bookshelfTitleSmallFontState.value, + titleCenter = BookshelfConfig.bookshelfTitleCenterState.value, + titleMaxLines = BookshelfConfig.bookshelfTitleMaxLinesState.value, + coverShadow = BookshelfConfig.bookshelfCoverShadowState.value, onClick = { scope.launch { pagerState.scrollToPage(index) } viewModel.setInFolderRoot(false) @@ -754,11 +768,11 @@ fun BookshelfScreen( previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(), countText = countText, - gridStyle = BookshelfConfig.bookshelfGridLayout, - titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont, - titleCenter = BookshelfConfig.bookshelfTitleCenter, - titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines, - coverShadow = BookshelfConfig.bookshelfCoverShadow, + gridStyle = BookshelfConfig.bookshelfGridLayoutState.value, + titleSmallFont = BookshelfConfig.bookshelfTitleSmallFontState.value, + titleCenter = BookshelfConfig.bookshelfTitleCenterState.value, + titleMaxLines = BookshelfConfig.bookshelfTitleMaxLinesState.value, + coverShadow = BookshelfConfig.bookshelfCoverShadowState.value, onClick = { scope.launch { pagerState.scrollToPage(index) } viewModel.setInFolderRoot(false) @@ -810,7 +824,7 @@ fun BookshelfScreen( val isSelectedGroup = group.groupId == uiState.selectedGroupId val books = uiState.allGroupBooks[group.groupId] ?: if (isSelectedGroup) uiState.items - else emptyList() + else persistentListOf() val canReorderBooks = isEditMode && !uiState.isSearch && (group.bookSort.takeIf { it >= 0 } @@ -1155,17 +1169,17 @@ private data class BookshelfEditStickySummary( @Composable fun BookshelfPage( paddingValues: PaddingValues, - books: List, + books: ImmutableList, uiState: BookshelfUiState, - selectedBookUrls: Set, + selectedBookUrls: ImmutableSet, canReorderBooks: Boolean, onToggleBookSelection: (BookShelfItem) -> Unit, - draggingBooks: List?, - pendingSavedBooks: List?, - onDragStarted: (List) -> Unit, - onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: List) -> Unit, + draggingBooks: ImmutableList?, + pendingSavedBooks: ImmutableList?, + onDragStarted: (ImmutableList) -> Unit, + onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: ImmutableList) -> Unit, onDragFinished: () -> Unit, - onSyncDragState: (books: List, canReorderBooks: Boolean) -> Unit, + onSyncDragState: (books: ImmutableList, canReorderBooks: Boolean) -> Unit, onGlobalSearch: () -> Unit, onBookClick: (BookShelfItem) -> Unit, onBookLongClick: (BookShelfItem) -> Unit, @@ -1203,14 +1217,37 @@ fun BookshelfPage( val configuration = LocalConfiguration.current val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE - val bookshelfLayoutMode = - if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape else BookshelfConfig.bookshelfLayoutModePortrait - val bookshelfLayoutGrid = - if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait - val bookshelfLayoutList = - if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait - val columns = if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid - val isGridMode = bookshelfLayoutMode != 0 + val bookshelfLayoutMode by remember { + derivedStateOf { + if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscapeState.value + else BookshelfConfig.bookshelfLayoutModePortraitState.value + } + } + val bookshelfLayoutGrid by remember { + derivedStateOf { + if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscapeState.value + else BookshelfConfig.bookshelfLayoutGridPortraitState.value + } + } + val bookshelfLayoutList by remember { + derivedStateOf { + if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscapeState.value + else BookshelfConfig.bookshelfLayoutListPortraitState.value + } + } + val columns by remember { + derivedStateOf { + if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid + } + } + val isGridMode by remember { derivedStateOf { bookshelfLayoutMode != 0 } } + val bookItemGridStyle by BookshelfConfig.bookshelfGridLayoutState + val bookItemIsCompact by BookshelfConfig.bookshelfLayoutCompactState + val bookItemTitleSmallFont by BookshelfConfig.bookshelfTitleSmallFontState + val bookItemTitleCenter by BookshelfConfig.bookshelfTitleCenterState + val bookItemTitleMaxLines by BookshelfConfig.bookshelfTitleMaxLinesState + val bookItemCoverShadow by BookshelfConfig.bookshelfCoverShadowState + val showFastScroll by BookshelfConfig.showBookshelfFastScrollerState val totalHorizontalPadding = if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp val gridContentHorizontalPadding = totalHorizontalPadding / 2 @@ -1247,7 +1284,7 @@ fun BookshelfPage( state = gridState, modifier = Modifier .fillMaxSize() - .testTag("bookshelf_list") + .semantics(mergeDescendants = true) { contentDescription = "bookshelf_list" } .then( with(sharedTransitionScope) { if (this != null) Modifier.skipToLookaheadSize() else Modifier @@ -1260,7 +1297,7 @@ fun BookshelfPage( ), verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), - showFastScroll = BookshelfConfig.showBookshelfFastScroller + showFastScroll = showFastScroll ) { items(displayBooks, key = { it.bookUrl }) { book -> val isSelected = selectedBookUrls.contains(book.bookUrl) @@ -1292,13 +1329,13 @@ fun BookshelfPage( ), layoutMode = bookshelfLayoutMode, isSelected = isSelected, - gridStyle = BookshelfConfig.bookshelfGridLayout, - isCompact = BookshelfConfig.bookshelfLayoutCompact, + gridStyle = bookItemGridStyle, + isCompact = bookItemIsCompact, isUpdating = uiState.updatingBooks.contains(book.bookUrl), - titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont, - titleCenter = BookshelfConfig.bookshelfTitleCenter, - titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines, - coverShadow = BookshelfConfig.bookshelfCoverShadow, + titleSmallFont = bookItemTitleSmallFont, + titleCenter = bookItemTitleCenter, + titleMaxLines = bookItemTitleMaxLines, + coverShadow = bookItemCoverShadow, isSearchMode = uiState.isSearch, searchKey = uiState.searchKey, sharedTransitionScope = sharedTransitionScope, diff --git a/baselineProfile/build.gradle.kts b/baselineProfile/build.gradle.kts index 863f6b70a..3f741ef8c 100644 --- a/baselineProfile/build.gradle.kts +++ b/baselineProfile/build.gradle.kts @@ -21,7 +21,8 @@ android { targetSdk = 37 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } + testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE" +} targetProjectPath = ":app" diff --git a/baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt b/baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt index 51b042dab..847601a22 100644 --- a/baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt +++ b/baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt @@ -30,36 +30,41 @@ class BaselineProfileGenerator { packageName = packageName, includeInStartupProfile = true ) { - // 1. 启动应用 pressHome() startActivityAndWait() - // 2. 等待书架加载 (我们在 BookshelfScreen 中添加了 ReportDrawnWhen) - // 等待书架列表 testTag 出现 - device.wait(Until.hasObject(By.res(packageName, "bookshelf_list")), 10000) + val bookshelfSelector = By.desc("bookshelf_list") + device.wait(Until.hasObject(bookshelfSelector), 10000) - // 3. 模拟滑动书架 (优化列表渲染性能) - val bookshelf = device.findObject(By.res(packageName, "bookshelf_list")) - bookshelf?.apply { - setGestureMargin(device.displayWidth / 10) - fling(Direction.DOWN) - device.waitForIdle() - fling(Direction.UP) - device.waitForIdle() + device.waitForIdle() + Thread.sleep(2000) + + val screenWidth = device.displayWidth + val screenHeight = device.displayHeight + val startX = screenWidth / 2 + val startY = (screenHeight * 0.75).toInt() + val endY = (screenHeight * 0.25).toInt() + + repeat(3) { + device.swipe(startX, startY, startX, endY, 25) + Thread.sleep(1000) } - // 4. 遍历主要 Tab (优化 Compose 导航和各页面初始化) - val tabs = listOf("nav_explore", "nav_rss", "nav_my", "nav_bookshelf") - for (tabTag in tabs) { - val tab = device.findObject(By.res(packageName, tabTag)) + repeat(2) { + device.swipe(startX, endY, startX, startY, 10) + Thread.sleep(1000) + } + + // "nav_my" + /* val tabs = listOf("nav_explore", "nav_rss", "nav_bookshelf") + for (tabDesc in tabs) { + val tab = device.wait(Until.findObject(By.desc(tabDesc)), 5000) if (tab != null) { - tab.click() - device.waitForIdle() - // 稍微等待页面加载 - Thread.sleep(500) + val bounds = tab.visibleBounds + device.click(bounds.centerX(), bounds.centerY()) + Thread.sleep(3000) } - } - + }*/ } } } \ No newline at end of file diff --git a/baselineProfile/src/main/java/io/legado/baselineprofile/StartupBenchmarks.kt b/baselineProfile/src/main/java/io/legado/baselineprofile/StartupBenchmarks.kt index 089137fb9..1d42b9da7 100644 --- a/baselineProfile/src/main/java/io/legado/baselineprofile/StartupBenchmarks.kt +++ b/baselineProfile/src/main/java/io/legado/baselineprofile/StartupBenchmarks.kt @@ -44,21 +44,19 @@ class StartupBenchmarks { val rule = MacrobenchmarkRule() @Test - fun startupCompilationNone() = - benchmark(CompilationMode.None()) + fun startupCompilationNone() = benchmark(CompilationMode.None()) @Test fun startupCompilationBaselineProfiles() = benchmark(CompilationMode.Partial(BaselineProfileMode.Require)) private fun benchmark(compilationMode: CompilationMode) { - // The application id for the running build variant is read from the instrumentation arguments. val packageName = InstrumentationRegistry.getArguments().getString("targetAppId") ?: "io.legado.app" rule.measureRepeated( packageName = packageName, - metrics = listOf(StartupTimingMetric(), FrameTimingMetric()), + metrics = listOf(StartupTimingMetric()), compilationMode = compilationMode, startupMode = StartupMode.COLD, iterations = 3, @@ -67,18 +65,6 @@ class StartupBenchmarks { }, measureBlock = { startActivityAndWait() - - // 等待书架列表加载完成 - // 我们在 BookshelfScreen 中添加了 testTag("bookshelf_list") - device.wait(Until.hasObject(By.res(packageName, "bookshelf_list")), 10000) - - // 模拟交互:在书架上进行滑动,以确保 Profile 捕获到列表渲染和图片加载的路径 - val bookshelfList = device.findObject(By.res(packageName, "bookshelf_list")) - bookshelfList?.apply { - setGestureMargin(device.displayWidth / 10) - fling(Direction.DOWN) - device.waitForIdle() - } } ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5220398d..4ce7f6689 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,14 +5,14 @@ adaptive = "1.3.0-beta01" adaptiveLayout = "1.3.0-beta01" adaptiveNavigation = "1.3.0-beta01" animation = "1.11.1" -biometric = "1.4.0-alpha06" +biometric = "1.4.0-alpha07" coilCompose = "2.7.0" composeBom = "2026.05.00" constraintlayoutCompose = "1.1.1" coreSplashscreen = "1.2.0" datastorePreferences = "1.2.1" foundation = "1.11.1" -kotlin = "2.3.10" +kotlin = "2.3.21" kotlinxSerialization = "1.11.0" kotlinxCoroutinesAndroid = "1.10.2" kotlinxSerializationJson = "1.11.0" @@ -35,8 +35,8 @@ hutool = "5.8.22" libarchive = "1.1.6" lifecycle = "2.10.0" glide = "5.0.7" -gson = "2.13.2" -jsonPath = "2.10.0" +gson = "2.14.0" +jsonPath = "3.0.0" # issue #3811,不要更新版本,新版引入了一个破坏性变更(详见https://github.com/jhy/jsoup/pull/2017) # 若要升级请确保相关代码不会受此变更影响(如AnalyzeByJSoup.kt、JsoupXpath库等) jsoup = "1.16.2" @@ -51,7 +51,7 @@ material = "1.14.0-rc01" material3 = "1.5.0-alpha19" material3IconsExtended = "1.7.8" materialKolor = "4.1.1" -media = "1.7.1" +media = "1.8.0" media3 = "1.8.0" nanoHttpd = "2.3.1" navigationCompose = "2.9.8" @@ -62,7 +62,7 @@ protobufJavalite = "4.26.1" quickChineseTransfer = "0.2.16" room = "2.8.4" splitties = "3.0.0" -rhino = "1.8.1" +rhino = "1.9.1" desugar = "2.1.5" activity = "1.13.0" @@ -77,7 +77,7 @@ viewpager2 = "1.1.0" webkit = "1.16.0" collection = "1.6.0" -zxingLite = "3.3.0" +zxingLite = "3.4.1" koin-bom = "4.2.1" biometricKtx = "1.1.0" diff --git a/modules/rhino/src/main/java/com/script/rhino/ReadOnlyJavaObject.kt b/modules/rhino/src/main/java/com/script/rhino/ReadOnlyJavaObject.kt index 15477e212..7a8d5e43e 100644 --- a/modules/rhino/src/main/java/com/script/rhino/ReadOnlyJavaObject.kt +++ b/modules/rhino/src/main/java/com/script/rhino/ReadOnlyJavaObject.kt @@ -2,9 +2,10 @@ package com.script.rhino import org.mozilla.javascript.NativeJavaObject import org.mozilla.javascript.Scriptable +import org.mozilla.javascript.lc.type.TypeInfoFactory class ReadOnlyJavaObject(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) : - NativeJavaObject(scope, javaObject, staticType) { + NativeJavaObject(scope, javaObject, staticType?.let { TypeInfoFactory.GLOBAL.create(it) }) { override fun has(name: String, start: Scriptable): Boolean { if (name.length > 3 && name.startsWith("set")) { diff --git a/modules/rhino/src/main/java/com/script/rhino/VMBridgeReflect.kt b/modules/rhino/src/main/java/com/script/rhino/VMBridgeReflect.kt index eac5e209b..79febff5e 100644 --- a/modules/rhino/src/main/java/com/script/rhino/VMBridgeReflect.kt +++ b/modules/rhino/src/main/java/com/script/rhino/VMBridgeReflect.kt @@ -1,20 +1,31 @@ package com.script.rhino -import org.mozilla.javascript.VMBridge +import org.mozilla.javascript.Context +import org.mozilla.javascript.ContextFactory object VMBridgeReflect { - val instance: VMBridge by lazy { - VMBridge::class.java.getDeclaredField("instance").apply { - isAccessible = true - }.get(null) as VMBridge - } - val contextLocal: ThreadLocal by lazy { @Suppress("UNCHECKED_CAST") - instance::class.java.getDeclaredField("contextLocal").apply { - isAccessible = true - }.get(null) as ThreadLocal + try { + // Rhino 1.9.1+ uses threadContextLocal directly in Context class + val field = Context::class.java.getDeclaredField("threadContextLocal") + field.isAccessible = true + field.get(null) as ThreadLocal + } catch (e: Exception) { + try { + // Fallback for some versions or custom builds + val factory = ContextFactory.getGlobal() + val field = ContextFactory::class.java.getDeclaredField("localContext") + field.isAccessible = true + field.get(factory) as ThreadLocal + } catch (e2: Exception) { + // Older versions used threadContextHelper (Object) which held a ThreadLocal + val field = Context::class.java.getDeclaredField("threadContextHelper") + field.isAccessible = true + field.get(null) as ThreadLocal + } + } } } diff --git a/settings.gradle b/settings.gradle index d182c9738..c94f12283 100644 --- a/settings.gradle +++ b/settings.gradle @@ -54,3 +54,4 @@ include ':app' include ':modules:book' include ':modules:rhino' include ':baselineprofile' +project(':baselineprofile').projectDir = file('baselineProfile')