HapeLee hace 5 meses
padre
commit
7fda394d74

+ 2 - 1
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 com.script.rhino.JavaObjectWrapFactory
 import org.mozilla.javascript.NativeJavaObject
 import org.mozilla.javascript.NativeJavaObject
 import org.mozilla.javascript.Scriptable
 import org.mozilla.javascript.Scriptable
+import org.mozilla.javascript.lc.type.TypeInfoFactory
 
 
 class NativeBaseSource(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) :
 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 {
     override fun has(name: String, start: Scriptable): Boolean {
         if (name != "setVariable" && name.length > 3 && name.startsWith("set")) {
         if (name != "setVariable" && name.length > 3 && name.startsWith("set")) {

+ 17 - 0
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 android.content.SharedPreferences
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleOwner
@@ -21,9 +22,14 @@ import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
 import kotlin.reflect.KProperty
 
 
 interface PrefDelegate<T> : ReadWriteProperty<Any?, T> {
 interface PrefDelegate<T> : ReadWriteProperty<Any?, T> {
+    val state: State<T>
     fun dispose()
     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(
 fun <T> prefDelegate(
     key: String,
     key: String,
     defaultValue: T,
     defaultValue: T,
@@ -32,6 +38,7 @@ fun <T> prefDelegate(
 ): PrefDelegate<T> {
 ): PrefDelegate<T> {
     return object : PrefDelegate<T>, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver {
     return object : PrefDelegate<T>, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver {
         private var _value: MutableState<T> = mutableStateOf(readInitialValue())
         private var _value: MutableState<T> = mutableStateOf(readInitialValue())
+        override val state: State<T> get() = _value
 
 
         init {
         init {
             if (lifecycleOwner != null) {
             if (lifecycleOwner != null) {
@@ -98,4 +105,14 @@ 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)
 }
 }

+ 125 - 42
app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt

@@ -1,9 +1,10 @@
 package io.legado.app.ui.config.bookshelfConfig
 package io.legado.app.ui.config.bookshelfConfig
 
 
+import androidx.compose.runtime.State
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
 import io.legado.app.data.entities.BookGroup
 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
 import io.legado.app.utils.postEvent
 
 
 /**
 /**
@@ -14,213 +15,295 @@ object BookshelfConfig {
     /**
     /**
      * 书架分组样式: 0: Tab, 1:隐藏Tab, 2:文件夹
      * 书架分组样式: 0: Tab, 1:隐藏Tab, 2:文件夹
      */
      */
-    var bookGroupStyle by prefDelegate(PreferKey.bookGroupStyle, 0) {
+    private val _bookGroupStyle = prefStateDelegate(PreferKey.bookGroupStyle, 0) {
         postEvent(EventBus.NOTIFY_MAIN, false)
         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为显示全部)
      * 列表模式下简介显示行数 (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处显示分类展开按钮
      * 是否在书架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为无限制)
      * 书架同时更新书籍数量限制 (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:网格
      * 竖屏书架布局模式: 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:网格
      * 横屏书架布局模式: 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:网格
      * 文件夹竖屏布局模式: 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:网格
      * 文件夹横屏布局模式: 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:仅封面
      * 网格模式布局样式: 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)
         postEvent(EventBus.NOTIFY_MAIN, false)
     }
     }
+    var bookshelfCardColor by _bookshelfCardColor
+    val bookshelfCardColorState: State<Int> get() = _bookshelfCardColor.state
 
 
     /**
     /**
      * 文件夹在列表模式下的样式: 0: 默认, 2: 横排封面
      * 文件夹在列表模式下的样式: 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,
         PreferKey.bookshelfSearchActionDirectToSearch,
         true
         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
      * 保存上次停留的分组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
 
 
 }
 }

+ 14 - 3
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.LocalContext
 import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.testTag
 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.res.stringResource
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
@@ -241,7 +244,9 @@ fun MainScreen(
                     val haptic = LocalHapticFeedback.current
                     val haptic = LocalHapticFeedback.current
 
 
                     WideNavigationRailItem(
                     WideNavigationRailItem(
-                        modifier = Modifier.testTag("nav_${destination.route}"),
+                        modifier = Modifier.semantics(mergeDescendants = true) {
+                            contentDescription = "nav_${destination.route}"
+                        },
                         railExpanded = navState.targetValue == WideNavigationRailValue.Expanded,
                         railExpanded = navState.targetValue == WideNavigationRailValue.Expanded,
                         selected = selected,
                         selected = selected,
                         onClick = {
                         onClick = {
@@ -310,7 +315,9 @@ fun MainScreen(
                                 MainDestination.My -> ThemeConfig.navIconMy
                                 MainDestination.My -> ThemeConfig.navIconMy
                             }
                             }
                             AppNavigationBarItem(
                             AppNavigationBarItem(
-                                modifier = Modifier.testTag("nav_${destination.route}"),
+                                modifier = Modifier.semantics(mergeDescendants = true) {
+                                    contentDescription = "nav_${destination.route}"
+                                },
                                 selected = selected,
                                 selected = selected,
                                 onClick = {
                                 onClick = {
                                     coroutineScope.launch { pagerState.animateScrollToPage(index) }
                                     coroutineScope.launch { pagerState.animateScrollToPage(index) }
@@ -457,7 +464,11 @@ fun MainScreen(
                                             pagerState.animateScrollToPage(index)
                                             pagerState.animateScrollToPage(index)
                                         }
                                         }
                                     },
                                     },
-                                    modifier = Modifier.defaultMinSize(minWidth = 76.dp)
+                                    modifier = Modifier
+                                        .defaultMinSize(minWidth = 76.dp)
+                                        .semantics(mergeDescendants = true) {
+                                            contentDescription = "nav_${destination.route}"
+                                        }
                                 ) {
                                 ) {
                                     NavigationIcon(
                                     NavigationIcon(
                                         destination = destination,
                                         destination = destination,

+ 81 - 44
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.LocalConfiguration
 import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.testTag
 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.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import androidx.compose.ui.window.Dialog
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 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.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.tabRow.AppTabRow
 import io.legado.app.ui.widget.components.tabRow.AppTabRow
 import io.legado.app.ui.widget.components.text.AppText
 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.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
 import org.koin.androidx.compose.koinViewModel
@@ -321,32 +328,38 @@ fun BookshelfScreen(
     val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
     val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
     val bookshelfLayoutMode by remember {
     val bookshelfLayoutMode by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape else BookshelfConfig.bookshelfLayoutModePortrait
+            if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscapeState.value
+            else BookshelfConfig.bookshelfLayoutModePortraitState.value
         }
         }
     }
     }
     val bookshelfLayoutGrid by remember {
     val bookshelfLayoutGrid by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait
+            if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscapeState.value
+            else BookshelfConfig.bookshelfLayoutGridPortraitState.value
         }
         }
     }
     }
     val bookshelfLayoutList by remember {
     val bookshelfLayoutList by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
+            if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscapeState.value
+            else BookshelfConfig.bookshelfLayoutListPortraitState.value
         }
         }
     }
     }
     val bookshelfFolderLayoutMode by remember {
     val bookshelfFolderLayoutMode by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutModeLandscape else BookshelfConfig.bookshelfFolderLayoutModePortrait
+            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutModeLandscapeState.value
+            else BookshelfConfig.bookshelfFolderLayoutModePortraitState.value
         }
         }
     }
     }
     val bookshelfFolderLayoutGrid by remember {
     val bookshelfFolderLayoutGrid by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutGridLandscape else BookshelfConfig.bookshelfFolderLayoutGridPortrait
+            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutGridLandscapeState.value
+            else BookshelfConfig.bookshelfFolderLayoutGridPortraitState.value
         }
         }
     }
     }
     val bookshelfFolderLayoutList by remember {
     val bookshelfFolderLayoutList by remember {
         derivedStateOf {
         derivedStateOf {
-            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutListLandscape else BookshelfConfig.bookshelfFolderLayoutListPortrait
+            if (isLandscape) BookshelfConfig.bookshelfFolderLayoutListLandscapeState.value
+            else BookshelfConfig.bookshelfFolderLayoutListPortraitState.value
         }
         }
     }
     }
     val currentMenuGroupId by remember {
     val currentMenuGroupId by remember {
@@ -388,7 +401,7 @@ fun BookshelfScreen(
                 backNavigationIcon = AppIcons.Close,
                 backNavigationIcon = AppIcons.Close,
                 showSearchAction = true,
                 showSearchAction = true,
                 onSearchToggle = { active ->
                 onSearchToggle = { active ->
-                    if (BookshelfConfig.bookshelfSearchActionDirectToSearch) {
+                    if (BookshelfConfig.bookshelfSearchActionDirectToSearchState.value) {
                         onNavigateToSearch(uiState.searchKey.trim())
                         onNavigateToSearch(uiState.searchKey.trim())
                     } else {
                     } else {
                         viewModel.setSearchMode(active)
                         viewModel.setSearchMode(active)
@@ -561,7 +574,8 @@ fun BookshelfScreen(
                                     modifier = Modifier.weight(1f)
                                     modifier = Modifier.weight(1f)
                                 )
                                 )
 
 
-                                if (BookshelfConfig.shouldShowExpandButton) {
+                                val showExpandButton by BookshelfConfig.shouldShowExpandButtonState
+                                if (showExpandButton) {
                                     Box(modifier = Modifier) {
                                     Box(modifier = Modifier) {
                                         SmallOutlinedIconToggleButton(
                                         SmallOutlinedIconToggleButton(
                                             checked = showGroupMenu,
                                             checked = showGroupMenu,
@@ -716,12 +730,12 @@ fun BookshelfScreen(
                         ),
                         ),
                         verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
                         verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
                         horizontalArrangement = 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(
                         itemsIndexed(
                             uiState.groups,
                             uiState.groups,
                             key = { _, it -> it.groupId }) { index, group ->
                             key = { _, it -> it.groupId }) { index, group ->
-                            val countText = if (BookshelfConfig.showBookCount) {
+                            val countText = if (BookshelfConfig.showBookCountState.value) {
                                 uiState.groupBookCounts[group.groupId]?.let {
                                 uiState.groupBookCounts[group.groupId]?.let {
                                     stringResource(R.string.book_count, it)
                                     stringResource(R.string.book_count, it)
                                 }
                                 }
@@ -734,11 +748,11 @@ fun BookshelfScreen(
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                         ?: emptyList(),
                                         ?: emptyList(),
                                     countText = countText,
                                     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 = {
                                     onClick = {
                                         scope.launch { pagerState.scrollToPage(index) }
                                         scope.launch { pagerState.scrollToPage(index) }
                                         viewModel.setInFolderRoot(false)
                                         viewModel.setInFolderRoot(false)
@@ -754,11 +768,11 @@ fun BookshelfScreen(
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                         ?: emptyList(),
                                         ?: emptyList(),
                                     countText = countText,
                                     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 = {
                                     onClick = {
                                         scope.launch { pagerState.scrollToPage(index) }
                                         scope.launch { pagerState.scrollToPage(index) }
                                         viewModel.setInFolderRoot(false)
                                         viewModel.setInFolderRoot(false)
@@ -810,7 +824,7 @@ fun BookshelfScreen(
                                 val isSelectedGroup = group.groupId == uiState.selectedGroupId
                                 val isSelectedGroup = group.groupId == uiState.selectedGroupId
                                 val books = uiState.allGroupBooks[group.groupId]
                                 val books = uiState.allGroupBooks[group.groupId]
                                     ?: if (isSelectedGroup) uiState.items
                                     ?: if (isSelectedGroup) uiState.items
-                                    else emptyList()
+                                    else persistentListOf()
                                 val canReorderBooks = isEditMode &&
                                 val canReorderBooks = isEditMode &&
                                         !uiState.isSearch &&
                                         !uiState.isSearch &&
                                         (group.bookSort.takeIf { it >= 0 }
                                         (group.bookSort.takeIf { it >= 0 }
@@ -1155,17 +1169,17 @@ private data class BookshelfEditStickySummary(
 @Composable
 @Composable
 fun BookshelfPage(
 fun BookshelfPage(
     paddingValues: PaddingValues,
     paddingValues: PaddingValues,
-    books: List<BookShelfItem>,
+    books: ImmutableList<BookShelfItem>,
     uiState: BookshelfUiState,
     uiState: BookshelfUiState,
-    selectedBookUrls: Set<String>,
+    selectedBookUrls: ImmutableSet<String>,
     canReorderBooks: Boolean,
     canReorderBooks: Boolean,
     onToggleBookSelection: (BookShelfItem) -> Unit,
     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,
     onDragFinished: () -> Unit,
-    onSyncDragState: (books: List<BookShelfItem>, canReorderBooks: Boolean) -> Unit,
+    onSyncDragState: (books: ImmutableList<BookShelfItem>, canReorderBooks: Boolean) -> Unit,
     onGlobalSearch: () -> Unit,
     onGlobalSearch: () -> Unit,
     onBookClick: (BookShelfItem) -> Unit,
     onBookClick: (BookShelfItem) -> Unit,
     onBookLongClick: (BookShelfItem) -> Unit,
     onBookLongClick: (BookShelfItem) -> Unit,
@@ -1203,14 +1217,37 @@ fun BookshelfPage(
 
 
     val configuration = LocalConfiguration.current
     val configuration = LocalConfiguration.current
     val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
     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 =
     val totalHorizontalPadding =
         if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
         if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
     val gridContentHorizontalPadding = totalHorizontalPadding / 2
     val gridContentHorizontalPadding = totalHorizontalPadding / 2
@@ -1247,7 +1284,7 @@ fun BookshelfPage(
             state = gridState,
             state = gridState,
             modifier = Modifier
             modifier = Modifier
                 .fillMaxSize()
                 .fillMaxSize()
-                .testTag("bookshelf_list")
+                .semantics(mergeDescendants = true) { contentDescription = "bookshelf_list" }
                 .then(
                 .then(
                     with(sharedTransitionScope) {
                     with(sharedTransitionScope) {
                         if (this != null) Modifier.skipToLookaheadSize() else Modifier
                         if (this != null) Modifier.skipToLookaheadSize() else Modifier
@@ -1260,7 +1297,7 @@ fun BookshelfPage(
             ),
             ),
             verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
             verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
             horizontalArrangement = 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 ->
             items(displayBooks, key = { it.bookUrl }) { book ->
                 val isSelected = selectedBookUrls.contains(book.bookUrl)
                 val isSelected = selectedBookUrls.contains(book.bookUrl)
@@ -1292,13 +1329,13 @@ fun BookshelfPage(
                         ),
                         ),
                         layoutMode = bookshelfLayoutMode,
                         layoutMode = bookshelfLayoutMode,
                         isSelected = isSelected,
                         isSelected = isSelected,
-                        gridStyle = BookshelfConfig.bookshelfGridLayout,
-                        isCompact = BookshelfConfig.bookshelfLayoutCompact,
+                        gridStyle = bookItemGridStyle,
+                        isCompact = bookItemIsCompact,
                         isUpdating = uiState.updatingBooks.contains(book.bookUrl),
                         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,
                         isSearchMode = uiState.isSearch,
                         searchKey = uiState.searchKey,
                         searchKey = uiState.searchKey,
                         sharedTransitionScope = sharedTransitionScope,
                         sharedTransitionScope = sharedTransitionScope,

+ 2 - 1
baselineProfile/build.gradle.kts

@@ -21,7 +21,8 @@ android {
         targetSdk = 37
         targetSdk = 37
 
 
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-    }
+        testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE"
+}
 
 
     targetProjectPath = ":app"
     targetProjectPath = ":app"
 
 

+ 27 - 22
baselineProfile/src/main/java/io/legado/baselineprofile/BaselineProfileGenerator.kt

@@ -30,36 +30,41 @@ class BaselineProfileGenerator {
             packageName = packageName,
             packageName = packageName,
             includeInStartupProfile = true
             includeInStartupProfile = true
         ) {
         ) {
-            // 1. 启动应用
             pressHome()
             pressHome()
             startActivityAndWait()
             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))
-                if (tab != null) {
-                    tab.click()
-                    device.waitForIdle()
-                    // 稍微等待页面加载
-                    Thread.sleep(500) 
-                }
+            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) {
+                    val bounds = tab.visibleBounds
+                    device.click(bounds.centerX(), bounds.centerY())
+                    Thread.sleep(3000)
+                }
+            }*/
         }
         }
     }
     }
 }
 }

+ 2 - 16
baselineProfile/src/main/java/io/legado/baselineprofile/StartupBenchmarks.kt

@@ -44,21 +44,19 @@ class StartupBenchmarks {
     val rule = MacrobenchmarkRule()
     val rule = MacrobenchmarkRule()
 
 
     @Test
     @Test
-    fun startupCompilationNone() =
-        benchmark(CompilationMode.None())
+    fun startupCompilationNone() = benchmark(CompilationMode.None())
 
 
     @Test
     @Test
     fun startupCompilationBaselineProfiles() =
     fun startupCompilationBaselineProfiles() =
         benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
         benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
 
 
     private fun benchmark(compilationMode: CompilationMode) {
     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")
         val packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
             ?: "io.legado.app"
             ?: "io.legado.app"
 
 
         rule.measureRepeated(
         rule.measureRepeated(
             packageName = packageName,
             packageName = packageName,
-            metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
+            metrics = listOf(StartupTimingMetric()),
             compilationMode = compilationMode,
             compilationMode = compilationMode,
             startupMode = StartupMode.COLD,
             startupMode = StartupMode.COLD,
             iterations = 3,
             iterations = 3,
@@ -67,18 +65,6 @@ class StartupBenchmarks {
             },
             },
             measureBlock = {
             measureBlock = {
                 startActivityAndWait()
                 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
gradle/libs.versions.toml

@@ -5,14 +5,14 @@ adaptive = "1.3.0-beta01"
 adaptiveLayout = "1.3.0-beta01"
 adaptiveLayout = "1.3.0-beta01"
 adaptiveNavigation = "1.3.0-beta01"
 adaptiveNavigation = "1.3.0-beta01"
 animation = "1.11.1"
 animation = "1.11.1"
-biometric = "1.4.0-alpha06"
+biometric = "1.4.0-alpha07"
 coilCompose = "2.7.0"
 coilCompose = "2.7.0"
 composeBom = "2026.05.00"
 composeBom = "2026.05.00"
 constraintlayoutCompose = "1.1.1"
 constraintlayoutCompose = "1.1.1"
 coreSplashscreen = "1.2.0"
 coreSplashscreen = "1.2.0"
 datastorePreferences = "1.2.1"
 datastorePreferences = "1.2.1"
 foundation = "1.11.1"
 foundation = "1.11.1"
-kotlin = "2.3.10"
+kotlin = "2.3.21"
 kotlinxSerialization = "1.11.0"
 kotlinxSerialization = "1.11.0"
 kotlinxCoroutinesAndroid = "1.10.2"
 kotlinxCoroutinesAndroid = "1.10.2"
 kotlinxSerializationJson = "1.11.0"
 kotlinxSerializationJson = "1.11.0"
@@ -35,8 +35,8 @@ hutool = "5.8.22"
 libarchive = "1.1.6"
 libarchive = "1.1.6"
 lifecycle = "2.10.0"
 lifecycle = "2.10.0"
 glide = "5.0.7"
 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)
 # issue #3811,不要更新版本,新版引入了一个破坏性变更(详见https://github.com/jhy/jsoup/pull/2017)
 # 若要升级请确保相关代码不会受此变更影响(如AnalyzeByJSoup.kt、JsoupXpath库等)
 # 若要升级请确保相关代码不会受此变更影响(如AnalyzeByJSoup.kt、JsoupXpath库等)
 jsoup = "1.16.2"
 jsoup = "1.16.2"
@@ -51,7 +51,7 @@ material = "1.14.0-rc01"
 material3 = "1.5.0-alpha19"
 material3 = "1.5.0-alpha19"
 material3IconsExtended = "1.7.8"
 material3IconsExtended = "1.7.8"
 materialKolor = "4.1.1"
 materialKolor = "4.1.1"
-media = "1.7.1"
+media = "1.8.0"
 media3 = "1.8.0"
 media3 = "1.8.0"
 nanoHttpd = "2.3.1"
 nanoHttpd = "2.3.1"
 navigationCompose = "2.9.8"
 navigationCompose = "2.9.8"
@@ -62,7 +62,7 @@ protobufJavalite = "4.26.1"
 quickChineseTransfer = "0.2.16"
 quickChineseTransfer = "0.2.16"
 room = "2.8.4"
 room = "2.8.4"
 splitties = "3.0.0"
 splitties = "3.0.0"
-rhino = "1.8.1"
+rhino = "1.9.1"
 desugar = "2.1.5"
 desugar = "2.1.5"
 
 
 activity = "1.13.0"
 activity = "1.13.0"
@@ -77,7 +77,7 @@ viewpager2 = "1.1.0"
 webkit = "1.16.0"
 webkit = "1.16.0"
 collection = "1.6.0"
 collection = "1.6.0"
 
 
-zxingLite = "3.3.0"
+zxingLite = "3.4.1"
 
 
 koin-bom = "4.2.1"
 koin-bom = "4.2.1"
 biometricKtx = "1.1.0"
 biometricKtx = "1.1.0"

+ 2 - 1
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.NativeJavaObject
 import org.mozilla.javascript.Scriptable
 import org.mozilla.javascript.Scriptable
+import org.mozilla.javascript.lc.type.TypeInfoFactory
 
 
 class ReadOnlyJavaObject(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) :
 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 {
     override fun has(name: String, start: Scriptable): Boolean {
         if (name.length > 3 && name.startsWith("set")) {
         if (name.length > 3 && name.startsWith("set")) {

+ 21 - 10
modules/rhino/src/main/java/com/script/rhino/VMBridgeReflect.kt

@@ -1,20 +1,31 @@
 package com.script.rhino
 package com.script.rhino
 
 
-import org.mozilla.javascript.VMBridge
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.ContextFactory
 
 
 object VMBridgeReflect {
 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 {
     val contextLocal: ThreadLocal<Any> by lazy {
         @Suppress("UNCHECKED_CAST")
         @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 - 0
settings.gradle

@@ -54,3 +54,4 @@ include ':app'
 include ':modules:book'
 include ':modules:book'
 include ':modules:rhino'
 include ':modules:rhino'
 include ':baselineprofile'
 include ':baselineprofile'
+project(':baselineprofile').projectDir = file('baselineProfile')