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

Merge pull request #2016 from todoXu/main

feat:允许设置无规则匹配时的章节分割字数
Kudomaga 2 месяцев назад
Родитель
Сommit
edd17a422f

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

@@ -406,6 +406,7 @@ object PreferKey {
     const val bookshelfIntroMaxLines = "bookshelfIntroMaxLines"
     const val bookshelfIntroMaxLines = "bookshelfIntroMaxLines"
     const val sliderVibrator = "sliderVibrator"
     const val sliderVibrator = "sliderVibrator"
     const val useNewTocSheet = "useNewTocSheet"
     const val useNewTocSheet = "useNewTocSheet"
+    const val maxLengthWithNoToc = "maxLengthWithNoToc"
     const val selectVibrator = "selectVibrator"
     const val selectVibrator = "selectVibrator"
     const val audioCacheCleanTime = "audioCacheCleanTime"
     const val audioCacheCleanTime = "audioCacheCleanTime"
     const val audioPreDownloadNum = "audioPreDownloadNum"
     const val audioPreDownloadNum = "audioPreDownloadNum"

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

@@ -125,6 +125,9 @@ class ReadSettingsRepository(
     suspend fun setUseNewTocSheet(value: Boolean) =
     suspend fun setUseNewTocSheet(value: Boolean) =
         settingsRepository.putBoolean(PreferKey.useNewTocSheet, value)
         settingsRepository.putBoolean(PreferKey.useNewTocSheet, value)
 
 
+    suspend fun setMaxLengthWithNoToc(value: Int) =
+        settingsRepository.putInt(PreferKey.maxLengthWithNoToc, value)
+
     suspend fun setSelectVibrator(value: Boolean) =
     suspend fun setSelectVibrator(value: Boolean) =
         settingsRepository.putBoolean(PreferKey.selectVibrator, value)
         settingsRepository.putBoolean(PreferKey.selectVibrator, value)
 
 
@@ -368,6 +371,7 @@ class ReadSettingsRepository(
             pageTouchSlop = compatDsValue(Keys.PageTouchSlop, 0),
             pageTouchSlop = compatDsValue(Keys.PageTouchSlop, 0),
             sliderVibrator = compatDsValue(Keys.SliderVibrator, false),
             sliderVibrator = compatDsValue(Keys.SliderVibrator, false),
             useNewTocSheet = compatDsValue(Keys.UseNewTocSheet, true),
             useNewTocSheet = compatDsValue(Keys.UseNewTocSheet, true),
+            maxLengthWithNoToc = compatDsValue(Keys.MaxLengthWithNoToc, 3000),
             selectVibrator = compatDsValue(Keys.SelectVibrator, false),
             selectVibrator = compatDsValue(Keys.SelectVibrator, false),
             autoChangeSource = compatDsValue(Keys.AutoChangeSource, true),
             autoChangeSource = compatDsValue(Keys.AutoChangeSource, true),
             autoSuggestDayNight = compatDsValue(Keys.AutoSuggestDayNight, false),
             autoSuggestDayNight = compatDsValue(Keys.AutoSuggestDayNight, false),
@@ -486,6 +490,7 @@ class ReadSettingsRepository(
         val PageTouchSlop = intPreferencesKey(PreferKey.pageTouchSlop)
         val PageTouchSlop = intPreferencesKey(PreferKey.pageTouchSlop)
         val SliderVibrator = booleanPreferencesKey(PreferKey.sliderVibrator)
         val SliderVibrator = booleanPreferencesKey(PreferKey.sliderVibrator)
         val UseNewTocSheet = booleanPreferencesKey(PreferKey.useNewTocSheet)
         val UseNewTocSheet = booleanPreferencesKey(PreferKey.useNewTocSheet)
+        val MaxLengthWithNoToc = intPreferencesKey(PreferKey.maxLengthWithNoToc)
         val SelectVibrator = booleanPreferencesKey(PreferKey.selectVibrator)
         val SelectVibrator = booleanPreferencesKey(PreferKey.selectVibrator)
         val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource)
         val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource)
         val AutoSuggestDayNight = booleanPreferencesKey(PreferKey.autoSuggestDayNight)
         val AutoSuggestDayNight = booleanPreferencesKey(PreferKey.autoSuggestDayNight)
@@ -614,6 +619,7 @@ internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.pageTouchSlop to pageTouchSlop,
     PreferKey.pageTouchSlop to pageTouchSlop,
     PreferKey.sliderVibrator to sliderVibrator,
     PreferKey.sliderVibrator to sliderVibrator,
     PreferKey.useNewTocSheet to useNewTocSheet,
     PreferKey.useNewTocSheet to useNewTocSheet,
+    PreferKey.maxLengthWithNoToc to maxLengthWithNoToc,
     PreferKey.selectVibrator to selectVibrator,
     PreferKey.selectVibrator to selectVibrator,
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoChangeSource to autoChangeSource,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,
     PreferKey.autoSuggestDayNight to autoSuggestDayNight,

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

@@ -35,6 +35,7 @@ data class ReadSettings(
     val pageTouchSlop: Int = 0,
     val pageTouchSlop: Int = 0,
     val sliderVibrator: Boolean = false,
     val sliderVibrator: Boolean = false,
     val useNewTocSheet: Boolean = true,
     val useNewTocSheet: Boolean = true,
+    val maxLengthWithNoToc: Int = 3000,
     val selectVibrator: Boolean = false,
     val selectVibrator: Boolean = false,
     val autoChangeSource: Boolean = true,
     val autoChangeSource: Boolean = true,
     val autoSuggestDayNight: Boolean = false,
     val autoSuggestDayNight: Boolean = false,

+ 22 - 5
app/src/main/java/io/legado/app/model/localBook/TextFile.kt

@@ -9,6 +9,7 @@ import io.legado.app.exception.EmptyFileException
 import io.legado.app.help.DefaultData
 import io.legado.app.help.DefaultData
 import io.legado.app.help.book.isLocalModified
 import io.legado.app.help.book.isLocalModified
 import io.legado.app.help.book.upKind
 import io.legado.app.help.book.upKind
+import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.EncodingDetect
 import io.legado.app.utils.EncodingDetect
 import io.legado.app.utils.MD5Utils
 import io.legado.app.utils.MD5Utils
 import io.legado.app.utils.StringUtils
 import io.legado.app.utils.StringUtils
@@ -60,9 +61,6 @@ class TextFile(private var book: Book) {
     //默认从文件中获取数据的长度
     //默认从文件中获取数据的长度
     private val bufferSize = 512000
     private val bufferSize = 512000
 
 
-    //没有标题的时候,每个章节的最大长度
-    private val maxLengthWithNoToc = 10 * 1024
-
     //使用正则划分目录,每个章节的最大允许长度
     //使用正则划分目录,每个章节的最大允许长度
     private val maxLengthWithToc = 102400
     private val maxLengthWithToc = 102400
 
 
@@ -364,6 +362,9 @@ class TextFile(private var book: Book) {
     ): Pair<ArrayList<BookChapter>, Int> {
     ): Pair<ArrayList<BookChapter>, Int> {
         val toc = arrayListOf<BookChapter>()
         val toc = arrayListOf<BookChapter>()
         var bookWordCount = 0
         var bookWordCount = 0
+        val maxByteLengthWithNoToc = maxLengthWithNoTocBytes(
+            charset, ReadConfig.maxLengthWithNoToc
+        )
         LocalBook.getBookInputStream(book).use { bis ->
         LocalBook.getBookInputStream(book).use { bis ->
             //block的个数
             //block的个数
             var blockPos = 0
             var blockPos = 0
@@ -404,10 +405,10 @@ class TextFile(private var book: Book) {
                 while (strLength > 0) {
                 while (strLength > 0) {
                     chapterPos++
                     chapterPos++
                     //是否长度超过一章
                     //是否长度超过一章
-                    if (strLength > maxLengthWithNoToc) { //在buffer中一章的终止点
+                    if (strLength > maxByteLengthWithNoToc) { //在buffer中一章的终止点
                         var end = length
                         var end = length
                         //寻找换行符作为终止点
                         //寻找换行符作为终止点
-                        for (i in chapterOffset + maxLengthWithNoToc until length) {
+                        for (i in chapterOffset + maxByteLengthWithNoToc until length) {
                             if (buffer[i] == blank) {
                             if (buffer[i] == blank) {
                                 end = i
                                 end = i
                                 break
                                 break
@@ -457,6 +458,22 @@ class TextFile(private var book: Book) {
         return toc to bookWordCount
         return toc to bookWordCount
     }
     }
 
 
+    /**
+     * 把无目录时章节的"字数"上限换算成 buffer 的字节长度上限。
+     * 不同编码单个汉字占用的字节数不同:UTF-8 为 3 字节、GBK/GB18030 为 2 字节、
+     * UTF-16 基本平面为 2 字节(代理对按 2 字节估算),其余单字节编码按 1 字节处理。
+     */
+    private fun maxLengthWithNoTocBytes(charset: Charset, chars: Int): Int {
+        val name = charset.name().uppercase()
+        val bytesPerChar = when {
+            name.contains("UTF-8") -> 3
+            name.startsWith("GB") -> 2
+            name.contains("UTF-16") -> 2
+            else -> 1
+        }
+        return chars * bytesPerChar
+    }
+
     /**
     /**
      * 获取合适的目录规则
      * 获取合适的目录规则
      */
      */

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

@@ -1572,6 +1572,9 @@ sealed interface ConfigUpdate {
     data class UseNewTocSheet(val value: Boolean) : ConfigUpdate {
     data class UseNewTocSheet(val value: Boolean) : ConfigUpdate {
         override val actions = emptySet<ConfigUpdateAction>()
         override val actions = emptySet<ConfigUpdateAction>()
     }
     }
+    data class MaxLengthWithNoToc(val value: Int) : ConfigUpdate {
+        override val actions = emptySet<ConfigUpdateAction>()
+    }
     data class SelectVibrator(val value: Boolean) : ConfigUpdate {
     data class SelectVibrator(val value: Boolean) : ConfigUpdate {
         override val actions = emptySet<ConfigUpdateAction>()
         override val actions = emptySet<ConfigUpdateAction>()
     }
     }

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

@@ -658,6 +658,11 @@ class ReadConfigUpdateDelegate(
                     readSettingsRepository.setUseNewTocSheet(update.value)
                     readSettingsRepository.setUseNewTocSheet(update.value)
                 }
                 }
             }
             }
+            is ConfigUpdate.MaxLengthWithNoToc -> {
+                scope.launch {
+                    readSettingsRepository.setMaxLengthWithNoToc(update.value)
+                }
+            }
             is ConfigUpdate.SelectVibrator -> {
             is ConfigUpdate.SelectVibrator -> {
                 scope.launch {
                 scope.launch {
                     readSettingsRepository.setSelectVibrator(update.value)
                     readSettingsRepository.setSelectVibrator(update.value)

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

@@ -129,6 +129,9 @@ fun MoreConfigSheet(
                 onUseNewTocSheetChange = {
                 onUseNewTocSheetChange = {
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseNewTocSheet(it)))
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseNewTocSheet(it)))
                 },
                 },
+                onMaxLengthWithNoTocChange = {
+                    onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MaxLengthWithNoToc(it)))
+                },
                 onSelectVibratorChange = {
                 onSelectVibratorChange = {
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectVibrator(it)))
                     onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectVibrator(it)))
                 },
                 },
@@ -325,6 +328,7 @@ private fun OtherSettings(
     preferences: ReadPreferences,
     preferences: ReadPreferences,
     onSliderVibratorChange: (Boolean) -> Unit,
     onSliderVibratorChange: (Boolean) -> Unit,
     onUseNewTocSheetChange: (Boolean) -> Unit,
     onUseNewTocSheetChange: (Boolean) -> Unit,
+    onMaxLengthWithNoTocChange: (Int) -> Unit,
     onSelectVibratorChange: (Boolean) -> Unit,
     onSelectVibratorChange: (Boolean) -> Unit,
     onAutoChangeSourceChange: (Boolean) -> Unit,
     onAutoChangeSourceChange: (Boolean) -> Unit,
     onDefaultSourceChangeAllChange: (Boolean) -> Unit,
     onDefaultSourceChangeAllChange: (Boolean) -> Unit,
@@ -355,6 +359,19 @@ private fun OtherSettings(
         onCheckedChange = onUseNewTocSheetChange,
         onCheckedChange = onUseNewTocSheetChange,
     )
     )
 
 
+    TinySliderSettingItem(
+        title = stringResource(R.string.no_toc_split_length_title),
+        description = stringResource(
+            R.string.no_toc_split_length_summary,
+            preferences.maxLengthWithNoToc
+        ),
+        value = preferences.maxLengthWithNoToc.toFloat(),
+        valueRange = 3000f..100000f,
+        stepSize = 100f,
+        valueFormat = { it.roundToInt().toString() },
+        onValueChange = { onMaxLengthWithNoTocChange(it.roundToInt()) },
+    )
+
     TinySwitchSettingItem(
     TinySwitchSettingItem(
         title = stringResource(R.string.enable_select_vibrator),
         title = stringResource(R.string.enable_select_vibrator),
         checked = preferences.selectVibrator,
         checked = preferences.selectVibrator,

+ 1 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt

@@ -78,6 +78,7 @@ object ReadConfig {
     val bookmarkBadgeSize get() = read.bookmarkBadgeSize
     val bookmarkBadgeSize get() = read.bookmarkBadgeSize
     val sliderVibrator get() = read.sliderVibrator
     val sliderVibrator get() = read.sliderVibrator
     val useNewTocSheet get() = read.useNewTocSheet
     val useNewTocSheet get() = read.useNewTocSheet
+    val maxLengthWithNoToc get() = read.maxLengthWithNoToc
     val selectVibrator get() = read.selectVibrator
     val selectVibrator get() = read.selectVibrator
 
 
     val speechRatePlay get() = if (aloud.ttsFollowSys) 5 else aloud.ttsSpeechRate
     val speechRatePlay get() = if (aloud.ttsFollowSys) 5 else aloud.ttsSpeechRate

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

@@ -33,6 +33,7 @@ data class ReadConfigUiState(
     val pageTouchSlop: Int = 0,
     val pageTouchSlop: Int = 0,
     val sliderVibrator: Boolean = false,
     val sliderVibrator: Boolean = false,
     val useNewTocSheet: Boolean = true,
     val useNewTocSheet: Boolean = true,
+    val maxLengthWithNoToc: Int = 3000,
     val selectVibrator: Boolean = false,
     val selectVibrator: Boolean = false,
     val autoChangeSource: Boolean = true,
     val autoChangeSource: Boolean = true,
     val autoSuggestDayNight: Boolean = false,
     val autoSuggestDayNight: Boolean = false,
@@ -94,6 +95,7 @@ sealed interface ReadConfigIntent {
     data class PageTouchSlopChanged(val value: Int) : ReadConfigIntent
     data class PageTouchSlopChanged(val value: Int) : ReadConfigIntent
     data class SliderVibratorChanged(val value: Boolean) : ReadConfigIntent
     data class SliderVibratorChanged(val value: Boolean) : ReadConfigIntent
     data class UseNewTocSheetChanged(val value: Boolean) : ReadConfigIntent
     data class UseNewTocSheetChanged(val value: Boolean) : ReadConfigIntent
+    data class MaxLengthWithNoTocChanged(val value: Int) : ReadConfigIntent
     data class SelectVibratorChanged(val value: Boolean) : ReadConfigIntent
     data class SelectVibratorChanged(val value: Boolean) : ReadConfigIntent
     data class AutoChangeSourceChanged(val value: Boolean) : ReadConfigIntent
     data class AutoChangeSourceChanged(val value: Boolean) : ReadConfigIntent
     data class SelectTextChanged(val value: Boolean) : ReadConfigIntent
     data class SelectTextChanged(val value: Boolean) : ReadConfigIntent

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

@@ -305,6 +305,20 @@ fun ReadConfigScreen(
                     }
                     }
                 )
                 )
 
 
+                SliderSettingItem(
+                    title = stringResource(R.string.no_toc_split_length_title),
+                    description = stringResource(
+                        R.string.no_toc_split_length_summary,
+                        settings.maxLengthWithNoToc
+                    ),
+                    value = settings.maxLengthWithNoToc.toFloat(),
+                    defaultValue = 3000f,
+                    valueRange = 3000f..100000f,
+                    onValueChange = {
+                        onIntent(ReadConfigIntent.MaxLengthWithNoTocChanged(it.toInt()))
+                    }
+                )
+
                 SwitchSettingItem(
                 SwitchSettingItem(
                     title = stringResource(R.string.enable_select_vibrator),
                     title = stringResource(R.string.enable_select_vibrator),
                     checked = settings.selectVibrator,
                     checked = settings.selectVibrator,

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

@@ -130,6 +130,9 @@ private fun ReadConfigIntent.toSettingsTransform(): (ReadSettings) -> ReadSettin
     is ReadConfigIntent.PageTouchSlopChanged -> { settings -> settings.copy(pageTouchSlop = value) }
     is ReadConfigIntent.PageTouchSlopChanged -> { settings -> settings.copy(pageTouchSlop = value) }
     is ReadConfigIntent.SliderVibratorChanged -> { settings -> settings.copy(sliderVibrator = value) }
     is ReadConfigIntent.SliderVibratorChanged -> { settings -> settings.copy(sliderVibrator = value) }
     is ReadConfigIntent.UseNewTocSheetChanged -> { settings -> settings.copy(useNewTocSheet = value) }
     is ReadConfigIntent.UseNewTocSheetChanged -> { settings -> settings.copy(useNewTocSheet = value) }
+    is ReadConfigIntent.MaxLengthWithNoTocChanged -> {
+        settings -> settings.copy(maxLengthWithNoToc = value.coerceIn(3000, 100000))
+    }
     is ReadConfigIntent.SelectVibratorChanged -> { settings -> settings.copy(selectVibrator = value) }
     is ReadConfigIntent.SelectVibratorChanged -> { settings -> settings.copy(selectVibrator = value) }
     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) }
@@ -198,6 +201,7 @@ private fun ReadSettings.toUiState(
         pageTouchSlop = pageTouchSlop,
         pageTouchSlop = pageTouchSlop,
         sliderVibrator = sliderVibrator,
         sliderVibrator = sliderVibrator,
         useNewTocSheet = useNewTocSheet,
         useNewTocSheet = useNewTocSheet,
+        maxLengthWithNoToc = maxLengthWithNoToc,
         selectVibrator = selectVibrator,
         selectVibrator = selectVibrator,
         autoChangeSource = autoChangeSource,
         autoChangeSource = autoChangeSource,
         autoSuggestDayNight = autoSuggestDayNight,
         autoSuggestDayNight = autoSuggestDayNight,

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

@@ -1818,6 +1818,8 @@
     <string name="default_color">使用默认字体颜色</string>
     <string name="default_color">使用默认字体颜色</string>
     <string name="enable_slider_vibrator">滑动进度条时震动</string>
     <string name="enable_slider_vibrator">滑动进度条时震动</string>
     <string name="use_new_toc_sheet">使用新版目录</string>
     <string name="use_new_toc_sheet">使用新版目录</string>
+    <string name="no_toc_split_length_title">无规则匹配时章节分割字数</string>
+    <string name="no_toc_split_length_summary">未匹配到目录规则时每个章节的分割字数(当前 %s 字)</string>
     <string name="edit_remark">编辑备注</string>
     <string name="edit_remark">编辑备注</string>
     <string name="book_remark">书籍备注</string>
     <string name="book_remark">书籍备注</string>
     <string name="save_to_local">保存至本地</string>
     <string name="save_to_local">保存至本地</string>

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

@@ -1875,6 +1875,8 @@
     <string name="default_color">Use Default Font Color</string>
     <string name="default_color">Use Default Font Color</string>
     <string name="enable_slider_vibrator">Vibrate While Sliding Progress Bar</string>
     <string name="enable_slider_vibrator">Vibrate While Sliding Progress Bar</string>
     <string name="use_new_toc_sheet">Use New TOC Sheet</string>
     <string name="use_new_toc_sheet">Use New TOC Sheet</string>
+    <string name="no_toc_split_length_title">Chapter Split Length (No Rule Match)</string>
+    <string name="no_toc_split_length_summary">Characters per chapter when no TOC rule matches (currently %s)</string>
     <string name="edit_remark">Edit remark</string>
     <string name="edit_remark">Edit remark</string>
     <string name="book_remark">Book Alias</string>
     <string name="book_remark">Book Alias</string>
     <string name="save_to_local">Save to local</string>
     <string name="save_to_local">Save to local</string>

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

@@ -133,6 +133,7 @@ private fun readSettingsMappingSamples(): List<ReadSettings> {
         tocUiUseReplace = true,
         tocUiUseReplace = true,
         tocCountWords = false,
         tocCountWords = false,
         useNewTocSheet = false,
         useNewTocSheet = false,
+        maxLengthWithNoToc = 3000,
         readStyleSelect = 1007,
         readStyleSelect = 1007,
         comicStyleSelect = 1008,
         comicStyleSelect = 1008,
         shareLayout = true,
         shareLayout = true,
@@ -273,6 +274,7 @@ private fun ReadSettings.expectedGatewayPrefMap(): Map<String, Any?> = mapOf(
     PreferKey.tocUiUseReplace to tocUiUseReplace,
     PreferKey.tocUiUseReplace to tocUiUseReplace,
     PreferKey.tocCountWords to tocCountWords,
     PreferKey.tocCountWords to tocCountWords,
     PreferKey.useNewTocSheet to useNewTocSheet,
     PreferKey.useNewTocSheet to useNewTocSheet,
+    PreferKey.maxLengthWithNoToc to maxLengthWithNoToc,
     PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
     PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
     PreferKey.readStyleSelect to readStyleSelect,
     PreferKey.readStyleSelect to readStyleSelect,
     PreferKey.comicStyleSelect to comicStyleSelect,
     PreferKey.comicStyleSelect to comicStyleSelect,