fix: 补全修复阅读界面缺失的一些功能和同步问题
This commit is contained in:
62 files changed
+1489
-673
No files matched your search
@@ -113,7 +113,8 @@
|
|||||||
"Bash(dir /s /b \"%USERPROFILE%\\\\.gradle\\\\caches\\\\*kyant*\")",
|
"Bash(dir /s /b \"%USERPROFILE%\\\\.gradle\\\\caches\\\\*kyant*\")",
|
||||||
"Bash(Select-String -Pattern \"\\(BUILD|error:|FAILED\\)\")",
|
"Bash(Select-String -Pattern \"\\(BUILD|error:|FAILED\\)\")",
|
||||||
"PowerShell(grep *)",
|
"PowerShell(grep *)",
|
||||||
"Bash(powershell -Command \"./gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 30\")"
|
"Bash(powershell -Command \"./gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 30\")",
|
||||||
|
"Bash(git revert *)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,11 +234,6 @@
|
|||||||
android:name=".ui.book.toc.TocActivity"
|
android:name=".ui.book.toc.TocActivity"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:launchMode="singleTop" />
|
android:launchMode="singleTop" />
|
||||||
<!-- 正文搜索 -->
|
|
||||||
<activity
|
|
||||||
android:name=".ui.book.searchContent.SearchContentActivity"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:launchMode="singleTop" />
|
|
||||||
<!-- 订阅源调试 -->
|
<!-- 订阅源调试 -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.rss.source.debug.RssSourceDebugActivity"
|
android:name=".ui.rss.source.debug.RssSourceDebugActivity"
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ object PreferKey {
|
|||||||
const val clearWebViewData = "clearWebViewData"
|
const val clearWebViewData = "clearWebViewData"
|
||||||
const val onlyLatestBackup = "onlyLatestBackup"
|
const val onlyLatestBackup = "onlyLatestBackup"
|
||||||
const val brightnessVwPos = "brightnessVwPos"
|
const val brightnessVwPos = "brightnessVwPos"
|
||||||
|
const val brightnessAuto = "brightnessAuto"
|
||||||
const val shrinkDatabase = "shrinkDatabase"
|
const val shrinkDatabase = "shrinkDatabase"
|
||||||
const val batchChangeSourceDelay = "batchChangeSourceDelay"
|
const val batchChangeSourceDelay = "batchChangeSourceDelay"
|
||||||
const val openBookInfoByClickTitle = "openBookInfoByClickTitle"
|
const val openBookInfoByClickTitle = "openBookInfoByClickTitle"
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ class ReadAloudSettingsRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setTtsTimer(value: Int) {
|
suspend fun setTtsTimer(value: Int) {
|
||||||
ReadConfig.ttsTimer = value.coerceIn(0, 180)
|
val timer = value.coerceIn(0, 180)
|
||||||
|
ReadConfig.ttsTimer = timer
|
||||||
|
settingsRepository.putInt(PreferKey.ttsTimer, timer)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setTtsFollowSys(value: Boolean) {
|
suspend fun setTtsFollowSys(value: Boolean) {
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ data class ReadPreferences(
|
|||||||
val textBottomJustify: Boolean = true,
|
val textBottomJustify: Boolean = true,
|
||||||
val adaptSpecialStyle: Boolean = true,
|
val adaptSpecialStyle: Boolean = true,
|
||||||
val useZhLayout: Boolean = false,
|
val useZhLayout: Boolean = false,
|
||||||
val showBrightnessView: Boolean = true,
|
val showBrightnessView: String = "1",
|
||||||
|
val brightnessVwPos: String = "1",
|
||||||
|
val readBrightness: Int = 100,
|
||||||
|
val brightnessAuto: Boolean = false,
|
||||||
val useUnderline: Boolean = false,
|
val useUnderline: Boolean = false,
|
||||||
val readSliderMode: String = "0",
|
val readSliderMode: String = "0",
|
||||||
val doubleHorizontalPage: String = "0",
|
val doubleHorizontalPage: String = "0",
|
||||||
@@ -157,8 +160,17 @@ class ReadSettingsRepository(
|
|||||||
suspend fun setUseZhLayout(value: Boolean) =
|
suspend fun setUseZhLayout(value: Boolean) =
|
||||||
settingsRepository.putBoolean(PreferKey.useZhLayout, value)
|
settingsRepository.putBoolean(PreferKey.useZhLayout, value)
|
||||||
|
|
||||||
suspend fun setShowBrightnessView(value: Boolean) =
|
suspend fun setShowBrightnessView(value: String) =
|
||||||
settingsRepository.putBoolean(PreferKey.showBrightnessView, value)
|
settingsRepository.putString(PreferKey.showBrightnessView, value)
|
||||||
|
|
||||||
|
suspend fun setBrightnessVwPos(value: String) =
|
||||||
|
settingsRepository.putString(PreferKey.brightnessVwPos, value)
|
||||||
|
|
||||||
|
suspend fun setReadBrightness(value: Int) =
|
||||||
|
settingsRepository.putInt(PreferKey.brightness, value)
|
||||||
|
|
||||||
|
suspend fun setBrightnessAuto(value: Boolean) =
|
||||||
|
settingsRepository.putBoolean(PreferKey.brightnessAuto, value)
|
||||||
|
|
||||||
suspend fun setUseUnderline(value: Boolean) =
|
suspend fun setUseUnderline(value: Boolean) =
|
||||||
settingsRepository.putBoolean(PreferKey.useUnderline, value)
|
settingsRepository.putBoolean(PreferKey.useUnderline, value)
|
||||||
@@ -370,7 +382,10 @@ class ReadSettingsRepository(
|
|||||||
textBottomJustify = this[Keys.TextBottomJustify] ?: true,
|
textBottomJustify = this[Keys.TextBottomJustify] ?: true,
|
||||||
adaptSpecialStyle = this[Keys.AdaptSpecialStyle] ?: true,
|
adaptSpecialStyle = this[Keys.AdaptSpecialStyle] ?: true,
|
||||||
useZhLayout = this[Keys.UseZhLayout] ?: false,
|
useZhLayout = this[Keys.UseZhLayout] ?: false,
|
||||||
showBrightnessView = this[Keys.ShowBrightnessView] ?: true,
|
showBrightnessView = this[Keys.ShowBrightnessView] ?: "1",
|
||||||
|
brightnessVwPos = this[Keys.BrightnessVwPos] ?: "1",
|
||||||
|
readBrightness = this[Keys.ReadBrightness] ?: 100,
|
||||||
|
brightnessAuto = this[Keys.BrightnessAuto] ?: false,
|
||||||
useUnderline = this[Keys.UseUnderline] ?: false,
|
useUnderline = this[Keys.UseUnderline] ?: false,
|
||||||
readSliderMode = this[Keys.ReadSliderMode] ?: "0",
|
readSliderMode = this[Keys.ReadSliderMode] ?: "0",
|
||||||
doubleHorizontalPage = this[Keys.DoubleHorizontalPage] ?: "0",
|
doubleHorizontalPage = this[Keys.DoubleHorizontalPage] ?: "0",
|
||||||
@@ -461,7 +476,10 @@ class ReadSettingsRepository(
|
|||||||
val TextBottomJustify = booleanPreferencesKey(PreferKey.textBottomJustify)
|
val TextBottomJustify = booleanPreferencesKey(PreferKey.textBottomJustify)
|
||||||
val AdaptSpecialStyle = booleanPreferencesKey(PreferKey.adaptSpecialStyle)
|
val AdaptSpecialStyle = booleanPreferencesKey(PreferKey.adaptSpecialStyle)
|
||||||
val UseZhLayout = booleanPreferencesKey(PreferKey.useZhLayout)
|
val UseZhLayout = booleanPreferencesKey(PreferKey.useZhLayout)
|
||||||
val ShowBrightnessView = booleanPreferencesKey(PreferKey.showBrightnessView)
|
val ShowBrightnessView = stringPreferencesKey(PreferKey.showBrightnessView)
|
||||||
|
val BrightnessVwPos = stringPreferencesKey(PreferKey.brightnessVwPos)
|
||||||
|
val ReadBrightness = intPreferencesKey(PreferKey.brightness)
|
||||||
|
val BrightnessAuto = booleanPreferencesKey(PreferKey.brightnessAuto)
|
||||||
val UseUnderline = booleanPreferencesKey(PreferKey.useUnderline)
|
val UseUnderline = booleanPreferencesKey(PreferKey.useUnderline)
|
||||||
val ReadSliderMode = stringPreferencesKey(PreferKey.readSliderMode)
|
val ReadSliderMode = stringPreferencesKey(PreferKey.readSliderMode)
|
||||||
val DoubleHorizontalPage = stringPreferencesKey(PreferKey.doublePageHorizontal)
|
val DoubleHorizontalPage = stringPreferencesKey(PreferKey.doublePageHorizontal)
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ import io.legado.app.ui.config.readConfig.ReadConfigViewModel
|
|||||||
import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel
|
import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel
|
||||||
import io.legado.app.ui.dict.DictViewModel
|
import io.legado.app.ui.dict.DictViewModel
|
||||||
import io.legado.app.ui.dict.rule.DictRuleViewModel
|
import io.legado.app.ui.dict.rule.DictRuleViewModel
|
||||||
|
import io.legado.app.ui.main.MainRouteSearchContent
|
||||||
import io.legado.app.ui.main.MainViewModel
|
import io.legado.app.ui.main.MainViewModel
|
||||||
import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
||||||
import io.legado.app.ui.main.explore.ExploreViewModel
|
import io.legado.app.ui.main.explore.ExploreViewModel
|
||||||
@@ -307,6 +308,14 @@ val appModule = module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModelOf(::SearchContentViewModel)
|
viewModel { (route: MainRouteSearchContent) ->
|
||||||
|
SearchContentViewModel(
|
||||||
|
bookUrl = route.bookUrl,
|
||||||
|
initialSearchWord = route.searchWord,
|
||||||
|
searchResultIndex = route.searchResultIndex,
|
||||||
|
bookRepository = get(),
|
||||||
|
searchContentRepository = get()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ import io.legado.app.lib.webdav.WebDav
|
|||||||
import io.legado.app.lib.webdav.WebDavException
|
import io.legado.app.lib.webdav.WebDavException
|
||||||
import io.legado.app.lib.webdav.WebDavFile
|
import io.legado.app.lib.webdav.WebDavFile
|
||||||
import io.legado.app.model.remote.RemoteBookWebDav
|
import io.legado.app.model.remote.RemoteBookWebDav
|
||||||
|
import io.legado.app.ui.config.backupConfig.BackupConfig
|
||||||
import io.legado.app.utils.AlphanumComparator
|
import io.legado.app.utils.AlphanumComparator
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
@@ -24,7 +25,6 @@ import io.legado.app.utils.NetworkUtils
|
|||||||
import io.legado.app.utils.UrlUtil
|
import io.legado.app.utils.UrlUtil
|
||||||
import io.legado.app.utils.compress.ZipUtils
|
import io.legado.app.utils.compress.ZipUtils
|
||||||
import io.legado.app.utils.fromJsonObject
|
import io.legado.app.utils.fromJsonObject
|
||||||
import io.legado.app.ui.config.backupConfig.BackupConfig
|
|
||||||
import io.legado.app.utils.isJson
|
import io.legado.app.utils.isJson
|
||||||
import io.legado.app.utils.normalizeFileName
|
import io.legado.app.utils.normalizeFileName
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -33,7 +33,6 @@ import kotlinx.coroutines.ensureActive
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.coroutines.coroutineContext
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* webDav初始化会访问网络,不要放到主线程
|
* webDav初始化会访问网络,不要放到主线程
|
||||||
@@ -359,10 +358,10 @@ object AppWebDav {
|
|||||||
appDb.bookDao.all.forEach { book ->
|
appDb.bookDao.all.forEach { book ->
|
||||||
val progressFileName = getProgressFileName(book.name, book.author)
|
val progressFileName = getProgressFileName(book.name, book.author)
|
||||||
val webDavFile = map[progressFileName]
|
val webDavFile = map[progressFileName]
|
||||||
webDavFile ?: return
|
webDavFile ?: return@forEach
|
||||||
if (webDavFile.lastModify <= book.syncTime) {
|
if (webDavFile.lastModify <= book.syncTime) {
|
||||||
//本地同步时间大于上传时间不用同步
|
//本地同步时间大于上传时间不用同步
|
||||||
return
|
return@forEach
|
||||||
}
|
}
|
||||||
getBookProgress(book)?.let { bookProgress ->
|
getBookProgress(book)?.let { bookProgress ->
|
||||||
if (bookProgress.durChapterIndex > book.durChapterIndex
|
if (bookProgress.durChapterIndex > book.durChapterIndex
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
import android.graphics.ColorFilter
|
import android.graphics.ColorFilter
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
@@ -95,10 +94,11 @@ class GlideImageGetter(private val context: Context, textView: TextView, private
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
targets.forEach {
|
val snapshot = targets.toList()
|
||||||
|
targets.clear()
|
||||||
|
snapshot.forEach {
|
||||||
Glide.with(context).clear(it)
|
Glide.with(context).clear(it)
|
||||||
}
|
}
|
||||||
targets.clear()
|
|
||||||
textViewRef.clear()
|
textViewRef.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package io.legado.app.help
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.ui.config.backupConfig.BackupConfig
|
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.BookProgress
|
import io.legado.app.data.entities.BookProgress
|
||||||
@@ -18,6 +17,7 @@ import io.legado.app.lib.webdav.WebDav
|
|||||||
import io.legado.app.lib.webdav.WebDavException
|
import io.legado.app.lib.webdav.WebDavException
|
||||||
import io.legado.app.lib.webdav.WebDavFile
|
import io.legado.app.lib.webdav.WebDavFile
|
||||||
import io.legado.app.model.remote.RemoteBookWebDav
|
import io.legado.app.model.remote.RemoteBookWebDav
|
||||||
|
import io.legado.app.ui.config.backupConfig.BackupConfig
|
||||||
import io.legado.app.utils.AlphanumComparator
|
import io.legado.app.utils.AlphanumComparator
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
@@ -253,9 +253,9 @@ class WebDavManager(
|
|||||||
}
|
}
|
||||||
appDb.bookDao.all.forEach { book ->
|
appDb.bookDao.all.forEach { book ->
|
||||||
val progressFileName = getProgressFileName(book.name, book.author)
|
val progressFileName = getProgressFileName(book.name, book.author)
|
||||||
val webDavFile = map[progressFileName] ?: return
|
val webDavFile = map[progressFileName] ?: return@forEach
|
||||||
if (webDavFile.lastModify <= book.syncTime) {
|
if (webDavFile.lastModify <= book.syncTime) {
|
||||||
return
|
return@forEach
|
||||||
}
|
}
|
||||||
getBookProgress(book)?.let { bookProgress ->
|
getBookProgress(book)?.let { bookProgress ->
|
||||||
if (bookProgress.durChapterIndex > book.durChapterIndex
|
if (bookProgress.durChapterIndex > book.durChapterIndex
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import io.legado.app.data.appDb
|
|||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.BookChapter
|
import io.legado.app.data.entities.BookChapter
|
||||||
import io.legado.app.data.entities.BookSource
|
import io.legado.app.data.entities.BookSource
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.model.localBook.LocalBook
|
import io.legado.app.model.localBook.LocalBook
|
||||||
import io.legado.app.model.localBook.TextFile
|
import io.legado.app.model.localBook.TextFile
|
||||||
|
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.ArchiveUtils
|
import io.legado.app.utils.ArchiveUtils
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.ImageUtils
|
import io.legado.app.utils.ImageUtils
|
||||||
@@ -133,12 +133,12 @@ object BookHelp {
|
|||||||
private fun clearComicCache(book: Book) {
|
private fun clearComicCache(book: Book) {
|
||||||
//只处理漫画
|
//只处理漫画
|
||||||
//为0的时候,不清除已缓存数据
|
//为0的时候,不清除已缓存数据
|
||||||
if (!book.isImage || AppConfig.imageRetainNum == 0) {
|
if (!book.isImage || ReadConfig.imageRetainNum == 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//向前保留设定数量,向后保留预下载数量
|
//向前保留设定数量,向后保留预下载数量
|
||||||
val startIndex = book.durChapterIndex - AppConfig.imageRetainNum
|
val startIndex = book.durChapterIndex - ReadConfig.imageRetainNum
|
||||||
val endIndex = book.durChapterIndex + AppConfig.preDownloadNum
|
val endIndex = book.durChapterIndex + ReadConfig.preDownloadNum
|
||||||
val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl, startIndex, endIndex)
|
val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl, startIndex, endIndex)
|
||||||
val imgNames = hashSetOf<String>()
|
val imgNames = hashSetOf<String>()
|
||||||
//获取需要保留章节的图片信息
|
//获取需要保留章节的图片信息
|
||||||
@@ -202,7 +202,7 @@ object BookHelp {
|
|||||||
book.getFolderName(),
|
book.getFolderName(),
|
||||||
bookChapter.getFileName(),
|
bookChapter.getFileName(),
|
||||||
).writeText(content)
|
).writeText(content)
|
||||||
if (book.isOnLineTxt && AppConfig.tocCountWords) {
|
if (book.isOnLineTxt && ReadConfig.tocCountWords) {
|
||||||
val wordCount = StringUtils.wordCountFormat(content.length)
|
val wordCount = StringUtils.wordCountFormat(content.length)
|
||||||
bookChapter.wordCount = wordCount
|
bookChapter.wordCount = wordCount
|
||||||
appDb.bookChapterDao.update(bookChapter)
|
appDb.bookChapterDao.update(bookChapter)
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
|||||||
ReadConfig.defaultSourceChangeAll = preferences.defaultSourceChangeAll
|
ReadConfig.defaultSourceChangeAll = preferences.defaultSourceChangeAll
|
||||||
ReadConfig.sliderVibrator = preferences.sliderVibrator
|
ReadConfig.sliderVibrator = preferences.sliderVibrator
|
||||||
ReadConfig.selectVibrator = preferences.selectVibrator
|
ReadConfig.selectVibrator = preferences.selectVibrator
|
||||||
|
ReadConfig.brightnessVwPos = preferences.brightnessVwPos
|
||||||
|
ReadConfig.readBrightness = preferences.readBrightness
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateReadBarStyleCache(value: Int) {
|
fun updateReadBarStyleCache(value: Int) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import io.legado.app.help.DefaultData
|
|||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.ui.config.PrefDelegate
|
import io.legado.app.ui.config.PrefDelegate
|
||||||
import io.legado.app.ui.config.prefDelegate
|
import io.legado.app.ui.config.prefDelegate
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.fromJsonObject
|
import io.legado.app.utils.fromJsonObject
|
||||||
import io.legado.app.utils.getMeanColor
|
import io.legado.app.utils.getMeanColor
|
||||||
@@ -208,6 +209,10 @@ object ReadBookConfig {
|
|||||||
var readMenuBorderColorNight by prefDelegate(PreferKey.readMenuBorderColorNight, 0)
|
var readMenuBorderColorNight by prefDelegate(PreferKey.readMenuBorderColorNight, 0)
|
||||||
var showTitleBarIcons by prefDelegate(PreferKey.showTitleBarIcons, true)
|
var showTitleBarIcons by prefDelegate(PreferKey.showTitleBarIcons, true)
|
||||||
var readSliderMode by prefDelegate(PreferKey.readSliderMode, "0")
|
var readSliderMode by prefDelegate(PreferKey.readSliderMode, "0")
|
||||||
|
var showBrightnessView by prefDelegate(PreferKey.showBrightnessView, "1")
|
||||||
|
var brightnessVwPos by prefDelegate(PreferKey.brightnessVwPos, "1")
|
||||||
|
var readBrightness by prefDelegate(PreferKey.brightness, 100)
|
||||||
|
var brightnessAuto by prefDelegate(PreferKey.brightnessAuto, false)
|
||||||
|
|
||||||
var styleSelect: Int
|
var styleSelect: Int
|
||||||
get() = if (isComic) comicStyleSelect else readStyleSelect
|
get() = if (isComic) comicStyleSelect else readStyleSelect
|
||||||
@@ -301,7 +306,7 @@ object ReadBookConfig {
|
|||||||
val resolvedMenuBgColor: Int
|
val resolvedMenuBgColor: Int
|
||||||
get() {
|
get() {
|
||||||
val isNight = ReadStyleResolver.isNightTheme()
|
val isNight = ReadStyleResolver.isNightTheme()
|
||||||
return when (AppConfig.readBarStyle) {
|
return when (ReadConfig.readBarStyle) {
|
||||||
1 -> { // 跟随阅读背景
|
1 -> { // 跟随阅读背景
|
||||||
val background = ReadStyleResolver.currentBackground(durConfig)
|
val background = ReadStyleResolver.currentBackground(durConfig)
|
||||||
if (background.type == 0) {
|
if (background.type == 0) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.graphics.drawable.Drawable
|
|||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.BitmapUtils
|
import io.legado.app.utils.BitmapUtils
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.externalFiles
|
import io.legado.app.utils.externalFiles
|
||||||
@@ -28,14 +29,14 @@ object ReadStyleResolver {
|
|||||||
|
|
||||||
fun currentMode(): ReadStyleMode {
|
fun currentMode(): ReadStyleMode {
|
||||||
return when {
|
return when {
|
||||||
AppConfig.isEInkMode -> ReadStyleMode.EInk
|
ReadConfig.isEInkMode -> ReadStyleMode.EInk
|
||||||
AppConfig.isNightTheme -> ReadStyleMode.Night
|
ReadConfig.isNightTheme -> ReadStyleMode.Night
|
||||||
else -> ReadStyleMode.Day
|
else -> ReadStyleMode.Day
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNightTheme(): Boolean {
|
fun isNightTheme(): Boolean {
|
||||||
return AppConfig.isNightTheme
|
return ReadConfig.isNightTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCurrentBackground(
|
fun setCurrentBackground(
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import io.legado.app.constant.EventBus
|
|||||||
import io.legado.app.constant.IntentAction
|
import io.legado.app.constant.IntentAction
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.HttpTTS
|
import io.legado.app.data.entities.HttpTTS
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.service.BaseReadAloudService
|
import io.legado.app.service.BaseReadAloudService
|
||||||
import io.legado.app.service.HttpReadAloudService
|
import io.legado.app.service.HttpReadAloudService
|
||||||
import io.legado.app.service.TTSReadAloudService
|
import io.legado.app.service.TTSReadAloudService
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.LogUtils
|
import io.legado.app.utils.LogUtils
|
||||||
import io.legado.app.utils.StringUtils
|
import io.legado.app.utils.StringUtils
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
@@ -21,7 +21,7 @@ import splitties.init.appCtx
|
|||||||
|
|
||||||
object ReadAloud {
|
object ReadAloud {
|
||||||
private var aloudClass: Class<*> = getReadAloudClass()
|
private var aloudClass: Class<*> = getReadAloudClass()
|
||||||
val ttsEngine get() = ReadBook.book?.getTtsEngine() ?: AppConfig.ttsEngine
|
val ttsEngine get() = ReadBook.book?.getTtsEngine() ?: ReadConfig.ttsEngine
|
||||||
var httpTTS: HttpTTS? = null
|
var httpTTS: HttpTTS? = null
|
||||||
|
|
||||||
private fun getReadAloudClass(): Class<*> {
|
private fun getReadAloudClass(): Class<*> {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import io.legado.app.help.book.isSameNameAuthor
|
|||||||
import io.legado.app.help.book.readSimulating
|
import io.legado.app.help.book.readSimulating
|
||||||
import io.legado.app.help.book.simulatedTotalChapterNum
|
import io.legado.app.help.book.simulatedTotalChapterNum
|
||||||
import io.legado.app.help.book.update
|
import io.legado.app.help.book.update
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.help.globalExecutor
|
import io.legado.app.help.globalExecutor
|
||||||
@@ -29,7 +28,6 @@ import io.legado.app.model.localBook.TextFile
|
|||||||
import io.legado.app.model.translation.TranslationChapterState
|
import io.legado.app.model.translation.TranslationChapterState
|
||||||
import io.legado.app.model.translation.TranslationChapterStatus
|
import io.legado.app.model.translation.TranslationChapterStatus
|
||||||
import io.legado.app.model.translation.TranslationManager
|
import io.legado.app.model.translation.TranslationManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import io.legado.app.model.webBook.WebBook
|
import io.legado.app.model.webBook.WebBook
|
||||||
import io.legado.app.service.BaseReadAloudService
|
import io.legado.app.service.BaseReadAloudService
|
||||||
import io.legado.app.service.CacheBookService
|
import io.legado.app.service.CacheBookService
|
||||||
@@ -37,6 +35,7 @@ import io.legado.app.ui.book.read.page.entities.TextChapter
|
|||||||
import io.legado.app.ui.book.read.page.entities.TextPage
|
import io.legado.app.ui.book.read.page.entities.TextPage
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
import io.legado.app.utils.stackTraceStr
|
import io.legado.app.utils.stackTraceStr
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -51,6 +50,7 @@ import kotlinx.coroutines.TimeoutCancellationException
|
|||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@@ -211,7 +211,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
ReadBookConfig.isComic = book.isImage
|
ReadBookConfig.isComic = book.isImage
|
||||||
if (oldIndex != ReadBookConfig.styleSelect) {
|
if (oldIndex != ReadBookConfig.styleSelect) {
|
||||||
postEvent(EventBus.UP_CONFIG, arrayListOf(1, 2, 5))
|
postEvent(EventBus.UP_CONFIG, arrayListOf(1, 2, 5))
|
||||||
if (AppConfig.readBarStyleFollowPage) {
|
if (ReadConfig.readBarStyleFollowPage) {
|
||||||
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
uploadSuccessAction: (() -> Unit)? = null,
|
uploadSuccessAction: (() -> Unit)? = null,
|
||||||
syncSuccessAction: (() -> Unit)? = null
|
syncSuccessAction: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
if (!AppConfig.syncBookProgress) return
|
if (!ReadConfig.syncBookProgress) return
|
||||||
val book = book ?: return
|
val book = book ?: return
|
||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
AppWebDav.getBookProgress(book)
|
AppWebDav.getBookProgress(book)
|
||||||
@@ -301,7 +301,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
) {
|
) {
|
||||||
// 服务器没有进度或者进度比服务器快,上传现有进度
|
// 服务器没有进度或者进度比服务器快,上传现有进度
|
||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
AppWebDav.uploadBookProgress(BookProgress(book), uploadSuccessAction)
|
AppWebDav.uploadBookProgress(book, onSuccess = uploadSuccessAction)
|
||||||
book.update()
|
book.update()
|
||||||
}
|
}
|
||||||
} else if (progress.durChapterIndex > book.durChapterIndex ||
|
} else if (progress.durChapterIndex > book.durChapterIndex ||
|
||||||
@@ -560,7 +560,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun recycleRecorders(beforeIndex: Int, afterIndex: Int) {
|
fun recycleRecorders(beforeIndex: Int, afterIndex: Int) {
|
||||||
if (!AppConfig.optimizeRender) {
|
if (!ReadConfig.optimizeRender) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
executor.execute {
|
executor.execute {
|
||||||
@@ -1131,7 +1131,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
private fun preDownload() {
|
private fun preDownload() {
|
||||||
if (book?.isLocal == true) return
|
if (book?.isLocal == true) return
|
||||||
executor.execute {
|
executor.execute {
|
||||||
if (AppConfig.preDownloadNum < 2) {
|
if (ReadConfig.preDownloadNum < 2) {
|
||||||
return@execute
|
return@execute
|
||||||
}
|
}
|
||||||
preDownloadTask?.cancel()
|
preDownloadTask?.cancel()
|
||||||
@@ -1139,7 +1139,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
//预下载
|
//预下载
|
||||||
launch {
|
launch {
|
||||||
val maxChapterIndex =
|
val maxChapterIndex =
|
||||||
min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
|
min(durChapterIndex + ReadConfig.preDownloadNum, chapterSize)
|
||||||
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
||||||
if (downloadedChapters.contains(i)) continue
|
if (downloadedChapters.contains(i)) continue
|
||||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||||
@@ -1147,7 +1147,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
|
val minChapterIndex = durChapterIndex - min(5, ReadConfig.preDownloadNum)
|
||||||
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
||||||
if (downloadedChapters.contains(i)) continue
|
if (downloadedChapters.contains(i)) continue
|
||||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import io.legado.app.help.book.isSameNameAuthor
|
|||||||
import io.legado.app.help.book.readSimulating
|
import io.legado.app.help.book.readSimulating
|
||||||
import io.legado.app.help.book.simulatedTotalChapterNum
|
import io.legado.app.help.book.simulatedTotalChapterNum
|
||||||
import io.legado.app.help.book.update
|
import io.legado.app.help.book.update
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
|
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.help.globalExecutor
|
import io.legado.app.help.globalExecutor
|
||||||
import io.legado.app.model.webBook.WebBook
|
import io.legado.app.model.webBook.WebBook
|
||||||
@@ -28,6 +26,8 @@ import io.legado.app.ui.book.manga.entities.MangaChapter
|
|||||||
import io.legado.app.ui.book.manga.entities.MangaContent
|
import io.legado.app.ui.book.manga.entities.MangaContent
|
||||||
import io.legado.app.ui.book.manga.entities.MangaPage
|
import io.legado.app.ui.book.manga.entities.MangaPage
|
||||||
import io.legado.app.ui.book.manga.entities.ReaderLoading
|
import io.legado.app.ui.book.manga.entities.ReaderLoading
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
|
import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
|
||||||
import io.legado.app.utils.mapIndexed
|
import io.legado.app.utils.mapIndexed
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineStart
|
||||||
@@ -45,7 +45,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import kotlin.getValue
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@@ -472,7 +471,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
|
|||||||
private fun preDownload() {
|
private fun preDownload() {
|
||||||
if (book?.isLocal == true) return
|
if (book?.isLocal == true) return
|
||||||
executor.execute {
|
executor.execute {
|
||||||
if (AppConfig.preDownloadNum < 2) {
|
if (ReadConfig.preDownloadNum < 2) {
|
||||||
return@execute
|
return@execute
|
||||||
}
|
}
|
||||||
preDownloadTask?.cancel()
|
preDownloadTask?.cancel()
|
||||||
@@ -480,7 +479,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
|
|||||||
//预下载
|
//预下载
|
||||||
launch {
|
launch {
|
||||||
val maxChapterIndex =
|
val maxChapterIndex =
|
||||||
min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
|
min(durChapterIndex + ReadConfig.preDownloadNum, chapterSize)
|
||||||
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
||||||
if (downloadedChapters.contains(i)) continue
|
if (downloadedChapters.contains(i)) continue
|
||||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||||
@@ -488,7 +487,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
|
val minChapterIndex = durChapterIndex - min(5, ReadConfig.preDownloadNum)
|
||||||
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
||||||
if (downloadedChapters.contains(i)) continue
|
if (downloadedChapters.contains(i)) continue
|
||||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||||
@@ -594,7 +593,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
|
|||||||
uploadSuccessAction: (() -> Unit)? = null,
|
uploadSuccessAction: (() -> Unit)? = null,
|
||||||
syncSuccessAction: (() -> Unit)? = null,
|
syncSuccessAction: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
if (!AppConfig.syncBookProgress) return
|
if (!ReadConfig.syncBookProgress) return
|
||||||
val book = book ?: return
|
val book = book ?: return
|
||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
AppWebDav.getBookProgress(book)
|
AppWebDav.getBookProgress(book)
|
||||||
@@ -607,7 +606,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
|
|||||||
) {
|
) {
|
||||||
// 服务器没有进度或者进度比服务器快,上传现有进度
|
// 服务器没有进度或者进度比服务器快,上传现有进度
|
||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
AppWebDav.uploadBookProgress(BookProgress(book), uploadSuccessAction)
|
AppWebDav.uploadBookProgress(book, onSuccess = uploadSuccessAction)
|
||||||
book.update()
|
book.update()
|
||||||
}
|
}
|
||||||
} else if (progress.durChapterIndex > book.durChapterIndex ||
|
} else if (progress.durChapterIndex > book.durChapterIndex ||
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ import io.legado.app.data.entities.BookChapter
|
|||||||
import io.legado.app.data.entities.HttpTTS
|
import io.legado.app.data.entities.HttpTTS
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfig
|
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.help.exoplayer.InputStreamDataSource
|
import io.legado.app.help.exoplayer.InputStreamDataSource
|
||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
@@ -43,6 +41,7 @@ import io.legado.app.model.ReadAloud
|
|||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.ui.book.read.page.entities.TextChapter
|
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.MD5Utils
|
import io.legado.app.utils.MD5Utils
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
@@ -98,7 +97,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
private val loadErrorHandlingPolicy by lazy {
|
private val loadErrorHandlingPolicy by lazy {
|
||||||
CustomLoadErrorHandlingPolicy()
|
CustomLoadErrorHandlingPolicy()
|
||||||
}
|
}
|
||||||
private var speechRate: Int = AppConfig.speechRatePlay + 5
|
private var speechRate: Int = ReadConfig.speechRatePlay + 5
|
||||||
private var downloadTask: Coroutine<*>? = null
|
private var downloadTask: Coroutine<*>? = null
|
||||||
private var playIndexJob: Job? = null
|
private var playIndexJob: Job? = null
|
||||||
private var downloadErrorNo: Int = 0
|
private var downloadErrorNo: Int = 0
|
||||||
@@ -210,7 +209,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
private suspend fun preDownloadAudios(httpTts: HttpTTS) {
|
private suspend fun preDownloadAudios(httpTts: HttpTTS) {
|
||||||
val book = ReadBook.book ?: return
|
val book = ReadBook.book ?: return
|
||||||
val currentIdx = ReadBook.durChapterIndex
|
val currentIdx = ReadBook.durChapterIndex
|
||||||
val limit = AppConfig.audioPreDownloadNum
|
val limit = ReadConfig.audioPreDownloadNum
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (i in 1..limit) {
|
for (i in 1..limit) {
|
||||||
@@ -300,7 +299,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
) {
|
) {
|
||||||
val book = ReadBook.book ?: return
|
val book = ReadBook.book ?: return
|
||||||
val currentIdx = ReadBook.durChapterIndex
|
val currentIdx = ReadBook.durChapterIndex
|
||||||
val limit = AppConfig.audioPreDownloadNum
|
val limit = ReadConfig.audioPreDownloadNum
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (i in 1..limit) {
|
for (i in 1..limit) {
|
||||||
@@ -491,7 +490,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
* 如果时间设置为0,则不再保护当前章节,退出即全删。
|
* 如果时间设置为0,则不再保护当前章节,退出即全删。
|
||||||
*/
|
*/
|
||||||
private fun removeCacheFile() {
|
private fun removeCacheFile() {
|
||||||
val keepTime = AppConfig.audioCacheCleanTime
|
val keepTime = ReadConfig.audioCacheCleanTime
|
||||||
// 只有当时间大于0时,才需要保护当前章节。如果为0,说明用户想彻底不留缓存。
|
// 只有当时间大于0时,才需要保护当前章节。如果为0,说明用户想彻底不留缓存。
|
||||||
val protectCurrentChapter = keepTime > 0
|
val protectCurrentChapter = keepTime > 0
|
||||||
val titleMd5 = if (protectCurrentChapter) MD5Utils.md5Encode16(this.textChapter?.chapter?.title ?: "") else ""
|
val titleMd5 = if (protectCurrentChapter) MD5Utils.md5Encode16(this.textChapter?.chapter?.title ?: "") else ""
|
||||||
@@ -572,7 +571,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
override fun upSpeechRate(reset: Boolean) {
|
override fun upSpeechRate(reset: Boolean) {
|
||||||
downloadTask?.cancel()
|
downloadTask?.cancel()
|
||||||
exoPlayer.stop()
|
exoPlayer.stop()
|
||||||
speechRate = AppConfig.speechRatePlay + 5
|
speechRate = ReadConfig.speechRatePlay + 5
|
||||||
if (ReadConfig.streamReadAloudAudio) {
|
if (ReadConfig.streamReadAloudAudio) {
|
||||||
downloadAndPlayAudiosStream()
|
downloadAndPlayAudiosStream()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,12 +38,9 @@ import io.legado.app.data.entities.BookSource
|
|||||||
import io.legado.app.databinding.ActivityMangaBinding
|
import io.legado.app.databinding.ActivityMangaBinding
|
||||||
import io.legado.app.databinding.ViewLoadMoreBinding
|
import io.legado.app.databinding.ViewLoadMoreBinding
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.AppFreezeMonitor.handler
|
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.book.isImage
|
import io.legado.app.help.book.isImage
|
||||||
import io.legado.app.help.book.isLocal
|
import io.legado.app.help.book.isLocal
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
|
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.help.source.getSourceType
|
import io.legado.app.help.source.getSourceType
|
||||||
import io.legado.app.help.storage.Backup
|
import io.legado.app.help.storage.Backup
|
||||||
@@ -78,6 +75,8 @@ import io.legado.app.ui.book.read.observeEyeProtectionEvents
|
|||||||
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
||||||
import io.legado.app.ui.book.toc.TocActivityResult
|
import io.legado.app.ui.book.toc.TocActivityResult
|
||||||
import io.legado.app.ui.browser.WebViewActivity
|
import io.legado.app.ui.browser.WebViewActivity
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
|
import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
|
||||||
import io.legado.app.ui.login.SourceLoginActivity
|
import io.legado.app.ui.login.SourceLoginActivity
|
||||||
import io.legado.app.ui.widget.number.NumberPickerDialog
|
import io.legado.app.ui.widget.number.NumberPickerDialog
|
||||||
import io.legado.app.ui.widget.recycler.LoadMoreView
|
import io.legado.app.ui.widget.recycler.LoadMoreView
|
||||||
@@ -220,7 +219,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
|||||||
binding.llRetry.isGone = true
|
binding.llRetry.isGone = true
|
||||||
ReadManga.loadOrUpContent()
|
ReadManga.loadOrUpContent()
|
||||||
}
|
}
|
||||||
binding.flLoading.isVisible = !AppConfig.isEInkMode
|
binding.flLoading.isVisible = !ReadConfig.isEInkMode
|
||||||
binding.eyeProtectionOverlay.refresh()
|
binding.eyeProtectionOverlay.refresh()
|
||||||
mAdapter.addFooterView {
|
mAdapter.addFooterView {
|
||||||
ViewLoadMoreBinding.bind(loadMoreView)
|
ViewLoadMoreBinding.bind(loadMoreView)
|
||||||
@@ -458,7 +457,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
|||||||
networkChangedListener.register()
|
networkChangedListener.register()
|
||||||
networkChangedListener.onNetworkChanged = {
|
networkChangedListener.onNetworkChanged = {
|
||||||
// 当网络是可用状态且无需初始化时同步进度(初始化中已有同步进度逻辑)
|
// 当网络是可用状态且无需初始化时同步进度(初始化中已有同步进度逻辑)
|
||||||
if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
if (ReadConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
||||||
ReadManga.syncProgress({ progress -> sureNewProgress(progress) })
|
ReadManga.syncProgress({ progress -> sureNewProgress(progress) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,7 +479,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
|||||||
if (ReadManga.inBookshelf) {
|
if (ReadManga.inBookshelf) {
|
||||||
ReadManga.saveRead()
|
ReadManga.saveRead()
|
||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
if (AppConfig.syncBookProgressPlus) {
|
if (ReadConfig.syncBookProgressPlus) {
|
||||||
ReadManga.syncProgress()
|
ReadManga.syncProgress()
|
||||||
} else {
|
} else {
|
||||||
ReadManga.uploadProgress()
|
ReadManga.uploadProgress()
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ import io.legado.app.help.book.isLocal
|
|||||||
import io.legado.app.help.book.isLocalModified
|
import io.legado.app.help.book.isLocalModified
|
||||||
import io.legado.app.help.book.removeType
|
import io.legado.app.help.book.removeType
|
||||||
import io.legado.app.help.book.simulatedTotalChapterNum
|
import io.legado.app.help.book.simulatedTotalChapterNum
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.model.ReadBook
|
|
||||||
import io.legado.app.model.ReadManga
|
import io.legado.app.model.ReadManga
|
||||||
import io.legado.app.model.localBook.LocalBook
|
import io.legado.app.model.localBook.LocalBook
|
||||||
import io.legado.app.model.webBook.WebBook
|
import io.legado.app.model.webBook.WebBook
|
||||||
@@ -48,6 +46,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig as GlobalReadConfig
|
||||||
|
|
||||||
class ReadMangaViewModel(
|
class ReadMangaViewModel(
|
||||||
application: Application,
|
application: Application,
|
||||||
@@ -112,7 +111,7 @@ class ReadMangaViewModel(
|
|||||||
// 有章节跳转不同步阅读进度
|
// 有章节跳转不同步阅读进度
|
||||||
ReadManga.chapterChanged = false
|
ReadManga.chapterChanged = false
|
||||||
} else if (!isSameBook) {
|
} else if (!isSameBook) {
|
||||||
if (AppConfig.syncBookProgressPlus) {
|
if (GlobalReadConfig.syncBookProgressPlus) {
|
||||||
ReadManga.syncProgress(
|
ReadManga.syncProgress(
|
||||||
{ progress -> ReadManga.mCallback?.sureNewProgress(progress) })
|
{ progress -> ReadManga.mCallback?.sureNewProgress(progress) })
|
||||||
} else {
|
} else {
|
||||||
@@ -195,7 +194,7 @@ class ReadMangaViewModel(
|
|||||||
* 自动换源
|
* 自动换源
|
||||||
*/
|
*/
|
||||||
private fun autoChangeSource(name: String, author: String) {
|
private fun autoChangeSource(name: String, author: String) {
|
||||||
if (!AppConfig.autoChangeSource) return
|
if (!GlobalReadConfig.autoChangeSource) return
|
||||||
execute {
|
execute {
|
||||||
val sources = appDb.bookSourceDao.allTextEnabledPart
|
val sources = appDb.bookSourceDao.allTextEnabledPart
|
||||||
flow {
|
flow {
|
||||||
@@ -246,7 +245,7 @@ class ReadMangaViewModel(
|
|||||||
book: Book,
|
book: Book,
|
||||||
alertSync: ((progress: BookProgress) -> Unit)? = null
|
alertSync: ((progress: BookProgress) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
if (!AppConfig.syncBookProgress) return
|
if (!GlobalReadConfig.syncBookProgress) return
|
||||||
execute {
|
execute {
|
||||||
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
|
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
|
||||||
}.onError {
|
}.onError {
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import androidx.core.view.isVisible
|
|||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.databinding.ViewMangaMenuBinding
|
import io.legado.app.databinding.ViewMangaMenuBinding
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.source.getSourceType
|
import io.legado.app.help.source.getSourceType
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.model.ReadManga
|
import io.legado.app.model.ReadManga
|
||||||
import io.legado.app.ui.browser.WebViewActivity
|
import io.legado.app.ui.browser.WebViewActivity
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.activity
|
import io.legado.app.utils.activity
|
||||||
import io.legado.app.utils.applyNavigationBarPadding
|
import io.legado.app.utils.applyNavigationBarPadding
|
||||||
import io.legado.app.utils.gone
|
import io.legado.app.utils.gone
|
||||||
@@ -112,13 +112,13 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
// val brightnessBackground = GradientDrawable()
|
// val brightnessBackground = GradientDrawable()
|
||||||
// brightnessBackground.cornerRadius = 5F.dpToPx()
|
// brightnessBackground.cornerRadius = 5F.dpToPx()
|
||||||
// //brightnessBackground.setColor(ColorUtils.adjustAlpha(bgColor, 0.5f))
|
// //brightnessBackground.setColor(ColorUtils.adjustAlpha(bgColor, 0.5f))
|
||||||
// if (AppConfig.isEInkMode) {
|
// if (ReadConfig.isEInkMode) {
|
||||||
// titleBar.setBackgroundResource(R.drawable.bg_eink_border_bottom)
|
// titleBar.setBackgroundResource(R.drawable.bg_eink_border_bottom)
|
||||||
// bottomMenu.setBackgroundResource(R.drawable.bg_eink_border_top)
|
// bottomMenu.setBackgroundResource(R.drawable.bg_eink_border_top)
|
||||||
// } else {
|
// } else {
|
||||||
// //bottomMenu.setBackgroundColor(bgColor)
|
// //bottomMenu.setBackgroundColor(bgColor)
|
||||||
// }
|
// }
|
||||||
if (AppConfig.showReadTitleBarAddition) {
|
if (ReadConfig.showReadTitleAddition) {
|
||||||
titleBarAddition.visible()
|
titleBarAddition.visible()
|
||||||
} else {
|
} else {
|
||||||
titleBarAddition.gone()
|
titleBarAddition.gone()
|
||||||
@@ -134,7 +134,7 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
menuTopOut.setAnimationListener(menuOutListener)
|
menuTopOut.setAnimationListener(menuOutListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runMenuOut(anim: Boolean = !AppConfig.isEInkMode) {
|
fun runMenuOut(anim: Boolean = !ReadConfig.isEInkMode) {
|
||||||
if (isMenuOutAnimating) {
|
if (isMenuOutAnimating) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runMenuIn(anim: Boolean = !AppConfig.isEInkMode) {
|
fun runMenuIn(anim: Boolean = !ReadConfig.isEInkMode) {
|
||||||
this.visible()
|
this.visible()
|
||||||
binding.titleBar.visible()
|
binding.titleBar.visible()
|
||||||
binding.bottomMenu.visible()
|
binding.bottomMenu.visible()
|
||||||
@@ -181,7 +181,7 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
callBack.openBookInfoActivity()
|
callBack.openBookInfoActivity()
|
||||||
}
|
}
|
||||||
val chapterViewClickListener = OnClickListener {
|
val chapterViewClickListener = OnClickListener {
|
||||||
if (AppConfig.readUrlInBrowser) {
|
if (ReadConfig.readUrlInBrowser) {
|
||||||
context.openUrl(tvChapterUrl.text.toString().substringBefore(",{"))
|
context.openUrl(tvChapterUrl.text.toString().substringBefore(",{"))
|
||||||
} else {
|
} else {
|
||||||
context.startActivity<WebViewActivity> {
|
context.startActivity<WebViewActivity> {
|
||||||
@@ -199,10 +199,10 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
context.alert(R.string.open_fun) {
|
context.alert(R.string.open_fun) {
|
||||||
setMessage(R.string.use_browser_open)
|
setMessage(R.string.use_browser_open)
|
||||||
okButton {
|
okButton {
|
||||||
AppConfig.readUrlInBrowser = true
|
ReadConfig.readUrlInBrowser = true
|
||||||
}
|
}
|
||||||
noButton {
|
noButton {
|
||||||
AppConfig.readUrlInBrowser = false
|
ReadConfig.readUrlInBrowser = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -196,6 +196,10 @@ data class ReadMenuConfig(
|
|||||||
val readMenuCustomIcons: ImmutableMap<String, String> = persistentMapOf(),
|
val readMenuCustomIcons: ImmutableMap<String, String> = persistentMapOf(),
|
||||||
val titleBarButtons: ImmutableList<ReadBookButtonConfigItem> = persistentListOf(),
|
val titleBarButtons: ImmutableList<ReadBookButtonConfigItem> = persistentListOf(),
|
||||||
val bottomBarButtons: ImmutableList<ReadBookButtonConfigItem> = persistentListOf(),
|
val bottomBarButtons: ImmutableList<ReadBookButtonConfigItem> = persistentListOf(),
|
||||||
|
val showBrightnessView: String = "1",
|
||||||
|
val brightnessVwPos: String = "1",
|
||||||
|
val readBrightness: Int = 100,
|
||||||
|
val brightnessAuto: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -300,7 +304,7 @@ sealed interface ReadBookIntent {
|
|||||||
|
|
||||||
// Brightness
|
// Brightness
|
||||||
data class SetBrightness(val value: Int) : ReadBookIntent
|
data class SetBrightness(val value: Int) : ReadBookIntent
|
||||||
data object ToggleBrightnessAuto : ReadBookIntent
|
data class ToggleBrightnessAuto(val auto: Boolean) : ReadBookIntent
|
||||||
|
|
||||||
// Seek bar jump
|
// Seek bar jump
|
||||||
data class SeekToChapter(val index: Int) : ReadBookIntent
|
data class SeekToChapter(val index: Int) : ReadBookIntent
|
||||||
@@ -526,7 +530,7 @@ sealed interface ReadBookEffect {
|
|||||||
data object CancelSelect : ReadBookEffect
|
data object CancelSelect : ReadBookEffect
|
||||||
data object UpSystemUiVisibility : ReadBookEffect
|
data object UpSystemUiVisibility : ReadBookEffect
|
||||||
data class SetBrightness(val value: Int) : ReadBookEffect
|
data class SetBrightness(val value: Int) : ReadBookEffect
|
||||||
data object ToggleBrightnessAuto : ReadBookEffect
|
data class ToggleBrightnessAuto(val auto: Boolean, val value: Int) : ReadBookEffect
|
||||||
|
|
||||||
// Read aloud / auto page
|
// Read aloud / auto page
|
||||||
data object ToggleReadAloud : ReadBookEffect
|
data object ToggleReadAloud : ReadBookEffect
|
||||||
@@ -535,7 +539,15 @@ sealed interface ReadBookEffect {
|
|||||||
|
|
||||||
// Search
|
// Search
|
||||||
data class OpenSearchActivity(val word: String?, val bookUrl: String) : ReadBookEffect
|
data class OpenSearchActivity(val word: String?, val bookUrl: String) : ReadBookEffect
|
||||||
data class NavigateToSearchResult(val result: SearchResult) : ReadBookEffect
|
data class NavigateToSearchResult(
|
||||||
|
val result: SearchResult,
|
||||||
|
val chapterIndex: Int,
|
||||||
|
val pageIndex: Int,
|
||||||
|
val lineIndex: Int,
|
||||||
|
val startCharIndex: Int,
|
||||||
|
val endLineIndex: Int,
|
||||||
|
val endCharIndex: Int,
|
||||||
|
) : ReadBookEffect
|
||||||
data object ExitSearch : ReadBookEffect
|
data object ExitSearch : ReadBookEffect
|
||||||
|
|
||||||
// Source actions
|
// Source actions
|
||||||
@@ -1040,7 +1052,15 @@ sealed interface ConfigUpdate {
|
|||||||
data class UseZhLayout(val value: Boolean) : ConfigUpdate {
|
data class UseZhLayout(val value: Boolean) : ConfigUpdate {
|
||||||
override val actions = setOf(ConfigUpdateAction.ReloadContent)
|
override val actions = setOf(ConfigUpdateAction.ReloadContent)
|
||||||
}
|
}
|
||||||
data class ShowBrightnessView(val value: Boolean) : ConfigUpdate {
|
data class ShowBrightnessView(val value: String) : ConfigUpdate {
|
||||||
|
override val actions = emptySet<ConfigUpdateAction>()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class BrightnessVwPos(val value: String) : ConfigUpdate {
|
||||||
|
override val actions = emptySet<ConfigUpdateAction>()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class BrightnessAuto(val value: Boolean) : ConfigUpdate {
|
||||||
override val actions = emptySet<ConfigUpdateAction>()
|
override val actions = emptySet<ConfigUpdateAction>()
|
||||||
}
|
}
|
||||||
data class UseUnderlineGlobal(val value: Boolean) : ConfigUpdate {
|
data class UseUnderlineGlobal(val value: Boolean) : ConfigUpdate {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.view.KeyEvent
|
|||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.HapticFeedbackConstantsCompat
|
import androidx.core.view.HapticFeedbackConstantsCompat
|
||||||
@@ -23,7 +24,6 @@ import io.legado.app.constant.BookType
|
|||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.BookProgress
|
import io.legado.app.data.entities.BookProgress
|
||||||
import io.legado.app.help.TTS
|
import io.legado.app.help.TTS
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.storage.Backup
|
import io.legado.app.help.storage.Backup
|
||||||
import io.legado.app.lib.dialogs.SelectItem
|
import io.legado.app.lib.dialogs.SelectItem
|
||||||
@@ -40,8 +40,10 @@ import io.legado.app.service.BaseReadAloudService
|
|||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
import io.legado.app.ui.book.read.page.ReadView
|
import io.legado.app.ui.book.read.page.ReadView
|
||||||
import io.legado.app.ui.book.read.page.entities.PageDirection
|
import io.legado.app.ui.book.read.page.entities.PageDirection
|
||||||
|
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.login.SourceLoginJsExtensions
|
import io.legado.app.ui.login.SourceLoginJsExtensions
|
||||||
import io.legado.app.ui.widget.PopupAction
|
import io.legado.app.ui.widget.PopupAction
|
||||||
import io.legado.app.utils.ColorUtils
|
import io.legado.app.utils.ColorUtils
|
||||||
@@ -60,6 +62,7 @@ import io.legado.app.utils.throttle
|
|||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import io.legado.app.utils.visible
|
import io.legado.app.utils.visible
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,7 +344,7 @@ class ReadBookController(
|
|||||||
r.textMenuPosition.x = x
|
r.textMenuPosition.x = x
|
||||||
r.textMenuPosition.y = top
|
r.textMenuPosition.y = top
|
||||||
|
|
||||||
if (AppConfig.selectVibrator) {
|
if (ReadConfig.selectVibrator) {
|
||||||
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
|
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +354,7 @@ class ReadBookController(
|
|||||||
r.cursorRight.x = x
|
r.cursorRight.x = x
|
||||||
r.cursorRight.y = y
|
r.cursorRight.y = y
|
||||||
r.cursorRight.visible(true)
|
r.cursorRight.visible(true)
|
||||||
if (AppConfig.selectVibrator) {
|
if (ReadConfig.selectVibrator) {
|
||||||
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
|
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -616,7 +619,7 @@ class ReadBookController(
|
|||||||
is ReadBookEffect.LongToast -> activity.longToastOnUi(effect.message)
|
is ReadBookEffect.LongToast -> activity.longToastOnUi(effect.message)
|
||||||
is ReadBookEffect.SetBrightness -> {
|
is ReadBookEffect.SetBrightness -> {
|
||||||
val lp = activity.window.attributes
|
val lp = activity.window.attributes
|
||||||
lp.screenBrightness = effect.value / 255f
|
lp.screenBrightness = effect.value / 100f
|
||||||
activity.window.attributes = lp
|
activity.window.attributes = lp
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,7 +685,14 @@ class ReadBookController(
|
|||||||
screenOffTimerStart()
|
screenOffTimerStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookEffect.ToggleBrightnessAuto -> { /* TODO */
|
is ReadBookEffect.ToggleBrightnessAuto -> {
|
||||||
|
val lp = activity.window.attributes
|
||||||
|
if (effect.auto) {
|
||||||
|
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||||
|
} else {
|
||||||
|
lp.screenBrightness = effect.value / 100f
|
||||||
|
}
|
||||||
|
activity.window.attributes = lp
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Phase 4: Activity-dependent effects ──
|
// ── Phase 4: Activity-dependent effects ──
|
||||||
@@ -695,15 +705,48 @@ class ReadBookController(
|
|||||||
|
|
||||||
is ReadBookEffect.TextActionSpeak -> speak(effect.text)
|
is ReadBookEffect.TextActionSpeak -> speak(effect.text)
|
||||||
is ReadBookEffect.NavigateToSearchResult -> {
|
is ReadBookEffect.NavigateToSearchResult -> {
|
||||||
// Navigate handled by ReadView — no external callback needed
|
if (effect.pageIndex < 0) {
|
||||||
|
// Chapter not loaded — open it, then mark in the success callback
|
||||||
|
ReadBook.openChapter(effect.chapterIndex) {
|
||||||
|
val tc = ReadBook.curTextChapter ?: return@openChapter
|
||||||
|
val query = effect.result.query
|
||||||
|
val pos = viewModel.searchResultPositions(tc, effect.result, query)
|
||||||
|
val lineIndex = pos[1]
|
||||||
|
val charIndex = pos[2]
|
||||||
|
val addLine = pos[3]
|
||||||
|
val charIndex2 = pos[4]
|
||||||
|
val queryLength = query.length
|
||||||
|
val endLineIndex = lineIndex + addLine.coerceAtLeast(0)
|
||||||
|
val endCharIndex = if (addLine == 0) {
|
||||||
|
charIndex + queryLength
|
||||||
|
} else {
|
||||||
|
charIndex2 + 1
|
||||||
|
}
|
||||||
|
activity.lifecycleScope.launch(Main) {
|
||||||
|
navigatePageByPos(tc, pos[0])
|
||||||
|
markSearchResultOnPage(
|
||||||
|
intArrayOf(
|
||||||
|
pos[0], lineIndex, charIndex, endLineIndex, endCharIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Same chapter — navigate to page, then mark
|
||||||
|
val tc = ReadBook.curTextChapter ?: return
|
||||||
|
navigatePageByPos(tc, effect.pageIndex)
|
||||||
|
markSearchResultOnPage(
|
||||||
|
intArrayOf(
|
||||||
|
effect.pageIndex, effect.lineIndex,
|
||||||
|
effect.startCharIndex, effect.endLineIndex, effect.endCharIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookEffect.ExitSearch -> {
|
is ReadBookEffect.ExitSearch -> {
|
||||||
if (viewModel.uiState.value.isShowingSearchResult) {
|
|
||||||
viewModel.onIntent(ReadBookIntent.SetShowingSearchResult(false))
|
|
||||||
ReadBook.clearSearchResult()
|
ReadBook.clearSearchResult()
|
||||||
refs?.readView?.cancelSelect(true)
|
refs?.readView?.cancelSelect(clearSearchResult = true)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookEffect.SyncBookProgress -> {
|
is ReadBookEffect.SyncBookProgress -> {
|
||||||
@@ -935,17 +978,17 @@ class ReadBookController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mouseWheelPage(direction: PageDirection) {
|
override fun mouseWheelPage(direction: PageDirection) {
|
||||||
if (menuLayoutIsVisible || !AppConfig.mouseWheelPage) {
|
if (menuLayoutIsVisible || !ReadConfig.mouseWheelPage) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keyPageDebounce(direction, mouseWheel = true, longPress = false)
|
keyPageDebounce(direction, mouseWheel = true, longPress = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
|
private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
|
||||||
if (!AppConfig.volumeKeyPage) {
|
if (!ReadConfig.volumeKeyPage) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!AppConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
|
if (!ReadConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
handleKeyPage(direction, longPress)
|
handleKeyPage(direction, longPress)
|
||||||
@@ -953,7 +996,7 @@ class ReadBookController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
|
override fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
|
||||||
if (AppConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
|
if (ReadConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
|
||||||
keyPage(direction)
|
keyPage(direction)
|
||||||
} else {
|
} else {
|
||||||
keyPageDebounce(direction, longPress = longPress)
|
keyPageDebounce(direction, longPress = longPress)
|
||||||
@@ -1057,7 +1100,7 @@ class ReadBookController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setOrientation() {
|
fun setOrientation() {
|
||||||
when (AppConfig.screenOrientation) {
|
when (ReadConfig.screenOrientation) {
|
||||||
"0" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
"0" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
||||||
"1" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
"1" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
"2" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
"2" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
@@ -1066,4 +1109,38 @@ class ReadBookController(
|
|||||||
"5" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
"5" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Search result navigation helpers ─────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the page at [pageIndex] within the current chapter.
|
||||||
|
*/
|
||||||
|
private fun navigatePageByPos(textChapter: TextChapter, pageIndex: Int) {
|
||||||
|
val pagePos = textChapter.getReadLength(pageIndex)
|
||||||
|
if (ReadBook.durChapterPos != pagePos) {
|
||||||
|
ReadBook.durChapterPos = pagePos
|
||||||
|
ReadBook.callBack?.upContent()
|
||||||
|
ReadBook.callBack?.cancelSelect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark search result columns on the current page for highlighting.
|
||||||
|
* @param pos array of [pageIndex, lineIndex, startCharIndex, endLineIndex, endCharIndex]
|
||||||
|
*/
|
||||||
|
private fun markSearchResultOnPage(pos: IntArray) {
|
||||||
|
val readView = refs?.readView ?: return
|
||||||
|
val lineIndex = pos[1]
|
||||||
|
val startCharIndex = pos[2]
|
||||||
|
val endLineIndex = pos[3]
|
||||||
|
val endCharIndex = pos[4]
|
||||||
|
readView.cancelSelect(clearSearchResult = true)
|
||||||
|
isSelectingSearchResult = true
|
||||||
|
try {
|
||||||
|
readView.curPage.selectStartMoveIndex(0, lineIndex, startCharIndex)
|
||||||
|
readView.curPage.selectEndMoveIndex(0, endLineIndex, endCharIndex)
|
||||||
|
} finally {
|
||||||
|
isSelectingSearchResult = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,11 +6,15 @@ import androidx.compose.animation.AnimatedContent
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.SizeTransform
|
import androidx.compose.animation.SizeTransform
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
@@ -27,6 +31,7 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
@@ -54,10 +59,14 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.automirrored.filled.Login
|
import androidx.compose.material.icons.automirrored.filled.Login
|
||||||
import androidx.compose.material.icons.filled.Block
|
import androidx.compose.material.icons.filled.Block
|
||||||
|
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Payment
|
import androidx.compose.material.icons.filled.Payment
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.SwapHoriz
|
import androidx.compose.material.icons.filled.SwapHoriz
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
@@ -126,7 +135,6 @@ import io.legado.app.R
|
|||||||
import io.legado.app.constant.ReadMenuBlurMode
|
import io.legado.app.constant.ReadMenuBlurMode
|
||||||
import io.legado.app.constant.ReadMenuBlurStyle
|
import io.legado.app.constant.ReadMenuBlurStyle
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadStyleResolver
|
import io.legado.app.help.config.ReadStyleResolver
|
||||||
import io.legado.app.ui.animation.DampedDragAnimation
|
import io.legado.app.ui.animation.DampedDragAnimation
|
||||||
import io.legado.app.ui.animation.InteractiveHighlight
|
import io.legado.app.ui.animation.InteractiveHighlight
|
||||||
@@ -137,9 +145,11 @@ import io.legado.app.ui.book.read.sheet.ReadMenuButtonInfo
|
|||||||
import io.legado.app.ui.book.read.sheet.ReadStyleContent
|
import io.legado.app.ui.book.read.sheet.ReadStyleContent
|
||||||
import io.legado.app.ui.book.read.sheet.ReadStyleTextTitleContent
|
import io.legado.app.ui.book.read.sheet.ReadStyleTextTitleContent
|
||||||
import io.legado.app.ui.book.read.sheet.readMenuButtonInfos
|
import io.legado.app.ui.book.read.sheet.readMenuButtonInfos
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.theme.hazeStyle.HazeLegado
|
import io.legado.app.ui.theme.hazeStyle.HazeLegado
|
||||||
import io.legado.app.ui.widget.components.AppSlider
|
import io.legado.app.ui.widget.components.AppSlider
|
||||||
|
import io.legado.app.ui.widget.components.AppVerticalSlider
|
||||||
import io.legado.app.ui.widget.components.bookmark.BookmarkEditContent
|
import io.legado.app.ui.widget.components.bookmark.BookmarkEditContent
|
||||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
@@ -168,12 +178,29 @@ fun ReadBookMenuBar(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val currentRoute = state.menuState.currentRoute
|
val currentRoute = state.menuState.currentRoute
|
||||||
|
val searchMenuVisible = state.isShowingSearchResult &&
|
||||||
|
state.searchMenuVisible &&
|
||||||
|
!state.menuVisible
|
||||||
|
val contentTarget = if (searchMenuVisible) {
|
||||||
|
ReadBookMenuContent.Search
|
||||||
|
} else {
|
||||||
|
ReadBookMenuContent.Route(currentRoute)
|
||||||
|
}
|
||||||
val dialogLikeRoute = currentRoute == ReadBookMenuRoute.PaddingConfig
|
val dialogLikeRoute = currentRoute == ReadBookMenuRoute.PaddingConfig
|
||||||
|
var readStylePage by remember { mutableIntStateOf(0) }
|
||||||
|
LaunchedEffect(currentRoute) {
|
||||||
|
if (currentRoute != ReadBookMenuRoute.ReadStyle) {
|
||||||
|
readStylePage = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val hideTopBar = dialogLikeRoute ||
|
||||||
|
currentRoute == ReadBookMenuRoute.ReadStyle && readStylePage >= 1 ||
|
||||||
|
currentRoute == ReadBookMenuRoute.TextTitle
|
||||||
val menuColors = readMenuColors()
|
val menuColors = readMenuColors()
|
||||||
|
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = state.menuVisible,
|
visible = state.menuVisible || searchMenuVisible,
|
||||||
enter = fadeIn(),
|
enter = fadeIn(),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
) {
|
) {
|
||||||
@@ -183,13 +210,19 @@ fun ReadBookMenuBar(
|
|||||||
.clickable(
|
.clickable(
|
||||||
indication = null,
|
indication = null,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
) { onIntent(ReadBookIntent.HideMenu) }
|
) {
|
||||||
|
if (searchMenuVisible) {
|
||||||
|
onIntent(ReadBookIntent.HideSearchMenu)
|
||||||
|
} else {
|
||||||
|
onIntent(ReadBookIntent.HideMenu)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top title bar + floating icon row (top positions)
|
// Top title bar + floating icon row (top positions)
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = state.menuVisible && !dialogLikeRoute,
|
visible = state.menuVisible && !hideTopBar,
|
||||||
enter = slideInVertically(initialOffsetY = { -it }) + fadeIn(),
|
enter = slideInVertically(initialOffsetY = { -it }) + fadeIn(),
|
||||||
exit = slideOutVertically(targetOffsetY = { -it }) + fadeOut(),
|
exit = slideOutVertically(targetOffsetY = { -it }) + fadeOut(),
|
||||||
modifier = Modifier.align(Alignment.TopCenter),
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
@@ -218,15 +251,97 @@ fun ReadBookMenuBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vertical brightness bar (right or left side)
|
||||||
|
val brightnessMode = state.menuConfig.showBrightnessView
|
||||||
|
val brightnessVwPos = state.menuConfig.brightnessVwPos
|
||||||
|
val brightnessIsLeft = brightnessVwPos == "0"
|
||||||
|
val brightnessShape = RoundedCornerShape(40.dp)
|
||||||
|
val useBrightnessHaze = readMenuBottomBarHazeEnabled(
|
||||||
|
hazeState = hazeState,
|
||||||
|
menuConfig = state.menuConfig,
|
||||||
|
isFloating = false,
|
||||||
|
)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = brightnessMode == "2" && state.menuVisible,
|
||||||
|
enter = slideInHorizontally(
|
||||||
|
initialOffsetX = { if (brightnessIsLeft) -it else it }
|
||||||
|
) + fadeIn(),
|
||||||
|
exit = slideOutHorizontally(
|
||||||
|
targetOffsetX = { if (brightnessIsLeft) -it else it }
|
||||||
|
) + fadeOut(),
|
||||||
|
modifier = Modifier.align(
|
||||||
|
if (brightnessIsLeft) Alignment.CenterStart else Alignment.CenterEnd
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(
|
||||||
|
start = if (brightnessIsLeft) 8.dp else 0.dp,
|
||||||
|
end = if (brightnessIsLeft) 0.dp else 8.dp,
|
||||||
|
),
|
||||||
|
contentAlignment = if (brightnessIsLeft) Alignment.CenterStart else Alignment.CenterEnd,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = if (useBrightnessHaze && hazeState != null) {
|
||||||
|
Modifier.readMenuBottomBarHazeEffect(
|
||||||
|
state = hazeState,
|
||||||
|
colors = menuColors,
|
||||||
|
shape = brightnessShape,
|
||||||
|
menuConfig = state.menuConfig,
|
||||||
|
progressive = false,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
},
|
||||||
|
shape = brightnessShape,
|
||||||
|
color = if (useBrightnessHaze) {
|
||||||
|
Color.Transparent
|
||||||
|
} else {
|
||||||
|
menuColors.background.copy(
|
||||||
|
alpha = state.menuConfig.readMenuBlurAlpha.coerceIn(0, 100) / 100f
|
||||||
|
)
|
||||||
|
},
|
||||||
|
contentColor = LegadoTheme.colorScheme.onSurface,
|
||||||
|
) {
|
||||||
|
BrightnessBar(
|
||||||
|
brightness = state.menuConfig.readBrightness,
|
||||||
|
onBrightnessChange = { value ->
|
||||||
|
onIntent(ReadBookIntent.SetBrightness(value))
|
||||||
|
},
|
||||||
|
brightnessAuto = state.menuConfig.brightnessAuto,
|
||||||
|
onToggleAuto = {
|
||||||
|
onIntent(ReadBookIntent.ToggleBrightnessAuto(!state.menuConfig.brightnessAuto))
|
||||||
|
},
|
||||||
|
onTogglePosition = {
|
||||||
|
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.BrightnessVwPos(if (brightnessIsLeft) "1" else "0")))
|
||||||
|
},
|
||||||
|
vertical = true,
|
||||||
|
colors = menuColors,
|
||||||
|
menuConfig = state.menuConfig,
|
||||||
|
backdrop = backdrop,
|
||||||
|
buttonGlassEnabled = readMenuBottomBarButtonLiquidGlassEnabled(
|
||||||
|
backdrop = backdrop,
|
||||||
|
menuConfig = state.menuConfig,
|
||||||
|
),
|
||||||
|
glassThumbEnabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Bottom menu + floating icon row (bottom positions)
|
// Bottom menu + floating icon row (bottom positions)
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = state.menuVisible,
|
visible = state.menuVisible || searchMenuVisible,
|
||||||
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
||||||
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
|
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
|
||||||
modifier = Modifier.align(Alignment.BottomCenter),
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
) {
|
) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
if (state.menuConfig.showTitleBarIcons && state.menuConfig.titleBarIconPosition >= 2) {
|
if (state.menuVisible &&
|
||||||
|
state.menuConfig.showTitleBarIcons &&
|
||||||
|
state.menuConfig.titleBarIconPosition >= 2
|
||||||
|
) {
|
||||||
FloatingIconRow(
|
FloatingIconRow(
|
||||||
state = state,
|
state = state,
|
||||||
colors = menuColors,
|
colors = menuColors,
|
||||||
@@ -240,29 +355,42 @@ fun ReadBookMenuBar(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
ReadBookMenuSurface(
|
ReadBookMenuSurface(
|
||||||
route = currentRoute,
|
contentTarget = contentTarget,
|
||||||
state = state,
|
state = state,
|
||||||
colors = menuColors,
|
colors = menuColors,
|
||||||
onIntent = onIntent,
|
onIntent = onIntent,
|
||||||
context = context,
|
context = context,
|
||||||
backdrop = backdrop,
|
backdrop = backdrop,
|
||||||
hazeState = hazeState,
|
hazeState = hazeState,
|
||||||
|
readStylePage = readStylePage,
|
||||||
|
onReadStylePageChanged = { readStylePage = it },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed interface ReadBookMenuContent {
|
||||||
|
data object Search : ReadBookMenuContent
|
||||||
|
data class Route(val route: ReadBookMenuRoute) : ReadBookMenuContent
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ReadBookMenuSurface(
|
private fun ReadBookMenuSurface(
|
||||||
route: ReadBookMenuRoute,
|
contentTarget: ReadBookMenuContent,
|
||||||
state: ReadBookUiState,
|
state: ReadBookUiState,
|
||||||
colors: ReadMenuColors,
|
colors: ReadMenuColors,
|
||||||
onIntent: (ReadBookIntent) -> Unit,
|
onIntent: (ReadBookIntent) -> Unit,
|
||||||
context: Context,
|
context: Context,
|
||||||
backdrop: Backdrop?,
|
backdrop: Backdrop?,
|
||||||
hazeState: HazeState?,
|
hazeState: HazeState?,
|
||||||
|
readStylePage: Int,
|
||||||
|
onReadStylePageChanged: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val route = when (contentTarget) {
|
||||||
|
ReadBookMenuContent.Search -> ReadBookMenuRoute.Main
|
||||||
|
is ReadBookMenuContent.Route -> contentTarget.route
|
||||||
|
}
|
||||||
val expanded = route != ReadBookMenuRoute.Main
|
val expanded = route != ReadBookMenuRoute.Main
|
||||||
val dialogLikeRoute = route == ReadBookMenuRoute.PaddingConfig
|
val dialogLikeRoute = route == ReadBookMenuRoute.PaddingConfig
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
@@ -272,6 +400,18 @@ private fun ReadBookMenuSurface(
|
|||||||
targetValue = if (dialogLikeRoute) 1f else 0f,
|
targetValue = if (dialogLikeRoute) 1f else 0f,
|
||||||
label = "ReadBookMenuMorph",
|
label = "ReadBookMenuMorph",
|
||||||
)
|
)
|
||||||
|
val headerFooterLift by animateDpAsState(
|
||||||
|
targetValue = if (route == ReadBookMenuRoute.ReadStyle && readStylePage == 2) {
|
||||||
|
72.dp
|
||||||
|
} else {
|
||||||
|
0.dp
|
||||||
|
},
|
||||||
|
animationSpec = tween(
|
||||||
|
durationMillis = 280,
|
||||||
|
easing = LinearOutSlowInEasing,
|
||||||
|
),
|
||||||
|
label = "ReadBookMenuHeaderFooterLift",
|
||||||
|
)
|
||||||
val maxHeight = with(density) {
|
val maxHeight = with(density) {
|
||||||
windowSize.height.toDp() * 0.64f
|
windowSize.height.toDp() * 0.64f
|
||||||
}
|
}
|
||||||
@@ -377,7 +517,8 @@ private fun ReadBookMenuSurface(
|
|||||||
.heightIn(max = maxHeight)
|
.heightIn(max = maxHeight)
|
||||||
.onSizeChanged { surfaceHeightPx = it.height }
|
.onSizeChanged { surfaceHeightPx = it.height }
|
||||||
.offset {
|
.offset {
|
||||||
val liftPx = ((windowSize.height - surfaceHeightPx) / 2f) * morphProgress
|
val dialogLiftPx = ((windowSize.height - surfaceHeightPx) / 2f) * morphProgress
|
||||||
|
val liftPx = dialogLiftPx + headerFooterLift.toPx()
|
||||||
IntOffset(x = 0, y = -liftPx.roundToInt())
|
IntOffset(x = 0, y = -liftPx.roundToInt())
|
||||||
}
|
}
|
||||||
.then(
|
.then(
|
||||||
@@ -433,15 +574,26 @@ private fun ReadBookMenuSurface(
|
|||||||
contentColor = colors.content
|
contentColor = colors.content
|
||||||
) {
|
) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = route,
|
targetState = contentTarget,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
(slideInVertically { it / 4 } + fadeIn())
|
(slideInVertically { it / 4 } + fadeIn())
|
||||||
.togetherWith(slideOutVertically { -it / 4 } + fadeOut())
|
.togetherWith(slideOutVertically { -it / 4 } + fadeOut())
|
||||||
.using(SizeTransform(clip = true))
|
.using(SizeTransform(clip = true))
|
||||||
},
|
},
|
||||||
label = "ReadBookMenuRoute",
|
label = "ReadBookMenuRoute",
|
||||||
) { targetRoute ->
|
) { target ->
|
||||||
when (targetRoute) {
|
when (target) {
|
||||||
|
ReadBookMenuContent.Search -> {
|
||||||
|
SearchBottomMenuContent(
|
||||||
|
state = state,
|
||||||
|
colors = colors,
|
||||||
|
onIntent = onIntent,
|
||||||
|
bottomPadding = if (extendSurfaceToNavigationBar) navBarHeight + 16.dp else 16.dp,
|
||||||
|
surfaceEffectEnabled = useLiquidGlass || useHaze,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ReadBookMenuContent.Route -> when (val targetRoute = target.route) {
|
||||||
ReadBookMenuRoute.Main -> {
|
ReadBookMenuRoute.Main -> {
|
||||||
MenuBottomBar(
|
MenuBottomBar(
|
||||||
state = state,
|
state = state,
|
||||||
@@ -482,6 +634,7 @@ private fun ReadBookMenuSurface(
|
|||||||
onToggleDayNight = {
|
onToggleDayNight = {
|
||||||
onIntent(ReadBookIntent.ToggleDayNight)
|
onIntent(ReadBookIntent.ToggleDayNight)
|
||||||
},
|
},
|
||||||
|
onPageChanged = onReadStylePageChanged,
|
||||||
readMenuCustomIcons = state.menuConfig.readMenuCustomIcons,
|
readMenuCustomIcons = state.menuConfig.readMenuCustomIcons,
|
||||||
bottomBarButtons = state.menuConfig.bottomBarButtons,
|
bottomBarButtons = state.menuConfig.bottomBarButtons,
|
||||||
onIntent = onIntent,
|
onIntent = onIntent,
|
||||||
@@ -600,6 +753,7 @@ private fun ReadBookMenuSurface(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -663,7 +817,7 @@ private fun MenuTitleBar(
|
|||||||
backdrop: Backdrop?,
|
backdrop: Backdrop?,
|
||||||
hazeState: HazeState?,
|
hazeState: HazeState?,
|
||||||
) {
|
) {
|
||||||
val titleBarMode = AppConfig.titleBarMode
|
val titleBarMode = ReadConfig.titleBarMode
|
||||||
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -1404,6 +1558,159 @@ private fun OverflowDropdownMenu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SearchBottomMenuContent(
|
||||||
|
state: ReadBookUiState,
|
||||||
|
colors: ReadMenuColors,
|
||||||
|
onIntent: (ReadBookIntent) -> Unit,
|
||||||
|
bottomPadding: Dp = 0.dp,
|
||||||
|
surfaceEffectEnabled: Boolean = false,
|
||||||
|
) {
|
||||||
|
val totalResults = state.searchResultList.size
|
||||||
|
val currentIndex = state.searchResultIndex.coerceIn(0, (totalResults - 1).coerceAtLeast(0))
|
||||||
|
val percent = if (totalResults > 0) {
|
||||||
|
((currentIndex + 1) * 100 / totalResults)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
if (surfaceEffectEnabled) Color.Transparent else colors.background.copy(
|
||||||
|
alpha = state.menuConfig.readMenuBlurAlpha.coerceIn(0, 100) / 100f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.windowInsetsPadding(
|
||||||
|
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
|
||||||
|
)
|
||||||
|
.padding(top = 12.dp, bottom = bottomPadding)
|
||||||
|
.animateContentSize(),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
SearchInfoPill(
|
||||||
|
modifier = Modifier.weight(0.7f),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = if (totalResults > 0) "${currentIndex + 1} / $totalResults" else "0 / 0",
|
||||||
|
style = LegadoTheme.typography.titleSmall,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "$percent%",
|
||||||
|
style = LegadoTheme.typography.bodySmall,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchInfoPill(
|
||||||
|
modifier = Modifier.weight(1.3f),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = state.chapterName.ifBlank { "-" },
|
||||||
|
style = LegadoTheme.typography.bodyMedium,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
SearchMenuActionButton(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
icon = Icons.Default.Search,
|
||||||
|
text = "搜索内容",
|
||||||
|
onClick = { onIntent(ReadBookIntent.OpenSearch(null)) },
|
||||||
|
)
|
||||||
|
SearchMenuActionButton(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
icon = Icons.Default.Menu,
|
||||||
|
text = "主菜单",
|
||||||
|
onClick = {
|
||||||
|
onIntent(ReadBookIntent.HideSearchMenu)
|
||||||
|
onIntent(ReadBookIntent.ShowMenu)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
SearchMenuActionButton(
|
||||||
|
modifier = Modifier.weight(0.55f),
|
||||||
|
icon = Icons.Default.Close,
|
||||||
|
text = "退出",
|
||||||
|
onClick = { onIntent(ReadBookIntent.ExitSearch) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SearchInfoPill(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.height(40.dp)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(LegadoTheme.colorScheme.surfaceContainerLow)
|
||||||
|
.padding(horizontal = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SearchMenuActionButton(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
icon: ImageVector,
|
||||||
|
text: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.height(44.dp)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(LegadoTheme.colorScheme.surfaceContainerLow)
|
||||||
|
.clickable(role = Role.Button, onClick = onClick)
|
||||||
|
.padding(horizontal = 10.dp),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = LegadoTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = LegadoTheme.typography.labelMediumEmphasized,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MenuBottomBar(
|
private fun MenuBottomBar(
|
||||||
state: ReadBookUiState,
|
state: ReadBookUiState,
|
||||||
@@ -1431,7 +1738,7 @@ private fun MenuBottomBar(
|
|||||||
val target = value.roundToInt().coerceIn(0, seekMax)
|
val target = value.roundToInt().coerceIn(0, seekMax)
|
||||||
sliderDragging = false
|
sliderDragging = false
|
||||||
sliderValue = target.toFloat()
|
sliderValue = target.toFloat()
|
||||||
val behavior = AppConfig.progressBarBehavior
|
val behavior = ReadConfig.progressBarBehavior
|
||||||
if (behavior == "page") {
|
if (behavior == "page") {
|
||||||
onIntent(ReadBookIntent.SkipToPage(target))
|
onIntent(ReadBookIntent.SkipToPage(target))
|
||||||
} else {
|
} else {
|
||||||
@@ -1459,6 +1766,27 @@ private fun MenuBottomBar(
|
|||||||
.padding(top = 8.dp, bottom = contentBottomPadding)
|
.padding(top = 8.dp, bottom = contentBottomPadding)
|
||||||
.animateContentSize(),
|
.animateContentSize(),
|
||||||
) {
|
) {
|
||||||
|
if (state.menuConfig.showBrightnessView == "1") {
|
||||||
|
BrightnessBar(
|
||||||
|
brightness = state.menuConfig.readBrightness,
|
||||||
|
onBrightnessChange = { value ->
|
||||||
|
onIntent(ReadBookIntent.SetBrightness(value))
|
||||||
|
},
|
||||||
|
brightnessAuto = state.menuConfig.brightnessAuto,
|
||||||
|
onToggleAuto = {
|
||||||
|
onIntent(ReadBookIntent.ToggleBrightnessAuto(!state.menuConfig.brightnessAuto))
|
||||||
|
},
|
||||||
|
onTogglePosition = {},
|
||||||
|
vertical = false,
|
||||||
|
colors = colors,
|
||||||
|
menuConfig = state.menuConfig,
|
||||||
|
backdrop = backdrop,
|
||||||
|
buttonGlassEnabled = buttonGlassEnabled,
|
||||||
|
glassThumbEnabled = buttonGlassEnabled,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
}
|
||||||
|
|
||||||
// Seek bar row: prev + slider + next
|
// Seek bar row: prev + slider + next
|
||||||
AnimatedVisibility(visible = state.menuConfig.readSliderMode != "1") {
|
AnimatedVisibility(visible = state.menuConfig.readSliderMode != "1") {
|
||||||
Row(
|
Row(
|
||||||
@@ -2181,8 +2509,8 @@ private fun Modifier.readMenuLiquidGlass(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.then(if (interactiveHighlight != null) interactiveHighlight.modifier else Modifier)
|
.then(interactiveHighlight?.modifier ?: Modifier)
|
||||||
.then(if (interactiveHighlight != null) interactiveHighlight.gestureModifier else Modifier)
|
.then(interactiveHighlight?.gestureModifier ?: Modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@@ -2288,7 +2616,7 @@ private fun readMenuBottomBarSurfaceBrush(
|
|||||||
private fun readMenuColors(): ReadMenuColors {
|
private fun readMenuColors(): ReadMenuColors {
|
||||||
val themeBackground = LegadoTheme.colorScheme.surfaceContainerHigh
|
val themeBackground = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||||
val themeContent = LegadoTheme.colorScheme.onSurface
|
val themeContent = LegadoTheme.colorScheme.onSurface
|
||||||
return when (AppConfig.readBarStyle) {
|
return when (ReadConfig.readBarStyle) {
|
||||||
1 -> ReadMenuColors(
|
1 -> ReadMenuColors(
|
||||||
background = themeBackground,
|
background = themeBackground,
|
||||||
content = themeContent,
|
content = themeContent,
|
||||||
@@ -2370,3 +2698,116 @@ private fun loadFloatingIcons(
|
|||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BrightnessBar(
|
||||||
|
brightness: Int,
|
||||||
|
onBrightnessChange: (Int) -> Unit,
|
||||||
|
brightnessAuto: Boolean,
|
||||||
|
onToggleAuto: () -> Unit,
|
||||||
|
onTogglePosition: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
vertical: Boolean = false,
|
||||||
|
colors: ReadMenuColors,
|
||||||
|
menuConfig: ReadMenuConfig,
|
||||||
|
backdrop: Backdrop? = null,
|
||||||
|
buttonGlassEnabled: Boolean = false,
|
||||||
|
glassThumbEnabled: Boolean = false,
|
||||||
|
) {
|
||||||
|
var sliderValue by remember { mutableFloatStateOf(brightness.toFloat()) }
|
||||||
|
var sliderDragging by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(brightness) {
|
||||||
|
if (!sliderDragging) {
|
||||||
|
sliderValue = brightness.toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun commitSliderValue(value: Float) {
|
||||||
|
val target = value.roundToInt().coerceIn(0, 100)
|
||||||
|
sliderDragging = false
|
||||||
|
sliderValue = target.toFloat()
|
||||||
|
onBrightnessChange(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertical) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.width(if (buttonGlassEnabled) 64.dp else 56.dp)
|
||||||
|
.padding(vertical = 12.dp, horizontal = 4.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
) {
|
||||||
|
ReadMenuGlassIconButton(
|
||||||
|
onClick = onToggleAuto,
|
||||||
|
icon = Icons.Filled.BrightnessAuto,
|
||||||
|
colors = colors,
|
||||||
|
backdrop = backdrop,
|
||||||
|
menuConfig = menuConfig,
|
||||||
|
glassEnabled = buttonGlassEnabled,
|
||||||
|
selected = brightnessAuto,
|
||||||
|
contentDescription = stringResource(R.string.brightness_follow_system),
|
||||||
|
)
|
||||||
|
AppVerticalSlider(
|
||||||
|
value = sliderValue.coerceIn(0f, 100f),
|
||||||
|
onValueChange = { value ->
|
||||||
|
sliderDragging = true
|
||||||
|
sliderValue = value.coerceIn(0f, 100f)
|
||||||
|
},
|
||||||
|
onValueChangeFinished = {
|
||||||
|
commitSliderValue(sliderValue)
|
||||||
|
},
|
||||||
|
valueRange = 0f..100f,
|
||||||
|
enabled = !brightnessAuto,
|
||||||
|
height = 168.dp,
|
||||||
|
)
|
||||||
|
ReadMenuGlassIconButton(
|
||||||
|
onClick = onTogglePosition,
|
||||||
|
icon = Icons.Filled.SwapHoriz,
|
||||||
|
colors = colors,
|
||||||
|
backdrop = backdrop,
|
||||||
|
menuConfig = menuConfig,
|
||||||
|
glassEnabled = buttonGlassEnabled,
|
||||||
|
contentDescription = stringResource(R.string.brightness_bar_position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val buttonSize = if (buttonGlassEnabled) 48.dp else 40.dp
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
ReadMenuGlassIconButton(
|
||||||
|
onClick = onToggleAuto,
|
||||||
|
icon = Icons.Filled.BrightnessAuto,
|
||||||
|
colors = colors,
|
||||||
|
backdrop = backdrop,
|
||||||
|
menuConfig = menuConfig,
|
||||||
|
glassEnabled = buttonGlassEnabled,
|
||||||
|
selected = brightnessAuto,
|
||||||
|
contentDescription = stringResource(R.string.brightness_follow_system),
|
||||||
|
)
|
||||||
|
ReadMenuSlider(
|
||||||
|
value = sliderValue.coerceIn(0f, 100f),
|
||||||
|
onValueChange = { value ->
|
||||||
|
sliderDragging = true
|
||||||
|
sliderValue = value.coerceIn(0f, 100f)
|
||||||
|
},
|
||||||
|
onValueChangeFinished = {
|
||||||
|
commitSliderValue(sliderValue)
|
||||||
|
},
|
||||||
|
valueRange = 0f..100f,
|
||||||
|
enabled = !brightnessAuto,
|
||||||
|
backdrop = backdrop,
|
||||||
|
glassThumbEnabled = glassThumbEnabled,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(buttonSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,14 +27,12 @@ import dev.chrisbanes.haze.hazeSource
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.constant.ReadMenuBlurMode
|
import io.legado.app.constant.ReadMenuBlurMode
|
||||||
import io.legado.app.help.IntentData
|
|
||||||
import io.legado.app.help.IntentHelp
|
import io.legado.app.help.IntentHelp
|
||||||
import io.legado.app.ui.book.info.BookInfoActivity
|
import io.legado.app.ui.book.info.BookInfoActivity
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
import io.legado.app.ui.book.read.page.ReadView
|
import io.legado.app.ui.book.read.page.ReadView
|
||||||
import io.legado.app.ui.book.read.page.entities.PageDirection
|
import io.legado.app.ui.book.read.page.entities.PageDirection
|
||||||
import io.legado.app.ui.book.searchContent.SearchContentActivity
|
import io.legado.app.ui.book.searchContent.SearchContentResult
|
||||||
import io.legado.app.ui.book.searchContent.SearchResult
|
|
||||||
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
||||||
import io.legado.app.ui.book.toc.TocActivityResult
|
import io.legado.app.ui.book.toc.TocActivityResult
|
||||||
import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
|
import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
|
||||||
@@ -45,6 +43,7 @@ import io.legado.app.ui.replace.ReplaceRuleActivity
|
|||||||
import io.legado.app.utils.StartActivityContract
|
import io.legado.app.utils.StartActivityContract
|
||||||
import io.legado.app.utils.takePersistablePermissionSafely
|
import io.legado.app.utils.takePersistablePermissionSafely
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.flow.onSubscription
|
import kotlinx.coroutines.flow.onSubscription
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -95,10 +94,12 @@ fun ReadBookRouteScreen(
|
|||||||
host: ReadBookRouteHost,
|
host: ReadBookRouteHost,
|
||||||
controller: ReadBookController,
|
controller: ReadBookController,
|
||||||
onEffectsReady: () -> Unit = {},
|
onEffectsReady: () -> Unit = {},
|
||||||
|
onOpenSearch: (word: String?, bookUrl: String) -> Unit = { _, _ -> },
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val readPreferences by viewModel.readPreferences.collectAsStateWithLifecycle()
|
val readPreferences by viewModel.readPreferences.collectAsStateWithLifecycle()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val effectsReady = remember(viewModel) { CompletableDeferred<Unit>() }
|
||||||
val menuBackdrop = rememberLayerBackdrop()
|
val menuBackdrop = rememberLayerBackdrop()
|
||||||
val menuHazeState = remember { HazeState() }
|
val menuHazeState = remember { HazeState() }
|
||||||
val useMenuHazeSource = state.menuConfig.readMenuTopBarBlurMode == ReadMenuBlurMode.Haze ||
|
val useMenuHazeSource = state.menuConfig.readMenuTopBarBlurMode == ReadMenuBlurMode.Haze ||
|
||||||
@@ -209,19 +210,6 @@ fun ReadBookRouteScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val searchContentLauncher = rememberLauncherForActivityResult(
|
|
||||||
StartActivityContract(SearchContentActivity::class.java)
|
|
||||||
) { result ->
|
|
||||||
val data = result.data ?: return@rememberLauncherForActivityResult
|
|
||||||
val key = data.getLongExtra("key", System.currentTimeMillis())
|
|
||||||
val index = data.getIntExtra("index", 0)
|
|
||||||
val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
|
||||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
|
||||||
if (searchResult != null && searchResultList != null) {
|
|
||||||
viewModel.onIntent(ReadBookIntent.SetSearchResults(searchResultList, index, searchResult.query))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val importHttpTtsPicker = rememberLauncherForActivityResult(
|
val importHttpTtsPicker = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.OpenDocument()
|
ActivityResultContracts.OpenDocument()
|
||||||
) { uri ->
|
) { uri ->
|
||||||
@@ -245,7 +233,10 @@ fun ReadBookRouteScreen(
|
|||||||
LaunchedEffect(viewModel) {
|
LaunchedEffect(viewModel) {
|
||||||
launch {
|
launch {
|
||||||
viewModel.effects
|
viewModel.effects
|
||||||
.onSubscription { onEffectsReady() }
|
.onSubscription {
|
||||||
|
effectsReady.complete(Unit)
|
||||||
|
onEffectsReady()
|
||||||
|
}
|
||||||
.collect { effect ->
|
.collect { effect ->
|
||||||
try {
|
try {
|
||||||
when (effect) {
|
when (effect) {
|
||||||
@@ -292,18 +283,7 @@ fun ReadBookRouteScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is ReadBookEffect.OpenSearchActivity -> {
|
is ReadBookEffect.OpenSearchActivity -> {
|
||||||
val currentState = viewModel.uiState.value
|
onOpenSearch(effect.word, effect.bookUrl)
|
||||||
val lambda: (Intent.() -> Unit)? = { intent ->
|
|
||||||
intent.putExtra("bookUrl", effect.bookUrl)
|
|
||||||
intent.putExtra("searchWord", effect.word)
|
|
||||||
intent.putExtra("searchResultIndex", currentState.searchResultIndex)
|
|
||||||
currentState.searchResultList.firstOrNull()?.let {
|
|
||||||
if (it.query == currentState.searchContentQuery) {
|
|
||||||
IntentData.put("searchResultList", currentState.searchResultList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
searchContentLauncher.launch(lambda)
|
|
||||||
}
|
}
|
||||||
is ReadBookEffect.MenuSettingReplace -> {
|
is ReadBookEffect.MenuSettingReplace -> {
|
||||||
replaceLauncher.launch(Intent(context, ReplaceRuleActivity::class.java))
|
replaceLauncher.launch(Intent(context, ReplaceRuleActivity::class.java))
|
||||||
@@ -392,6 +372,24 @@ fun ReadBookRouteScreen(
|
|||||||
LaunchedEffect(state.menuVisible) {
|
LaunchedEffect(state.menuVisible) {
|
||||||
host.upSystemUiVisibility(host.isInMultiWindowModeCompat, !state.menuVisible)
|
host.upSystemUiVisibility(host.isInMultiWindowModeCompat, !state.menuVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Search result collection (from Navigation3 search route) ──────
|
||||||
|
|
||||||
|
LaunchedEffect(viewModel) {
|
||||||
|
SearchContentResult.results.collect { result ->
|
||||||
|
effectsReady.await()
|
||||||
|
viewModel.onIntent(
|
||||||
|
ReadBookIntent.SetSearchResults(result.searchResults, result.index, result.query)
|
||||||
|
)
|
||||||
|
result.searchResults.getOrNull(result.index)?.let { searchResult ->
|
||||||
|
viewModel.onIntent(
|
||||||
|
ReadBookIntent.NavigateToSearchResult(searchResult, result.index)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SearchContentResult.resetReplayCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── View layer + Compose UI ───────────────────────────────────────
|
// ── View layer + Compose UI ───────────────────────────────────────
|
||||||
|
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import io.legado.app.ui.book.read.sheet.ContentEditSheet
|
|||||||
import io.legado.app.ui.book.read.sheet.DictSheet
|
import io.legado.app.ui.book.read.sheet.DictSheet
|
||||||
import io.legado.app.ui.book.read.sheet.DownloadSheet
|
import io.legado.app.ui.book.read.sheet.DownloadSheet
|
||||||
import io.legado.app.ui.book.read.sheet.EffectiveReplacesSheet
|
import io.legado.app.ui.book.read.sheet.EffectiveReplacesSheet
|
||||||
import io.legado.app.ui.widget.components.FontSelectSheet
|
|
||||||
import io.legado.app.ui.book.read.sheet.HighlightRuleConfigSheet
|
import io.legado.app.ui.book.read.sheet.HighlightRuleConfigSheet
|
||||||
import io.legado.app.ui.book.read.sheet.HttpTtsEditSheet
|
import io.legado.app.ui.book.read.sheet.HttpTtsEditSheet
|
||||||
import io.legado.app.ui.book.read.sheet.MoreConfigSheet
|
import io.legado.app.ui.book.read.sheet.MoreConfigSheet
|
||||||
@@ -38,6 +37,7 @@ import io.legado.app.ui.book.read.sheet.SpeakEngineConfigSheet
|
|||||||
import io.legado.app.ui.book.read.sheet.TitleBarIconSheet
|
import io.legado.app.ui.book.read.sheet.TitleBarIconSheet
|
||||||
import io.legado.app.ui.book.read.sheet.ToolButtonConfigSheet
|
import io.legado.app.ui.book.read.sheet.ToolButtonConfigSheet
|
||||||
import io.legado.app.ui.book.read.sheet.UnderlineConfigSheet
|
import io.legado.app.ui.book.read.sheet.UnderlineConfigSheet
|
||||||
|
import io.legado.app.ui.widget.components.FontSelectSheet
|
||||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||||
import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet
|
import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet
|
||||||
import io.legado.app.ui.widget.components.log.AppLogSheet
|
import io.legado.app.ui.widget.components.log.AppLogSheet
|
||||||
@@ -135,6 +135,10 @@ fun ReadBookScreen(
|
|||||||
onIntent(ReadBookIntent.OpenReplaceEditor(id, pattern))
|
onIntent(ReadBookIntent.OpenReplaceEditor(id, pattern))
|
||||||
},
|
},
|
||||||
onReplaceRuleChanged = { onIntent(ReadBookIntent.ReplaceRuleChanged) },
|
onReplaceRuleChanged = { onIntent(ReadBookIntent.ReplaceRuleChanged) },
|
||||||
|
onNavigateToTextEffects = {
|
||||||
|
onIntent(ReadBookIntent.DismissSheet)
|
||||||
|
onIntent(ReadBookIntent.OpenReadMenuRoute(ReadBookMenuRoute.TextTitle))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
UnderlineConfigSheet(
|
UnderlineConfigSheet(
|
||||||
show = state.activeSheet is ReadBookSheet.UnderlineConfig,
|
show = state.activeSheet is ReadBookSheet.UnderlineConfig,
|
||||||
|
|||||||
@@ -3,44 +3,23 @@ package io.legado.app.ui.book.read
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.filled.Close
|
|
||||||
import androidx.compose.material.icons.filled.Menu
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.SmallFloatingActionButton
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compose replacement for SearchMenu — search result navigation overlay.
|
* Search result navigation overlay. The bottom search menu is hosted by [ReadBookMenuBar].
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadBookSearchBar(
|
fun ReadBookSearchBar(
|
||||||
@@ -52,9 +31,6 @@ fun ReadBookSearchBar(
|
|||||||
val hasResults = state.searchResultList.isNotEmpty()
|
val hasResults = state.searchResultList.isNotEmpty()
|
||||||
val currentIndex = state.searchResultIndex
|
val currentIndex = state.searchResultIndex
|
||||||
val totalResults = state.searchResultList.size
|
val totalResults = state.searchResultList.size
|
||||||
val currentResult = if (hasResults && currentIndex in state.searchResultList.indices) {
|
|
||||||
state.searchResultList[currentIndex]
|
|
||||||
} else null
|
|
||||||
|
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
// Left FAB - previous result
|
// Left FAB - previous result
|
||||||
@@ -66,7 +42,7 @@ fun ReadBookSearchBar(
|
|||||||
.align(Alignment.CenterStart)
|
.align(Alignment.CenterStart)
|
||||||
.padding(start = 16.dp),
|
.padding(start = 16.dp),
|
||||||
) {
|
) {
|
||||||
SmallFloatingActionButton(
|
AppFloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
val prevIndex = currentIndex - 1
|
val prevIndex = currentIndex - 1
|
||||||
onIntent(
|
onIntent(
|
||||||
@@ -75,11 +51,13 @@ fun ReadBookSearchBar(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
tooltipText = "上一个结果",
|
||||||
|
containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||||
|
contentColor = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Filled.ArrowBack,
|
Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = "Previous",
|
contentDescription = "上一个结果",
|
||||||
modifier = Modifier.size(20.dp),
|
modifier = Modifier.size(20.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -94,7 +72,7 @@ fun ReadBookSearchBar(
|
|||||||
.align(Alignment.CenterEnd)
|
.align(Alignment.CenterEnd)
|
||||||
.padding(end = 16.dp),
|
.padding(end = 16.dp),
|
||||||
) {
|
) {
|
||||||
SmallFloatingActionButton(
|
AppFloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
val nextIndex = currentIndex + 1
|
val nextIndex = currentIndex + 1
|
||||||
onIntent(
|
onIntent(
|
||||||
@@ -103,144 +81,16 @@ fun ReadBookSearchBar(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
tooltipText = "下一个结果",
|
||||||
|
containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||||
|
contentColor = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Filled.ArrowForward,
|
Icons.AutoMirrored.Filled.ArrowForward,
|
||||||
contentDescription = "Next",
|
contentDescription = "下一个结果",
|
||||||
modifier = Modifier.size(20.dp),
|
modifier = Modifier.size(20.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tap background to dismiss search menu
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = searchVisible && state.searchMenuVisible,
|
|
||||||
enter = fadeIn(),
|
|
||||||
exit = fadeOut(),
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.clickable(
|
|
||||||
indication = null,
|
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
|
||||||
) { onIntent(ReadBookIntent.HideSearchMenu) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom menu
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = searchVisible && state.searchMenuVisible,
|
|
||||||
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
|
||||||
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
|
|
||||||
modifier = Modifier.align(Alignment.BottomCenter),
|
|
||||||
) {
|
|
||||||
SearchBottomMenu(
|
|
||||||
state = state,
|
|
||||||
currentResult = currentResult,
|
|
||||||
onIntent = onIntent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SearchBottomMenu(
|
|
||||||
state: ReadBookUiState,
|
|
||||||
currentResult: io.legado.app.ui.book.searchContent.SearchResult?,
|
|
||||||
onIntent: (ReadBookIntent) -> Unit,
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(MaterialTheme.colorScheme.surfaceContainer)
|
|
||||||
.padding(bottom = 16.dp),
|
|
||||||
) {
|
|
||||||
// Search progress info
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
// Fraction: "3 / 10"
|
|
||||||
Card(
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "${state.searchResultIndex + 1} / ${state.searchResultList.size}",
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
val percent = if (state.searchResultList.isNotEmpty()) {
|
|
||||||
((state.searchResultIndex + 1) * 100 / state.searchResultList.size)
|
|
||||||
} else 0
|
|
||||||
Text(
|
|
||||||
text = "$percent%",
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current chapter
|
|
||||||
Card(
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = state.chapterName,
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
maxLines = 1,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(4.dp))
|
|
||||||
|
|
||||||
// Action buttons
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
|
||||||
) {
|
|
||||||
TextButton(
|
|
||||||
onClick = { onIntent(ReadBookIntent.OpenSearch(null)) },
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Search,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(18.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(4.dp))
|
|
||||||
Text("搜索内容")
|
|
||||||
}
|
|
||||||
TextButton(
|
|
||||||
onClick = { onIntent(ReadBookIntent.ShowMenu) },
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Menu, contentDescription = null, modifier = Modifier.size(18.dp))
|
|
||||||
Spacer(Modifier.width(4.dp))
|
|
||||||
Text("主菜单")
|
|
||||||
}
|
|
||||||
IconButton(
|
|
||||||
onClick = { onIntent(ReadBookIntent.ExitSearch) },
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Close, contentDescription = "Exit search")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,6 @@ import io.legado.app.domain.usecase.GetReadingProgressUseCase
|
|||||||
import io.legado.app.domain.usecase.UploadReadingProgressUseCase
|
import io.legado.app.domain.usecase.UploadReadingProgressUseCase
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.DefaultData
|
import io.legado.app.help.DefaultData
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfig
|
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.book.ContentProcessor
|
import io.legado.app.help.book.ContentProcessor
|
||||||
import io.legado.app.help.book.isEpub
|
import io.legado.app.help.book.isEpub
|
||||||
@@ -45,7 +44,6 @@ import io.legado.app.help.book.isLocalTxt
|
|||||||
import io.legado.app.help.book.isMobi
|
import io.legado.app.help.book.isMobi
|
||||||
import io.legado.app.help.book.removeType
|
import io.legado.app.help.book.removeType
|
||||||
import io.legado.app.help.book.simulatedTotalChapterNum
|
import io.legado.app.help.book.simulatedTotalChapterNum
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.help.source.getSourceType
|
import io.legado.app.help.source.getSourceType
|
||||||
@@ -66,6 +64,7 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
|||||||
import io.legado.app.ui.book.read.page.provider.TextChapterLayout
|
import io.legado.app.ui.book.read.page.provider.TextChapterLayout
|
||||||
import io.legado.app.ui.book.searchContent.SearchResult
|
import io.legado.app.ui.book.searchContent.SearchResult
|
||||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.ImageSaveUtils
|
import io.legado.app.utils.ImageSaveUtils
|
||||||
@@ -78,7 +77,6 @@ import io.legado.app.utils.isTrue
|
|||||||
import io.legado.app.utils.mapParallelSafe
|
import io.legado.app.utils.mapParallelSafe
|
||||||
import io.legado.app.utils.openUrl
|
import io.legado.app.utils.openUrl
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
import io.legado.app.utils.putPrefInt
|
|
||||||
import io.legado.app.utils.toStringArray
|
import io.legado.app.utils.toStringArray
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
@@ -155,7 +153,7 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
AppConfig.detectClickArea()
|
ReadConfig.detectClickArea()
|
||||||
ReadBook.register(this)
|
ReadBook.register(this)
|
||||||
refreshButtonConfigs()
|
refreshButtonConfigs()
|
||||||
collectReadPreferences()
|
collectReadPreferences()
|
||||||
@@ -253,7 +251,7 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
is ReadBookIntent.NavigateToSearchResult -> {
|
is ReadBookIntent.NavigateToSearchResult -> {
|
||||||
_uiState.update { it.copy(searchResultIndex = intent.index) }
|
_uiState.update { it.copy(searchResultIndex = intent.index) }
|
||||||
_effects.tryEmit(ReadBookEffect.NavigateToSearchResult(intent.result))
|
navigateToSearchResult(intent.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookIntent.ToggleReadAloud -> {
|
is ReadBookIntent.ToggleReadAloud -> {
|
||||||
@@ -296,8 +294,28 @@ class ReadBookViewModel(
|
|||||||
is ReadBookIntent.CancelSelect -> _effects.tryEmit(ReadBookEffect.CancelSelect)
|
is ReadBookIntent.CancelSelect -> _effects.tryEmit(ReadBookEffect.CancelSelect)
|
||||||
is ReadBookIntent.UpSystemUiVisibility -> _effects.tryEmit(ReadBookEffect.UpSystemUiVisibility)
|
is ReadBookIntent.UpSystemUiVisibility -> _effects.tryEmit(ReadBookEffect.UpSystemUiVisibility)
|
||||||
is ReadBookIntent.UpContent -> ReadBook.loadOrUpContent()
|
is ReadBookIntent.UpContent -> ReadBook.loadOrUpContent()
|
||||||
is ReadBookIntent.SetBrightness -> _effects.tryEmit(ReadBookEffect.SetBrightness(intent.value))
|
is ReadBookIntent.SetBrightness -> {
|
||||||
is ReadBookIntent.ToggleBrightnessAuto -> _effects.tryEmit(ReadBookEffect.ToggleBrightnessAuto)
|
ReadBookConfig.readBrightness = intent.value
|
||||||
|
_uiState.update { it.copy(menuConfig = it.menuConfig.copy(readBrightness = intent.value)) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
readSettingsRepository.setReadBrightness(intent.value)
|
||||||
|
}
|
||||||
|
_effects.tryEmit(ReadBookEffect.SetBrightness(intent.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
is ReadBookIntent.ToggleBrightnessAuto -> {
|
||||||
|
ReadBookConfig.brightnessAuto = intent.auto
|
||||||
|
_uiState.update { it.copy(menuConfig = it.menuConfig.copy(brightnessAuto = intent.auto)) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
readSettingsRepository.setBrightnessAuto(intent.auto)
|
||||||
|
}
|
||||||
|
_effects.tryEmit(
|
||||||
|
ReadBookEffect.ToggleBrightnessAuto(
|
||||||
|
intent.auto,
|
||||||
|
_uiState.value.menuConfig.readBrightness
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
is ReadBookIntent.SeekToChapter -> {
|
is ReadBookIntent.SeekToChapter -> {
|
||||||
ReadBook.saveCurrentBookProgress()
|
ReadBook.saveCurrentBookProgress()
|
||||||
openChapter(intent.index)
|
openChapter(intent.index)
|
||||||
@@ -627,7 +645,7 @@ class ReadBookViewModel(
|
|||||||
is ReadBookIntent.OpenPreDownloadNumPicker -> {
|
is ReadBookIntent.OpenPreDownloadNumPicker -> {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
preDownloadNum = AppConfig.preDownloadNum,
|
preDownloadNum = ReadConfig.preDownloadNum,
|
||||||
activeSheet = ReadBookSheet.PreDownloadConfig,
|
activeSheet = ReadBookSheet.PreDownloadConfig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -636,7 +654,7 @@ class ReadBookViewModel(
|
|||||||
is ReadBookIntent.OpenCacheCleanTimePicker -> {
|
is ReadBookIntent.OpenCacheCleanTimePicker -> {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
audioCacheCleanTime = AppConfig.audioCacheCleanTimeOrgin,
|
audioCacheCleanTime = ReadConfig.audioCacheCleanTimeOrgin,
|
||||||
activeSheet = ReadBookSheet.AudioCacheCleanConfig,
|
activeSheet = ReadBookSheet.AudioCacheCleanConfig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -644,7 +662,8 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
is ReadBookIntent.ApplySpeakEngine -> {
|
is ReadBookIntent.ApplySpeakEngine -> {
|
||||||
ReadBook.book?.setTtsEngine(null)
|
ReadBook.book?.setTtsEngine(null)
|
||||||
AppConfig.ttsEngine = intent.value
|
ReadConfig.ttsEngine = intent.value
|
||||||
|
ReadAloud.upReadAloudClass()
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
selectedTtsEngine = ReadAloud.ttsEngine,
|
selectedTtsEngine = ReadAloud.ttsEngine,
|
||||||
@@ -655,7 +674,7 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookIntent.ApplyPreDownloadNum -> {
|
is ReadBookIntent.ApplyPreDownloadNum -> {
|
||||||
AppConfig.preDownloadNum = intent.value
|
ReadConfig.preDownloadNum = intent.value
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
preDownloadNum = intent.value,
|
preDownloadNum = intent.value,
|
||||||
@@ -665,7 +684,7 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is ReadBookIntent.ApplyAudioCacheCleanTime -> {
|
is ReadBookIntent.ApplyAudioCacheCleanTime -> {
|
||||||
context.putPrefInt(PreferKey.audioCacheCleanTime, intent.value)
|
ReadConfig.audioCacheCleanTimeOrgin = intent.value
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
audioCacheCleanTime = intent.value,
|
audioCacheCleanTime = intent.value,
|
||||||
@@ -704,7 +723,7 @@ class ReadBookViewModel(
|
|||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
loadTtsEngineItems()
|
loadTtsEngineItems()
|
||||||
if (ReadAloud.ttsEngine == intent.engineId.toString()) {
|
if (ReadAloud.ttsEngine == intent.engineId.toString()) {
|
||||||
AppConfig.ttsEngine = null
|
ReadConfig.ttsEngine = null
|
||||||
ReadAloud.upReadAloudClass()
|
ReadAloud.upReadAloudClass()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -726,6 +745,7 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
is ReadBookIntent.ApplySpeakEnginePerBook -> {
|
is ReadBookIntent.ApplySpeakEnginePerBook -> {
|
||||||
ReadBook.book?.setTtsEngine(intent.value)
|
ReadBook.book?.setTtsEngine(intent.value)
|
||||||
|
ReadAloud.upReadAloudClass()
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
selectedTtsEngine = ReadAloud.ttsEngine,
|
selectedTtsEngine = ReadAloud.ttsEngine,
|
||||||
@@ -841,7 +861,7 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
is ReadBookIntent.SelectFont -> selectFont(intent.path)
|
is ReadBookIntent.SelectFont -> selectFont(intent.path)
|
||||||
is ReadBookIntent.SelectSystemTypeface -> {
|
is ReadBookIntent.SelectSystemTypeface -> {
|
||||||
AppConfig.systemTypefaces = intent.index
|
ReadConfig.systemTypefaces = intent.index
|
||||||
ReadBookConfig.textFont = ""
|
ReadBookConfig.textFont = ""
|
||||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||||
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.ReloadContent)
|
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.ReloadContent)
|
||||||
@@ -937,7 +957,7 @@ class ReadBookViewModel(
|
|||||||
is ReadBookIntent.ToggleDayNight -> toggleDayNight()
|
is ReadBookIntent.ToggleDayNight -> toggleDayNight()
|
||||||
// Text action menu
|
// Text action menu
|
||||||
is ReadBookIntent.TextActionAloud -> {
|
is ReadBookIntent.TextActionAloud -> {
|
||||||
when (AppConfig.contentSelectSpeakMod) {
|
when (ReadConfig.contentSelectSpeakMod) {
|
||||||
1 -> _effects.tryEmit(ReadBookEffect.TextActionAloudSelect)
|
1 -> _effects.tryEmit(ReadBookEffect.TextActionAloudSelect)
|
||||||
else -> _effects.tryEmit(ReadBookEffect.TextActionSpeak(intent.text))
|
else -> _effects.tryEmit(ReadBookEffect.TextActionSpeak(intent.text))
|
||||||
}
|
}
|
||||||
@@ -1052,7 +1072,7 @@ class ReadBookViewModel(
|
|||||||
_effects.tryEmit(ReadBookEffect.UnregisterNetworkListener)
|
_effects.tryEmit(ReadBookEffect.UnregisterNetworkListener)
|
||||||
|
|
||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
if (AppConfig.syncBookProgressPlus) {
|
if (ReadConfig.syncBookProgressPlus) {
|
||||||
ReadBook.syncProgress()
|
ReadBook.syncProgress()
|
||||||
} else {
|
} else {
|
||||||
ReadBook.uploadProgress()
|
ReadBook.uploadProgress()
|
||||||
@@ -1123,7 +1143,7 @@ class ReadBookViewModel(
|
|||||||
* Called from the network changed listener (registered by route).
|
* Called from the network changed listener (registered by route).
|
||||||
*/
|
*/
|
||||||
fun onNetworkChanged() {
|
fun onNetworkChanged() {
|
||||||
if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
if (ReadConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
||||||
ReadBook.syncProgress(newProgressAction = { progress ->
|
ReadBook.syncProgress(newProgressAction = { progress ->
|
||||||
sureNewProgress(progress)
|
sureNewProgress(progress)
|
||||||
})
|
})
|
||||||
@@ -1149,7 +1169,7 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
private fun handleChangeSource() {
|
private fun handleChangeSource() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (AppConfig.defaultSourceChangeAll) {
|
if (ReadConfig.defaultSourceChangeAll) {
|
||||||
_uiState.update { it.copy(activeSheet = ReadBookSheet.ChangeBookSource) }
|
_uiState.update { it.copy(activeSheet = ReadBookSheet.ChangeBookSource) }
|
||||||
} else {
|
} else {
|
||||||
val book = ReadBook.book ?: return@launch
|
val book = ReadBook.book ?: return@launch
|
||||||
@@ -1393,10 +1413,10 @@ class ReadBookViewModel(
|
|||||||
readSettingsRepository.preferences.collect { preferences ->
|
readSettingsRepository.preferences.collect { preferences ->
|
||||||
val old = previous
|
val old = previous
|
||||||
previous = preferences
|
previous = preferences
|
||||||
AppConfig.syncReadPreferences(preferences)
|
ReadConfig.syncReadPreferences(preferences)
|
||||||
_readPreferences.value = preferences
|
_readPreferences.value = preferences
|
||||||
if (!preferences.hasMenuClickArea()) {
|
if (!preferences.hasMenuClickArea()) {
|
||||||
AppConfig.detectClickArea()
|
ReadConfig.detectClickArea()
|
||||||
readSettingsRepository.setClickAction(PreferKey.clickActionMC, 0)
|
readSettingsRepository.setClickAction(PreferKey.clickActionMC, 0)
|
||||||
}
|
}
|
||||||
if (old != null && old.keepLight != preferences.keepLight) {
|
if (old != null && old.keepLight != preferences.keepLight) {
|
||||||
@@ -1423,11 +1443,7 @@ class ReadBookViewModel(
|
|||||||
readAloudStreamAudio = prefs.streamReadAloudAudio,
|
readAloudStreamAudio = prefs.streamReadAloudAudio,
|
||||||
readAloudTtsFollowSys = prefs.ttsFollowSys,
|
readAloudTtsFollowSys = prefs.ttsFollowSys,
|
||||||
readAloudTtsSpeechRate = prefs.ttsSpeechRate,
|
readAloudTtsSpeechRate = prefs.ttsSpeechRate,
|
||||||
readAloudTtsTimer = if (BaseReadAloudService.timeMinute > 0) {
|
readAloudTtsTimer = prefs.ttsTimer,
|
||||||
BaseReadAloudService.timeMinute
|
|
||||||
} else {
|
|
||||||
prefs.ttsTimer
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1445,11 +1461,12 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setReadAloudTtsTimer(value: Int) {
|
private fun setReadAloudTtsTimer(value: Int) {
|
||||||
|
val timer = value.coerceIn(0, 180)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
readAloudSettingsRepository.setTtsTimer(value)
|
readAloudSettingsRepository.setTtsTimer(timer)
|
||||||
}
|
}
|
||||||
ReadAloud.setTimer(context, value)
|
ReadAloud.setTimer(context, timer)
|
||||||
_uiState.update { it.copy(readAloudTtsTimer = value) }
|
_uiState.update { it.copy(readAloudTtsTimer = timer) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setReadAloudTtsSpeechRate(value: Int) {
|
private fun setReadAloudTtsSpeechRate(value: Int) {
|
||||||
@@ -1557,6 +1574,10 @@ class ReadBookViewModel(
|
|||||||
readMenuCustomIcons = ReadBookConfig.readMenuCustomIcons.toImmutableMap(),
|
readMenuCustomIcons = ReadBookConfig.readMenuCustomIcons.toImmutableMap(),
|
||||||
titleBarButtons = current.menuConfig.titleBarButtons,
|
titleBarButtons = current.menuConfig.titleBarButtons,
|
||||||
bottomBarButtons = current.menuConfig.bottomBarButtons,
|
bottomBarButtons = current.menuConfig.bottomBarButtons,
|
||||||
|
showBrightnessView = ReadBookConfig.showBrightnessView,
|
||||||
|
brightnessVwPos = ReadBookConfig.brightnessVwPos,
|
||||||
|
readBrightness = ReadBookConfig.readBrightness,
|
||||||
|
brightnessAuto = ReadBookConfig.brightnessAuto,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1659,14 +1680,14 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateSeekProgress(): Int {
|
private fun calculateSeekProgress(): Int {
|
||||||
return when (AppConfig.progressBarBehavior) {
|
return when (ReadConfig.progressBarBehavior) {
|
||||||
"page" -> ReadBook.durPageIndex
|
"page" -> ReadBook.durPageIndex
|
||||||
else -> ReadBook.durChapterIndex
|
else -> ReadBook.durChapterIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateSeekMax(): Int {
|
private fun calculateSeekMax(): Int {
|
||||||
return when (AppConfig.progressBarBehavior) {
|
return when (ReadConfig.progressBarBehavior) {
|
||||||
"page" -> (ReadBook.curTextChapter?.pages?.size ?: 1) - 1
|
"page" -> (ReadBook.curTextChapter?.pages?.size ?: 1) - 1
|
||||||
else -> ReadBook.chapterSize - 1
|
else -> ReadBook.chapterSize - 1
|
||||||
}
|
}
|
||||||
@@ -1761,7 +1782,7 @@ class ReadBookViewModel(
|
|||||||
if (ReadBook.chapterChanged) {
|
if (ReadBook.chapterChanged) {
|
||||||
ReadBook.chapterChanged = false
|
ReadBook.chapterChanged = false
|
||||||
} else if (!(isSameBook && BaseReadAloudService.isRun) && ReadBook.inBookshelf) {
|
} else if (!(isSameBook && BaseReadAloudService.isRun) && ReadBook.inBookshelf) {
|
||||||
if (AppConfig.syncBookProgressPlus) {
|
if (ReadConfig.syncBookProgressPlus) {
|
||||||
ReadBook.syncProgress({ progress -> sureNewProgress(progress) })
|
ReadBook.syncProgress({ progress -> sureNewProgress(progress) })
|
||||||
} else {
|
} else {
|
||||||
syncBookProgress(book)
|
syncBookProgress(book)
|
||||||
@@ -1857,7 +1878,7 @@ class ReadBookViewModel(
|
|||||||
book: Book,
|
book: Book,
|
||||||
alertSync: ((progress: BookProgress) -> Unit)? = null
|
alertSync: ((progress: BookProgress) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
if (!AppConfig.syncBookProgress) return
|
if (!ReadConfig.syncBookProgress) return
|
||||||
execute {
|
execute {
|
||||||
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
|
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
|
||||||
}.onError {
|
}.onError {
|
||||||
@@ -1952,7 +1973,7 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun autoChangeSource(name: String, author: String) {
|
private fun autoChangeSource(name: String, author: String) {
|
||||||
if (!AppConfig.autoChangeSource) return
|
if (!ReadConfig.autoChangeSource) return
|
||||||
execute {
|
execute {
|
||||||
val sources = appDb.bookSourceDao.allTextEnabledPart
|
val sources = appDb.bookSourceDao.allTextEnabledPart
|
||||||
flow {
|
flow {
|
||||||
@@ -2164,11 +2185,11 @@ class ReadBookViewModel(
|
|||||||
|
|
||||||
fun searchResultPositions(
|
fun searchResultPositions(
|
||||||
textChapter: TextChapter,
|
textChapter: TextChapter,
|
||||||
searchResult: SearchResult
|
searchResult: SearchResult,
|
||||||
|
query: String = _uiState.value.searchContentQuery,
|
||||||
): Array<Int> {
|
): Array<Int> {
|
||||||
val pages = textChapter.pages
|
val pages = textChapter.pages
|
||||||
val content = textChapter.getContent()
|
val content = textChapter.getContent()
|
||||||
val query = _uiState.value.searchContentQuery
|
|
||||||
val queryLength = query.length
|
val queryLength = query.length
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
@@ -2178,6 +2199,7 @@ class ReadBookViewModel(
|
|||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
val contentPosition = index
|
val contentPosition = index
|
||||||
|
if (contentPosition < 0) return arrayOf(0, 0, 0, 0, 0)
|
||||||
var pageIndex = 0
|
var pageIndex = 0
|
||||||
var length = pages[pageIndex].text.length
|
var length = pages[pageIndex].text.length
|
||||||
while (length < contentPosition && pageIndex + 1 < pages.size) {
|
while (length < contentPosition && pageIndex + 1 < pages.size) {
|
||||||
@@ -2191,7 +2213,7 @@ class ReadBookViewModel(
|
|||||||
var curLine = curTextLines[lineIndex]
|
var curLine = curTextLines[lineIndex]
|
||||||
length = length - currentPage.text.length + curLine.text.length
|
length = length - currentPage.text.length + curLine.text.length
|
||||||
if (curLine.isParagraphEnd) length++
|
if (curLine.isParagraphEnd) length++
|
||||||
while (length <= contentPosition && lineIndex + 1 < curTextLines.size) {
|
while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
|
||||||
lineIndex += 1
|
lineIndex += 1
|
||||||
curLine = curTextLines[lineIndex]
|
curLine = curTextLines[lineIndex]
|
||||||
length += curLine.text.length
|
length += curLine.text.length
|
||||||
@@ -2217,6 +2239,55 @@ class ReadBookViewModel(
|
|||||||
return arrayOf(pageIndex, lineIndex, charIndex, addLine, charIndex2)
|
return arrayOf(pageIndex, lineIndex, charIndex, addLine, charIndex2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the search result position and emit [ReadBookEffect.NavigateToSearchResult]
|
||||||
|
* so the Controller can navigate and highlight.
|
||||||
|
*/
|
||||||
|
private fun navigateToSearchResult(result: SearchResult) {
|
||||||
|
val query = _uiState.value.searchContentQuery
|
||||||
|
if (query.isEmpty()) return
|
||||||
|
val chapterIndex = result.chapterIndex
|
||||||
|
val textChapter = ReadBook.curTextChapter
|
||||||
|
if (textChapter != null && textChapter.chapter.index == chapterIndex) {
|
||||||
|
val pos = searchResultPositions(textChapter, result, query)
|
||||||
|
val lineIndex = pos[1]
|
||||||
|
val charIndex = pos[2]
|
||||||
|
val addLine = pos[3]
|
||||||
|
val charIndex2 = pos[4]
|
||||||
|
val queryLength = query.length
|
||||||
|
val endLineIndex = lineIndex + addLine.coerceAtLeast(0)
|
||||||
|
val endCharIndex = if (addLine == 0) {
|
||||||
|
charIndex + queryLength
|
||||||
|
} else {
|
||||||
|
charIndex2 + 1
|
||||||
|
}
|
||||||
|
_effects.tryEmit(
|
||||||
|
ReadBookEffect.NavigateToSearchResult(
|
||||||
|
result = result,
|
||||||
|
chapterIndex = chapterIndex,
|
||||||
|
pageIndex = pos[0],
|
||||||
|
lineIndex = lineIndex,
|
||||||
|
startCharIndex = charIndex,
|
||||||
|
endLineIndex = endLineIndex,
|
||||||
|
endCharIndex = endCharIndex,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Chapter not loaded — emit with -1 so the Controller knows to open the chapter first
|
||||||
|
_effects.tryEmit(
|
||||||
|
ReadBookEffect.NavigateToSearchResult(
|
||||||
|
result = result,
|
||||||
|
chapterIndex = chapterIndex,
|
||||||
|
pageIndex = -1,
|
||||||
|
lineIndex = 0,
|
||||||
|
startCharIndex = 0,
|
||||||
|
endLineIndex = 0,
|
||||||
|
endCharIndex = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun reverseRemoveSameTitle() {
|
fun reverseRemoveSameTitle() {
|
||||||
execute {
|
execute {
|
||||||
val book = ReadBook.book ?: return@execute
|
val book = ReadBook.book ?: return@execute
|
||||||
@@ -2423,7 +2494,7 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
is ConfigUpdate.ReadBarStyle -> {
|
is ConfigUpdate.ReadBarStyle -> {
|
||||||
val value = update.value.coerceIn(0, 2)
|
val value = update.value.coerceIn(0, 2)
|
||||||
AppConfig.updateReadBarStyleCache(value)
|
ReadConfig.readBarStyle = value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
readSettingsRepository.setReadBarStyle(value)
|
readSettingsRepository.setReadBarStyle(value)
|
||||||
}
|
}
|
||||||
@@ -2760,11 +2831,41 @@ class ReadBookViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ConfigUpdate.ShowBrightnessView -> {
|
is ConfigUpdate.ShowBrightnessView -> {
|
||||||
|
ReadBookConfig.showBrightnessView = update.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
readSettingsRepository.setShowBrightnessView(update.value)
|
readSettingsRepository.setShowBrightnessView(update.value)
|
||||||
}
|
}
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(menuConfig = it.menuConfig.copy(showBrightnessView = update.value))
|
||||||
|
}
|
||||||
postEvent(PreferKey.showBrightnessView, "")
|
postEvent(PreferKey.showBrightnessView, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ConfigUpdate.BrightnessVwPos -> {
|
||||||
|
ReadBookConfig.brightnessVwPos = update.value
|
||||||
|
viewModelScope.launch {
|
||||||
|
readSettingsRepository.setBrightnessVwPos(update.value)
|
||||||
|
}
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(menuConfig = it.menuConfig.copy(brightnessVwPos = update.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConfigUpdate.BrightnessAuto -> {
|
||||||
|
ReadBookConfig.brightnessAuto = update.value
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(menuConfig = it.menuConfig.copy(brightnessAuto = update.value))
|
||||||
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
readSettingsRepository.setBrightnessAuto(update.value)
|
||||||
|
}
|
||||||
|
_effects.tryEmit(
|
||||||
|
ReadBookEffect.ToggleBrightnessAuto(
|
||||||
|
update.value,
|
||||||
|
_uiState.value.menuConfig.readBrightness
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
is ConfigUpdate.UseUnderlineGlobal -> {
|
is ConfigUpdate.UseUnderlineGlobal -> {
|
||||||
ReadConfig.useUnderline = update.value
|
ReadConfig.useUnderline = update.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -3051,7 +3152,7 @@ class ReadBookViewModel(
|
|||||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||||
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
|
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
|
||||||
))
|
))
|
||||||
if (AppConfig.readBarStyleFollowPage) {
|
if (ReadConfig.readBarStyleFollowPage) {
|
||||||
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3061,7 +3162,7 @@ class ReadBookViewModel(
|
|||||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||||
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
|
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
|
||||||
))
|
))
|
||||||
if (AppConfig.readBarStyleFollowPage) {
|
if (ReadConfig.readBarStyleFollowPage) {
|
||||||
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3071,7 +3172,7 @@ class ReadBookViewModel(
|
|||||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||||
setOf(ConfigUpdateAction.UpdateBackground)
|
setOf(ConfigUpdateAction.UpdateBackground)
|
||||||
))
|
))
|
||||||
if (AppConfig.readBarStyleFollowPage) {
|
if (ReadConfig.readBarStyleFollowPage) {
|
||||||
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ import android.widget.PopupWindow
|
|||||||
import androidx.appcompat.view.SupportMenuInflater
|
import androidx.appcompat.view.SupportMenuInflater
|
||||||
import androidx.appcompat.view.menu.MenuBuilder
|
import androidx.appcompat.view.menu.MenuBuilder
|
||||||
import androidx.appcompat.view.menu.MenuItemImpl
|
import androidx.appcompat.view.menu.MenuItemImpl
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.adapter.ItemViewHolder
|
import io.legado.app.base.adapter.ItemViewHolder
|
||||||
import io.legado.app.base.adapter.RecyclerAdapter
|
import io.legado.app.base.adapter.RecyclerAdapter
|
||||||
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.databinding.ItemTextBinding
|
import io.legado.app.databinding.ItemTextBinding
|
||||||
import io.legado.app.databinding.PopupActionMenuBinding
|
import io.legado.app.databinding.PopupActionMenuBinding
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.gone
|
import io.legado.app.utils.gone
|
||||||
import io.legado.app.utils.isAbsUrl
|
import io.legado.app.utils.isAbsUrl
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
@@ -28,8 +30,6 @@ import io.legado.app.utils.sendToClip
|
|||||||
import io.legado.app.utils.share
|
import io.legado.app.utils.share
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import io.legado.app.utils.visible
|
import io.legado.app.utils.visible
|
||||||
import androidx.core.net.toUri
|
|
||||||
import io.legado.app.constant.AppLog
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
class TextActionMenu(
|
class TextActionMenu(
|
||||||
@@ -185,11 +185,11 @@ class TextActionMenu(
|
|||||||
callBack.onMenuActionFinally()
|
callBack.onMenuActionFinally()
|
||||||
}
|
}
|
||||||
holder.itemView.setOnLongClickListener {
|
holder.itemView.setOnLongClickListener {
|
||||||
if (AppConfig.contentSelectSpeakMod == 0) {
|
if (ReadConfig.contentSelectSpeakMod == 0) {
|
||||||
AppConfig.contentSelectSpeakMod = 1
|
ReadConfig.contentSelectSpeakMod = 1
|
||||||
context.toastOnUi("切换为从选择的地方开始一直朗读")
|
context.toastOnUi("切换为从选择的地方开始一直朗读")
|
||||||
} else {
|
} else {
|
||||||
AppConfig.contentSelectSpeakMod = 0
|
ReadConfig.contentSelectSpeakMod = 0
|
||||||
context.toastOnUi("切换为朗读选择内容")
|
context.toastOnUi("切换为朗读选择内容")
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import android.graphics.Canvas
|
|||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.core.graphics.withClip
|
import androidx.core.graphics.withClip
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.page.entities.PageDirection
|
import io.legado.app.ui.book.read.page.entities.PageDirection
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
||||||
import io.legado.app.utils.themeColor
|
import io.legado.app.utils.themeColor
|
||||||
@@ -30,7 +30,7 @@ class AutoPager(private val readView: ReadView) : Runnable {
|
|||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
isRunning = true
|
isRunning = true
|
||||||
isEInkMode = AppConfig.isEInkMode
|
isEInkMode = ReadConfig.isEInkMode
|
||||||
readView.curPage.upSelectAble(false)
|
readView.curPage.upSelectAble(false)
|
||||||
if (isEInkMode) {
|
if (isEInkMode) {
|
||||||
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
||||||
@@ -49,7 +49,7 @@ class AutoPager(private val readView: ReadView) : Runnable {
|
|||||||
isPausing = false
|
isPausing = false
|
||||||
isEInkMode = false
|
isEInkMode = false
|
||||||
readView.removeCallbacks(this)
|
readView.removeCallbacks(this)
|
||||||
readView.curPage.upSelectAble(AppConfig.textSelectAble)
|
readView.curPage.upSelectAble(ReadConfig.selectText)
|
||||||
readView.invalidate()
|
readView.invalidate()
|
||||||
reset()
|
reset()
|
||||||
canvasRecorder.recycle()
|
canvasRecorder.recycle()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import android.view.View
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
import io.legado.app.help.book.isOnLineTxt
|
import io.legado.app.help.book.isOnLineTxt
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.ui.association.OpenUrlConfirmActivity
|
import io.legado.app.ui.association.OpenUrlConfirmActivity
|
||||||
import io.legado.app.ui.book.read.page.delegate.PageDelegate
|
import io.legado.app.ui.book.read.page.delegate.PageDelegate
|
||||||
@@ -25,6 +24,7 @@ import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
|||||||
import io.legado.app.ui.book.read.page.entities.column.TextHtmlColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextHtmlColumn
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.widget.dialog.PhotoDialog
|
import io.legado.app.ui.widget.dialog.PhotoDialog
|
||||||
import io.legado.app.utils.activity
|
import io.legado.app.utils.activity
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
@@ -40,7 +40,7 @@ import kotlin.math.min
|
|||||||
* 阅读内容视图
|
* 阅读内容视图
|
||||||
*/
|
*/
|
||||||
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
||||||
var selectAble = AppConfig.textSelectAble
|
var selectAble = ReadConfig.selectText
|
||||||
val selectedPaint by lazy {
|
val selectedPaint by lazy {
|
||||||
Paint().apply {
|
Paint().apply {
|
||||||
color = context.getCompatColor(R.color.btn_bg_press_2)
|
color = context.getCompatColor(R.color.btn_bg_press_2)
|
||||||
@@ -73,7 +73,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
//绘制图片的paint
|
//绘制图片的paint
|
||||||
val imagePaint by lazy {
|
val imagePaint by lazy {
|
||||||
Paint().apply {
|
Paint().apply {
|
||||||
isAntiAlias = AppConfig.useAntiAlias
|
isAntiAlias = ReadConfig.useAntiAlias
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
is ImageColumn -> when (AppConfig.clickImgWay) {
|
is ImageColumn -> when (ReadConfig.clickImgWay) {
|
||||||
"1" -> { //预览图片
|
"1" -> { //预览图片
|
||||||
activity?.showDialogFragment(PhotoDialog(column.src, isBook = true))
|
activity?.showDialogFragment(PhotoDialog(column.src, isBook = true))
|
||||||
handled = true
|
handled = true
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import io.legado.app.R
|
|||||||
import io.legado.app.constant.AppConst.timeFormat
|
import io.legado.app.constant.AppConst.timeFormat
|
||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
import io.legado.app.databinding.ViewBookPageBinding
|
import io.legado.app.databinding.ViewBookPageBinding
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.ui.book.read.page.entities.TextLine
|
import io.legado.app.ui.book.read.page.entities.TextLine
|
||||||
import io.legado.app.ui.book.read.page.entities.TextPage
|
import io.legado.app.ui.book.read.page.entities.TextPage
|
||||||
import io.legado.app.ui.book.read.page.entities.TextPos
|
import io.legado.app.ui.book.read.page.entities.TextPos
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.widget.BatteryView
|
import io.legado.app.ui.widget.BatteryView
|
||||||
import io.legado.app.utils.activity
|
import io.legado.app.utils.activity
|
||||||
import io.legado.app.utils.applyNavigationBarPadding
|
import io.legado.app.utils.applyNavigationBarPadding
|
||||||
@@ -169,7 +169,7 @@ class PageView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upPaddingDisplayCutouts() {
|
fun upPaddingDisplayCutouts() {
|
||||||
if (AppConfig.paddingDisplayCutouts) {
|
if (ReadConfig.paddingDisplayCutouts) {
|
||||||
binding.vwRoot.setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
|
binding.vwRoot.setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
|
||||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||||
binding.vwRoot.setPadding(
|
binding.vwRoot.setPadding(
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import android.widget.FrameLayout
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.PageAnim
|
import io.legado.app.constant.PageAnim
|
||||||
import io.legado.app.data.entities.BookProgress
|
import io.legado.app.data.entities.BookProgress
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.model.ReadAloud
|
import io.legado.app.model.ReadAloud
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
@@ -37,6 +36,7 @@ import io.legado.app.ui.book.read.page.entities.column.TextBaseColumn
|
|||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
||||||
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.activity
|
import io.legado.app.utils.activity
|
||||||
import io.legado.app.utils.invisible
|
import io.legado.app.utils.invisible
|
||||||
import io.legado.app.utils.longToastOnUi
|
import io.legado.app.utils.longToastOnUi
|
||||||
@@ -272,7 +272,7 @@ class ReadView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cancelSelect(clearSearchResult: Boolean = false) {
|
fun cancelSelect(clearSearchResult: Boolean = false) {
|
||||||
if (isTextSelected) {
|
if (isTextSelected || clearSearchResult) {
|
||||||
curPage.cancelSelect(clearSearchResult)
|
curPage.cancelSelect(clearSearchResult)
|
||||||
isTextSelected = false
|
isTextSelected = false
|
||||||
}
|
}
|
||||||
@@ -398,39 +398,39 @@ class ReadView(
|
|||||||
when {
|
when {
|
||||||
isTextSelected -> Unit
|
isTextSelected -> Unit
|
||||||
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
|
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
|
||||||
click(AppConfig.clickActionMC)
|
click(ReadConfig.clickActionMC)
|
||||||
}
|
}
|
||||||
|
|
||||||
bcRect.contains(startX, startY) -> {
|
bcRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionBC)
|
click(ReadConfig.clickActionBC)
|
||||||
}
|
}
|
||||||
|
|
||||||
blRect.contains(startX, startY) -> {
|
blRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionBL)
|
click(ReadConfig.clickActionBL)
|
||||||
}
|
}
|
||||||
|
|
||||||
brRect.contains(startX, startY) -> {
|
brRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionBR)
|
click(ReadConfig.clickActionBR)
|
||||||
}
|
}
|
||||||
|
|
||||||
mlRect.contains(startX, startY) -> {
|
mlRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionML)
|
click(ReadConfig.clickActionML)
|
||||||
}
|
}
|
||||||
|
|
||||||
mrRect.contains(startX, startY) -> {
|
mrRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionMR)
|
click(ReadConfig.clickActionMR)
|
||||||
}
|
}
|
||||||
|
|
||||||
tlRect.contains(startX, startY) -> {
|
tlRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionTL)
|
click(ReadConfig.clickActionTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
tcRect.contains(startX, startY) -> {
|
tcRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionTC)
|
click(ReadConfig.clickActionTC)
|
||||||
}
|
}
|
||||||
|
|
||||||
trRect.contains(startX, startY) -> {
|
trRect.contains(startX, startY) -> {
|
||||||
click(AppConfig.clickActionTR)
|
click(ReadConfig.clickActionTR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,7 +552,7 @@ class ReadView(
|
|||||||
pageDelegate = NoAnimPageDelegate(this)
|
pageDelegate = NoAnimPageDelegate(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(pageDelegate as? ScrollPageDelegate)?.noAnim = AppConfig.noAnimScrollPage
|
(pageDelegate as? ScrollPageDelegate)?.noAnim = ReadConfig.noAnimScrollPage
|
||||||
if (upRecorder) {
|
if (upRecorder) {
|
||||||
(pageDelegate as? HorizontalPageDelegate)?.upRecorder()
|
(pageDelegate as? HorizontalPageDelegate)?.upRecorder()
|
||||||
autoPager.upRecorder()
|
autoPager.upRecorder()
|
||||||
@@ -603,7 +603,7 @@ class ReadView(
|
|||||||
* 更新滑动距离
|
* 更新滑动距离
|
||||||
*/
|
*/
|
||||||
fun upPageSlopSquare() {
|
fun upPageSlopSquare() {
|
||||||
val pageTouchSlop = AppConfig.pageTouchSlop
|
val pageTouchSlop = ReadConfig.pageTouchSlop
|
||||||
this.pageSlopSquare = if (pageTouchSlop == 0) slopSquare else pageTouchSlop
|
this.pageSlopSquare = if (pageTouchSlop == 0) slopSquare else pageTouchSlop
|
||||||
pageSlopSquare2 = this.pageSlopSquare * this.pageSlopSquare
|
pageSlopSquare2 = this.pageSlopSquare * this.pageSlopSquare
|
||||||
}
|
}
|
||||||
@@ -697,7 +697,7 @@ class ReadView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateTextPage() {
|
fun invalidateTextPage() {
|
||||||
if (!AppConfig.optimizeRender) {
|
if (!ReadConfig.optimizeRender) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pageFactory.run {
|
pageFactory.run {
|
||||||
@@ -722,7 +722,7 @@ class ReadView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun submitRenderTask() {
|
fun submitRenderTask() {
|
||||||
if (!AppConfig.optimizeRender) {
|
if (!ReadConfig.optimizeRender) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
curPage.submitRenderTask()
|
curPage.submitRenderTask()
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ import android.graphics.Paint
|
|||||||
import android.graphics.Path
|
import android.graphics.Path
|
||||||
import android.graphics.Shader
|
import android.graphics.Shader
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.text.TextPaint
|
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import io.legado.app.help.PaintPool
|
import io.legado.app.help.PaintPool
|
||||||
import io.legado.app.help.book.isImage
|
import io.legado.app.help.book.isImage
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
@@ -22,6 +20,7 @@ import io.legado.app.ui.book.read.page.entities.column.BaseColumn
|
|||||||
import io.legado.app.ui.book.read.page.entities.column.TextBaseColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextBaseColumn
|
||||||
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
@@ -77,7 +76,7 @@ data class TextLine(
|
|||||||
var textPage: TextPage = emptyTextPage
|
var textPage: TextPage = emptyTextPage
|
||||||
var isLeftLine = true
|
var isLeftLine = true
|
||||||
val useUnderline: Boolean
|
val useUnderline: Boolean
|
||||||
get() = AppConfig.useUnderline
|
get() = ReadConfig.useUnderline
|
||||||
|
|
||||||
fun addColumn(column: BaseColumn) {
|
fun addColumn(column: BaseColumn) {
|
||||||
if (column !is TextColumn) {
|
if (column !is TextColumn) {
|
||||||
@@ -160,7 +159,7 @@ data class TextLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun draw(view: ContentTextView, canvas: Canvas) {
|
fun draw(view: ContentTextView, canvas: Canvas) {
|
||||||
if (AppConfig.optimizeRender) {
|
if (ReadConfig.optimizeRender) {
|
||||||
canvasRecorder.recordIfNeededThenDraw(canvas, view.width, height.toInt()) {
|
canvasRecorder.recordIfNeededThenDraw(canvas, view.width, height.toInt()) {
|
||||||
drawTextLine(view, this)
|
drawTextLine(view, this)
|
||||||
}
|
}
|
||||||
@@ -236,7 +235,7 @@ data class TextLine(
|
|||||||
val paint = ChapterProvider.contentPaint
|
val paint = ChapterProvider.contentPaint
|
||||||
paint.color = ReadBookConfig.durConfig.curUnderlineColor()
|
paint.color = ReadBookConfig.durConfig.curUnderlineColor()
|
||||||
paint.strokeWidth = ReadBookConfig.underlineHeight.toFloat()
|
paint.strokeWidth = ReadBookConfig.underlineHeight.toFloat()
|
||||||
paint.pathEffect = if (dottedLine && !AppConfig.isEInkMode)
|
paint.pathEffect = if (dottedLine && !ReadConfig.isEInkMode)
|
||||||
ChapterProvider.dashEffect
|
ChapterProvider.dashEffect
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
@@ -557,7 +556,7 @@ data class TextLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkFastDraw(): Boolean {
|
fun checkFastDraw(): Boolean {
|
||||||
if (!AppConfig.optimizeRender || exceed || !onlyTextColumn || textPage.isMsgPage) {
|
if (!ReadConfig.optimizeRender || exceed || !onlyTextColumn || textPage.isMsgPage) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (wordSpacing != 0f && (!atLeastApi26 || !wordSpacingWorking)) {
|
if (wordSpacing != 0f && (!atLeastApi26 || !wordSpacingWorking)) {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import androidx.annotation.Keep
|
|||||||
import androidx.core.graphics.withTranslation
|
import androidx.core.graphics.withTranslation
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.help.PaintPool
|
import io.legado.app.help.PaintPool
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
|
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
|
||||||
import io.legado.app.ui.book.read.page.entities.column.TextBaseColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextBaseColumn
|
||||||
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
@@ -299,7 +299,7 @@ data class TextPage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun draw(view: ContentTextView, canvas: Canvas, relativeOffset: Float) {
|
fun draw(view: ContentTextView, canvas: Canvas, relativeOffset: Float) {
|
||||||
if (AppConfig.optimizeRender) {
|
if (ReadConfig.optimizeRender) {
|
||||||
render(view)
|
render(view)
|
||||||
canvas.withTranslation(0f, relativeOffset) {
|
canvas.withTranslation(0f, relativeOffset) {
|
||||||
canvasRecorder.draw(this)
|
canvasRecorder.draw(this)
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import io.legado.app.constant.EventBus
|
|||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.BookChapter
|
import io.legado.app.data.entities.BookChapter
|
||||||
import io.legado.app.help.book.BookContent
|
import io.legado.app.help.book.BookContent
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.config.ReadBookConfig.dottedBase
|
import io.legado.app.help.config.ReadBookConfig.dottedBase
|
||||||
import io.legado.app.help.config.ReadBookConfig.dottedRatio
|
import io.legado.app.help.config.ReadBookConfig.dottedRatio
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.ui.book.read.page.entities.TextChapter
|
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.buildMainHandler
|
import io.legado.app.utils.buildMainHandler
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
import io.legado.app.utils.isContentScheme
|
import io.legado.app.utils.isContentScheme
|
||||||
@@ -190,7 +190,7 @@ object ChapterProvider {
|
|||||||
displayTitle.splitNotBlank("\n").forEach { text ->
|
displayTitle.splitNotBlank("\n").forEach { text ->
|
||||||
setTypeText(
|
setTypeText(
|
||||||
book, absStartX, durY,
|
book, absStartX, durY,
|
||||||
if (AppConfig.enableReview) text + reviewChar else text,
|
if (ReadConfig.enableReview) text + reviewChar else text,
|
||||||
textPages,
|
textPages,
|
||||||
stringBuilder,
|
stringBuilder,
|
||||||
titlePaint,
|
titlePaint,
|
||||||
@@ -279,7 +279,7 @@ object ChapterProvider {
|
|||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
setTypeText(
|
setTypeText(
|
||||||
book, absStartX, durY,
|
book, absStartX, durY,
|
||||||
if (AppConfig.enableReview) text + reviewChar else text,
|
if (ReadConfig.enableReview) text + reviewChar else text,
|
||||||
textPages,
|
textPages,
|
||||||
stringBuilder,
|
stringBuilder,
|
||||||
contentPaint,
|
contentPaint,
|
||||||
@@ -918,7 +918,7 @@ object ChapterProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
when (AppConfig.systemTypefaces) {
|
when (ReadConfig.systemTypefaces) {
|
||||||
1 -> Typeface.SERIF
|
1 -> Typeface.SERIF
|
||||||
2 -> Typeface.MONOSPACE
|
2 -> Typeface.MONOSPACE
|
||||||
else -> Typeface.SANS_SERIF
|
else -> Typeface.SANS_SERIF
|
||||||
@@ -1013,7 +1013,7 @@ object ChapterProvider {
|
|||||||
tPaint.setFontVariationSettings("'wght' ${ReadBookConfig.titleBold}")
|
tPaint.setFontVariationSettings("'wght' ${ReadBookConfig.titleBold}")
|
||||||
tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.toFloat().spToPx()
|
tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.toFloat().spToPx()
|
||||||
tPaint.isAntiAlias = true
|
tPaint.isAntiAlias = true
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && AppConfig.optimizeRender) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && ReadConfig.optimizeRender) {
|
||||||
tPaint.isLinearText = true
|
tPaint.isLinearText = true
|
||||||
}
|
}
|
||||||
if (ReadBookConfig.textItalic) {
|
if (ReadBookConfig.textItalic) {
|
||||||
@@ -1036,7 +1036,7 @@ object ChapterProvider {
|
|||||||
cPaint.setFontVariationSettings("'wght' ${ReadBookConfig.textBold}")
|
cPaint.setFontVariationSettings("'wght' ${ReadBookConfig.textBold}")
|
||||||
cPaint.textSize = ReadBookConfig.textSize.toFloat().spToPx()
|
cPaint.textSize = ReadBookConfig.textSize.toFloat().spToPx()
|
||||||
cPaint.isAntiAlias = true
|
cPaint.isAntiAlias = true
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && AppConfig.optimizeRender) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && ReadConfig.optimizeRender) {
|
||||||
cPaint.isLinearText = true
|
cPaint.isLinearText = true
|
||||||
}
|
}
|
||||||
if (ReadBookConfig.textItalic) {
|
if (ReadBookConfig.textItalic) {
|
||||||
@@ -1086,7 +1086,7 @@ object ChapterProvider {
|
|||||||
* 更新绘制尺寸
|
* 更新绘制尺寸
|
||||||
*/
|
*/
|
||||||
fun upLayout() {
|
fun upLayout() {
|
||||||
when (AppConfig.doublePageHorizontal) {
|
when (ReadConfig.doubleHorizontalPage) {
|
||||||
"0" -> doublePage = false
|
"0" -> doublePage = false
|
||||||
"1" -> doublePage = true
|
"1" -> doublePage = true
|
||||||
"2" -> {
|
"2" -> {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import io.legado.app.data.repository.HighlightRuleRepository
|
|||||||
import io.legado.app.help.book.BookContent
|
import io.legado.app.help.book.BookContent
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.book.getBookSource
|
import io.legado.app.help.book.getBookSource
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.model.ImageProvider
|
import io.legado.app.model.ImageProvider
|
||||||
@@ -45,6 +44,7 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider.reviewChar
|
|||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceChar
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceChar
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceCharC
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceCharC
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceCharD
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider.srcReplaceCharD
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.StringUtils
|
import io.legado.app.utils.StringUtils
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
@@ -141,7 +141,7 @@ class TextChapterLayout(
|
|||||||
private val useZhLayout = ReadBookConfig.useZhLayout
|
private val useZhLayout = ReadBookConfig.useZhLayout
|
||||||
private val isMiddleTitle = ReadBookConfig.isMiddleTitle
|
private val isMiddleTitle = ReadBookConfig.isMiddleTitle
|
||||||
private val textFullJustify = ReadBookConfig.textFullJustify
|
private val textFullJustify = ReadBookConfig.textFullJustify
|
||||||
private val adaptSpecialStyle = AppConfig.adaptSpecialStyle
|
private val adaptSpecialStyle = ReadConfig.adaptSpecialStyle
|
||||||
private val pageAnim = book.getPageAnim()
|
private val pageAnim = book.getPageAnim()
|
||||||
private val titleSegType = ReadBookConfig.titleSegType
|
private val titleSegType = ReadBookConfig.titleSegType
|
||||||
private val titleSegDistance = ReadBookConfig.titleSegDistance
|
private val titleSegDistance = ReadBookConfig.titleSegDistance
|
||||||
@@ -490,7 +490,7 @@ class TextChapterLayout(
|
|||||||
text = sb.toString()
|
text = sb.toString()
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
wordCount += text.replace(noWordCountRegex,"").length
|
wordCount += text.replace(noWordCountRegex,"").length
|
||||||
val textToType = if (AppConfig.enableReview) text + reviewChar else text
|
val textToType = if (ReadConfig.enableReview) text + reviewChar else text
|
||||||
setTypeText(
|
setTypeText(
|
||||||
book,
|
book,
|
||||||
textToType,
|
textToType,
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
package io.legado.app.ui.book.read.sheet
|
package io.legado.app.ui.book.read.sheet
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
|
import io.legado.app.data.appDb
|
||||||
|
import io.legado.app.data.entities.ReplaceRule
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.button.series.MediumTonalButton
|
||||||
|
import io.legado.app.ui.widget.components.card.NormalCard
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
private const val CHINESE_CONVERT_ID = -1L
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EffectiveReplacesSheet(
|
fun EffectiveReplacesSheet(
|
||||||
@@ -26,12 +44,26 @@ fun EffectiveReplacesSheet(
|
|||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onOpenReplaceEditor: (id: Long, pattern: String?) -> Unit,
|
onOpenReplaceEditor: (id: Long, pattern: String?) -> Unit,
|
||||||
onReplaceRuleChanged: () -> Unit,
|
onReplaceRuleChanged: () -> Unit,
|
||||||
|
onNavigateToTextEffects: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val effectiveRules = remember {
|
val scope = rememberCoroutineScope()
|
||||||
|
val chineseConvertActive = ReadConfig.chineseConverterType > 0
|
||||||
|
val chineseConvertItem = remember { ReplaceRule(CHINESE_CONVERT_ID, "繁简转换") }
|
||||||
|
|
||||||
|
val effectiveRules = remember(show) {
|
||||||
ReadBook.curTextChapter?.effectiveReplaceRules ?: emptyList()
|
ReadBook.curTextChapter?.effectiveReplaceRules ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
var isEdited by remember { mutableStateOf(false) }
|
val items = remember(show, effectiveRules, chineseConvertActive) {
|
||||||
|
if (chineseConvertActive) {
|
||||||
|
effectiveRules + chineseConvertItem
|
||||||
|
} else {
|
||||||
|
effectiveRules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isEdited by remember(show) { mutableStateOf(false) }
|
||||||
|
var disabledIds by remember(show) { mutableStateOf(emptySet<Long>()) }
|
||||||
|
|
||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
show = show,
|
show = show,
|
||||||
@@ -44,18 +76,80 @@ fun EffectiveReplacesSheet(
|
|||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
items(effectiveRules, key = { it.id }) { rule ->
|
items(items, key = { it.id }) { rule ->
|
||||||
Text(
|
if (rule.id !in disabledIds) {
|
||||||
text = rule.name,
|
NormalCard(
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
onClick = {
|
||||||
modifier = Modifier
|
if (rule.id == CHINESE_CONVERT_ID) {
|
||||||
.fillMaxWidth()
|
onNavigateToTextEffects()
|
||||||
.clickable {
|
} else {
|
||||||
onOpenReplaceEditor(rule.id, rule.pattern)
|
onOpenReplaceEditor(rule.id, rule.pattern)
|
||||||
}
|
}
|
||||||
.padding(vertical = 12.dp),
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
containerColor = LegadoTheme.colorScheme.onSheetContent
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
AppText(
|
||||||
|
text = rule.name,
|
||||||
|
style = LegadoTheme.typography.labelLargeEmphasized,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
HorizontalDivider()
|
if (rule.id != CHINESE_CONVERT_ID) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
AppText(
|
||||||
|
text = rule.pattern,
|
||||||
|
style = LegadoTheme.typography.bodySmall,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(4.dp))
|
||||||
|
AppIcon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(14.dp),
|
||||||
|
tint = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(4.dp))
|
||||||
|
AppText(
|
||||||
|
text = rule.replacement.ifEmpty { "∅" },
|
||||||
|
style = LegadoTheme.typography.bodySmall,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MediumTonalButton(
|
||||||
|
onClick = {
|
||||||
|
disabledIds = disabledIds + rule.id
|
||||||
|
isEdited = true
|
||||||
|
if (rule.id == CHINESE_CONVERT_ID) {
|
||||||
|
ReadConfig.chineseConverterType = 0
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
rule.isEnabled = false
|
||||||
|
appDb.replaceRuleDao.insert(rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = Icons.Default.Close
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ fun MoreConfigSheet(
|
|||||||
onUseZhLayoutChange = {
|
onUseZhLayoutChange = {
|
||||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseZhLayout(it)))
|
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseZhLayout(it)))
|
||||||
},
|
},
|
||||||
onShowBrightnessViewChange = {
|
|
||||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ShowBrightnessView(it)))
|
|
||||||
},
|
|
||||||
onUseUnderlineChange = {
|
onUseUnderlineChange = {
|
||||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseUnderlineGlobal(it)))
|
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseUnderlineGlobal(it)))
|
||||||
},
|
},
|
||||||
@@ -170,7 +167,6 @@ private fun ScreenSettings(
|
|||||||
onTextBottomJustifyChange: (Boolean) -> Unit,
|
onTextBottomJustifyChange: (Boolean) -> Unit,
|
||||||
onAdaptSpecialStyleChange: (Boolean) -> Unit,
|
onAdaptSpecialStyleChange: (Boolean) -> Unit,
|
||||||
onUseZhLayoutChange: (Boolean) -> Unit,
|
onUseZhLayoutChange: (Boolean) -> Unit,
|
||||||
onShowBrightnessViewChange: (Boolean) -> Unit,
|
|
||||||
onUseUnderlineChange: (Boolean) -> Unit,
|
onUseUnderlineChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val screenDirectionEntries = stringArrayResource(R.array.screen_direction_title)
|
val screenDirectionEntries = stringArrayResource(R.array.screen_direction_title)
|
||||||
@@ -232,11 +228,6 @@ private fun ScreenSettings(
|
|||||||
checked = preferences.useZhLayout,
|
checked = preferences.useZhLayout,
|
||||||
onCheckedChange = onUseZhLayoutChange,
|
onCheckedChange = onUseZhLayoutChange,
|
||||||
)
|
)
|
||||||
TinySwitchSettingItem(
|
|
||||||
title = stringResource(R.string.show_brightness_view),
|
|
||||||
checked = preferences.showBrightnessView,
|
|
||||||
onCheckedChange = onShowBrightnessViewChange,
|
|
||||||
)
|
|
||||||
TinySwitchSettingItem(
|
TinySwitchSettingItem(
|
||||||
title = stringResource(R.string.use_underline),
|
title = stringResource(R.string.use_underline),
|
||||||
checked = preferences.useUnderline,
|
checked = preferences.useUnderline,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -23,62 +22,9 @@ import io.legado.app.ui.book.read.ConfigUpdate
|
|||||||
import io.legado.app.ui.book.read.ReadBookButtonConfigItem
|
import io.legado.app.ui.book.read.ReadBookButtonConfigItem
|
||||||
import io.legado.app.ui.book.read.ReadBookIntent
|
import io.legado.app.ui.book.read.ReadBookIntent
|
||||||
import io.legado.app.ui.book.read.ReadBookStyleConfig
|
import io.legado.app.ui.book.read.ReadBookStyleConfig
|
||||||
import io.legado.app.ui.widget.components.SectionTitle
|
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
|
||||||
import io.legado.app.ui.widget.components.tabRow.CardTabRow
|
import io.legado.app.ui.widget.components.tabRow.CardTabRow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ReadStyleSheet(
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onOpenPaddingConfig: () -> Unit,
|
|
||||||
onOpenMoreConfig: () -> Unit,
|
|
||||||
onOpenBgTextConfig: (Int) -> Unit,
|
|
||||||
onOpenShadowSet: () -> Unit,
|
|
||||||
onOpenUnderlineConfig: () -> Unit,
|
|
||||||
onOpenHighlightRule: () -> Unit,
|
|
||||||
onOpenFontSelect: () -> Unit,
|
|
||||||
onToggleDayNight: () -> Unit,
|
|
||||||
readMenuCustomIcons: Map<String, String> = emptyMap(),
|
|
||||||
bottomBarButtons: List<ReadBookButtonConfigItem> = emptyList(),
|
|
||||||
onIntent: (ReadBookIntent) -> Unit,
|
|
||||||
styleConfig: ReadBookStyleConfig = ReadBookStyleConfig(),
|
|
||||||
) {
|
|
||||||
var showTextTitle by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
AppModalBottomSheet(
|
|
||||||
show = true,
|
|
||||||
onDismissRequest = {
|
|
||||||
onIntent(ReadBookIntent.SaveReadStyleConfig)
|
|
||||||
onDismissRequest()
|
|
||||||
},
|
|
||||||
title = stringResource(R.string.read_config),
|
|
||||||
) {
|
|
||||||
ReadStyleContent(
|
|
||||||
onOpenPaddingConfig = onOpenPaddingConfig,
|
|
||||||
onOpenMoreConfig = onOpenMoreConfig,
|
|
||||||
onOpenBgTextConfig = onOpenBgTextConfig,
|
|
||||||
onOpenTextTitle = { showTextTitle = true },
|
|
||||||
onOpenFontSelect = onOpenFontSelect,
|
|
||||||
onToggleDayNight = onToggleDayNight,
|
|
||||||
readMenuCustomIcons = readMenuCustomIcons,
|
|
||||||
bottomBarButtons = bottomBarButtons,
|
|
||||||
onIntent = onIntent,
|
|
||||||
styleConfig = styleConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
TextTitlePage(
|
|
||||||
show = showTextTitle,
|
|
||||||
onDismissRequest = { showTextTitle = false },
|
|
||||||
onOpenShadowSet = onOpenShadowSet,
|
|
||||||
onOpenUnderlineConfig = onOpenUnderlineConfig,
|
|
||||||
onOpenHighlightRule = onOpenHighlightRule,
|
|
||||||
onOpenFontSelect = onOpenFontSelect,
|
|
||||||
onIntent = onIntent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadStyleContent(
|
fun ReadStyleContent(
|
||||||
onOpenPaddingConfig: () -> Unit,
|
onOpenPaddingConfig: () -> Unit,
|
||||||
@@ -87,6 +33,7 @@ fun ReadStyleContent(
|
|||||||
onOpenTextTitle: () -> Unit,
|
onOpenTextTitle: () -> Unit,
|
||||||
onOpenFontSelect: () -> Unit,
|
onOpenFontSelect: () -> Unit,
|
||||||
onToggleDayNight: () -> Unit,
|
onToggleDayNight: () -> Unit,
|
||||||
|
onPageChanged: (Int) -> Unit = {},
|
||||||
readMenuCustomIcons: Map<String, String> = emptyMap(),
|
readMenuCustomIcons: Map<String, String> = emptyMap(),
|
||||||
bottomBarButtons: List<ReadBookButtonConfigItem> = emptyList(),
|
bottomBarButtons: List<ReadBookButtonConfigItem> = emptyList(),
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -100,6 +47,7 @@ fun ReadStyleContent(
|
|||||||
LaunchedEffect(pagerState) {
|
LaunchedEffect(pagerState) {
|
||||||
snapshotFlow { pagerState.currentPage }.collect { page ->
|
snapshotFlow { pagerState.currentPage }.collect { page ->
|
||||||
currentPage = page
|
currentPage = page
|
||||||
|
onPageChanged(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -340,6 +340,31 @@ private fun GlobalMenuTab(
|
|||||||
|
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|
||||||
|
SectionTitle(stringResource(R.string.show_brightness_view))
|
||||||
|
|
||||||
|
TinyDropdownSettingItem(
|
||||||
|
title = stringResource(R.string.show_brightness_view),
|
||||||
|
selectedValue = preferences.showBrightnessView,
|
||||||
|
displayEntries = stringArrayResource(R.array.brightness_bar_mode_title),
|
||||||
|
entryValues = stringArrayResource(R.array.brightness_bar_mode_value),
|
||||||
|
onValueChange = {
|
||||||
|
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ShowBrightnessView(it)))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if (preferences.showBrightnessView == "2") {
|
||||||
|
TinyDropdownSettingItem(
|
||||||
|
title = stringResource(R.string.brightness_bar_position),
|
||||||
|
selectedValue = preferences.brightnessVwPos,
|
||||||
|
displayEntries = stringArrayResource(R.array.brightness_bar_position_title),
|
||||||
|
entryValues = stringArrayResource(R.array.brightness_bar_position_value),
|
||||||
|
onValueChange = {
|
||||||
|
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.BrightnessVwPos(it)))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|
||||||
var borderEnabled by remember {
|
var borderEnabled by remember {
|
||||||
mutableStateOf(preferences.readMenuBorderWidth > 0)
|
mutableStateOf(preferences.readMenuBorderWidth > 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import io.legado.app.ui.book.read.ConfigUpdate
|
|||||||
import io.legado.app.ui.book.read.ReadBookIntent
|
import io.legado.app.ui.book.read.ReadBookIntent
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfig
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
|
||||||
import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
|
import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.TinyColorSettingItem
|
import io.legado.app.ui.widget.components.settingItem.TinyColorSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
|
import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
|
||||||
@@ -55,51 +54,6 @@ private const val COLOR_TEXT = 1
|
|||||||
private const val COLOR_ACCENT = 2
|
private const val COLOR_ACCENT = 2
|
||||||
private const val COLOR_TITLE = 4
|
private const val COLOR_TITLE = 4
|
||||||
|
|
||||||
// ========== Text & Title Sheet ==========
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun TextTitlePage(
|
|
||||||
show: Boolean,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onOpenShadowSet: () -> Unit,
|
|
||||||
onOpenUnderlineConfig: () -> Unit,
|
|
||||||
onOpenHighlightRule: () -> Unit,
|
|
||||||
onOpenFontSelect: () -> Unit,
|
|
||||||
onIntent: (ReadBookIntent) -> Unit,
|
|
||||||
) {
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val tabTitles = listOf(
|
|
||||||
stringResource(R.string.read_config_text_effects),
|
|
||||||
stringResource(R.string.read_config_layout_spacing),
|
|
||||||
stringResource(R.string.read_config_title_settings),
|
|
||||||
)
|
|
||||||
val pagerState = rememberPagerState(pageCount = { 3 })
|
|
||||||
var selectedTab by remember { mutableIntStateOf(0) }
|
|
||||||
|
|
||||||
LaunchedEffect(pagerState) {
|
|
||||||
snapshotFlow { pagerState.settledPage }.collect { selectedTab = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
AppModalBottomSheet(
|
|
||||||
show = show,
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
title = stringResource(R.string.read_config_text_effects),
|
|
||||||
) {
|
|
||||||
ReadStyleTextTitleContent(
|
|
||||||
tabTitles = tabTitles,
|
|
||||||
selectedTab = selectedTab,
|
|
||||||
onSelectedTabChange = { selectedTab = it },
|
|
||||||
pagerState = pagerState,
|
|
||||||
onOpenShadowSet = onOpenShadowSet,
|
|
||||||
onOpenUnderlineConfig = onOpenUnderlineConfig,
|
|
||||||
onOpenHighlightRule = onOpenHighlightRule,
|
|
||||||
onOpenFontSelect = onOpenFontSelect,
|
|
||||||
animateToPage = { page -> scope.launch { pagerState.animateScrollToPage(page) } },
|
|
||||||
onIntent = onIntent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadStyleTextTitleContent(
|
fun ReadStyleTextTitleContent(
|
||||||
onOpenShadowSet: () -> Unit,
|
onOpenShadowSet: () -> Unit,
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package io.legado.app.ui.book.searchContent
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import io.legado.app.base.BaseComposeActivity
|
|
||||||
import io.legado.app.ui.theme.AppTheme
|
|
||||||
|
|
||||||
class SearchContentActivity : BaseComposeActivity() {
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
|
|
||||||
AppTheme {
|
|
||||||
SearchContentScreen(
|
|
||||||
onBack = { finish() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package io.legado.app.ui.book.searchContent
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton bus for passing search results from [SearchContentScreen] back to the reader.
|
||||||
|
*
|
||||||
|
* Uses [MutableSharedFlow] with `replay = 1` so the reader entry receives the result
|
||||||
|
* even if it subscribes after the emission (the reader entry is not composed while
|
||||||
|
* the search route is on top of the NavDisplay back stack).
|
||||||
|
*/
|
||||||
|
object SearchContentResult {
|
||||||
|
|
||||||
|
data class Result(
|
||||||
|
val searchResults: List<SearchResult>,
|
||||||
|
val index: Int,
|
||||||
|
val query: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val _results = MutableSharedFlow<Result>(replay = 1)
|
||||||
|
val results = _results.asSharedFlow()
|
||||||
|
|
||||||
|
fun emitResult(result: Result) {
|
||||||
|
_results.tryEmit(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetReplayCache() {
|
||||||
|
_results.resetReplayCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package io.legado.app.ui.book.searchContent
|
package io.legado.app.ui.book.searchContent
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.activity.compose.LocalActivity
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -81,8 +78,6 @@ fun SearchContentScreen(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
viewModel: SearchContentViewModel = koinViewModel()
|
viewModel: SearchContentViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val activity = LocalActivity.current
|
|
||||||
|
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
val searchQuery by viewModel.searchQuery.collectAsState()
|
val searchQuery by viewModel.searchQuery.collectAsState()
|
||||||
val replaceEnabled by viewModel.replaceEnabled.collectAsState()
|
val replaceEnabled by viewModel.replaceEnabled.collectAsState()
|
||||||
@@ -259,13 +254,8 @@ fun SearchContentScreen(
|
|||||||
result = result,
|
result = result,
|
||||||
isCurrentChapter = result.chapterIndex == durChapterIndex,
|
isCurrentChapter = result.chapterIndex == durChapterIndex,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.onSearchResultClick(result) { key ->
|
if (viewModel.onSearchResultClick(result)) {
|
||||||
val intent = Intent().apply {
|
onBack()
|
||||||
putExtra("key", key)
|
|
||||||
putExtra("index", index)
|
|
||||||
}
|
|
||||||
activity?.setResult(Activity.RESULT_OK, intent)
|
|
||||||
activity?.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package io.legado.app.ui.book.searchContent
|
package io.legado.app.ui.book.searchContent
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import io.legado.app.constant.EventBus
|
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.SearchContentHistory
|
import io.legado.app.data.entities.SearchContentHistory
|
||||||
import io.legado.app.data.repository.BookRepository
|
import io.legado.app.data.repository.BookRepository
|
||||||
import io.legado.app.data.repository.SearchContentRepository
|
import io.legado.app.data.repository.SearchContentRepository
|
||||||
import io.legado.app.help.IntentData
|
|
||||||
import io.legado.app.utils.postEvent
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -42,15 +38,13 @@ sealed interface SearchContentState {
|
|||||||
|
|
||||||
|
|
||||||
class SearchContentViewModel(
|
class SearchContentViewModel(
|
||||||
savedStateHandle: SavedStateHandle,
|
val bookUrl: String,
|
||||||
|
initialSearchWord: String?,
|
||||||
|
private val searchResultIndex: Int,
|
||||||
private val bookRepository: BookRepository,
|
private val bookRepository: BookRepository,
|
||||||
private val searchContentRepository: SearchContentRepository
|
private val searchContentRepository: SearchContentRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val bookUrl: String = savedStateHandle.get<String>("bookUrl") ?: ""
|
|
||||||
private val initialSearchWord: String? = savedStateHandle.get<String>("searchWord")
|
|
||||||
private val searchResultIndex: Int = savedStateHandle.get<Int>("searchResultIndex") ?: 0
|
|
||||||
|
|
||||||
private val _searchQuery = MutableStateFlow(initialSearchWord ?: "")
|
private val _searchQuery = MutableStateFlow(initialSearchWord ?: "")
|
||||||
val searchQuery = _searchQuery.asStateFlow()
|
val searchQuery = _searchQuery.asStateFlow()
|
||||||
|
|
||||||
@@ -189,12 +183,18 @@ class SearchContentViewModel(
|
|||||||
hasAutoScrolled = true
|
hasAutoScrolled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSearchResultClick(searchResult: SearchResult, onSuccess: (key: Long) -> Unit) {
|
fun onSearchResultClick(searchResult: SearchResult): Boolean {
|
||||||
stopSearch()
|
stopSearch()
|
||||||
postEvent(EventBus.SEARCH_RESULT, uiState.value.searchResults)
|
val results = uiState.value.searchResults
|
||||||
val key = System.currentTimeMillis()
|
val index = results.indexOf(searchResult)
|
||||||
IntentData.put("searchResult$key", searchResult)
|
if (index < 0) return false
|
||||||
IntentData.put("searchResultList$key", uiState.value.searchResults)
|
SearchContentResult.emitResult(
|
||||||
onSuccess(key)
|
SearchContentResult.Result(
|
||||||
|
searchResults = results,
|
||||||
|
index = index,
|
||||||
|
query = searchResult.query
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,139 @@
|
|||||||
package io.legado.app.ui.config.readConfig
|
package io.legado.app.ui.config.readConfig
|
||||||
|
|
||||||
|
import io.legado.app.BuildConfig
|
||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
|
import io.legado.app.data.repository.ReadPreferences
|
||||||
import io.legado.app.ui.config.prefDelegate
|
import io.legado.app.ui.config.prefDelegate
|
||||||
|
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||||
|
import io.legado.app.utils.isNightMode
|
||||||
|
import io.legado.app.utils.sysConfiguration
|
||||||
|
|
||||||
object ReadConfig {
|
object ReadConfig {
|
||||||
|
|
||||||
|
val isEInkMode: Boolean
|
||||||
|
get() = ThemeConfig.appTheme == "4"
|
||||||
|
|
||||||
|
var isNightTheme: Boolean
|
||||||
|
get() = when (ThemeConfig.themeMode) {
|
||||||
|
"1" -> false
|
||||||
|
"2" -> true
|
||||||
|
else -> sysConfiguration.isNightMode
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
val newMode = if (value) "2" else "1"
|
||||||
|
if (ThemeConfig.themeMode != newMode) {
|
||||||
|
ThemeConfig.themeMode = newMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var useAntiAlias by prefDelegate(
|
||||||
|
PreferKey.antiAlias,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
val enableReview: Boolean
|
||||||
|
get() = BuildConfig.DEBUG && enableReviewPref
|
||||||
|
|
||||||
|
private var enableReviewPref by prefDelegate(
|
||||||
|
PreferKey.enableReview,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
var systemTypefaces by prefDelegate(
|
||||||
|
PreferKey.systemTypefaces,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
var readUrlInBrowser by prefDelegate(
|
||||||
|
PreferKey.readUrlOpenInBrowser,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
var contentSelectSpeakMod by prefDelegate(
|
||||||
|
PreferKey.contentSelectSpeakMod,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
var syncBookProgress by prefDelegate(
|
||||||
|
PreferKey.syncBookProgress,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
var syncBookProgressPlus by prefDelegate(
|
||||||
|
PreferKey.syncBookProgressPlus,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
var preDownloadNum by prefDelegate(
|
||||||
|
PreferKey.preDownloadNum,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
|
||||||
|
var imageRetainNum by prefDelegate(
|
||||||
|
PreferKey.imageRetainNum,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
var audioCacheCleanTimeOrgin by prefDelegate(
|
||||||
|
PreferKey.audioCacheCleanTime,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
|
||||||
|
val audioCacheCleanTime: Long
|
||||||
|
get() = audioCacheCleanTimeOrgin * 60 * 1000L
|
||||||
|
|
||||||
|
var audioPreDownloadNum by prefDelegate(
|
||||||
|
PreferKey.audioPreDownloadNum,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val defaultSpeechRate = 5
|
||||||
|
|
||||||
|
val speechRatePlay: Int
|
||||||
|
get() = if (ttsFollowSys) defaultSpeechRate else ttsSpeechRate
|
||||||
|
|
||||||
|
fun syncReadPreferences(preferences: ReadPreferences) {
|
||||||
|
optimizeRender = preferences.optimizeRender
|
||||||
|
adaptSpecialStyle = preferences.adaptSpecialStyle
|
||||||
|
useUnderline = preferences.useUnderline
|
||||||
|
brightnessVwPos = preferences.brightnessVwPos
|
||||||
|
readBrightness = preferences.readBrightness
|
||||||
|
brightnessAuto = preferences.brightnessAuto
|
||||||
|
chineseConverterType = preferences.chineseConverterType
|
||||||
|
clickActionTL = preferences.clickActionTL
|
||||||
|
clickActionTC = preferences.clickActionTC
|
||||||
|
clickActionTR = preferences.clickActionTR
|
||||||
|
clickActionML = preferences.clickActionML
|
||||||
|
clickActionMC = preferences.clickActionMC
|
||||||
|
clickActionMR = preferences.clickActionMR
|
||||||
|
clickActionBL = preferences.clickActionBL
|
||||||
|
clickActionBC = preferences.clickActionBC
|
||||||
|
clickActionBR = preferences.clickActionBR
|
||||||
|
screenOrientation = preferences.screenOrientation
|
||||||
|
noAnimScrollPage = preferences.noAnimScrollPage
|
||||||
|
tocUiUseReplace = preferences.tocUiUseReplace
|
||||||
|
tocCountWords = preferences.tocCountWords
|
||||||
|
autoChangeSource = preferences.autoChangeSource
|
||||||
|
clickImgWay = preferences.clickImgWay
|
||||||
|
doubleHorizontalPage = preferences.doubleHorizontalPage
|
||||||
|
progressBarBehavior = preferences.progressBarBehavior
|
||||||
|
keyPageOnLongPress = preferences.keyPageOnLongPress
|
||||||
|
volumeKeyPage = preferences.volumeKeyPage
|
||||||
|
volumeKeyPageOnPlay = preferences.volumeKeyPageOnPlay
|
||||||
|
mouseWheelPage = preferences.mouseWheelPage
|
||||||
|
paddingDisplayCutouts = preferences.paddingDisplayCutouts
|
||||||
|
pageTouchSlop = preferences.pageTouchSlop
|
||||||
|
showReadTitleAddition = preferences.showReadTitleAddition
|
||||||
|
titleBarMode = preferences.titleBarMode
|
||||||
|
menuAlpha = preferences.menuAlpha
|
||||||
|
readSliderMode = preferences.readSliderMode
|
||||||
|
readBarStyleFollowPage = preferences.readBarStyleFollowPage
|
||||||
|
readBarStyle = preferences.readBarStyle
|
||||||
|
defaultSourceChangeAll = preferences.defaultSourceChangeAll
|
||||||
|
sliderVibrator = preferences.sliderVibrator
|
||||||
|
selectVibrator = preferences.selectVibrator
|
||||||
|
}
|
||||||
|
|
||||||
var tocUiUseReplace by prefDelegate(
|
var tocUiUseReplace by prefDelegate(
|
||||||
PreferKey.tocUiUseReplace,
|
PreferKey.tocUiUseReplace,
|
||||||
false
|
false
|
||||||
@@ -82,7 +211,22 @@ object ReadConfig {
|
|||||||
|
|
||||||
var showBrightnessView by prefDelegate(
|
var showBrightnessView by prefDelegate(
|
||||||
PreferKey.showBrightnessView,
|
PreferKey.showBrightnessView,
|
||||||
true
|
"1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var brightnessVwPos by prefDelegate(
|
||||||
|
PreferKey.brightnessVwPos,
|
||||||
|
"1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var readBrightness by prefDelegate(
|
||||||
|
PreferKey.brightness,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
|
||||||
|
var brightnessAuto by prefDelegate(
|
||||||
|
PreferKey.brightnessAuto,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
var useUnderline by prefDelegate(
|
var useUnderline by prefDelegate(
|
||||||
@@ -180,6 +324,73 @@ object ReadConfig {
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var clickActionTL by prefDelegate(
|
||||||
|
PreferKey.clickActionTL,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionTC by prefDelegate(
|
||||||
|
PreferKey.clickActionTC,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionTR by prefDelegate(
|
||||||
|
PreferKey.clickActionTR,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionML by prefDelegate(
|
||||||
|
PreferKey.clickActionML,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionMC by prefDelegate(
|
||||||
|
PreferKey.clickActionMC,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionMR by prefDelegate(
|
||||||
|
PreferKey.clickActionMR,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionBL by prefDelegate(
|
||||||
|
PreferKey.clickActionBL,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionBC by prefDelegate(
|
||||||
|
PreferKey.clickActionBC,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
var clickActionBR by prefDelegate(
|
||||||
|
PreferKey.clickActionBR,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
fun hasMenuClickArea(): Boolean {
|
||||||
|
return clickActionTL * clickActionTC * clickActionTR *
|
||||||
|
clickActionML * clickActionMC * clickActionMR *
|
||||||
|
clickActionBL * clickActionBC * clickActionBR == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detectClickArea() {
|
||||||
|
if (!hasMenuClickArea()) {
|
||||||
|
clickActionMC = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readBarStyleFollowPage by prefDelegate(
|
||||||
|
PreferKey.readBarStyleFollowPage,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
var readBarStyle by prefDelegate(
|
||||||
|
PreferKey.readBarStyle,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
var prevKeys by prefDelegate(
|
var prevKeys by prefDelegate(
|
||||||
PreferKey.prevKeys,
|
PreferKey.prevKeys,
|
||||||
""
|
""
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ data class ReadConfigUiState(
|
|||||||
val textBottomJustify: Boolean = true,
|
val textBottomJustify: Boolean = true,
|
||||||
val adaptSpecialStyle: Boolean = true,
|
val adaptSpecialStyle: Boolean = true,
|
||||||
val useZhLayout: Boolean = false,
|
val useZhLayout: Boolean = false,
|
||||||
val showBrightnessView: Boolean = true,
|
val showBrightnessView: String = "1",
|
||||||
|
val brightnessVwPos: String = "1",
|
||||||
|
val brightnessAuto: Boolean = false,
|
||||||
val useUnderline: Boolean = false,
|
val useUnderline: Boolean = false,
|
||||||
val readSliderMode: String = "0",
|
val readSliderMode: String = "0",
|
||||||
val doubleHorizontalPage: String = "0",
|
val doubleHorizontalPage: String = "0",
|
||||||
@@ -53,7 +55,8 @@ sealed interface ReadConfigIntent {
|
|||||||
data class TextBottomJustifyChanged(val value: Boolean) : ReadConfigIntent
|
data class TextBottomJustifyChanged(val value: Boolean) : ReadConfigIntent
|
||||||
data class AdaptSpecialStyleChanged(val value: Boolean) : ReadConfigIntent
|
data class AdaptSpecialStyleChanged(val value: Boolean) : ReadConfigIntent
|
||||||
data class UseZhLayoutChanged(val value: Boolean) : ReadConfigIntent
|
data class UseZhLayoutChanged(val value: Boolean) : ReadConfigIntent
|
||||||
data class ShowBrightnessViewChanged(val value: Boolean) : ReadConfigIntent
|
data class ShowBrightnessViewChanged(val value: String) : ReadConfigIntent
|
||||||
|
data class BrightnessVwPosChanged(val value: String) : ReadConfigIntent
|
||||||
data class UseUnderlineChanged(val value: Boolean) : ReadConfigIntent
|
data class UseUnderlineChanged(val value: Boolean) : ReadConfigIntent
|
||||||
data class ReadSliderModeChanged(val value: String) : ReadConfigIntent
|
data class ReadSliderModeChanged(val value: String) : ReadConfigIntent
|
||||||
data class DoubleHorizontalPageChanged(val value: String) : ReadConfigIntent
|
data class DoubleHorizontalPageChanged(val value: String) : ReadConfigIntent
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ import io.legado.app.R
|
|||||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
|
||||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
|
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
|
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
|
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@@ -173,14 +173,28 @@ fun ReadConfigScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SwitchSettingItem(
|
DropdownListSettingItem(
|
||||||
title = stringResource(R.string.show_brightness_view),
|
title = stringResource(R.string.show_brightness_view),
|
||||||
checked = state.showBrightnessView,
|
selectedValue = state.showBrightnessView,
|
||||||
onCheckedChange = {
|
displayEntries = stringArrayResource(R.array.brightness_bar_mode_title),
|
||||||
|
entryValues = stringArrayResource(R.array.brightness_bar_mode_value),
|
||||||
|
onValueChange = {
|
||||||
viewModel.onIntent(ReadConfigIntent.ShowBrightnessViewChanged(it))
|
viewModel.onIntent(ReadConfigIntent.ShowBrightnessViewChanged(it))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (state.showBrightnessView == "2") {
|
||||||
|
DropdownListSettingItem(
|
||||||
|
title = stringResource(R.string.brightness_bar_position),
|
||||||
|
selectedValue = state.brightnessVwPos,
|
||||||
|
displayEntries = stringArrayResource(R.array.brightness_bar_position_title),
|
||||||
|
entryValues = stringArrayResource(R.array.brightness_bar_position_value),
|
||||||
|
onValueChange = {
|
||||||
|
viewModel.onIntent(ReadConfigIntent.BrightnessVwPosChanged(it))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
SwitchSettingItem(
|
SwitchSettingItem(
|
||||||
title = stringResource(R.string.use_underline),
|
title = stringResource(R.string.use_underline),
|
||||||
checked = state.useUnderline,
|
checked = state.useUnderline,
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ class ReadConfigViewModel(
|
|||||||
readSettingsRepository.setShowBrightnessView(intent.value)
|
readSettingsRepository.setShowBrightnessView(intent.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ReadConfigIntent.BrightnessVwPosChanged -> {
|
||||||
|
readSettingsRepository.setBrightnessVwPos(intent.value)
|
||||||
|
}
|
||||||
|
|
||||||
is ReadConfigIntent.UseUnderlineChanged -> {
|
is ReadConfigIntent.UseUnderlineChanged -> {
|
||||||
readSettingsRepository.setUseUnderline(intent.value)
|
readSettingsRepository.setUseUnderline(intent.value)
|
||||||
}
|
}
|
||||||
@@ -209,6 +213,8 @@ class ReadConfigViewModel(
|
|||||||
adaptSpecialStyle = adaptSpecialStyle,
|
adaptSpecialStyle = adaptSpecialStyle,
|
||||||
useZhLayout = useZhLayout,
|
useZhLayout = useZhLayout,
|
||||||
showBrightnessView = showBrightnessView,
|
showBrightnessView = showBrightnessView,
|
||||||
|
brightnessVwPos = brightnessVwPos,
|
||||||
|
brightnessAuto = brightnessAuto,
|
||||||
useUnderline = useUnderline,
|
useUnderline = useUnderline,
|
||||||
readSliderMode = readSliderMode,
|
readSliderMode = readSliderMode,
|
||||||
doubleHorizontalPage = doubleHorizontalPage,
|
doubleHorizontalPage = doubleHorizontalPage,
|
||||||
|
|||||||
@@ -41,17 +41,19 @@ import io.legado.app.ui.book.readRecord.ReadRecordScreen
|
|||||||
import io.legado.app.ui.book.search.SearchIntent
|
import io.legado.app.ui.book.search.SearchIntent
|
||||||
import io.legado.app.ui.book.search.SearchScreen
|
import io.legado.app.ui.book.search.SearchScreen
|
||||||
import io.legado.app.ui.book.search.SearchViewModel
|
import io.legado.app.ui.book.search.SearchViewModel
|
||||||
|
import io.legado.app.ui.book.searchContent.SearchContentScreen
|
||||||
|
import io.legado.app.ui.book.searchContent.SearchContentViewModel
|
||||||
import io.legado.app.ui.book.source.manage.BookSourceActivity
|
import io.legado.app.ui.book.source.manage.BookSourceActivity
|
||||||
import io.legado.app.ui.config.ConfigNavScreen
|
import io.legado.app.ui.config.ConfigNavScreen
|
||||||
import io.legado.app.ui.config.backupConfig.BackupConfigScreen
|
import io.legado.app.ui.config.backupConfig.BackupConfigScreen
|
||||||
import io.legado.app.ui.config.coverConfig.CoverConfigScreen
|
import io.legado.app.ui.config.coverConfig.CoverConfigScreen
|
||||||
import io.legado.app.ui.config.customTheme.CustomThemeScreen
|
import io.legado.app.ui.config.customTheme.CustomThemeScreen
|
||||||
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
|
import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
|
||||||
|
import io.legado.app.ui.config.labConfig.LabConfigScreen
|
||||||
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
||||||
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
|
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
|
||||||
import io.legado.app.ui.config.themeManage.ThemeManageScreen
|
import io.legado.app.ui.config.themeManage.ThemeManageScreen
|
||||||
import io.legado.app.ui.config.labConfig.LabConfigScreen
|
|
||||||
import io.legado.app.ui.config.translation.TranslationConfigScreen
|
import io.legado.app.ui.config.translation.TranslationConfigScreen
|
||||||
import io.legado.app.ui.rss.article.MainRouteRssSort
|
import io.legado.app.ui.rss.article.MainRouteRssSort
|
||||||
import io.legado.app.ui.rss.article.RssSortRouteScreen
|
import io.legado.app.ui.rss.article.RssSortRouteScreen
|
||||||
@@ -69,6 +71,7 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
fun MainActivity.mainEntryProvider(
|
fun MainActivity.mainEntryProvider(
|
||||||
@@ -294,6 +297,15 @@ fun MainActivity.mainEntryProvider(
|
|||||||
host = controller,
|
host = controller,
|
||||||
controller = controller,
|
controller = controller,
|
||||||
onEffectsReady = { effectsReady.complete(Unit) },
|
onEffectsReady = { effectsReady.complete(Unit) },
|
||||||
|
onOpenSearch = { word, bookUrl ->
|
||||||
|
onNavigateToRoute(
|
||||||
|
MainRouteSearchContent(
|
||||||
|
bookUrl = bookUrl,
|
||||||
|
searchWord = word,
|
||||||
|
searchResultIndex = readBookViewModel.uiState.value.searchResultIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
DisposableEffect(controller, lifecycleOwner, route.readAloud) {
|
DisposableEffect(controller, lifecycleOwner, route.readAloud) {
|
||||||
@@ -340,6 +352,16 @@ fun MainActivity.mainEntryProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry<MainRouteSearchContent> { route ->
|
||||||
|
val viewModel = koinViewModel<SearchContentViewModel>(
|
||||||
|
parameters = { parametersOf(route) }
|
||||||
|
)
|
||||||
|
SearchContentScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onBack = { onNavigateBack() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
entry<MainRouteSearch> { route ->
|
entry<MainRouteSearch> { route ->
|
||||||
val searchViewModel = koinViewModel<SearchViewModel>()
|
val searchViewModel = koinViewModel<SearchViewModel>()
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ data class MainRouteExploreShow(
|
|||||||
val exploreUrl: String?,
|
val exploreUrl: String?,
|
||||||
) : MainRoute
|
) : MainRoute
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MainRouteSearchContent(
|
||||||
|
val bookUrl: String,
|
||||||
|
val searchWord: String? = null,
|
||||||
|
val searchResultIndex: Int = 0,
|
||||||
|
) : MainRoute
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object MainRouteAbout : MainRoute
|
data object MainRouteAbout : MainRoute
|
||||||
|
|
||||||
@@ -118,6 +125,7 @@ object MainRouteConst {
|
|||||||
const val ROUTE_BOOK_CACHE_MANAGE = "book/cache/manage"
|
const val ROUTE_BOOK_CACHE_MANAGE = "book/cache/manage"
|
||||||
const val ROUTE_READ_BOOK = "book/read"
|
const val ROUTE_READ_BOOK = "book/read"
|
||||||
const val ROUTE_SEARCH = "search"
|
const val ROUTE_SEARCH = "search"
|
||||||
|
const val ROUTE_SEARCH_CONTENT = "book/searchContent"
|
||||||
const val ROUTE_BOOK_INFO = "book/info"
|
const val ROUTE_BOOK_INFO = "book/info"
|
||||||
const val ROUTE_EXPLORE_SHOW = "explore/show"
|
const val ROUTE_EXPLORE_SHOW = "explore/show"
|
||||||
const val ROUTE_RSS_SORT = "rss/sort"
|
const val ROUTE_RSS_SORT = "rss/sort"
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ object MainNavigator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is MainRouteSearchContent -> {
|
||||||
|
backStack.add(route)
|
||||||
|
}
|
||||||
|
|
||||||
is MainRouteSearch -> {
|
is MainRouteSearch -> {
|
||||||
if (
|
if (
|
||||||
currentRoute == MainRouteHome ||
|
currentRoute == MainRouteHome ||
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import android.text.StaticLayout
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
||||||
import io.legado.app.utils.dpToPx
|
import io.legado.app.utils.dpToPx
|
||||||
@@ -75,7 +75,7 @@ class BatteryViewOrgin @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
if (AppConfig.optimizeRender) {
|
if (ReadConfig.optimizeRender) {
|
||||||
canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
|
canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
|
||||||
super.onDraw(this)
|
super.onDraw(this)
|
||||||
drawBattery(this)
|
drawBattery(this)
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.requiredHeight
|
||||||
|
import androidx.compose.foundation.layout.requiredWidth
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import androidx.compose.material3.Slider as M3Slider
|
||||||
|
import top.yukonga.miuix.kmp.basic.Slider as MiuixSlider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical slider component that supports both Miuix and M3 engines.
|
||||||
|
*
|
||||||
|
* @param height Visual height of the vertical slider.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AppVerticalSlider(
|
||||||
|
value: Float,
|
||||||
|
onValueChange: (Float) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
|
||||||
|
steps: Int = 0,
|
||||||
|
onValueChangeFinished: (() -> Unit)? = null,
|
||||||
|
height: Dp = 160.dp,
|
||||||
|
) {
|
||||||
|
val sliderModifier = Modifier
|
||||||
|
.requiredWidth(height)
|
||||||
|
.requiredHeight(48.dp)
|
||||||
|
.graphicsLayer { rotationZ = -90f }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.width(48.dp)
|
||||||
|
.height(height),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
MiuixSlider(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = sliderModifier,
|
||||||
|
enabled = enabled,
|
||||||
|
valueRange = valueRange,
|
||||||
|
steps = steps,
|
||||||
|
onValueChangeFinished = onValueChangeFinished,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
M3Slider(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = sliderModifier,
|
||||||
|
enabled = enabled,
|
||||||
|
valueRange = valueRange,
|
||||||
|
steps = steps,
|
||||||
|
onValueChangeFinished = onValueChangeFinished,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,14 +143,14 @@ private fun FontItem(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clip(RoundedCornerShape(8.dp))
|
.clip(RoundedCornerShape(8.dp))
|
||||||
.background(LegadoTheme.colorScheme.surfaceVariant)
|
.background(LegadoTheme.colorScheme.surface)
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(12.dp),
|
.padding(12.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.height(48.dp)
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { ctx ->
|
factory = { ctx ->
|
||||||
android.widget.TextView(ctx).apply {
|
android.widget.TextView(ctx).apply {
|
||||||
@@ -178,7 +178,7 @@ private fun FontItem(
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Check,
|
Icons.Default.Check,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = LegadoTheme.colorScheme.primary,
|
||||||
modifier = Modifier.size(18.dp),
|
modifier = Modifier.size(18.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package io.legado.app.utils.canvasrecorder
|
package io.legado.app.utils.canvasrecorder
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.ui.config.readConfig.ReadConfig
|
||||||
|
|
||||||
object CanvasRecorderFactory {
|
object CanvasRecorderFactory {
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ object CanvasRecorderFactory {
|
|||||||
// issue 3868
|
// issue 3868
|
||||||
fun create(locked: Boolean = false): CanvasRecorder {
|
fun create(locked: Boolean = false): CanvasRecorder {
|
||||||
val impl = when {
|
val impl = when {
|
||||||
!AppConfig.optimizeRender -> CanvasRecorderImpl()
|
!ReadConfig.optimizeRender -> CanvasRecorderImpl()
|
||||||
atLeastApi29 -> CanvasRecorderApi29Impl()
|
atLeastApi29 -> CanvasRecorderApi29Impl()
|
||||||
atLeastApi24 -> CanvasRecorderApi23Impl()
|
atLeastApi24 -> CanvasRecorderApi23Impl()
|
||||||
else -> CanvasRecorderImpl()
|
else -> CanvasRecorderImpl()
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<io.legado.app.ui.widget.TitleBar
|
|
||||||
android:id="@+id/title_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:contentLayout="@layout/view_search"
|
|
||||||
app:contentInsetRight="24dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/refresh_progress_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:indeterminate="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title_bar" />
|
|
||||||
|
|
||||||
<io.legado.app.ui.widget.recycler.scroller.FastScrollRecyclerView
|
|
||||||
android:id="@+id/recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:overScrollMode="never"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/ll_search_base_info"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/refresh_progress_bar"
|
|
||||||
tools:ignore="SpeakableTextPresentCheck" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/ll_search_base_info"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorSurfaceContainer"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_current_search_info"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:layout_width="20dp"
|
|
||||||
android:layout_height="1dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/iv_search_content_top"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
|
||||||
android:contentDescription="@string/go_to_top"
|
|
||||||
app:icon="@drawable/ic_arrow_drop_up"
|
|
||||||
app:iconGravity="textStart"
|
|
||||||
android:tooltipText="@string/go_to_top"
|
|
||||||
tools:ignore="UnusedAttribute" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/iv_search_content_bottom"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
|
||||||
android:contentDescription="@string/go_to_bottom"
|
|
||||||
app:icon="@drawable/ic_arrow_drop_down"
|
|
||||||
app:iconGravity="textStart"
|
|
||||||
android:tooltipText="@string/go_to_bottom"
|
|
||||||
tools:ignore="UnusedAttribute" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/fb_stop"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:src="@drawable/ic_stop_black_24dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:contentDescription="@string/stop"
|
|
||||||
app:fabSize="mini"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/ll_search_base_info"
|
|
||||||
app:layout_constraintRight_toRightOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -907,6 +907,13 @@
|
|||||||
<string name="wrong_format">格式不对</string>
|
<string name="wrong_format">格式不对</string>
|
||||||
<string name="error">错误</string>
|
<string name="error">错误</string>
|
||||||
<string name="show_brightness_view">显示亮度调节控件</string>
|
<string name="show_brightness_view">显示亮度调节控件</string>
|
||||||
|
<string name="brightness_bar_off">关闭</string>
|
||||||
|
<string name="brightness_bar_horizontal">横排</string>
|
||||||
|
<string name="brightness_bar_vertical">竖排</string>
|
||||||
|
<string name="brightness_bar_position">亮度条位置</string>
|
||||||
|
<string name="brightness_bar_left">左侧</string>
|
||||||
|
<string name="brightness_bar_right">右侧</string>
|
||||||
|
<string name="brightness_follow_system">跟随系统</string>
|
||||||
<string name="language">语言</string>
|
<string name="language">语言</string>
|
||||||
<string name="import_rss_source">导入订阅源</string>
|
<string name="import_rss_source">导入订阅源</string>
|
||||||
<string name="donate_summary">您的支持是我更新的动力</string>
|
<string name="donate_summary">您的支持是我更新的动力</string>
|
||||||
|
|||||||
@@ -73,6 +73,17 @@
|
|||||||
<item>chapter</item>
|
<item>chapter</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="brightness_bar_mode_value">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="brightness_bar_position_value">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="language_value">
|
<string-array name="language_value">
|
||||||
<item>auto</item>
|
<item>auto</item>
|
||||||
<item>zh</item>
|
<item>zh</item>
|
||||||
|
|||||||
@@ -147,6 +147,17 @@
|
|||||||
<item>@string/adjust_chapter_index</item>
|
<item>@string/adjust_chapter_index</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="brightness_bar_mode_title">
|
||||||
|
<item>@string/brightness_bar_off</item>
|
||||||
|
<item>@string/brightness_bar_horizontal</item>
|
||||||
|
<item>@string/brightness_bar_vertical</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="brightness_bar_position_title">
|
||||||
|
<item>@string/brightness_bar_left</item>
|
||||||
|
<item>@string/brightness_bar_right</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="icon_names" tools:ignore="Typos">
|
<string-array name="icon_names" tools:ignore="Typos">
|
||||||
<item>iconMain</item>
|
<item>iconMain</item>
|
||||||
<item>iconWhite</item>
|
<item>iconWhite</item>
|
||||||
|
|||||||
@@ -932,6 +932,13 @@
|
|||||||
<string name="wrong_format">Wrong format</string>
|
<string name="wrong_format">Wrong format</string>
|
||||||
<string name="error">Error</string>
|
<string name="error">Error</string>
|
||||||
<string name="show_brightness_view">Show Brightness Widget</string>
|
<string name="show_brightness_view">Show Brightness Widget</string>
|
||||||
|
<string name="brightness_bar_off">Off</string>
|
||||||
|
<string name="brightness_bar_horizontal">Horizontal</string>
|
||||||
|
<string name="brightness_bar_vertical">Vertical</string>
|
||||||
|
<string name="brightness_bar_position">Brightness Bar Position</string>
|
||||||
|
<string name="brightness_bar_left">Left</string>
|
||||||
|
<string name="brightness_bar_right">Right</string>
|
||||||
|
<string name="brightness_follow_system">Follow System</string>
|
||||||
<string name="language">Language</string>
|
<string name="language">Language</string>
|
||||||
<string name="import_rss_source">Import rss source</string>
|
<string name="import_rss_source">Import rss source</string>
|
||||||
<string name="donate_summary">Your donation makes this app better</string>
|
<string name="donate_summary">Your donation makes this app better</string>
|
||||||
|
|||||||
@@ -93,10 +93,12 @@
|
|||||||
android:title="@string/use_zh_layout"
|
android:title="@string/use_zh_layout"
|
||||||
app:isBottomBackground="true" />
|
app:isBottomBackground="true" />
|
||||||
|
|
||||||
<io.legado.app.lib.prefs.SwitchPreference
|
<io.legado.app.lib.prefs.ListPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="1"
|
||||||
android:key="showBrightnessView"
|
android:key="showBrightnessView"
|
||||||
android:title="@string/show_brightness_view"
|
android:title="@string/show_brightness_view"
|
||||||
|
android:entries="@array/brightness_bar_mode_title"
|
||||||
|
android:entryValues="@array/brightness_bar_mode_value"
|
||||||
app:isBottomBackground="true" />
|
app:isBottomBackground="true" />
|
||||||
|
|
||||||
<io.legado.app.lib.prefs.SwitchPreference
|
<io.legado.app.lib.prefs.SwitchPreference
|
||||||
|
|||||||
Reference in new issue
Block a user