代码优化

This commit is contained in:
HapeLee
2026-05-14 15:37:26 +08:00
parent 4d3ae1e537
commit 7fda394d74
12 changed files with 301 additions and 147 deletions
@@ -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")) {
@@ -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<T> : ReadWriteProperty<Any?, T> {
val state: State<T>
fun dispose()
}
class PrefStateDelegate<T>(private val delegate: PrefDelegate<T>) : ReadWriteProperty<Any?, T> by delegate {
val state: State<T> get() = delegate.state
}
fun <T> prefDelegate(
key: String,
defaultValue: T,
@@ -32,6 +38,7 @@ fun <T> prefDelegate(
): PrefDelegate<T> {
return object : PrefDelegate<T>, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver {
private var _value: MutableState<T> = mutableStateOf(readInitialValue())
override val state: State<T> get() = _value
init {
if (lifecycleOwner != null) {
@@ -99,3 +106,13 @@ fun <T> prefDelegate(
}
}
}
fun <T> prefStateDelegate(
key: String,
defaultValue: T,
lifecycleOwner: LifecycleOwner? = null,
onValueChange: ((T) -> Unit)? = null
): PrefStateDelegate<T> {
val delegate = prefDelegate(key, defaultValue, lifecycleOwner, onValueChange)
return PrefStateDelegate(delegate)
}
@@ -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<Int> get() = _bookGroupStyle.state
/**
* 书架排序方式
*/
var bookshelfSort by prefDelegate(PreferKey.bookshelfSort, 0)
private val _bookshelfSort = prefStateDelegate(PreferKey.bookshelfSort, 0)
var bookshelfSort by _bookshelfSort
val bookshelfSortState: State<Int> get() = _bookshelfSort.state
/**
* 书架排序顺序
*/
var bookshelfSortOrder by prefDelegate(PreferKey.bookshelfSortOrder, 1)
private val _bookshelfSortOrder = prefStateDelegate(PreferKey.bookshelfSortOrder, 1)
var bookshelfSortOrder by _bookshelfSortOrder
val bookshelfSortOrderState: State<Int> get() = _bookshelfSortOrder.state
/**
* 是否显示未读标志
*/
var showUnread by prefDelegate(PreferKey.showUnread, true)
private val _showUnread = prefStateDelegate(PreferKey.showUnread, true)
var showUnread by _showUnread
val showUnreadState: State<Boolean> get() = _showUnread.state
/**
* 是否显示新未读标志(小圆点)
*/
var showUnreadNew by prefDelegate(PreferKey.showUnreadNew, true)
private val _showUnreadNew = prefStateDelegate(PreferKey.showUnreadNew, true)
var showUnreadNew by _showUnreadNew
val showUnreadNewState: State<Boolean> get() = _showUnreadNew.state
/**
* 是否显示更新提示
*/
var showTip by prefDelegate(PreferKey.showTip, false)
private val _showTip = prefStateDelegate(PreferKey.showTip, false)
var showTip by _showTip
val showTipState: State<Boolean> get() = _showTip.state
/**
* 鏄惁鏄剧ず鍒嗙粍鍐呬功绫嶆暟閲?
*/
var showBookCount by prefDelegate(PreferKey.showBookCount, true)
private val _showBookCount = prefStateDelegate(PreferKey.showBookCount, true)
var showBookCount by _showBookCount
val showBookCountState: State<Boolean> get() = _showBookCount.state
/**
* 是否在列表中显示最后更新时间
*/
var showLastUpdateTime by prefDelegate(PreferKey.showLastUpdateTime, false)
private val _showLastUpdateTime = prefStateDelegate(PreferKey.showLastUpdateTime, false)
var showLastUpdateTime by _showLastUpdateTime
val showLastUpdateTimeState: State<Boolean> get() = _showLastUpdateTime.state
/**
* 是否在列表中显示书籍简介
*/
var showBookIntro by prefDelegate(PreferKey.showBookIntro, false)
private val _showBookIntro = prefStateDelegate(PreferKey.showBookIntro, false)
var showBookIntro by _showBookIntro
val showBookIntroState: State<Boolean> get() = _showBookIntro.state
/**
* 是否显示简介
*/
var bookshelfShowIntro by prefDelegate(PreferKey.bookshelfShowIntro, true)
private val _bookshelfShowIntro = prefStateDelegate(PreferKey.bookshelfShowIntro, true)
var bookshelfShowIntro by _bookshelfShowIntro
val bookshelfShowIntroState: State<Boolean> get() = _bookshelfShowIntro.state
/**
* 是否显示标签
*/
var bookshelfShowTag by prefDelegate(PreferKey.bookshelfShowTag, true)
private val _bookshelfShowTag = prefStateDelegate(PreferKey.bookshelfShowTag, true)
var bookshelfShowTag by _bookshelfShowTag
val bookshelfShowTagState: State<Boolean> 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<Int> get() = _bookshelfIntroMaxLines.state
/**
* 是否显示等待更新的书籍数量
*/
var showWaitUpCount by prefDelegate(PreferKey.showWaitUpCount, false)
private val _showWaitUpCount = prefStateDelegate(PreferKey.showWaitUpCount, false)
var showWaitUpCount by _showWaitUpCount
val showWaitUpCountState: State<Boolean> get() = _showWaitUpCount.state
/**
* 是否显示书架快速滚动条
*/
var showBookshelfFastScroller by prefDelegate(PreferKey.showBookshelfFastScroller, false)
private val _showBookshelfFastScroller = prefStateDelegate(PreferKey.showBookshelfFastScroller, false)
var showBookshelfFastScroller by _showBookshelfFastScroller
val showBookshelfFastScrollerState: State<Boolean> 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<Boolean> 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<Int> 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<Int> get() = _bookshelfLayoutModePortrait.state
/**
* 竖屏书架网格列数
*/
var bookshelfLayoutGridPortrait by prefDelegate(PreferKey.bookshelfLayoutGridPortrait, 3)
private val _bookshelfLayoutGridPortrait = prefStateDelegate(PreferKey.bookshelfLayoutGridPortrait, 3)
var bookshelfLayoutGridPortrait by _bookshelfLayoutGridPortrait
val bookshelfLayoutGridPortraitState: State<Int> 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<Int> get() = _bookshelfLayoutModeLandscape.state
/**
* 横屏书架网格列数
*/
var bookshelfLayoutGridLandscape by prefDelegate(PreferKey.bookshelfLayoutGridLandscape, 7)
private val _bookshelfLayoutGridLandscape = prefStateDelegate(PreferKey.bookshelfLayoutGridLandscape, 7)
var bookshelfLayoutGridLandscape by _bookshelfLayoutGridLandscape
val bookshelfLayoutGridLandscapeState: State<Int> get() = _bookshelfLayoutGridLandscape.state
/**
* 竖屏书架列表列数
*/
var bookshelfLayoutListPortrait by prefDelegate(PreferKey.bookshelfLayoutListPortrait, 1)
private val _bookshelfLayoutListPortrait = prefStateDelegate(PreferKey.bookshelfLayoutListPortrait, 1)
var bookshelfLayoutListPortrait by _bookshelfLayoutListPortrait
val bookshelfLayoutListPortraitState: State<Int> get() = _bookshelfLayoutListPortrait.state
/**
* 横屏书架列表列数
*/
var bookshelfLayoutListLandscape by prefDelegate(PreferKey.bookshelfLayoutListLandscape, 1)
private val _bookshelfLayoutListLandscape = prefStateDelegate(PreferKey.bookshelfLayoutListLandscape, 1)
var bookshelfLayoutListLandscape by _bookshelfLayoutListLandscape
val bookshelfLayoutListLandscapeState: State<Int> 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<Int> get() = _bookshelfFolderLayoutModePortrait.state
/**
* 文件夹竖屏网格列数
*/
var bookshelfFolderLayoutGridPortrait by prefDelegate(PreferKey.bookshelfFolderLayoutGridPortrait, 3)
private val _bookshelfFolderLayoutGridPortrait = prefStateDelegate(PreferKey.bookshelfFolderLayoutGridPortrait, 3)
var bookshelfFolderLayoutGridPortrait by _bookshelfFolderLayoutGridPortrait
val bookshelfFolderLayoutGridPortraitState: State<Int> 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<Int> get() = _bookshelfFolderLayoutModeLandscape.state
/**
* 文件夹横屏网格列数
*/
var bookshelfFolderLayoutGridLandscape by prefDelegate(PreferKey.bookshelfFolderLayoutGridLandscape, 7)
private val _bookshelfFolderLayoutGridLandscape = prefStateDelegate(PreferKey.bookshelfFolderLayoutGridLandscape, 7)
var bookshelfFolderLayoutGridLandscape by _bookshelfFolderLayoutGridLandscape
val bookshelfFolderLayoutGridLandscapeState: State<Int> get() = _bookshelfFolderLayoutGridLandscape.state
/**
* 文件夹竖屏列表列数
*/
var bookshelfFolderLayoutListPortrait by prefDelegate(PreferKey.bookshelfFolderLayoutListPortrait, 1)
private val _bookshelfFolderLayoutListPortrait = prefStateDelegate(PreferKey.bookshelfFolderLayoutListPortrait, 1)
var bookshelfFolderLayoutListPortrait by _bookshelfFolderLayoutListPortrait
val bookshelfFolderLayoutListPortraitState: State<Int> get() = _bookshelfFolderLayoutListPortrait.state
/**
* 文件夹横屏列表列数
*/
var bookshelfFolderLayoutListLandscape by prefDelegate(PreferKey.bookshelfFolderLayoutListLandscape, 1)
private val _bookshelfFolderLayoutListLandscape = prefStateDelegate(PreferKey.bookshelfFolderLayoutListLandscape, 1)
var bookshelfFolderLayoutListLandscape by _bookshelfFolderLayoutListLandscape
val bookshelfFolderLayoutListLandscapeState: State<Int> 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<Int> get() = _bookshelfGridLayout.state
/**
* 紧凑模式
*/
var bookshelfLayoutCompact by prefDelegate(PreferKey.bookshelfLayoutCompact, false)
private val _bookshelfLayoutCompact = prefStateDelegate(PreferKey.bookshelfLayoutCompact, false)
var bookshelfLayoutCompact by _bookshelfLayoutCompact
val bookshelfLayoutCompactState: State<Boolean> get() = _bookshelfLayoutCompact.state
/**
* 是否显示书架分隔线
*/
var bookshelfShowDivider by prefDelegate(PreferKey.bookshelfShowDivider, true)
private val _bookshelfShowDivider = prefStateDelegate(PreferKey.bookshelfShowDivider, true)
var bookshelfShowDivider by _bookshelfShowDivider
val bookshelfShowDividerState: State<Boolean> get() = _bookshelfShowDivider.state
/**
* 书架标题小字体
*/
var bookshelfTitleSmallFont by prefDelegate(PreferKey.bookshelfTitleSmallFont, false)
private val _bookshelfTitleSmallFont = prefStateDelegate(PreferKey.bookshelfTitleSmallFont, false)
var bookshelfTitleSmallFont by _bookshelfTitleSmallFont
val bookshelfTitleSmallFontState: State<Boolean> get() = _bookshelfTitleSmallFont.state
/**
* 书架标题居中
*/
var bookshelfTitleCenter by prefDelegate(PreferKey.bookshelfTitleCenter, true)
private val _bookshelfTitleCenter = prefStateDelegate(PreferKey.bookshelfTitleCenter, true)
var bookshelfTitleCenter by _bookshelfTitleCenter
val bookshelfTitleCenterState: State<Boolean> get() = _bookshelfTitleCenter.state
/**
* 书架标题最大行数
*/
var bookshelfTitleMaxLines by prefDelegate(PreferKey.bookshelfTitleMaxLines, 2)
private val _bookshelfTitleMaxLines = prefStateDelegate(PreferKey.bookshelfTitleMaxLines, 2)
var bookshelfTitleMaxLines by _bookshelfTitleMaxLines
val bookshelfTitleMaxLinesState: State<Int> get() = _bookshelfTitleMaxLines.state
/**
* 书架封面阴影
*/
var bookshelfCoverShadow by prefDelegate(PreferKey.bookshelfCoverShadow, false)
private val _bookshelfCoverShadow = prefStateDelegate(PreferKey.bookshelfCoverShadow, false)
var bookshelfCoverShadow by _bookshelfCoverShadow
val bookshelfCoverShadowState: State<Boolean> 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<Int> 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<Int> get() = _bookshelfGroupListStyle.state
/**
* 文件夹在列表模式下横排封面的数量
*/
var bookshelfGroupCoverCount by prefDelegate(PreferKey.bookshelfGroupCoverCount, 4)
private val _bookshelfGroupCoverCount = prefStateDelegate(PreferKey.bookshelfGroupCoverCount, 4)
var bookshelfGroupCoverCount by _bookshelfGroupCoverCount
val bookshelfGroupCoverCountState: State<Int> get() = _bookshelfGroupCoverCount.state
/**
* 书架搜索按钮是否直接跳转搜索页
*/
var bookshelfSearchActionDirectToSearch by prefDelegate(
private val _bookshelfSearchActionDirectToSearch = prefStateDelegate(
PreferKey.bookshelfSearchActionDirectToSearch,
true
)
var bookshelfSearchActionDirectToSearch by _bookshelfSearchActionDirectToSearch
val bookshelfSearchActionDirectToSearchState: State<Boolean> get() = _bookshelfSearchActionDirectToSearch.state
/**
* 启动时自动刷新书架
*/
var autoRefreshBook by prefDelegate(PreferKey.autoRefresh, false)
private val _autoRefreshBook = prefStateDelegate(PreferKey.autoRefresh, false)
var autoRefreshBook by _autoRefreshBook
val autoRefreshBookState: State<Boolean> 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<Long> get() = _saveTabPosition.state
}
@@ -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,
@@ -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<BookShelfItem>,
books: ImmutableList<BookShelfItem>,
uiState: BookshelfUiState,
selectedBookUrls: Set<String>,
selectedBookUrls: ImmutableSet<String>,
canReorderBooks: Boolean,
onToggleBookSelection: (BookShelfItem) -> Unit,
draggingBooks: List<BookShelfItem>?,
pendingSavedBooks: List<BookShelfItem>?,
onDragStarted: (List<BookShelfItem>) -> Unit,
onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: List<BookShelfItem>) -> Unit,
draggingBooks: ImmutableList<BookShelfItem>?,
pendingSavedBooks: ImmutableList<BookShelfItem>?,
onDragStarted: (ImmutableList<BookShelfItem>) -> Unit,
onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: ImmutableList<BookShelfItem>) -> Unit,
onDragFinished: () -> Unit,
onSyncDragState: (books: List<BookShelfItem>, canReorderBooks: Boolean) -> Unit,
onSyncDragState: (books: ImmutableList<BookShelfItem>, 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,
+2 -1
View File
@@ -21,7 +21,8 @@ android {
targetSdk = 37
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE"
}
targetProjectPath = ":app"
@@ -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)
}
}
}*/
}
}
}
@@ -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()
}
}
)
}
+7 -7
View File
@@ -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"
@@ -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")) {
@@ -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<Any> by lazy {
@Suppress("UNCHECKED_CAST")
instance::class.java.getDeclaredField("contextLocal").apply {
isAccessible = true
}.get(null) as ThreadLocal<Any>
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<Any>
} 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<Any>
} 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<Any>
}
}
}
}
+1
View File
@@ -54,3 +54,4 @@ include ':app'
include ':modules:book'
include ':modules:rhino'
include ':baselineprofile'
project(':baselineprofile').projectDir = file('baselineProfile')