Просмотр исходного кода

fix(R1.5): 补全 update{} 的键映射,消除 56 个字段的静默丢写

回应审计 F-09 附注。ReadSettings 有 102 个字段,但 toGatewayPrefMap 只映射
46 个——通用 `update {}` 里 copy 其余任何一个都会被静默丢弃:不报错、
调用方以为存了,重启后值回退。此前靠 91 个专用 setter 兜底,但没有任何
机制阻止新代码走 update{} 改到未覆盖的字段。

- toGatewayPrefMap 补齐到 102 项,与 ReadSettings 字段一一对应。键名取自
  toReadSettings 已有的读取路径(Keys.X → PreferKey.y),保证读写同键。
- 全量映射不会产生多余写入:AppConfigStore.atomicUpdate 只把与旧值不同的
  键入队(atomicDiffLocked),未被 transform 改动的字段不落盘。
- ReadSettingsGatewayCoverageTest 的 UNPERSISTED_BASELINE 由 56 项清空为
  emptySet——该测试本就声明「基线只允许下调」。
- ReadSettingsMappingTest:期望表同步补全(防串键的手写契约),样本 base
  给新覆盖的 56 个字段填入互不相同的值,并加一个全默认实例覆盖另一极性;
  新增反射断言「字段数 == 映射数」,防止新增字段忘了接线。
- 更新 ReadSettingsGateway.update 的 KDoc(原文承诺的 45 键边界已不成立)。

已注入一个未映射的新字段验证两条护栏确实变红。
验证:compileAppDebugKotlin + 414 单测全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5151561 3 месяцев назад
Родитель
Сommit
79db44533c

+ 73 - 10
app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt

@@ -558,8 +558,15 @@ class ReadSettingsRepository(
 }
 
 /**
- * ReadSettings 是 101 字段的读取超集;gateway update 当前只承诺持久化以下 48 个键。
- * 扩展此边界时必须同步更新 ReadSettingsMappingTest 的显式键集契约。
+ * [ReadSettings] 每个字段到 DataStore 键的完整映射。
+ *
+ * 必须覆盖全部字段:通用 `update {}` 只持久化本表里的键,漏一个就是静默丢写——
+ * 调用方以为存了,重启后值回退。此前只覆盖 48/102,其余靠专用 setter 兜底,
+ * 但没有任何机制阻止新代码走 `update {}` 改到未覆盖的字段。
+ * [ReadSettingsGatewayMapCoverageTest] 断言这张表与 [ReadSettings] 字段一一对应。
+ *
+ * 全量写不是问题:`AppConfigStore.atomicUpdate` 只把与旧值不同的键入队
+ * (见 `atomicDiffLocked`),未被 transform 改动的字段不会产生写入。
  */
 internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.screenOrientation to screenOrientation,
@@ -568,8 +575,7 @@ internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.hideNavigationBar to hideNavigationBar,
     PreferKey.paddingDisplayCutouts to paddingDisplayCutouts,
     PreferKey.titleBarMode to titleBarMode,
-    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
-    PreferKey.readMenuFloatingIconLiquidGlass to readMenuFloatingIconLiquidGlass,
+    PreferKey.menuAlpha to menuAlpha,
     PreferKey.readBodyToLh to readBodyToLh,
     PreferKey.defaultSourceChangeAll to defaultSourceChangeAll,
     PreferKey.textFullJustify to textFullJustify,
@@ -599,14 +605,71 @@ internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.clickImgWay to clickImgWay,
     PreferKey.optimizeRender to optimizeRender,
     PreferKey.disableReturnKey to disableReturnKey,
-    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.expandTextMenu to expandTextMenu,
+    PreferKey.showSelectMenuIcon to showSelectMenuIcon,
     PreferKey.textSelectMenuConfig to textSelectMenuConfig,
-    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
-    PreferKey.showMenuIcon to showMenuIcon,
-    PreferKey.titleBarCompact to titleBarCompact,
+    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.autoReadSpeed to autoReadSpeed,
+    PreferKey.systemTypefaces to systemTypefaces,
+    PreferKey.preDownloadNum to preDownloadNum,
     PreferKey.prevKeys to prevKeys,
     PreferKey.nextKeys to nextKeys,
+    PreferKey.tocUiUseReplace to tocUiUseReplace,
+    PreferKey.tocCountWords to tocCountWords,
+    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
+    PreferKey.readStyleSelect to readStyleSelect,
+    PreferKey.comicStyleSelect to comicStyleSelect,
+    PreferKey.shareLayout to shareLayout,
+    PreferKey.readBarStyleFollowPage to readBarStyleFollowPage,
+    PreferKey.readBarStyle to readBarStyle,
+    PreferKey.clickActionTL to clickActionTL,
+    PreferKey.clickActionTC to clickActionTC,
+    PreferKey.clickActionTR to clickActionTR,
+    PreferKey.clickActionML to clickActionML,
+    PreferKey.clickActionMC to clickActionMC,
+    PreferKey.clickActionMR to clickActionMR,
+    PreferKey.clickActionBL to clickActionBL,
+    PreferKey.clickActionBC to clickActionBC,
+    PreferKey.clickActionBR to clickActionBR,
     PreferKey.fontFolder to fontFolder,
-    PreferKey.systemTypefaces to systemTypefaces,
-    PreferKey.preDownloadNum to preDownloadNum,
+    PreferKey.readMenuBgColor to readMenuBgColor,
+    PreferKey.readMenuAccentColor to readMenuAccentColor,
+    PreferKey.readMenuContainerColor to readMenuContainerColor,
+    PreferKey.readMenuBgColorNight to readMenuBgColorNight,
+    PreferKey.readMenuAccentColorNight to readMenuAccentColorNight,
+    PreferKey.readMenuContainerColorNight to readMenuContainerColorNight,
+    PreferKey.readMenuTextColor to readMenuTextColor,
+    PreferKey.readMenuTextColorNight to readMenuTextColorNight,
+    PreferKey.readMenuColorMode to readMenuColorMode,
+    PreferKey.readMenuIconShowText to readMenuIconShowText,
+    PreferKey.readMenuIconStyle to readMenuIconStyle,
+    PreferKey.titleBarIconStyle to titleBarIconStyle,
+    PreferKey.readMenuIconItemsPerRow to readMenuIconItemsPerRow,
+    PreferKey.readMenuIconRowCount to readMenuIconRowCount,
+    PreferKey.readMenuBottomCornerRadius to readMenuBottomCornerRadius,
+    PreferKey.readMenuFloatingBottomBar to readMenuFloatingBottomBar,
+    PreferKey.readMenuTopBarBlurMode to readMenuTopBarBlurMode,
+    PreferKey.readMenuBottomBarBlurMode to readMenuBottomBarBlurMode,
+    PreferKey.readMenuTopBarLiquidGlassButtons to readMenuTopBarLiquidGlassButtons,
+    PreferKey.readMenuTopBarTitleCapsule to readMenuTopBarTitleCapsule,
+    PreferKey.readMenuBottomBarLiquidGlassButtons to readMenuBottomBarLiquidGlassButtons,
+    PreferKey.readMenuFloatingIconLiquidGlass to readMenuFloatingIconLiquidGlass,
+    PreferKey.readMenuTopBarBlurStyle to readMenuTopBarBlurStyle,
+    PreferKey.readMenuBottomBarBlurStyle to readMenuBottomBarBlurStyle,
+    PreferKey.readMenuBlurRadius to readMenuBlurRadius,
+    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
+    PreferKey.readMenuBlurColor to readMenuBlurColor,
+    PreferKey.readMenuBlurColorNight to readMenuBlurColorNight,
+    PreferKey.readMenuPaletteStyle to readMenuPaletteStyle,
+    PreferKey.readMenuLensRadius to readMenuLensRadius,
+    PreferKey.readMenuBorderWidth to readMenuBorderWidth,
+    PreferKey.readMenuBorderColor to readMenuBorderColor,
+    PreferKey.readMenuBorderColorNight to readMenuBorderColorNight,
+    PreferKey.readMenuCustomIcons to readMenuCustomIcons,
+    PreferKey.titleBarCustomIcons to titleBarCustomIcons,
+    PreferKey.titleBarIconPosition to titleBarIconPosition,
+    PreferKey.showTitleBarIcons to showTitleBarIcons,
+    PreferKey.chineseConverterType to chineseConverterType,
+    PreferKey.showMenuIcon to showMenuIcon,
+    PreferKey.titleBarCompact to titleBarCompact,
 )

+ 5 - 3
app/src/main/java/io/legado/app/domain/gateway/ReadSettingsGateway.kt

@@ -8,9 +8,11 @@ interface ReadSettingsGateway {
     val settings: Flow<ReadSettings>
 
     /**
-     * 仅持久化 gateway 实现映射声明的 45 个配置键。
-     * [ReadSettings] 是包含遗留阅读菜单/样式字段的读模型超集;未纳入 gateway 映射的字段
-     * 仍须通过对应的遗留 Repository setter 写入,不能在此处 copy 后期待自动落盘。
+     * 持久化 [ReadSettings] 的任意字段:transform 里 copy 谁,谁就落盘。
+     *
+     * R1.5 之前只覆盖 46/102 个键,copy 到未覆盖的字段会被静默丢弃;现在实现侧的映射
+     * 与 [ReadSettings] 一一对应,由 `ReadSettingsGatewayCoverageTest` 把关。
+     * 只有被 transform 改动的键会产生写入。
      */
     suspend fun update(transform: (ReadSettings) -> ReadSettings)
 }

+ 10 - 69
app/src/test/java/io/legado/app/data/repository/ReadSettingsGatewayCoverageTest.kt

@@ -8,16 +8,15 @@ import kotlin.reflect.full.memberProperties
 import kotlin.reflect.full.primaryConstructor
 
 /**
- * Track E · E0 —— `ReadSettingsGateway.update {}` 的持久化覆盖面不变式。
+ * Track E · E0 / R1.5 —— `ReadSettingsGateway.update {}` 的持久化覆盖面不变式。
  *
- * `ReadSettings` 是 101 字段的**读模型超集**,而 `update {}` 只落盘
- * `toGatewayPrefMap()` 声明的那 45 个键——这是 `ReadSettingsGateway.update` 的 KDoc
- * 明确记录的设计:其余字段须走各自的遗留 setter 写入。
+ * `ReadSettings` 的每个字段都必须能经 `update {}` 落盘。做不到的字段是**静默丢写**:
+ * 调用方 copy 了新值、`update {}` 也不报错,但重启后值回退。
  *
- * 所以本测试**不要求** 101 == 45(那会推翻既定设计),而是把「哪些字段走不通 `update {}`」
- * 冻结成基线,形成双向棘轮:
- * - 新增字段忘了接线 ⇒ 基线变大 ⇒ 红。作者必须显式选择「进 map」还是「加进基线并走 setter」。
- * - 字段补进了 map 却忘了从基线移除 ⇒ 红。
+ * R1.5 之前 `toGatewayPrefMap()` 只覆盖 46/102,其余 56 个靠各自的遗留 setter 兜底,
+ * 这些字段的名字冻结在下面的基线里。现在映射已补全,基线清空——它继续作为双向棘轮:
+ * - 新增字段忘了接线 ⇒ 基线要变大 ⇒ 红。
+ * - 字段补进 map 却忘了从基线移除 ⇒ 红。
  *
  * 判定方式是行为性的:改一个字段的值,看 `toGatewayPrefMap()` 的输出是否随之变化。
  */
@@ -90,67 +89,9 @@ class ReadSettingsGatewayCoverageTest {
 
     private companion object {
         /**
-         * 只能通过遗留 setter 写入、走不通 `update {}` 的字段。基线只允许下调。
-         * 主体是阅读菜单/标题栏外观一族(它们在 `ConfigUpdate` 侧也是同一个「无渲染副作用」
-         * 集群,见 `ConfigUpdateActionsInvariantTest`)。
+         * 走不通 `update {}` 的字段。R1.5 补全映射后已清零,基线只允许下调——
+         * 新增条目意味着又出现了一个静默丢写的字段,应当补映射而不是加进这里。
          */
-        val UNPERSISTED_BASELINE = setOf(
-            "menuAlpha",
-            "expandTextMenu",
-            "showSelectMenuIcon",
-            "autoReadSpeed",
-            "tocUiUseReplace",
-            "tocCountWords",
-            "readStyleSelect",
-            "comicStyleSelect",
-            "shareLayout",
-            "readBarStyleFollowPage",
-            "readBarStyle",
-            "clickActionTL",
-            "clickActionTC",
-            "clickActionTR",
-            "clickActionML",
-            "clickActionMC",
-            "clickActionMR",
-            "clickActionBL",
-            "clickActionBC",
-            "clickActionBR",
-            "readMenuBgColor",
-            "readMenuAccentColor",
-            "readMenuContainerColor",
-            "readMenuBgColorNight",
-            "readMenuAccentColorNight",
-            "readMenuContainerColorNight",
-            "readMenuTextColor",
-            "readMenuTextColorNight",
-            "readMenuColorMode",
-            "readMenuIconShowText",
-            "readMenuIconStyle",
-            "titleBarIconStyle",
-            "readMenuIconItemsPerRow",
-            "readMenuIconRowCount",
-            "readMenuBottomCornerRadius",
-            "readMenuFloatingBottomBar",
-            "readMenuTopBarBlurMode",
-            "readMenuBottomBarBlurMode",
-            "readMenuTopBarLiquidGlassButtons",
-            "readMenuTopBarTitleCapsule",
-            "readMenuBottomBarLiquidGlassButtons",
-            "readMenuTopBarBlurStyle",
-            "readMenuBottomBarBlurStyle",
-            "readMenuBlurRadius",
-            "readMenuBlurColor",
-            "readMenuBlurColorNight",
-            "readMenuPaletteStyle",
-            "readMenuLensRadius",
-            "readMenuBorderWidth",
-            "readMenuBorderColor",
-            "readMenuBorderColorNight",
-            "readMenuCustomIcons",
-            "titleBarCustomIcons",
-            "titleBarIconPosition",
-            "showTitleBarIcons",
-            "chineseConverterType",
-        )
+        val UNPERSISTED_BASELINE = emptySet<String>()
     }
 }

+ 143 - 11
app/src/test/java/io/legado/app/data/repository/ReadSettingsMappingTest.kt

@@ -9,20 +9,39 @@ import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Test
+import kotlin.reflect.full.primaryConstructor
 
 class ReadSettingsMappingTest {
 
     @Test
-    fun `gateway 持久化边界固定为显式声明的 46 键`() {
+    fun `gateway 持久化映射覆盖 ReadSettings 全部 102 个字段`() {
         val actualKeys = ReadSettings().toGatewayPrefMap().keys
         val expectedKeys = ReadSettings().expectedGatewayPrefMap().keys
 
-        assertEquals(46, actualKeys.size)
+        assertEquals(102, actualKeys.size)
         assertEquals(expectedKeys, actualKeys)
     }
 
+    /**
+     * 上面那条比对的是两张手写表,防串键;这条防的是「新增字段忘了加进映射」——
+     * 漏掉的字段走通用 `update {}` 会被静默丢写。字段数由反射得出,不写死。
+     */
     @Test
-    fun `阅读设置 gateway 46 键写读映射逐字段对应`() {
+    fun `新增 ReadSettings 字段必须同时加进 gateway 映射`() {
+        val fieldCount = ReadSettings::class.primaryConstructor!!.parameters.size
+        val mappedCount = ReadSettings().toGatewayPrefMap().size
+
+        assertEquals(
+            "ReadSettings 有 $fieldCount 个字段,toGatewayPrefMap 只映射了 $mappedCount 个。" +
+                "未映射的字段经 update {} 写入会被静默丢弃——请补齐映射与本文件的 " +
+                "expectedGatewayPrefMap。",
+            fieldCount,
+            mappedCount,
+        )
+    }
+
+    @Test
+    fun `阅读设置 gateway 全部键写读映射逐字段对应`() {
         val repository = ReadSettingsRepository(
             settingsRepository = SettingsRepository(),
             preferencesFlow = MutableStateFlow(mutablePreferencesOf()),
@@ -107,8 +126,65 @@ private fun readSettingsMappingSamples(): List<ReadSettings> {
         fontFolder = "font-folder",
         systemTypefaces = 23,
         preDownloadNum = 29,
+        menuAlpha = 1001,
+        expandTextMenu = true,
+        showSelectMenuIcon = false,
+        autoReadSpeed = 1004,
+        tocUiUseReplace = true,
+        tocCountWords = false,
+        readStyleSelect = 1007,
+        comicStyleSelect = 1008,
+        shareLayout = true,
+        readBarStyleFollowPage = true,
+        readBarStyle = 1011,
+        clickActionTL = 1012,
+        clickActionTC = 1013,
+        clickActionTR = 1014,
+        clickActionML = 1015,
+        clickActionMC = 1016,
+        clickActionMR = 1017,
+        clickActionBL = 1018,
+        clickActionBC = 1019,
+        clickActionBR = 1020,
+        readMenuBgColor = 1021,
+        readMenuAccentColor = 1022,
+        readMenuContainerColor = 1023,
+        readMenuBgColorNight = 1024,
+        readMenuAccentColorNight = 1025,
+        readMenuContainerColorNight = 1026,
+        readMenuTextColor = 1027,
+        readMenuTextColorNight = 1028,
+        readMenuColorMode = 1029,
+        readMenuIconShowText = true,
+        readMenuIconStyle = 1031,
+        titleBarIconStyle = 1032,
+        readMenuIconItemsPerRow = 1033,
+        readMenuIconRowCount = 1034,
+        readMenuBottomCornerRadius = 1035,
+        readMenuFloatingBottomBar = false,
+        readMenuTopBarBlurMode = 1037,
+        readMenuBottomBarBlurMode = 1038,
+        readMenuTopBarLiquidGlassButtons = true,
+        readMenuTopBarTitleCapsule = true,
+        readMenuBottomBarLiquidGlassButtons = true,
+        readMenuTopBarBlurStyle = 1042,
+        readMenuBottomBarBlurStyle = 1043,
+        readMenuBlurRadius = 1044,
+        readMenuBlurColor = 1045,
+        readMenuBlurColorNight = 1046,
+        readMenuPaletteStyle = "readMenuPalett-47",
+        readMenuLensRadius = 1048f,
+        readMenuBorderWidth = 1049,
+        readMenuBorderColor = 1050,
+        readMenuBorderColorNight = 1051,
+        readMenuCustomIcons = "readMenuCustom-52",
+        titleBarCustomIcons = "titleBarCustom-53",
+        titleBarIconPosition = 1054,
+        showTitleBarIcons = true,
+        chineseConverterType = 1056,
     )
     return listOf(
+        ReadSettings(),
         base,
         base.copy(hideStatusBar = true),
         base.copy(hideNavigationBar = true),
@@ -147,8 +223,7 @@ private fun ReadSettings.expectedGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.hideNavigationBar to hideNavigationBar,
     PreferKey.paddingDisplayCutouts to paddingDisplayCutouts,
     PreferKey.titleBarMode to titleBarMode,
-    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
-    PreferKey.readMenuFloatingIconLiquidGlass to readMenuFloatingIconLiquidGlass,
+    PreferKey.menuAlpha to menuAlpha,
     PreferKey.readBodyToLh to readBodyToLh,
     PreferKey.defaultSourceChangeAll to defaultSourceChangeAll,
     PreferKey.textFullJustify to textFullJustify,
@@ -177,14 +252,71 @@ private fun ReadSettings.expectedGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.clickImgWay to clickImgWay,
     PreferKey.optimizeRender to optimizeRender,
     PreferKey.disableReturnKey to disableReturnKey,
-    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.expandTextMenu to expandTextMenu,
+    PreferKey.showSelectMenuIcon to showSelectMenuIcon,
     PreferKey.textSelectMenuConfig to textSelectMenuConfig,
-    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
-    PreferKey.showMenuIcon to showMenuIcon,
-    PreferKey.titleBarCompact to titleBarCompact,
+    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.autoReadSpeed to autoReadSpeed,
+    PreferKey.systemTypefaces to systemTypefaces,
+    PreferKey.preDownloadNum to preDownloadNum,
     PreferKey.prevKeys to prevKeys,
     PreferKey.nextKeys to nextKeys,
+    PreferKey.tocUiUseReplace to tocUiUseReplace,
+    PreferKey.tocCountWords to tocCountWords,
+    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
+    PreferKey.readStyleSelect to readStyleSelect,
+    PreferKey.comicStyleSelect to comicStyleSelect,
+    PreferKey.shareLayout to shareLayout,
+    PreferKey.readBarStyleFollowPage to readBarStyleFollowPage,
+    PreferKey.readBarStyle to readBarStyle,
+    PreferKey.clickActionTL to clickActionTL,
+    PreferKey.clickActionTC to clickActionTC,
+    PreferKey.clickActionTR to clickActionTR,
+    PreferKey.clickActionML to clickActionML,
+    PreferKey.clickActionMC to clickActionMC,
+    PreferKey.clickActionMR to clickActionMR,
+    PreferKey.clickActionBL to clickActionBL,
+    PreferKey.clickActionBC to clickActionBC,
+    PreferKey.clickActionBR to clickActionBR,
     PreferKey.fontFolder to fontFolder,
-    PreferKey.systemTypefaces to systemTypefaces,
-    PreferKey.preDownloadNum to preDownloadNum,
+    PreferKey.readMenuBgColor to readMenuBgColor,
+    PreferKey.readMenuAccentColor to readMenuAccentColor,
+    PreferKey.readMenuContainerColor to readMenuContainerColor,
+    PreferKey.readMenuBgColorNight to readMenuBgColorNight,
+    PreferKey.readMenuAccentColorNight to readMenuAccentColorNight,
+    PreferKey.readMenuContainerColorNight to readMenuContainerColorNight,
+    PreferKey.readMenuTextColor to readMenuTextColor,
+    PreferKey.readMenuTextColorNight to readMenuTextColorNight,
+    PreferKey.readMenuColorMode to readMenuColorMode,
+    PreferKey.readMenuIconShowText to readMenuIconShowText,
+    PreferKey.readMenuIconStyle to readMenuIconStyle,
+    PreferKey.titleBarIconStyle to titleBarIconStyle,
+    PreferKey.readMenuIconItemsPerRow to readMenuIconItemsPerRow,
+    PreferKey.readMenuIconRowCount to readMenuIconRowCount,
+    PreferKey.readMenuBottomCornerRadius to readMenuBottomCornerRadius,
+    PreferKey.readMenuFloatingBottomBar to readMenuFloatingBottomBar,
+    PreferKey.readMenuTopBarBlurMode to readMenuTopBarBlurMode,
+    PreferKey.readMenuBottomBarBlurMode to readMenuBottomBarBlurMode,
+    PreferKey.readMenuTopBarLiquidGlassButtons to readMenuTopBarLiquidGlassButtons,
+    PreferKey.readMenuTopBarTitleCapsule to readMenuTopBarTitleCapsule,
+    PreferKey.readMenuBottomBarLiquidGlassButtons to readMenuBottomBarLiquidGlassButtons,
+    PreferKey.readMenuFloatingIconLiquidGlass to readMenuFloatingIconLiquidGlass,
+    PreferKey.readMenuTopBarBlurStyle to readMenuTopBarBlurStyle,
+    PreferKey.readMenuBottomBarBlurStyle to readMenuBottomBarBlurStyle,
+    PreferKey.readMenuBlurRadius to readMenuBlurRadius,
+    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
+    PreferKey.readMenuBlurColor to readMenuBlurColor,
+    PreferKey.readMenuBlurColorNight to readMenuBlurColorNight,
+    PreferKey.readMenuPaletteStyle to readMenuPaletteStyle,
+    PreferKey.readMenuLensRadius to readMenuLensRadius,
+    PreferKey.readMenuBorderWidth to readMenuBorderWidth,
+    PreferKey.readMenuBorderColor to readMenuBorderColor,
+    PreferKey.readMenuBorderColorNight to readMenuBorderColorNight,
+    PreferKey.readMenuCustomIcons to readMenuCustomIcons,
+    PreferKey.titleBarCustomIcons to titleBarCustomIcons,
+    PreferKey.titleBarIconPosition to titleBarIconPosition,
+    PreferKey.showTitleBarIcons to showTitleBarIcons,
+    PreferKey.chineseConverterType to chineseConverterType,
+    PreferKey.showMenuIcon to showMenuIcon,
+    PreferKey.titleBarCompact to titleBarCompact,
 )