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

feat: 朗读脱离提示增加启用开关

- ReadSettings 新增 readAloudDetachReminderEnabled(默认开启,DataStore 持久化)
- 关闭后朗读位置脱离当前页时不显示「回到朗读位置」悬浮条
- 开关入口:阅读设置 → 更多 与 阅读菜单 → 更多设置,均位于阅读锚点开关下方
- 补齐 PreferKey/读方向/写方向映射,同步 ReadSettingsMappingTest 字段守护(111 字段)
- 新增四语言文案
HapeLee 2 месяцев назад
Родитель
Сommit
c07f6fc778

+ 1 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -112,6 +112,7 @@ object PreferKey {
     const val autoChangeSource = "autoChangeSource"
     const val autoChangeSource = "autoChangeSource"
     const val autoSuggestDayNight = "autoSuggestDayNight"
     const val autoSuggestDayNight = "autoSuggestDayNight"
     const val readingAnchorEnabled = "readingAnchorEnabled"
     const val readingAnchorEnabled = "readingAnchorEnabled"
+    const val readAloudDetachReminderEnabled = "readAloudDetachReminderEnabled"
     const val importKeepName = "importKeepName"
     const val importKeepName = "importKeepName"
     const val importKeepGroup = "importKeepGroup"
     const val importKeepGroup = "importKeepGroup"
     const val screenOrientation = "screenOrientation"
     const val screenOrientation = "screenOrientation"

+ 6 - 0
app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt

@@ -140,6 +140,9 @@ class ReadSettingsRepository(
     suspend fun setReadingAnchorEnabled(value: Boolean) =
     suspend fun setReadingAnchorEnabled(value: Boolean) =
         settingsRepository.putBoolean(PreferKey.readingAnchorEnabled, value)
         settingsRepository.putBoolean(PreferKey.readingAnchorEnabled, value)
 
 
+    suspend fun setReadAloudDetachReminderEnabled(value: Boolean) =
+        settingsRepository.putBoolean(PreferKey.readAloudDetachReminderEnabled, value)
+
     suspend fun setSelectText(value: Boolean) =
     suspend fun setSelectText(value: Boolean) =
         settingsRepository.putBoolean(PreferKey.selectText, value)
         settingsRepository.putBoolean(PreferKey.selectText, value)
 
 
@@ -379,6 +382,7 @@ class ReadSettingsRepository(
             autoChangeSource = compatDsValue(Keys.AutoChangeSource, true),
             autoChangeSource = compatDsValue(Keys.AutoChangeSource, true),
             autoSuggestDayNight = compatDsValue(Keys.AutoSuggestDayNight, false),
             autoSuggestDayNight = compatDsValue(Keys.AutoSuggestDayNight, false),
             readingAnchorEnabled = compatDsValue(Keys.ReadingAnchorEnabled, true),
             readingAnchorEnabled = compatDsValue(Keys.ReadingAnchorEnabled, true),
+            readAloudDetachReminderEnabled = compatDsValue(Keys.ReadAloudDetachReminderEnabled, true),
             selectText = compatDsValue(Keys.SelectText, true),
             selectText = compatDsValue(Keys.SelectText, true),
             noAnimScrollPage = compatDsValue(Keys.NoAnimScrollPage, false),
             noAnimScrollPage = compatDsValue(Keys.NoAnimScrollPage, false),
             clickImgWay = compatDsValue(Keys.ClickImgWay, "2"),
             clickImgWay = compatDsValue(Keys.ClickImgWay, "2"),
@@ -499,6 +503,7 @@ class ReadSettingsRepository(
         val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource)
         val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource)
         val AutoSuggestDayNight = booleanPreferencesKey(PreferKey.autoSuggestDayNight)
         val AutoSuggestDayNight = booleanPreferencesKey(PreferKey.autoSuggestDayNight)
         val ReadingAnchorEnabled = booleanPreferencesKey(PreferKey.readingAnchorEnabled)
         val ReadingAnchorEnabled = booleanPreferencesKey(PreferKey.readingAnchorEnabled)
+        val ReadAloudDetachReminderEnabled = booleanPreferencesKey(PreferKey.readAloudDetachReminderEnabled)
         val SelectText = booleanPreferencesKey(PreferKey.selectText)
         val SelectText = booleanPreferencesKey(PreferKey.selectText)
         val NoAnimScrollPage = booleanPreferencesKey(PreferKey.noAnimScrollPage)
         val NoAnimScrollPage = booleanPreferencesKey(PreferKey.noAnimScrollPage)
         val ClickImgWay = stringPreferencesKey(PreferKey.clickImgWay)
         val ClickImgWay = stringPreferencesKey(PreferKey.clickImgWay)
@@ -629,6 +634,7 @@ internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,
     PreferKey.readingAnchorEnabled to readingAnchorEnabled,
     PreferKey.readingAnchorEnabled to readingAnchorEnabled,
+    PreferKey.readAloudDetachReminderEnabled to readAloudDetachReminderEnabled,
     PreferKey.selectText to selectText,
     PreferKey.selectText to selectText,
     PreferKey.noAnimScrollPage to noAnimScrollPage,
     PreferKey.noAnimScrollPage to noAnimScrollPage,
     PreferKey.clickImgWay to clickImgWay,
     PreferKey.clickImgWay to clickImgWay,

+ 1 - 0
app/src/main/java/io/legado/app/domain/model/settings/ReadSettings.kt

@@ -40,6 +40,7 @@ data class ReadSettings(
     val autoChangeSource: Boolean = true,
     val autoChangeSource: Boolean = true,
     val autoSuggestDayNight: Boolean = false,
     val autoSuggestDayNight: Boolean = false,
     val readingAnchorEnabled: Boolean = true,
     val readingAnchorEnabled: Boolean = true,
+    val readAloudDetachReminderEnabled: Boolean = true,
     val selectText: Boolean = true,
     val selectText: Boolean = true,
     val noAnimScrollPage: Boolean = false,
     val noAnimScrollPage: Boolean = false,
     val clickImgWay: String = "2",
     val clickImgWay: String = "2",

+ 1 - 0
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -77,6 +77,7 @@ object ReadBookConfig {
     val readMenuIconShowText get() = readSettings.readMenuIconShowText
     val readMenuIconShowText get() = readSettings.readMenuIconShowText
     val showMenuIcon get() = readSettings.showMenuIcon
     val showMenuIcon get() = readSettings.showMenuIcon
     val readingAnchorEnabled get() = readSettings.readingAnchorEnabled
     val readingAnchorEnabled get() = readSettings.readingAnchorEnabled
+    val readAloudDetachReminderEnabled get() = readSettings.readAloudDetachReminderEnabled
     val titleBarCompact get() = readSettings.titleBarCompact
     val titleBarCompact get() = readSettings.titleBarCompact
     val readMenuFloatingBottomBar get() = readSettings.readMenuFloatingBottomBar
     val readMenuFloatingBottomBar get() = readSettings.readMenuFloatingBottomBar
     val readMenuTopBarLiquidGlassButtons get() = readSettings.readMenuTopBarLiquidGlassButtons
     val readMenuTopBarLiquidGlassButtons get() = readSettings.readMenuTopBarLiquidGlassButtons

+ 5 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt

@@ -230,6 +230,8 @@ data class ReadBookUiState(
     val isReadAloudPaused: Boolean = false,
     val isReadAloudPaused: Boolean = false,
     /** 朗读位置是否跟随当前显示页;手动翻页/跳章后为 false(显示"回到朗读位置"悬浮条)。 */
     /** 朗读位置是否跟随当前显示页;手动翻页/跳章后为 false(显示"回到朗读位置"悬浮条)。 */
     val readAloudFollow: Boolean = true,
     val readAloudFollow: Boolean = true,
+    /** 朗读位置脱离当前页时的悬浮提示开关。 */
+    val readAloudDetachReminderEnabled: Boolean = true,
     val readAloudEngineName: String = "",
     val readAloudEngineName: String = "",
     val readAloudCharacterName: String = "",
     val readAloudCharacterName: String = "",
     val readAloudRoleType: SpeechRoleType = SpeechRoleType.Narrator,
     val readAloudRoleType: SpeechRoleType = SpeechRoleType.Narrator,
@@ -1606,6 +1608,9 @@ sealed interface ConfigUpdate {
     data class ReadingAnchorEnabled(val value: Boolean) : ConfigUpdate {
     data class ReadingAnchorEnabled(val value: Boolean) : ConfigUpdate {
         override val actions = emptySet<ConfigUpdateAction>()
         override val actions = emptySet<ConfigUpdateAction>()
     }
     }
+    data class ReadAloudDetachReminderEnabled(val value: Boolean) : ConfigUpdate {
+        override val actions = emptySet<ConfigUpdateAction>()
+    }
     data class SelectText(val value: Boolean) : ConfigUpdate {
     data class SelectText(val value: Boolean) : ConfigUpdate {
         override val actions = emptySet<ConfigUpdateAction>()
         override val actions = emptySet<ConfigUpdateAction>()
     }
     }

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookFloatingActionBar.kt

@@ -42,6 +42,7 @@ fun ReadBookFloatingActionBar(
         !state.menuVisible && !state.isShowingSearchResult
         !state.menuVisible && !state.isShowingSearchResult
     val reminder = state.activeReminder?.takeIf { !state.menuVisible }
     val reminder = state.activeReminder?.takeIf { !state.menuVisible }
     val readAloudDetached = state.isReadAloudRunning && !state.readAloudFollow &&
     val readAloudDetached = state.isReadAloudRunning && !state.readAloudFollow &&
+        state.readAloudDetachReminderEnabled &&
         !state.menuVisible && !state.isShowingSearchResult
         !state.menuVisible && !state.isShowingSearchResult
     Box(Modifier.fillMaxSize()) {
     Box(Modifier.fillMaxSize()) {
         AnimatedVisibility(
         AnimatedVisibility(

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -2007,6 +2007,7 @@ class ReadBookViewModel(
             seekProgress = calculateSeekProgress(),
             seekProgress = calculateSeekProgress(),
             seekMax = calculateSeekMax(),
             seekMax = calculateSeekMax(),
             readingAnchorAvailable = ReadBook.hasReadingAnchor(),
             readingAnchorAvailable = ReadBook.hasReadingAnchor(),
+            readAloudDetachReminderEnabled = ReadBookConfig.readAloudDetachReminderEnabled,
             replaceRuleEnabled = book?.getUseReplaceRule(
             replaceRuleEnabled = book?.getUseReplaceRule(
                 otherSettingsGateway.currentSettings.replaceEnableDefault
                 otherSettingsGateway.currentSettings.replaceEnableDefault
             ) ?: false,
             ) ?: false,

+ 5 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadConfigUpdateDelegate.kt

@@ -686,6 +686,11 @@ class ReadConfigUpdateDelegate(
                     readSettingsRepository.setReadingAnchorEnabled(update.value)
                     readSettingsRepository.setReadingAnchorEnabled(update.value)
                 }
                 }
             }
             }
+            is ConfigUpdate.ReadAloudDetachReminderEnabled -> {
+                scope.launch {
+                    readSettingsRepository.setReadAloudDetachReminderEnabled(update.value)
+                }
+            }
             is ConfigUpdate.SelectText -> {
             is ConfigUpdate.SelectText -> {
                 scope.launch {
                 scope.launch {
                     readSettingsRepository.setSelectText(update.value)
                     readSettingsRepository.setSelectText(update.value)

+ 10 - 0
app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt

@@ -147,6 +147,9 @@ fun MoreConfigSheet(
                 onReadingAnchorEnabledChange = {
                 onReadingAnchorEnabledChange = {
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ReadingAnchorEnabled(it)))
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ReadingAnchorEnabled(it)))
                 },
                 },
+                onReadAloudDetachReminderEnabledChange = {
+                    onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ReadAloudDetachReminderEnabled(it)))
+                },
                 onSelectTextChange = {
                 onSelectTextChange = {
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectText(it)))
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectText(it)))
                 },
                 },
@@ -337,6 +340,7 @@ private fun OtherSettings(
     onDefaultSourceChangeAllChange: (Boolean) -> Unit,
     onDefaultSourceChangeAllChange: (Boolean) -> Unit,
     onAutoSuggestDayNightChange: (Boolean) -> Unit,
     onAutoSuggestDayNightChange: (Boolean) -> Unit,
     onReadingAnchorEnabledChange: (Boolean) -> Unit,
     onReadingAnchorEnabledChange: (Boolean) -> Unit,
+    onReadAloudDetachReminderEnabledChange: (Boolean) -> Unit,
     onSelectTextChange: (Boolean) -> Unit,
     onSelectTextChange: (Boolean) -> Unit,
     onNoAnimScrollPageChange: (Boolean) -> Unit,
     onNoAnimScrollPageChange: (Boolean) -> Unit,
     onOptimizeRenderChange: (Boolean) -> Unit,
     onOptimizeRenderChange: (Boolean) -> Unit,
@@ -404,6 +408,12 @@ private fun OtherSettings(
         checked = preferences.readingAnchorEnabled,
         checked = preferences.readingAnchorEnabled,
         onCheckedChange = onReadingAnchorEnabledChange,
         onCheckedChange = onReadingAnchorEnabledChange,
     )
     )
+    TinySwitchSettingItem(
+        title = stringResource(R.string.read_aloud_detach_reminder),
+        description = stringResource(R.string.read_aloud_detach_reminder_summary),
+        checked = preferences.readAloudDetachReminderEnabled,
+        onCheckedChange = onReadAloudDetachReminderEnabledChange,
+    )
     TinySwitchSettingItem(
     TinySwitchSettingItem(
         title = stringResource(R.string.selectText),
         title = stringResource(R.string.selectText),
         checked = preferences.selectText,
         checked = preferences.selectText,

+ 2 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt

@@ -38,6 +38,7 @@ data class ReadConfigUiState(
     val autoChangeSource: Boolean = true,
     val autoChangeSource: Boolean = true,
     val autoSuggestDayNight: Boolean = false,
     val autoSuggestDayNight: Boolean = false,
     val readingAnchorEnabled: Boolean = true,
     val readingAnchorEnabled: Boolean = true,
+    val readAloudDetachReminderEnabled: Boolean = true,
     val selectText: Boolean = true,
     val selectText: Boolean = true,
     val noAnimScrollPage: Boolean = false,
     val noAnimScrollPage: Boolean = false,
     val clickImgWay: String = "2",
     val clickImgWay: String = "2",
@@ -108,6 +109,7 @@ sealed interface ReadConfigIntent {
     data class ShowMenuIconChanged(val value: Boolean) : ReadConfigIntent
     data class ShowMenuIconChanged(val value: Boolean) : ReadConfigIntent
     data class AutoSuggestDayNightChanged(val value: Boolean) : ReadConfigIntent
     data class AutoSuggestDayNightChanged(val value: Boolean) : ReadConfigIntent
     data class ReadingAnchorChanged(val value: Boolean) : ReadConfigIntent
     data class ReadingAnchorChanged(val value: Boolean) : ReadConfigIntent
+    data class ReadAloudDetachReminderChanged(val value: Boolean) : ReadConfigIntent
     data class PageKeysChanged(val prevKeys: String, val nextKeys: String) : ReadConfigIntent
     data class PageKeysChanged(val prevKeys: String, val nextKeys: String) : ReadConfigIntent
 }
 }
 
 

+ 9 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt

@@ -353,6 +353,15 @@ fun ReadConfigScreen(
                     }
                     }
                 )
                 )
 
 
+                SwitchSettingItem(
+                    title = stringResource(R.string.read_aloud_detach_reminder),
+                    description = stringResource(R.string.read_aloud_detach_reminder_summary),
+                    checked = settings.readAloudDetachReminderEnabled,
+                    onCheckedChange = {
+                        onIntent(ReadConfigIntent.ReadAloudDetachReminderChanged(it))
+                    }
+                )
+
                 SwitchSettingItem(
                 SwitchSettingItem(
                     title = stringResource(R.string.selectText),
                     title = stringResource(R.string.selectText),
                     checked = settings.selectText,
                     checked = settings.selectText,

+ 2 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt

@@ -137,6 +137,7 @@ private fun ReadConfigIntent.toSettingsTransform(): (ReadSettings) -> ReadSettin
     is ReadConfigIntent.AutoChangeSourceChanged -> { settings -> settings.copy(autoChangeSource = value) }
     is ReadConfigIntent.AutoChangeSourceChanged -> { settings -> settings.copy(autoChangeSource = value) }
     is ReadConfigIntent.AutoSuggestDayNightChanged -> { settings -> settings.copy(autoSuggestDayNight = value) }
     is ReadConfigIntent.AutoSuggestDayNightChanged -> { settings -> settings.copy(autoSuggestDayNight = value) }
     is ReadConfigIntent.ReadingAnchorChanged -> { settings -> settings.copy(readingAnchorEnabled = value) }
     is ReadConfigIntent.ReadingAnchorChanged -> { settings -> settings.copy(readingAnchorEnabled = value) }
+    is ReadConfigIntent.ReadAloudDetachReminderChanged -> { settings -> settings.copy(readAloudDetachReminderEnabled = value) }
     is ReadConfigIntent.SelectTextChanged -> { settings -> settings.copy(selectText = value) }
     is ReadConfigIntent.SelectTextChanged -> { settings -> settings.copy(selectText = value) }
     is ReadConfigIntent.NoAnimScrollPageChanged -> { settings -> settings.copy(noAnimScrollPage = value) }
     is ReadConfigIntent.NoAnimScrollPageChanged -> { settings -> settings.copy(noAnimScrollPage = value) }
     is ReadConfigIntent.ClickImgWayChanged -> { settings -> settings.copy(clickImgWay = value) }
     is ReadConfigIntent.ClickImgWayChanged -> { settings -> settings.copy(clickImgWay = value) }
@@ -207,6 +208,7 @@ private fun ReadSettings.toUiState(
         autoChangeSource = autoChangeSource,
         autoChangeSource = autoChangeSource,
         autoSuggestDayNight = autoSuggestDayNight,
         autoSuggestDayNight = autoSuggestDayNight,
         readingAnchorEnabled = readingAnchorEnabled,
         readingAnchorEnabled = readingAnchorEnabled,
+        readAloudDetachReminderEnabled = readAloudDetachReminderEnabled,
         selectText = selectText,
         selectText = selectText,
         noAnimScrollPage = noAnimScrollPage,
         noAnimScrollPage = noAnimScrollPage,
         clickImgWay = clickImgWay,
         clickImgWay = clickImgWay,

+ 2 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -2537,6 +2537,8 @@
     <string name="auto_switch_theme_reminder_desc">根据环境光提醒切换浅色/深色模式</string>
     <string name="auto_switch_theme_reminder_desc">根据环境光提醒切换浅色/深色模式</string>
     <string name="reading_anchor">阅读锚点</string>
     <string name="reading_anchor">阅读锚点</string>
     <string name="reading_anchor_summary">跳转章节两次及以上后,显示返回原阅读位置的悬浮按钮</string>
     <string name="reading_anchor_summary">跳转章节两次及以上后,显示返回原阅读位置的悬浮按钮</string>
+    <string name="read_aloud_detach_reminder">朗读脱离提示</string>
+    <string name="read_aloud_detach_reminder_summary">朗读位置离开当前页时,显示"回到朗读位置"悬浮按钮</string>
     <string name="switch_to_dark_mode_tip">当前环境光较暗,是否切换至深色模式?</string>
     <string name="switch_to_dark_mode_tip">当前环境光较暗,是否切换至深色模式?</string>
     <string name="switch_to_light_mode_tip">当前环境光较亮,是否切换至浅色模式?</string>
     <string name="switch_to_light_mode_tip">当前环境光较亮,是否切换至浅色模式?</string>
     <string name="switch_to_dark_mode_action">切换到深色模式</string>
     <string name="switch_to_dark_mode_action">切换到深色模式</string>

+ 2 - 0
app/src/main/res/values-zh-rHK/strings.xml

@@ -2420,6 +2420,8 @@
     <string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
     <string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
     <string name="reading_anchor">閱讀錨點</string>
     <string name="reading_anchor">閱讀錨點</string>
     <string name="reading_anchor_summary">跳轉章節兩次或以上後,顯示返回原閱讀位置的懸浮按鈕</string>
     <string name="reading_anchor_summary">跳轉章節兩次或以上後,顯示返回原閱讀位置的懸浮按鈕</string>
+    <string name="read_aloud_detach_reminder">朗讀脫離提示</string>
+    <string name="read_aloud_detach_reminder_summary">朗讀位置離開當前頁時,顯示"回到朗讀位置"懸浮按鈕</string>
     <string name="switch_to_dark_mode_tip">當前環境光較暗,是否切換至深色模式?</string>
     <string name="switch_to_dark_mode_tip">當前環境光較暗,是否切換至深色模式?</string>
     <string name="switch_to_light_mode_tip">當前環境光較亮,是否切換至淺色模式?</string>
     <string name="switch_to_light_mode_tip">當前環境光較亮,是否切換至淺色模式?</string>
     <string name="switch_to_dark_mode_action">切換至深色模式</string>
     <string name="switch_to_dark_mode_action">切換至深色模式</string>

+ 2 - 0
app/src/main/res/values-zh-rTW/strings.xml

@@ -2425,6 +2425,8 @@
     <string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
     <string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
     <string name="reading_anchor">閱讀錨點</string>
     <string name="reading_anchor">閱讀錨點</string>
     <string name="reading_anchor_summary">跳轉章節兩次或以上後,顯示返回原閱讀位置的懸浮按鈕</string>
     <string name="reading_anchor_summary">跳轉章節兩次或以上後,顯示返回原閱讀位置的懸浮按鈕</string>
+    <string name="read_aloud_detach_reminder">朗讀脫離提示</string>
+    <string name="read_aloud_detach_reminder_summary">朗讀位置離開目前頁面時,顯示「回到朗讀位置」懸浮按鈕</string>
     <string name="switch_to_dark_mode_tip">目前環境光較暗,是否切換至深色模式?</string>
     <string name="switch_to_dark_mode_tip">目前環境光較暗,是否切換至深色模式?</string>
     <string name="switch_to_light_mode_tip">目前環境光較亮,是否切換至淺色模式?</string>
     <string name="switch_to_light_mode_tip">目前環境光較亮,是否切換至淺色模式?</string>
     <string name="switch_to_dark_mode_action">切換至深色模式</string>
     <string name="switch_to_dark_mode_action">切換至深色模式</string>

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -2542,6 +2542,8 @@
     <string name="auto_switch_theme_reminder_desc">Remind to switch light/dark mode based on ambient light</string>
     <string name="auto_switch_theme_reminder_desc">Remind to switch light/dark mode based on ambient light</string>
     <string name="reading_anchor">Reading anchor</string>
     <string name="reading_anchor">Reading anchor</string>
     <string name="reading_anchor_summary">Show a floating button to return to the original reading position after jumping chapters twice or more</string>
     <string name="reading_anchor_summary">Show a floating button to return to the original reading position after jumping chapters twice or more</string>
+    <string name="read_aloud_detach_reminder">Read-aloud detach reminder</string>
+    <string name="read_aloud_detach_reminder_summary">Show a floating bar to return to the speaking position when the page leaves it</string>
     <string name="switch_to_dark_mode_tip">Ambient light is low. Switch to dark mode?</string>
     <string name="switch_to_dark_mode_tip">Ambient light is low. Switch to dark mode?</string>
     <string name="switch_to_light_mode_tip">Ambient light is bright. Switch to light mode?</string>
     <string name="switch_to_light_mode_tip">Ambient light is bright. Switch to light mode?</string>
     <string name="switch_to_dark_mode_action">Switch to dark mode</string>
     <string name="switch_to_dark_mode_action">Switch to dark mode</string>

+ 4 - 2
app/src/test/java/io/legado/app/data/repository/ReadSettingsMappingTest.kt

@@ -14,11 +14,11 @@ import kotlin.reflect.full.primaryConstructor
 class ReadSettingsMappingTest {
 class ReadSettingsMappingTest {
 
 
     @Test
     @Test
-    fun `gateway 持久化映射覆盖 ReadSettings 全部 110 个字段`() {
+    fun `gateway 持久化映射覆盖 ReadSettings 全部 111 个字段`() {
         val actualKeys = ReadSettings().toGatewayPrefMap().keys
         val actualKeys = ReadSettings().toGatewayPrefMap().keys
         val expectedKeys = ReadSettings().expectedGatewayPrefMap().keys
         val expectedKeys = ReadSettings().expectedGatewayPrefMap().keys
 
 
-        assertEquals(110, actualKeys.size)
+        assertEquals(111, actualKeys.size)
         assertEquals(expectedKeys, actualKeys)
         assertEquals(expectedKeys, actualKeys)
     }
     }
 
 
@@ -212,6 +212,7 @@ private fun readSettingsMappingSamples(): List<ReadSettings> {
         base.copy(autoChangeSource = false),
         base.copy(autoChangeSource = false),
         base.copy(autoSuggestDayNight = true),
         base.copy(autoSuggestDayNight = true),
         base.copy(readingAnchorEnabled = false),
         base.copy(readingAnchorEnabled = false),
+        base.copy(readAloudDetachReminderEnabled = false),
         base.copy(selectText = false),
         base.copy(selectText = false),
         base.copy(noAnimScrollPage = true),
         base.copy(noAnimScrollPage = true),
         base.copy(optimizeRender = true),
         base.copy(optimizeRender = true),
@@ -259,6 +260,7 @@ private fun ReadSettings.expectedGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,
     PreferKey.readingAnchorEnabled to readingAnchorEnabled,
     PreferKey.readingAnchorEnabled to readingAnchorEnabled,
+    PreferKey.readAloudDetachReminderEnabled to readAloudDetachReminderEnabled,
     PreferKey.selectText to selectText,
     PreferKey.selectText to selectText,
     PreferKey.noAnimScrollPage to noAnimScrollPage,
     PreferKey.noAnimScrollPage to noAnimScrollPage,
     PreferKey.clickImgWay to clickImgWay,
     PreferKey.clickImgWay to clickImgWay,