diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 9c7545b12..905dd7d57 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -113,7 +113,8 @@
"Bash(dir /s /b \"%USERPROFILE%\\\\.gradle\\\\caches\\\\*kyant*\")",
"Bash(Select-String -Pattern \"\\(BUILD|error:|FAILED\\)\")",
"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 *)"
]
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5895e8ac0..11f98360c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -234,11 +234,6 @@
android:name=".ui.book.toc.TocActivity"
android:enableOnBackInvokedCallback="true"
android:launchMode="singleTop" />
-
-
+ SearchContentViewModel(
+ bookUrl = route.bookUrl,
+ initialSearchWord = route.searchWord,
+ searchResultIndex = route.searchResultIndex,
+ bookRepository = get(),
+ searchContentRepository = get()
+ )
+ }
}
diff --git a/app/src/main/java/io/legado/app/help/AppWebDav.kt b/app/src/main/java/io/legado/app/help/AppWebDav.kt
index 84800342d..8f5f89ce8 100644
--- a/app/src/main/java/io/legado/app/help/AppWebDav.kt
+++ b/app/src/main/java/io/legado/app/help/AppWebDav.kt
@@ -17,6 +17,7 @@ import io.legado.app.lib.webdav.WebDav
import io.legado.app.lib.webdav.WebDavException
import io.legado.app.lib.webdav.WebDavFile
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.FileUtils
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.compress.ZipUtils
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.normalizeFileName
import io.legado.app.utils.toastOnUi
@@ -33,7 +33,6 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
import java.io.File
-import kotlin.coroutines.coroutineContext
/**
* webDav初始化会访问网络,不要放到主线程
@@ -359,10 +358,10 @@ object AppWebDav {
appDb.bookDao.all.forEach { book ->
val progressFileName = getProgressFileName(book.name, book.author)
val webDavFile = map[progressFileName]
- webDavFile ?: return
+ webDavFile ?: return@forEach
if (webDavFile.lastModify <= book.syncTime) {
//本地同步时间大于上传时间不用同步
- return
+ return@forEach
}
getBookProgress(book)?.let { bookProgress ->
if (bookProgress.durChapterIndex > book.durChapterIndex
diff --git a/app/src/main/java/io/legado/app/help/GlideImageGetter.kt b/app/src/main/java/io/legado/app/help/GlideImageGetter.kt
index 6cb1cea59..c0b69fa79 100644
--- a/app/src/main/java/io/legado/app/help/GlideImageGetter.kt
+++ b/app/src/main/java/io/legado/app/help/GlideImageGetter.kt
@@ -4,7 +4,6 @@ import android.content.Context
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
-import android.os.Build
import android.text.Html
import android.widget.TextView
import com.bumptech.glide.Glide
@@ -95,10 +94,11 @@ class GlideImageGetter(private val context: Context, textView: TextView, private
}
fun clear() {
- targets.forEach {
+ val snapshot = targets.toList()
+ targets.clear()
+ snapshot.forEach {
Glide.with(context).clear(it)
}
- targets.clear()
textViewRef.clear()
}
diff --git a/app/src/main/java/io/legado/app/help/WebDavManager.kt b/app/src/main/java/io/legado/app/help/WebDavManager.kt
index cd1dfb957..c2639e045 100644
--- a/app/src/main/java/io/legado/app/help/WebDavManager.kt
+++ b/app/src/main/java/io/legado/app/help/WebDavManager.kt
@@ -3,7 +3,6 @@ package io.legado.app.help
import android.content.SharedPreferences
import android.net.Uri
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.entities.Book
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.WebDavFile
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.FileUtils
import io.legado.app.utils.GSON
@@ -253,9 +253,9 @@ class WebDavManager(
}
appDb.bookDao.all.forEach { book ->
val progressFileName = getProgressFileName(book.name, book.author)
- val webDavFile = map[progressFileName] ?: return
+ val webDavFile = map[progressFileName] ?: return@forEach
if (webDavFile.lastModify <= book.syncTime) {
- return
+ return@forEach
}
getBookProgress(book)?.let { bookProgress ->
if (bookProgress.durChapterIndex > book.durChapterIndex
diff --git a/app/src/main/java/io/legado/app/help/book/BookHelp.kt b/app/src/main/java/io/legado/app/help/book/BookHelp.kt
index 9d79cfd18..fb994c5bf 100644
--- a/app/src/main/java/io/legado/app/help/book/BookHelp.kt
+++ b/app/src/main/java/io/legado/app/help/book/BookHelp.kt
@@ -12,11 +12,11 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
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.localBook.LocalBook
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.FileUtils
import io.legado.app.utils.ImageUtils
@@ -133,12 +133,12 @@ object BookHelp {
private fun clearComicCache(book: Book) {
//只处理漫画
//为0的时候,不清除已缓存数据
- if (!book.isImage || AppConfig.imageRetainNum == 0) {
+ if (!book.isImage || ReadConfig.imageRetainNum == 0) {
return
}
//向前保留设定数量,向后保留预下载数量
- val startIndex = book.durChapterIndex - AppConfig.imageRetainNum
- val endIndex = book.durChapterIndex + AppConfig.preDownloadNum
+ val startIndex = book.durChapterIndex - ReadConfig.imageRetainNum
+ val endIndex = book.durChapterIndex + ReadConfig.preDownloadNum
val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl, startIndex, endIndex)
val imgNames = hashSetOf()
//获取需要保留章节的图片信息
@@ -202,7 +202,7 @@ object BookHelp {
book.getFolderName(),
bookChapter.getFileName(),
).writeText(content)
- if (book.isOnLineTxt && AppConfig.tocCountWords) {
+ if (book.isOnLineTxt && ReadConfig.tocCountWords) {
val wordCount = StringUtils.wordCountFormat(content.length)
bookChapter.wordCount = wordCount
appDb.bookChapterDao.update(bookChapter)
diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt
index c42b19713..02ba1bb2f 100644
--- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt
+++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt
@@ -132,6 +132,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
ReadConfig.defaultSourceChangeAll = preferences.defaultSourceChangeAll
ReadConfig.sliderVibrator = preferences.sliderVibrator
ReadConfig.selectVibrator = preferences.selectVibrator
+ ReadConfig.brightnessVwPos = preferences.brightnessVwPos
+ ReadConfig.readBrightness = preferences.readBrightness
}
fun updateReadBarStyleCache(value: Int) {
diff --git a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt
index 83ba5a273..5527adeab 100644
--- a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt
+++ b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt
@@ -19,6 +19,7 @@ import io.legado.app.help.DefaultData
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.readConfig.ReadConfig
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.getMeanColor
@@ -208,6 +209,10 @@ object ReadBookConfig {
var readMenuBorderColorNight by prefDelegate(PreferKey.readMenuBorderColorNight, 0)
var showTitleBarIcons by prefDelegate(PreferKey.showTitleBarIcons, true)
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
get() = if (isComic) comicStyleSelect else readStyleSelect
@@ -301,7 +306,7 @@ object ReadBookConfig {
val resolvedMenuBgColor: Int
get() {
val isNight = ReadStyleResolver.isNightTheme()
- return when (AppConfig.readBarStyle) {
+ return when (ReadConfig.readBarStyle) {
1 -> { // 跟随阅读背景
val background = ReadStyleResolver.currentBackground(durConfig)
if (background.type == 0) {
diff --git a/app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt b/app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt
index 353767cde..8a5664fc7 100644
--- a/app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt
+++ b/app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt
@@ -5,6 +5,7 @@ import android.graphics.drawable.Drawable
import androidx.core.graphics.drawable.toDrawable
import androidx.core.graphics.toColorInt
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.FileUtils
import io.legado.app.utils.externalFiles
@@ -28,14 +29,14 @@ object ReadStyleResolver {
fun currentMode(): ReadStyleMode {
return when {
- AppConfig.isEInkMode -> ReadStyleMode.EInk
- AppConfig.isNightTheme -> ReadStyleMode.Night
+ ReadConfig.isEInkMode -> ReadStyleMode.EInk
+ ReadConfig.isNightTheme -> ReadStyleMode.Night
else -> ReadStyleMode.Day
}
}
fun isNightTheme(): Boolean {
- return AppConfig.isNightTheme
+ return ReadConfig.isNightTheme
}
fun setCurrentBackground(
diff --git a/app/src/main/java/io/legado/app/model/ReadAloud.kt b/app/src/main/java/io/legado/app/model/ReadAloud.kt
index 8c1a61398..dead400b1 100644
--- a/app/src/main/java/io/legado/app/model/ReadAloud.kt
+++ b/app/src/main/java/io/legado/app/model/ReadAloud.kt
@@ -8,10 +8,10 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb
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.HttpReadAloudService
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.StringUtils
import io.legado.app.utils.postEvent
@@ -21,7 +21,7 @@ import splitties.init.appCtx
object ReadAloud {
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
private fun getReadAloudClass(): Class<*> {
@@ -134,4 +134,4 @@ object ReadAloud {
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/legado/app/model/ReadBook.kt b/app/src/main/java/io/legado/app/model/ReadBook.kt
index cc35dbd69..2bafc8bd9 100644
--- a/app/src/main/java/io/legado/app/model/ReadBook.kt
+++ b/app/src/main/java/io/legado/app/model/ReadBook.kt
@@ -21,7 +21,6 @@ import io.legado.app.help.book.isSameNameAuthor
import io.legado.app.help.book.readSimulating
import io.legado.app.help.book.simulatedTotalChapterNum
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.coroutine.Coroutine
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.TranslationChapterStatus
import io.legado.app.model.translation.TranslationManager
-import kotlinx.coroutines.flow.MutableStateFlow
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService
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.provider.ChapterProvider
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.stackTraceStr
import io.legado.app.utils.toastOnUi
@@ -51,6 +50,7 @@ import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
@@ -211,7 +211,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
ReadBookConfig.isComic = book.isImage
if (oldIndex != ReadBookConfig.styleSelect) {
postEvent(EventBus.UP_CONFIG, arrayListOf(1, 2, 5))
- if (AppConfig.readBarStyleFollowPage) {
+ if (ReadConfig.readBarStyleFollowPage) {
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
}
}
@@ -288,7 +288,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
uploadSuccessAction: (() -> Unit)? = null,
syncSuccessAction: (() -> Unit)? = null
) {
- if (!AppConfig.syncBookProgress) return
+ if (!ReadConfig.syncBookProgress) return
val book = book ?: return
Coroutine.async {
AppWebDav.getBookProgress(book)
@@ -301,7 +301,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
) {
// 服务器没有进度或者进度比服务器快,上传现有进度
Coroutine.async {
- AppWebDav.uploadBookProgress(BookProgress(book), uploadSuccessAction)
+ AppWebDav.uploadBookProgress(book, onSuccess = uploadSuccessAction)
book.update()
}
} else if (progress.durChapterIndex > book.durChapterIndex ||
@@ -560,7 +560,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
}
fun recycleRecorders(beforeIndex: Int, afterIndex: Int) {
- if (!AppConfig.optimizeRender) {
+ if (!ReadConfig.optimizeRender) {
return
}
executor.execute {
@@ -1131,7 +1131,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
private fun preDownload() {
if (book?.isLocal == true) return
executor.execute {
- if (AppConfig.preDownloadNum < 2) {
+ if (ReadConfig.preDownloadNum < 2) {
return@execute
}
preDownloadTask?.cancel()
@@ -1139,7 +1139,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
//预下载
launch {
val maxChapterIndex =
- min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
+ min(durChapterIndex + ReadConfig.preDownloadNum, chapterSize)
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
if (downloadedChapters.contains(i)) continue
if ((downloadFailChapters[i] ?: 0) >= 3) continue
@@ -1147,7 +1147,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
}
}
launch {
- val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
+ val minChapterIndex = durChapterIndex - min(5, ReadConfig.preDownloadNum)
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
if (downloadedChapters.contains(i)) continue
if ((downloadFailChapters[i] ?: 0) >= 3) continue
diff --git a/app/src/main/java/io/legado/app/model/ReadManga.kt b/app/src/main/java/io/legado/app/model/ReadManga.kt
index 1520ff0cd..58f6a751f 100644
--- a/app/src/main/java/io/legado/app/model/ReadManga.kt
+++ b/app/src/main/java/io/legado/app/model/ReadManga.kt
@@ -18,8 +18,6 @@ import io.legado.app.help.book.isSameNameAuthor
import io.legado.app.help.book.readSimulating
import io.legado.app.help.book.simulatedTotalChapterNum
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.globalExecutor
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.MangaPage
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
@@ -45,7 +45,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
-import kotlin.getValue
import kotlin.math.min
@Suppress("MemberVisibilityCanBePrivate")
@@ -472,7 +471,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
private fun preDownload() {
if (book?.isLocal == true) return
executor.execute {
- if (AppConfig.preDownloadNum < 2) {
+ if (ReadConfig.preDownloadNum < 2) {
return@execute
}
preDownloadTask?.cancel()
@@ -480,7 +479,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
//预下载
launch {
val maxChapterIndex =
- min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
+ min(durChapterIndex + ReadConfig.preDownloadNum, chapterSize)
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
if (downloadedChapters.contains(i)) continue
if ((downloadFailChapters[i] ?: 0) >= 3) continue
@@ -488,7 +487,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
}
}
launch {
- val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
+ val minChapterIndex = durChapterIndex - min(5, ReadConfig.preDownloadNum)
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
if (downloadedChapters.contains(i)) continue
if ((downloadFailChapters[i] ?: 0) >= 3) continue
@@ -594,7 +593,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
uploadSuccessAction: (() -> Unit)? = null,
syncSuccessAction: (() -> Unit)? = null,
) {
- if (!AppConfig.syncBookProgress) return
+ if (!ReadConfig.syncBookProgress) return
val book = book ?: return
Coroutine.async {
AppWebDav.getBookProgress(book)
@@ -607,7 +606,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
) {
// 服务器没有进度或者进度比服务器快,上传现有进度
Coroutine.async {
- AppWebDav.uploadBookProgress(BookProgress(book), uploadSuccessAction)
+ AppWebDav.uploadBookProgress(book, onSuccess = uploadSuccessAction)
book.update()
}
} else if (progress.durChapterIndex > book.durChapterIndex ||
@@ -721,4 +720,4 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{
fun showLoading()
fun startLoad()
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
index 780ab6074..3b95c8455 100644
--- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
+++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
@@ -34,8 +34,6 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.HttpTTS
import io.legado.app.exception.NoStackTraceException
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.exoplayer.InputStreamDataSource
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.analyzeRule.AnalyzeUrl
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.MD5Utils
import io.legado.app.utils.printOnDebug
@@ -98,7 +97,7 @@ class HttpReadAloudService : BaseReadAloudService(),
private val loadErrorHandlingPolicy by lazy {
CustomLoadErrorHandlingPolicy()
}
- private var speechRate: Int = AppConfig.speechRatePlay + 5
+ private var speechRate: Int = ReadConfig.speechRatePlay + 5
private var downloadTask: Coroutine<*>? = null
private var playIndexJob: Job? = null
private var downloadErrorNo: Int = 0
@@ -210,7 +209,7 @@ class HttpReadAloudService : BaseReadAloudService(),
private suspend fun preDownloadAudios(httpTts: HttpTTS) {
val book = ReadBook.book ?: return
val currentIdx = ReadBook.durChapterIndex
- val limit = AppConfig.audioPreDownloadNum
+ val limit = ReadConfig.audioPreDownloadNum
try {
for (i in 1..limit) {
@@ -300,7 +299,7 @@ class HttpReadAloudService : BaseReadAloudService(),
) {
val book = ReadBook.book ?: return
val currentIdx = ReadBook.durChapterIndex
- val limit = AppConfig.audioPreDownloadNum
+ val limit = ReadConfig.audioPreDownloadNum
try {
for (i in 1..limit) {
@@ -491,7 +490,7 @@ class HttpReadAloudService : BaseReadAloudService(),
* 如果时间设置为0,则不再保护当前章节,退出即全删。
*/
private fun removeCacheFile() {
- val keepTime = AppConfig.audioCacheCleanTime
+ val keepTime = ReadConfig.audioCacheCleanTime
// 只有当时间大于0时,才需要保护当前章节。如果为0,说明用户想彻底不留缓存。
val protectCurrentChapter = keepTime > 0
val titleMd5 = if (protectCurrentChapter) MD5Utils.md5Encode16(this.textChapter?.chapter?.title ?: "") else ""
@@ -572,7 +571,7 @@ class HttpReadAloudService : BaseReadAloudService(),
override fun upSpeechRate(reset: Boolean) {
downloadTask?.cancel()
exoPlayer.stop()
- speechRate = AppConfig.speechRatePlay + 5
+ speechRate = ReadConfig.speechRatePlay + 5
if (ReadConfig.streamReadAloudAudio) {
downloadAndPlayAudiosStream()
} else {
diff --git a/app/src/main/java/io/legado/app/ui/book/manga/ReadMangaActivity.kt b/app/src/main/java/io/legado/app/ui/book/manga/ReadMangaActivity.kt
index b6795d1c1..cce9ca409 100644
--- a/app/src/main/java/io/legado/app/ui/book/manga/ReadMangaActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/manga/ReadMangaActivity.kt
@@ -38,12 +38,9 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.databinding.ActivityMangaBinding
import io.legado.app.databinding.ViewLoadMoreBinding
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.isImage
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.source.getSourceType
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.toc.TocActivityResult
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.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.recycler.LoadMoreView
@@ -220,7 +219,7 @@ class ReadMangaActivity : VMBaseActivity sureNewProgress(progress) })
}
}
@@ -480,7 +479,7 @@ class ReadMangaActivity : VMBaseActivity ReadManga.mCallback?.sureNewProgress(progress) })
} else {
@@ -195,7 +194,7 @@ class ReadMangaViewModel(
* 自动换源
*/
private fun autoChangeSource(name: String, author: String) {
- if (!AppConfig.autoChangeSource) return
+ if (!GlobalReadConfig.autoChangeSource) return
execute {
val sources = appDb.bookSourceDao.allTextEnabledPart
flow {
@@ -246,7 +245,7 @@ class ReadMangaViewModel(
book: Book,
alertSync: ((progress: BookProgress) -> Unit)? = null
) {
- if (!AppConfig.syncBookProgress) return
+ if (!GlobalReadConfig.syncBookProgress) return
execute {
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
}.onError {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/MangaMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/MangaMenu.kt
index e4979ee98..d1d184626 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/MangaMenu.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/MangaMenu.kt
@@ -14,12 +14,12 @@ import androidx.core.view.isVisible
import com.google.android.material.slider.Slider
import io.legado.app.R
import io.legado.app.databinding.ViewMangaMenuBinding
-import io.legado.app.help.config.AppConfig
import io.legado.app.help.source.getSourceType
import io.legado.app.lib.dialogs.alert
import io.legado.app.model.ReadBook
import io.legado.app.model.ReadManga
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.applyNavigationBarPadding
import io.legado.app.utils.gone
@@ -112,13 +112,13 @@ class MangaMenu @JvmOverloads constructor(
// val brightnessBackground = GradientDrawable()
// brightnessBackground.cornerRadius = 5F.dpToPx()
// //brightnessBackground.setColor(ColorUtils.adjustAlpha(bgColor, 0.5f))
-// if (AppConfig.isEInkMode) {
+// if (ReadConfig.isEInkMode) {
// titleBar.setBackgroundResource(R.drawable.bg_eink_border_bottom)
// bottomMenu.setBackgroundResource(R.drawable.bg_eink_border_top)
// } else {
// //bottomMenu.setBackgroundColor(bgColor)
// }
- if (AppConfig.showReadTitleBarAddition) {
+ if (ReadConfig.showReadTitleAddition) {
titleBarAddition.visible()
} else {
titleBarAddition.gone()
@@ -134,7 +134,7 @@ class MangaMenu @JvmOverloads constructor(
menuTopOut.setAnimationListener(menuOutListener)
}
- fun runMenuOut(anim: Boolean = !AppConfig.isEInkMode) {
+ fun runMenuOut(anim: Boolean = !ReadConfig.isEInkMode) {
if (isMenuOutAnimating) {
return
}
@@ -149,7 +149,7 @@ class MangaMenu @JvmOverloads constructor(
}
}
- fun runMenuIn(anim: Boolean = !AppConfig.isEInkMode) {
+ fun runMenuIn(anim: Boolean = !ReadConfig.isEInkMode) {
this.visible()
binding.titleBar.visible()
binding.bottomMenu.visible()
@@ -181,7 +181,7 @@ class MangaMenu @JvmOverloads constructor(
callBack.openBookInfoActivity()
}
val chapterViewClickListener = OnClickListener {
- if (AppConfig.readUrlInBrowser) {
+ if (ReadConfig.readUrlInBrowser) {
context.openUrl(tvChapterUrl.text.toString().substringBefore(",{"))
} else {
context.startActivity {
@@ -199,10 +199,10 @@ class MangaMenu @JvmOverloads constructor(
context.alert(R.string.open_fun) {
setMessage(R.string.use_browser_open)
okButton {
- AppConfig.readUrlInBrowser = true
+ ReadConfig.readUrlInBrowser = true
}
noButton {
- AppConfig.readUrlInBrowser = false
+ ReadConfig.readUrlInBrowser = false
}
}
true
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt
index f177160ed..eb731c67c 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt
@@ -196,6 +196,10 @@ data class ReadMenuConfig(
val readMenuCustomIcons: ImmutableMap = persistentMapOf(),
val titleBarButtons: ImmutableList = persistentListOf(),
val bottomBarButtons: ImmutableList = persistentListOf(),
+ val showBrightnessView: String = "1",
+ val brightnessVwPos: String = "1",
+ val readBrightness: Int = 100,
+ val brightnessAuto: Boolean = false,
)
@Immutable
@@ -300,7 +304,7 @@ sealed interface ReadBookIntent {
// Brightness
data class SetBrightness(val value: Int) : ReadBookIntent
- data object ToggleBrightnessAuto : ReadBookIntent
+ data class ToggleBrightnessAuto(val auto: Boolean) : ReadBookIntent
// Seek bar jump
data class SeekToChapter(val index: Int) : ReadBookIntent
@@ -526,7 +530,7 @@ sealed interface ReadBookEffect {
data object CancelSelect : ReadBookEffect
data object UpSystemUiVisibility : 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
data object ToggleReadAloud : ReadBookEffect
@@ -535,7 +539,15 @@ sealed interface ReadBookEffect {
// Search
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
// Source actions
@@ -1040,7 +1052,15 @@ sealed interface ConfigUpdate {
data class UseZhLayout(val value: Boolean) : ConfigUpdate {
override val actions = setOf(ConfigUpdateAction.ReloadContent)
}
- data class ShowBrightnessView(val value: Boolean) : ConfigUpdate {
+ data class ShowBrightnessView(val value: String) : ConfigUpdate {
+ override val actions = emptySet()
+ }
+
+ data class BrightnessVwPos(val value: String) : ConfigUpdate {
+ override val actions = emptySet()
+ }
+
+ data class BrightnessAuto(val value: Boolean) : ConfigUpdate {
override val actions = emptySet()
}
data class UseUnderlineGlobal(val value: Boolean) : ConfigUpdate {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt
index 20b1bdf82..6178045b1 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt
@@ -9,6 +9,7 @@ import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
+import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
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.entities.BookProgress
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.storage.Backup
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.ReadView
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.TextPageFactory
+import io.legado.app.ui.config.readConfig.ReadConfig
import io.legado.app.ui.login.SourceLoginJsExtensions
import io.legado.app.ui.widget.PopupAction
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.visible
import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
/**
@@ -341,7 +344,7 @@ class ReadBookController(
r.textMenuPosition.x = x
r.textMenuPosition.y = top
- if (AppConfig.selectVibrator) {
+ if (ReadConfig.selectVibrator) {
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
}
}
@@ -351,7 +354,7 @@ class ReadBookController(
r.cursorRight.x = x
r.cursorRight.y = y
r.cursorRight.visible(true)
- if (AppConfig.selectVibrator) {
+ if (ReadConfig.selectVibrator) {
r.root.performHapticFeedback(HapticFeedbackConstantsCompat.TEXT_HANDLE_MOVE)
}
}
@@ -616,7 +619,7 @@ class ReadBookController(
is ReadBookEffect.LongToast -> activity.longToastOnUi(effect.message)
is ReadBookEffect.SetBrightness -> {
val lp = activity.window.attributes
- lp.screenBrightness = effect.value / 255f
+ lp.screenBrightness = effect.value / 100f
activity.window.attributes = lp
}
@@ -682,7 +685,14 @@ class ReadBookController(
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 ──
@@ -695,15 +705,48 @@ class ReadBookController(
is ReadBookEffect.TextActionSpeak -> speak(effect.text)
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 -> {
- if (viewModel.uiState.value.isShowingSearchResult) {
- viewModel.onIntent(ReadBookIntent.SetShowingSearchResult(false))
- ReadBook.clearSearchResult()
- refs?.readView?.cancelSelect(true)
- }
+ ReadBook.clearSearchResult()
+ refs?.readView?.cancelSelect(clearSearchResult = true)
}
is ReadBookEffect.SyncBookProgress -> {
@@ -935,17 +978,17 @@ class ReadBookController(
}
override fun mouseWheelPage(direction: PageDirection) {
- if (menuLayoutIsVisible || !AppConfig.mouseWheelPage) {
+ if (menuLayoutIsVisible || !ReadConfig.mouseWheelPage) {
return
}
keyPageDebounce(direction, mouseWheel = true, longPress = false)
}
private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
- if (!AppConfig.volumeKeyPage) {
+ if (!ReadConfig.volumeKeyPage) {
return false
}
- if (!AppConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
+ if (!ReadConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
return false
}
handleKeyPage(direction, longPress)
@@ -953,7 +996,7 @@ class ReadBookController(
}
override fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
- if (AppConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
+ if (ReadConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
keyPage(direction)
} else {
keyPageDebounce(direction, longPress = longPress)
@@ -1057,7 +1100,7 @@ class ReadBookController(
}
fun setOrientation() {
- when (AppConfig.screenOrientation) {
+ when (ReadConfig.screenOrientation) {
"0" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
"1" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
"2" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
@@ -1066,4 +1109,38 @@ class ReadBookController(
"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
+ }
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBar.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBar.kt
index 0d45c2732..a8f53d000 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBar.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBar.kt
@@ -6,11 +6,15 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
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.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
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.Login
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.Menu
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Payment
import androidx.compose.material.icons.filled.Refresh
+import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.Icon
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.ReadMenuBlurStyle
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.ui.animation.DampedDragAnimation
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.ReadStyleTextTitleContent
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.hazeStyle.HazeLegado
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.button.series.SmallTonalButton
import io.legado.app.ui.widget.components.divider.PillDivider
@@ -168,12 +178,29 @@ fun ReadBookMenuBar(
) {
val context = LocalContext.current
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
+ 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()
Box(Modifier.fillMaxSize()) {
AnimatedVisibility(
- visible = state.menuVisible,
+ visible = state.menuVisible || searchMenuVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
@@ -183,13 +210,19 @@ fun ReadBookMenuBar(
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
- ) { onIntent(ReadBookIntent.HideMenu) }
+ ) {
+ if (searchMenuVisible) {
+ onIntent(ReadBookIntent.HideSearchMenu)
+ } else {
+ onIntent(ReadBookIntent.HideMenu)
+ }
+ }
)
}
// Top title bar + floating icon row (top positions)
AnimatedVisibility(
- visible = state.menuVisible && !dialogLikeRoute,
+ visible = state.menuVisible && !hideTopBar,
enter = slideInVertically(initialOffsetY = { -it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { -it }) + fadeOut(),
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)
AnimatedVisibility(
- visible = state.menuVisible,
+ visible = state.menuVisible || searchMenuVisible,
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
modifier = Modifier.align(Alignment.BottomCenter),
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
- if (state.menuConfig.showTitleBarIcons && state.menuConfig.titleBarIconPosition >= 2) {
+ if (state.menuVisible &&
+ state.menuConfig.showTitleBarIcons &&
+ state.menuConfig.titleBarIconPosition >= 2
+ ) {
FloatingIconRow(
state = state,
colors = menuColors,
@@ -240,29 +355,42 @@ fun ReadBookMenuBar(
)
}
ReadBookMenuSurface(
- route = currentRoute,
+ contentTarget = contentTarget,
state = state,
colors = menuColors,
onIntent = onIntent,
context = context,
backdrop = backdrop,
hazeState = hazeState,
+ readStylePage = readStylePage,
+ onReadStylePageChanged = { readStylePage = it },
)
}
}
}
}
+private sealed interface ReadBookMenuContent {
+ data object Search : ReadBookMenuContent
+ data class Route(val route: ReadBookMenuRoute) : ReadBookMenuContent
+}
+
@Composable
private fun ReadBookMenuSurface(
- route: ReadBookMenuRoute,
+ contentTarget: ReadBookMenuContent,
state: ReadBookUiState,
colors: ReadMenuColors,
onIntent: (ReadBookIntent) -> Unit,
context: Context,
backdrop: Backdrop?,
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 dialogLikeRoute = route == ReadBookMenuRoute.PaddingConfig
val density = LocalDensity.current
@@ -272,6 +400,18 @@ private fun ReadBookMenuSurface(
targetValue = if (dialogLikeRoute) 1f else 0f,
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) {
windowSize.height.toDp() * 0.64f
}
@@ -377,7 +517,8 @@ private fun ReadBookMenuSurface(
.heightIn(max = maxHeight)
.onSizeChanged { surfaceHeightPx = it.height }
.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())
}
.then(
@@ -433,16 +574,27 @@ private fun ReadBookMenuSurface(
contentColor = colors.content
) {
AnimatedContent(
- targetState = route,
+ targetState = contentTarget,
transitionSpec = {
(slideInVertically { it / 4 } + fadeIn())
.togetherWith(slideOutVertically { -it / 4 } + fadeOut())
.using(SizeTransform(clip = true))
},
label = "ReadBookMenuRoute",
- ) { targetRoute ->
- when (targetRoute) {
- ReadBookMenuRoute.Main -> {
+ ) { target ->
+ 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 -> {
MenuBottomBar(
state = state,
colors = colors,
@@ -482,6 +634,7 @@ private fun ReadBookMenuSurface(
onToggleDayNight = {
onIntent(ReadBookIntent.ToggleDayNight)
},
+ onPageChanged = onReadStylePageChanged,
readMenuCustomIcons = state.menuConfig.readMenuCustomIcons,
bottomBarButtons = state.menuConfig.bottomBarButtons,
onIntent = onIntent,
@@ -597,6 +750,7 @@ private fun ReadBookMenuSurface(
}
}
}
+ }
}
}
}
@@ -663,7 +817,7 @@ private fun MenuTitleBar(
backdrop: Backdrop?,
hazeState: HazeState?,
) {
- val titleBarMode = AppConfig.titleBarMode
+ val titleBarMode = ReadConfig.titleBarMode
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
private fun MenuBottomBar(
state: ReadBookUiState,
@@ -1431,7 +1738,7 @@ private fun MenuBottomBar(
val target = value.roundToInt().coerceIn(0, seekMax)
sliderDragging = false
sliderValue = target.toFloat()
- val behavior = AppConfig.progressBarBehavior
+ val behavior = ReadConfig.progressBarBehavior
if (behavior == "page") {
onIntent(ReadBookIntent.SkipToPage(target))
} else {
@@ -1459,6 +1766,27 @@ private fun MenuBottomBar(
.padding(top = 8.dp, bottom = contentBottomPadding)
.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
AnimatedVisibility(visible = state.menuConfig.readSliderMode != "1") {
Row(
@@ -2181,8 +2509,8 @@ private fun Modifier.readMenuLiquidGlass(
}
},
)
- .then(if (interactiveHighlight != null) interactiveHighlight.modifier else Modifier)
- .then(if (interactiveHighlight != null) interactiveHighlight.gestureModifier else Modifier)
+ .then(interactiveHighlight?.modifier ?: Modifier)
+ .then(interactiveHighlight?.gestureModifier ?: Modifier)
}
@OptIn(ExperimentalHazeMaterialsApi::class)
@@ -2288,7 +2616,7 @@ private fun readMenuBottomBarSurfaceBrush(
private fun readMenuColors(): ReadMenuColors {
val themeBackground = LegadoTheme.colorScheme.surfaceContainerHigh
val themeContent = LegadoTheme.colorScheme.onSurface
- return when (AppConfig.readBarStyle) {
+ return when (ReadConfig.readBarStyle) {
1 -> ReadMenuColors(
background = themeBackground,
content = themeContent,
@@ -2370,3 +2698,116 @@ private fun loadFloatingIcons(
}
.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))
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt
index ac0aad890..a843108fc 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt
@@ -27,14 +27,12 @@ import dev.chrisbanes.haze.hazeSource
import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.constant.ReadMenuBlurMode
-import io.legado.app.help.IntentData
import io.legado.app.help.IntentHelp
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.ReadView
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.SearchResult
+import io.legado.app.ui.book.searchContent.SearchContentResult
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.toc.TocActivityResult
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.takePersistablePermissionSafely
import io.legado.app.utils.toastOnUi
+import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.launch
@@ -95,10 +94,12 @@ fun ReadBookRouteScreen(
host: ReadBookRouteHost,
controller: ReadBookController,
onEffectsReady: () -> Unit = {},
+ onOpenSearch: (word: String?, bookUrl: String) -> Unit = { _, _ -> },
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
val readPreferences by viewModel.readPreferences.collectAsStateWithLifecycle()
val context = LocalContext.current
+ val effectsReady = remember(viewModel) { CompletableDeferred() }
val menuBackdrop = rememberLayerBackdrop()
val menuHazeState = remember { HazeState() }
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$key")
- val searchResultList = IntentData.get>("searchResultList$key")
- if (searchResult != null && searchResultList != null) {
- viewModel.onIntent(ReadBookIntent.SetSearchResults(searchResultList, index, searchResult.query))
- }
- }
-
val importHttpTtsPicker = rememberLauncherForActivityResult(
ActivityResultContracts.OpenDocument()
) { uri ->
@@ -245,7 +233,10 @@ fun ReadBookRouteScreen(
LaunchedEffect(viewModel) {
launch {
viewModel.effects
- .onSubscription { onEffectsReady() }
+ .onSubscription {
+ effectsReady.complete(Unit)
+ onEffectsReady()
+ }
.collect { effect ->
try {
when (effect) {
@@ -292,18 +283,7 @@ fun ReadBookRouteScreen(
)
}
is ReadBookEffect.OpenSearchActivity -> {
- val currentState = viewModel.uiState.value
- 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)
+ onOpenSearch(effect.word, effect.bookUrl)
}
is ReadBookEffect.MenuSettingReplace -> {
replaceLauncher.launch(Intent(context, ReplaceRuleActivity::class.java))
@@ -392,6 +372,24 @@ fun ReadBookRouteScreen(
LaunchedEffect(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 ───────────────────────────────────────
Box(Modifier.fillMaxSize()) {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt
index a1d4dc107..b0caf67b5 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt
@@ -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.DownloadSheet
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.HttpTtsEditSheet
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.ToolButtonConfigSheet
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.changeSource.ChangeSourceSheet
import io.legado.app.ui.widget.components.log.AppLogSheet
@@ -135,6 +135,10 @@ fun ReadBookScreen(
onIntent(ReadBookIntent.OpenReplaceEditor(id, pattern))
},
onReplaceRuleChanged = { onIntent(ReadBookIntent.ReplaceRuleChanged) },
+ onNavigateToTextEffects = {
+ onIntent(ReadBookIntent.DismissSheet)
+ onIntent(ReadBookIntent.OpenReadMenuRoute(ReadBookMenuRoute.TextTitle))
+ },
)
UnderlineConfigSheet(
show = state.activeSheet is ReadBookSheet.UnderlineConfig,
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt
index 0593368fa..3d5e53f20 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookSearchBar.kt
@@ -3,44 +3,23 @@ package io.legado.app.ui.book.read
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
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.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
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.size
-import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
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.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.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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
fun ReadBookSearchBar(
@@ -52,9 +31,6 @@ fun ReadBookSearchBar(
val hasResults = state.searchResultList.isNotEmpty()
val currentIndex = state.searchResultIndex
val totalResults = state.searchResultList.size
- val currentResult = if (hasResults && currentIndex in state.searchResultList.indices) {
- state.searchResultList[currentIndex]
- } else null
Box(Modifier.fillMaxSize()) {
// Left FAB - previous result
@@ -66,7 +42,7 @@ fun ReadBookSearchBar(
.align(Alignment.CenterStart)
.padding(start = 16.dp),
) {
- SmallFloatingActionButton(
+ AppFloatingActionButton(
onClick = {
val prevIndex = currentIndex - 1
onIntent(
@@ -75,11 +51,13 @@ fun ReadBookSearchBar(
)
)
},
- containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
+ tooltipText = "上一个结果",
+ containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
+ contentColor = LegadoTheme.colorScheme.onSurfaceVariant,
) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
- contentDescription = "Previous",
+ contentDescription = "上一个结果",
modifier = Modifier.size(20.dp),
)
}
@@ -94,7 +72,7 @@ fun ReadBookSearchBar(
.align(Alignment.CenterEnd)
.padding(end = 16.dp),
) {
- SmallFloatingActionButton(
+ AppFloatingActionButton(
onClick = {
val nextIndex = currentIndex + 1
onIntent(
@@ -103,144 +81,16 @@ fun ReadBookSearchBar(
)
)
},
- containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
+ tooltipText = "下一个结果",
+ containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
+ contentColor = LegadoTheme.colorScheme.onSurfaceVariant,
) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
- contentDescription = "Next",
+ contentDescription = "下一个结果",
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")
- }
- }
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
index 65df6279a..5ff1e3945 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
@@ -35,7 +35,6 @@ import io.legado.app.domain.usecase.GetReadingProgressUseCase
import io.legado.app.domain.usecase.UploadReadingProgressUseCase
import io.legado.app.exception.NoStackTraceException
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.ContentProcessor
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.removeType
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.coroutine.Coroutine
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.searchContent.SearchResult
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.utils.GSON
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.openUrl
import io.legado.app.utils.postEvent
-import io.legado.app.utils.putPrefInt
import io.legado.app.utils.toStringArray
import io.legado.app.utils.toastOnUi
import kotlinx.collections.immutable.toImmutableList
@@ -155,7 +153,7 @@ class ReadBookViewModel(
}
init {
- AppConfig.detectClickArea()
+ ReadConfig.detectClickArea()
ReadBook.register(this)
refreshButtonConfigs()
collectReadPreferences()
@@ -253,7 +251,7 @@ class ReadBookViewModel(
is ReadBookIntent.NavigateToSearchResult -> {
_uiState.update { it.copy(searchResultIndex = intent.index) }
- _effects.tryEmit(ReadBookEffect.NavigateToSearchResult(intent.result))
+ navigateToSearchResult(intent.result)
}
is ReadBookIntent.ToggleReadAloud -> {
@@ -296,8 +294,28 @@ class ReadBookViewModel(
is ReadBookIntent.CancelSelect -> _effects.tryEmit(ReadBookEffect.CancelSelect)
is ReadBookIntent.UpSystemUiVisibility -> _effects.tryEmit(ReadBookEffect.UpSystemUiVisibility)
is ReadBookIntent.UpContent -> ReadBook.loadOrUpContent()
- is ReadBookIntent.SetBrightness -> _effects.tryEmit(ReadBookEffect.SetBrightness(intent.value))
- is ReadBookIntent.ToggleBrightnessAuto -> _effects.tryEmit(ReadBookEffect.ToggleBrightnessAuto)
+ is ReadBookIntent.SetBrightness -> {
+ 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 -> {
ReadBook.saveCurrentBookProgress()
openChapter(intent.index)
@@ -627,7 +645,7 @@ class ReadBookViewModel(
is ReadBookIntent.OpenPreDownloadNumPicker -> {
_uiState.update {
it.copy(
- preDownloadNum = AppConfig.preDownloadNum,
+ preDownloadNum = ReadConfig.preDownloadNum,
activeSheet = ReadBookSheet.PreDownloadConfig,
)
}
@@ -636,7 +654,7 @@ class ReadBookViewModel(
is ReadBookIntent.OpenCacheCleanTimePicker -> {
_uiState.update {
it.copy(
- audioCacheCleanTime = AppConfig.audioCacheCleanTimeOrgin,
+ audioCacheCleanTime = ReadConfig.audioCacheCleanTimeOrgin,
activeSheet = ReadBookSheet.AudioCacheCleanConfig,
)
}
@@ -644,7 +662,8 @@ class ReadBookViewModel(
is ReadBookIntent.ApplySpeakEngine -> {
ReadBook.book?.setTtsEngine(null)
- AppConfig.ttsEngine = intent.value
+ ReadConfig.ttsEngine = intent.value
+ ReadAloud.upReadAloudClass()
_uiState.update {
it.copy(
selectedTtsEngine = ReadAloud.ttsEngine,
@@ -655,7 +674,7 @@ class ReadBookViewModel(
}
is ReadBookIntent.ApplyPreDownloadNum -> {
- AppConfig.preDownloadNum = intent.value
+ ReadConfig.preDownloadNum = intent.value
_uiState.update {
it.copy(
preDownloadNum = intent.value,
@@ -665,7 +684,7 @@ class ReadBookViewModel(
}
is ReadBookIntent.ApplyAudioCacheCleanTime -> {
- context.putPrefInt(PreferKey.audioCacheCleanTime, intent.value)
+ ReadConfig.audioCacheCleanTimeOrgin = intent.value
_uiState.update {
it.copy(
audioCacheCleanTime = intent.value,
@@ -704,7 +723,7 @@ class ReadBookViewModel(
}.onSuccess {
loadTtsEngineItems()
if (ReadAloud.ttsEngine == intent.engineId.toString()) {
- AppConfig.ttsEngine = null
+ ReadConfig.ttsEngine = null
ReadAloud.upReadAloudClass()
}
}
@@ -726,6 +745,7 @@ class ReadBookViewModel(
is ReadBookIntent.ApplySpeakEnginePerBook -> {
ReadBook.book?.setTtsEngine(intent.value)
+ ReadAloud.upReadAloudClass()
_uiState.update {
it.copy(
selectedTtsEngine = ReadAloud.ttsEngine,
@@ -841,7 +861,7 @@ class ReadBookViewModel(
is ReadBookIntent.SelectFont -> selectFont(intent.path)
is ReadBookIntent.SelectSystemTypeface -> {
- AppConfig.systemTypefaces = intent.index
+ ReadConfig.systemTypefaces = intent.index
ReadBookConfig.textFont = ""
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.ReloadContent)
@@ -937,7 +957,7 @@ class ReadBookViewModel(
is ReadBookIntent.ToggleDayNight -> toggleDayNight()
// Text action menu
is ReadBookIntent.TextActionAloud -> {
- when (AppConfig.contentSelectSpeakMod) {
+ when (ReadConfig.contentSelectSpeakMod) {
1 -> _effects.tryEmit(ReadBookEffect.TextActionAloudSelect)
else -> _effects.tryEmit(ReadBookEffect.TextActionSpeak(intent.text))
}
@@ -1052,7 +1072,7 @@ class ReadBookViewModel(
_effects.tryEmit(ReadBookEffect.UnregisterNetworkListener)
if (!BuildConfig.DEBUG) {
- if (AppConfig.syncBookProgressPlus) {
+ if (ReadConfig.syncBookProgressPlus) {
ReadBook.syncProgress()
} else {
ReadBook.uploadProgress()
@@ -1123,7 +1143,7 @@ class ReadBookViewModel(
* Called from the network changed listener (registered by route).
*/
fun onNetworkChanged() {
- if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
+ if (ReadConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
ReadBook.syncProgress(newProgressAction = { progress ->
sureNewProgress(progress)
})
@@ -1149,7 +1169,7 @@ class ReadBookViewModel(
private fun handleChangeSource() {
viewModelScope.launch {
- if (AppConfig.defaultSourceChangeAll) {
+ if (ReadConfig.defaultSourceChangeAll) {
_uiState.update { it.copy(activeSheet = ReadBookSheet.ChangeBookSource) }
} else {
val book = ReadBook.book ?: return@launch
@@ -1393,10 +1413,10 @@ class ReadBookViewModel(
readSettingsRepository.preferences.collect { preferences ->
val old = previous
previous = preferences
- AppConfig.syncReadPreferences(preferences)
+ ReadConfig.syncReadPreferences(preferences)
_readPreferences.value = preferences
if (!preferences.hasMenuClickArea()) {
- AppConfig.detectClickArea()
+ ReadConfig.detectClickArea()
readSettingsRepository.setClickAction(PreferKey.clickActionMC, 0)
}
if (old != null && old.keepLight != preferences.keepLight) {
@@ -1423,11 +1443,7 @@ class ReadBookViewModel(
readAloudStreamAudio = prefs.streamReadAloudAudio,
readAloudTtsFollowSys = prefs.ttsFollowSys,
readAloudTtsSpeechRate = prefs.ttsSpeechRate,
- readAloudTtsTimer = if (BaseReadAloudService.timeMinute > 0) {
- BaseReadAloudService.timeMinute
- } else {
- prefs.ttsTimer
- },
+ readAloudTtsTimer = prefs.ttsTimer,
)
}
}
@@ -1445,11 +1461,12 @@ class ReadBookViewModel(
}
private fun setReadAloudTtsTimer(value: Int) {
+ val timer = value.coerceIn(0, 180)
viewModelScope.launch {
- readAloudSettingsRepository.setTtsTimer(value)
+ readAloudSettingsRepository.setTtsTimer(timer)
}
- ReadAloud.setTimer(context, value)
- _uiState.update { it.copy(readAloudTtsTimer = value) }
+ ReadAloud.setTimer(context, timer)
+ _uiState.update { it.copy(readAloudTtsTimer = timer) }
}
private fun setReadAloudTtsSpeechRate(value: Int) {
@@ -1557,6 +1574,10 @@ class ReadBookViewModel(
readMenuCustomIcons = ReadBookConfig.readMenuCustomIcons.toImmutableMap(),
titleBarButtons = current.menuConfig.titleBarButtons,
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 {
- return when (AppConfig.progressBarBehavior) {
+ return when (ReadConfig.progressBarBehavior) {
"page" -> ReadBook.durPageIndex
else -> ReadBook.durChapterIndex
}
}
private fun calculateSeekMax(): Int {
- return when (AppConfig.progressBarBehavior) {
+ return when (ReadConfig.progressBarBehavior) {
"page" -> (ReadBook.curTextChapter?.pages?.size ?: 1) - 1
else -> ReadBook.chapterSize - 1
}
@@ -1761,7 +1782,7 @@ class ReadBookViewModel(
if (ReadBook.chapterChanged) {
ReadBook.chapterChanged = false
} else if (!(isSameBook && BaseReadAloudService.isRun) && ReadBook.inBookshelf) {
- if (AppConfig.syncBookProgressPlus) {
+ if (ReadConfig.syncBookProgressPlus) {
ReadBook.syncProgress({ progress -> sureNewProgress(progress) })
} else {
syncBookProgress(book)
@@ -1857,7 +1878,7 @@ class ReadBookViewModel(
book: Book,
alertSync: ((progress: BookProgress) -> Unit)? = null
) {
- if (!AppConfig.syncBookProgress) return
+ if (!ReadConfig.syncBookProgress) return
execute {
getReadingProgressUseCase.execute(book.name, book.author)?.toBookProgress()
}.onError {
@@ -1952,7 +1973,7 @@ class ReadBookViewModel(
}
private fun autoChangeSource(name: String, author: String) {
- if (!AppConfig.autoChangeSource) return
+ if (!ReadConfig.autoChangeSource) return
execute {
val sources = appDb.bookSourceDao.allTextEnabledPart
flow {
@@ -2164,11 +2185,11 @@ class ReadBookViewModel(
fun searchResultPositions(
textChapter: TextChapter,
- searchResult: SearchResult
+ searchResult: SearchResult,
+ query: String = _uiState.value.searchContentQuery,
): Array {
val pages = textChapter.pages
val content = textChapter.getContent()
- val query = _uiState.value.searchContentQuery
val queryLength = query.length
var count = 0
@@ -2178,6 +2199,7 @@ class ReadBookViewModel(
count += 1
}
val contentPosition = index
+ if (contentPosition < 0) return arrayOf(0, 0, 0, 0, 0)
var pageIndex = 0
var length = pages[pageIndex].text.length
while (length < contentPosition && pageIndex + 1 < pages.size) {
@@ -2191,7 +2213,7 @@ class ReadBookViewModel(
var curLine = curTextLines[lineIndex]
length = length - currentPage.text.length + curLine.text.length
if (curLine.isParagraphEnd) length++
- while (length <= contentPosition && lineIndex + 1 < curTextLines.size) {
+ while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
lineIndex += 1
curLine = curTextLines[lineIndex]
length += curLine.text.length
@@ -2217,6 +2239,55 @@ class ReadBookViewModel(
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() {
execute {
val book = ReadBook.book ?: return@execute
@@ -2423,7 +2494,7 @@ class ReadBookViewModel(
}
is ConfigUpdate.ReadBarStyle -> {
val value = update.value.coerceIn(0, 2)
- AppConfig.updateReadBarStyleCache(value)
+ ReadConfig.readBarStyle = value
viewModelScope.launch {
readSettingsRepository.setReadBarStyle(value)
}
@@ -2760,11 +2831,41 @@ class ReadBookViewModel(
}
}
is ConfigUpdate.ShowBrightnessView -> {
+ ReadBookConfig.showBrightnessView = update.value
viewModelScope.launch {
readSettingsRepository.setShowBrightnessView(update.value)
}
+ _uiState.update {
+ it.copy(menuConfig = it.menuConfig.copy(showBrightnessView = update.value))
+ }
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 -> {
ReadConfig.useUnderline = update.value
viewModelScope.launch {
@@ -3051,7 +3152,7 @@ class ReadBookViewModel(
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
))
- if (AppConfig.readBarStyleFollowPage) {
+ if (ReadConfig.readBarStyleFollowPage) {
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
}
}
@@ -3061,7 +3162,7 @@ class ReadBookViewModel(
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.UpdateContent, ConfigUpdateAction.InvalidateTextPage, ConfigUpdateAction.SubmitRenderTask)
))
- if (AppConfig.readBarStyleFollowPage) {
+ if (ReadConfig.readBarStyleFollowPage) {
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
}
}
@@ -3071,7 +3172,7 @@ class ReadBookViewModel(
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
setOf(ConfigUpdateAction.UpdateBackground)
))
- if (AppConfig.readBarStyleFollowPage) {
+ if (ReadConfig.readBarStyleFollowPage) {
postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
index 2bb14deff..e9e01d010 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
@@ -14,13 +14,15 @@ import android.widget.PopupWindow
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuItemImpl
+import androidx.core.net.toUri
import androidx.core.view.isVisible
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
+import io.legado.app.constant.AppLog
import io.legado.app.databinding.ItemTextBinding
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.isAbsUrl
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.toastOnUi
import io.legado.app.utils.visible
-import androidx.core.net.toUri
-import io.legado.app.constant.AppLog
@SuppressLint("RestrictedApi")
class TextActionMenu(
@@ -185,11 +185,11 @@ class TextActionMenu(
callBack.onMenuActionFinally()
}
holder.itemView.setOnLongClickListener {
- if (AppConfig.contentSelectSpeakMod == 0) {
- AppConfig.contentSelectSpeakMod = 1
+ if (ReadConfig.contentSelectSpeakMod == 0) {
+ ReadConfig.contentSelectSpeakMod = 1
context.toastOnUi("切换为从选择的地方开始一直朗读")
} else {
- AppConfig.contentSelectSpeakMod = 0
+ ReadConfig.contentSelectSpeakMod = 0
context.toastOnUi("切换为朗读选择内容")
}
true
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt b/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt
index 164d6c164..d61caf727 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt
@@ -4,9 +4,9 @@ import android.graphics.Canvas
import android.graphics.Paint
import android.os.SystemClock
import androidx.core.graphics.withClip
-import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig
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.recordIfNeeded
import io.legado.app.utils.themeColor
@@ -30,7 +30,7 @@ class AutoPager(private val readView: ReadView) : Runnable {
fun start() {
isRunning = true
- isEInkMode = AppConfig.isEInkMode
+ isEInkMode = ReadConfig.isEInkMode
readView.curPage.upSelectAble(false)
if (isEInkMode) {
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
@@ -49,7 +49,7 @@ class AutoPager(private val readView: ReadView) : Runnable {
isPausing = false
isEInkMode = false
readView.removeCallbacks(this)
- readView.curPage.upSelectAble(AppConfig.textSelectAble)
+ readView.curPage.upSelectAble(ReadConfig.selectText)
readView.invalidate()
reset()
canvasRecorder.recycle()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
index 915c1c5f4..9f686b6fa 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
@@ -9,7 +9,6 @@ import android.view.View
import io.legado.app.R
import io.legado.app.data.entities.Bookmark
import io.legado.app.help.book.isOnLineTxt
-import io.legado.app.help.config.AppConfig
import io.legado.app.model.ReadBook
import io.legado.app.ui.association.OpenUrlConfirmActivity
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.provider.ChapterProvider
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.utils.activity
import io.legado.app.utils.dpToPx
@@ -40,7 +40,7 @@ import kotlin.math.min
* 阅读内容视图
*/
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
- var selectAble = AppConfig.textSelectAble
+ var selectAble = ReadConfig.selectText
val selectedPaint by lazy {
Paint().apply {
color = context.getCompatColor(R.color.btn_bg_press_2)
@@ -73,7 +73,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
//绘制图片的paint
val imagePaint by lazy {
Paint().apply {
- isAntiAlias = AppConfig.useAntiAlias
+ isAntiAlias = ReadConfig.useAntiAlias
}
}
@@ -266,7 +266,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
handled = true
}
- is ImageColumn -> when (AppConfig.clickImgWay) {
+ is ImageColumn -> when (ReadConfig.clickImgWay) {
"1" -> { //预览图片
activity?.showDialogFragment(PhotoDialog(column.src, isBook = true))
handled = true
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
index 1b22fa616..ff9ab11c6 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
@@ -16,13 +16,13 @@ import io.legado.app.R
import io.legado.app.constant.AppConst.timeFormat
import io.legado.app.data.entities.Bookmark
import io.legado.app.databinding.ViewBookPageBinding
-import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig
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.TextPage
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.config.readConfig.ReadConfig
import io.legado.app.ui.widget.BatteryView
import io.legado.app.utils.activity
import io.legado.app.utils.applyNavigationBarPadding
@@ -169,7 +169,7 @@ class PageView(
}
fun upPaddingDisplayCutouts() {
- if (AppConfig.paddingDisplayCutouts) {
+ if (ReadConfig.paddingDisplayCutouts) {
binding.vwRoot.setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
binding.vwRoot.setPadding(
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
index 2774a9e10..a31bca765 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
@@ -13,7 +13,6 @@ import android.widget.FrameLayout
import io.legado.app.R
import io.legado.app.constant.PageAnim
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.model.ReadAloud
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.LayoutProgressListener
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.invisible
import io.legado.app.utils.longToastOnUi
@@ -272,7 +272,7 @@ class ReadView(
}
fun cancelSelect(clearSearchResult: Boolean = false) {
- if (isTextSelected) {
+ if (isTextSelected || clearSearchResult) {
curPage.cancelSelect(clearSearchResult)
isTextSelected = false
}
@@ -398,39 +398,39 @@ class ReadView(
when {
isTextSelected -> Unit
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
- click(AppConfig.clickActionMC)
+ click(ReadConfig.clickActionMC)
}
bcRect.contains(startX, startY) -> {
- click(AppConfig.clickActionBC)
+ click(ReadConfig.clickActionBC)
}
blRect.contains(startX, startY) -> {
- click(AppConfig.clickActionBL)
+ click(ReadConfig.clickActionBL)
}
brRect.contains(startX, startY) -> {
- click(AppConfig.clickActionBR)
+ click(ReadConfig.clickActionBR)
}
mlRect.contains(startX, startY) -> {
- click(AppConfig.clickActionML)
+ click(ReadConfig.clickActionML)
}
mrRect.contains(startX, startY) -> {
- click(AppConfig.clickActionMR)
+ click(ReadConfig.clickActionMR)
}
tlRect.contains(startX, startY) -> {
- click(AppConfig.clickActionTL)
+ click(ReadConfig.clickActionTL)
}
tcRect.contains(startX, startY) -> {
- click(AppConfig.clickActionTC)
+ click(ReadConfig.clickActionTC)
}
trRect.contains(startX, startY) -> {
- click(AppConfig.clickActionTR)
+ click(ReadConfig.clickActionTR)
}
}
}
@@ -552,7 +552,7 @@ class ReadView(
pageDelegate = NoAnimPageDelegate(this)
}
}
- (pageDelegate as? ScrollPageDelegate)?.noAnim = AppConfig.noAnimScrollPage
+ (pageDelegate as? ScrollPageDelegate)?.noAnim = ReadConfig.noAnimScrollPage
if (upRecorder) {
(pageDelegate as? HorizontalPageDelegate)?.upRecorder()
autoPager.upRecorder()
@@ -603,7 +603,7 @@ class ReadView(
* 更新滑动距离
*/
fun upPageSlopSquare() {
- val pageTouchSlop = AppConfig.pageTouchSlop
+ val pageTouchSlop = ReadConfig.pageTouchSlop
this.pageSlopSquare = if (pageTouchSlop == 0) slopSquare else pageTouchSlop
pageSlopSquare2 = this.pageSlopSquare * this.pageSlopSquare
}
@@ -697,7 +697,7 @@ class ReadView(
}
fun invalidateTextPage() {
- if (!AppConfig.optimizeRender) {
+ if (!ReadConfig.optimizeRender) {
return
}
pageFactory.run {
@@ -722,7 +722,7 @@ class ReadView(
}
fun submitRenderTask() {
- if (!AppConfig.optimizeRender) {
+ if (!ReadConfig.optimizeRender) {
return
}
curPage.submitRenderTask()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
index ca4e5a428..70d9df857 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
@@ -9,11 +9,9 @@ import android.graphics.Paint
import android.graphics.Path
import android.graphics.Shader
import android.os.Build
-import android.text.TextPaint
import androidx.annotation.Keep
import io.legado.app.help.PaintPool
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.model.ReadBook
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.TextColumn
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.recordIfNeededThenDraw
import io.legado.app.utils.dpToPx
@@ -77,7 +76,7 @@ data class TextLine(
var textPage: TextPage = emptyTextPage
var isLeftLine = true
val useUnderline: Boolean
- get() = AppConfig.useUnderline
+ get() = ReadConfig.useUnderline
fun addColumn(column: BaseColumn) {
if (column !is TextColumn) {
@@ -160,7 +159,7 @@ data class TextLine(
}
fun draw(view: ContentTextView, canvas: Canvas) {
- if (AppConfig.optimizeRender) {
+ if (ReadConfig.optimizeRender) {
canvasRecorder.recordIfNeededThenDraw(canvas, view.width, height.toInt()) {
drawTextLine(view, this)
}
@@ -236,7 +235,7 @@ data class TextLine(
val paint = ChapterProvider.contentPaint
paint.color = ReadBookConfig.durConfig.curUnderlineColor()
paint.strokeWidth = ReadBookConfig.underlineHeight.toFloat()
- paint.pathEffect = if (dottedLine && !AppConfig.isEInkMode)
+ paint.pathEffect = if (dottedLine && !ReadConfig.isEInkMode)
ChapterProvider.dashEffect
else
null
@@ -557,7 +556,7 @@ data class TextLine(
}
fun checkFastDraw(): Boolean {
- if (!AppConfig.optimizeRender || exceed || !onlyTextColumn || textPage.isMsgPage) {
+ if (!ReadConfig.optimizeRender || exceed || !onlyTextColumn || textPage.isMsgPage) {
return false
}
if (wordSpacing != 0f && (!atLeastApi26 || !wordSpacingWorking)) {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
index 561df6456..0c3a973e0 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
@@ -9,13 +9,13 @@ import androidx.annotation.Keep
import androidx.core.graphics.withTranslation
import io.legado.app.R
import io.legado.app.help.PaintPool
-import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig
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.column.TextBaseColumn
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.config.readConfig.ReadConfig
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeeded
import io.legado.app.utils.dpToPx
@@ -299,7 +299,7 @@ data class TextPage(
}
fun draw(view: ContentTextView, canvas: Canvas, relativeOffset: Float) {
- if (AppConfig.optimizeRender) {
+ if (ReadConfig.optimizeRender) {
render(view)
canvas.withTranslation(0f, relativeOffset) {
canvasRecorder.draw(this)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
index 27b747617..ef709ffaf 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
@@ -15,12 +15,12 @@ import io.legado.app.constant.EventBus
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
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.dottedBase
import io.legado.app.help.config.ReadBookConfig.dottedRatio
import io.legado.app.model.ReadBook
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.dpToPx
import io.legado.app.utils.isContentScheme
@@ -190,7 +190,7 @@ object ChapterProvider {
displayTitle.splitNotBlank("\n").forEach { text ->
setTypeText(
book, absStartX, durY,
- if (AppConfig.enableReview) text + reviewChar else text,
+ if (ReadConfig.enableReview) text + reviewChar else text,
textPages,
stringBuilder,
titlePaint,
@@ -279,7 +279,7 @@ object ChapterProvider {
if (text.isNotBlank()) {
setTypeText(
book, absStartX, durY,
- if (AppConfig.enableReview) text + reviewChar else text,
+ if (ReadConfig.enableReview) text + reviewChar else text,
textPages,
stringBuilder,
contentPaint,
@@ -918,7 +918,7 @@ object ChapterProvider {
}
else -> {
- when (AppConfig.systemTypefaces) {
+ when (ReadConfig.systemTypefaces) {
1 -> Typeface.SERIF
2 -> Typeface.MONOSPACE
else -> Typeface.SANS_SERIF
@@ -1013,7 +1013,7 @@ object ChapterProvider {
tPaint.setFontVariationSettings("'wght' ${ReadBookConfig.titleBold}")
tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.toFloat().spToPx()
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
}
if (ReadBookConfig.textItalic) {
@@ -1036,7 +1036,7 @@ object ChapterProvider {
cPaint.setFontVariationSettings("'wght' ${ReadBookConfig.textBold}")
cPaint.textSize = ReadBookConfig.textSize.toFloat().spToPx()
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
}
if (ReadBookConfig.textItalic) {
@@ -1086,7 +1086,7 @@ object ChapterProvider {
* 更新绘制尺寸
*/
fun upLayout() {
- when (AppConfig.doublePageHorizontal) {
+ when (ReadConfig.doubleHorizontalPage) {
"0" -> doublePage = false
"1" -> doublePage = true
"2" -> {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt
index 01d87b664..b9975da26 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt
@@ -27,7 +27,6 @@ import io.legado.app.data.repository.HighlightRuleRepository
import io.legado.app.help.book.BookContent
import io.legado.app.help.book.BookHelp
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.coroutine.Coroutine
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.srcReplaceCharC
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.StringUtils
import io.legado.app.utils.dpToPx
@@ -141,7 +141,7 @@ class TextChapterLayout(
private val useZhLayout = ReadBookConfig.useZhLayout
private val isMiddleTitle = ReadBookConfig.isMiddleTitle
private val textFullJustify = ReadBookConfig.textFullJustify
- private val adaptSpecialStyle = AppConfig.adaptSpecialStyle
+ private val adaptSpecialStyle = ReadConfig.adaptSpecialStyle
private val pageAnim = book.getPageAnim()
private val titleSegType = ReadBookConfig.titleSegType
private val titleSegDistance = ReadBookConfig.titleSegDistance
@@ -490,7 +490,7 @@ class TextChapterLayout(
text = sb.toString()
if (text.isNotBlank()) {
wordCount += text.replace(noWordCountRegex,"").length
- val textToType = if (AppConfig.enableReview) text + reviewChar else text
+ val textToType = if (ReadConfig.enableReview) text + reviewChar else text
setTypeText(
book,
textToType,
diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/EffectiveReplacesSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/EffectiveReplacesSheet.kt
index 12b108659..7759fb08d 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/sheet/EffectiveReplacesSheet.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/EffectiveReplacesSheet.kt
@@ -1,24 +1,42 @@
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.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowForward
+import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
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.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.text.AppText
+import kotlinx.coroutines.launch
+
+private const val CHINESE_CONVERT_ID = -1L
@Composable
fun EffectiveReplacesSheet(
@@ -26,12 +44,26 @@ fun EffectiveReplacesSheet(
onDismissRequest: () -> Unit,
onOpenReplaceEditor: (id: Long, pattern: String?) -> 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()
}
- 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()) }
AppModalBottomSheet(
show = show,
@@ -44,18 +76,80 @@ fun EffectiveReplacesSheet(
LazyColumn(
modifier = Modifier.fillMaxWidth(),
) {
- items(effectiveRules, key = { it.id }) { rule ->
- Text(
- text = rule.name,
- style = MaterialTheme.typography.bodyLarge,
- modifier = Modifier
- .fillMaxWidth()
- .clickable {
- onOpenReplaceEditor(rule.id, rule.pattern)
+ items(items, key = { it.id }) { rule ->
+ if (rule.id !in disabledIds) {
+ NormalCard(
+ onClick = {
+ if (rule.id == CHINESE_CONVERT_ID) {
+ onNavigateToTextEffects()
+ } else {
+ onOpenReplaceEditor(rule.id, rule.pattern)
+ }
+ },
+ 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,
+ )
+ 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
+ )
}
- .padding(vertical = 12.dp),
- )
- HorizontalDivider()
+ }
+ }
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt
index 243b8204f..c0162d031 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt
@@ -87,9 +87,6 @@ fun MoreConfigSheet(
onUseZhLayoutChange = {
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseZhLayout(it)))
},
- onShowBrightnessViewChange = {
- onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ShowBrightnessView(it)))
- },
onUseUnderlineChange = {
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.UseUnderlineGlobal(it)))
},
@@ -170,7 +167,6 @@ private fun ScreenSettings(
onTextBottomJustifyChange: (Boolean) -> Unit,
onAdaptSpecialStyleChange: (Boolean) -> Unit,
onUseZhLayoutChange: (Boolean) -> Unit,
- onShowBrightnessViewChange: (Boolean) -> Unit,
onUseUnderlineChange: (Boolean) -> Unit,
) {
val screenDirectionEntries = stringArrayResource(R.array.screen_direction_title)
@@ -232,11 +228,6 @@ private fun ScreenSettings(
checked = preferences.useZhLayout,
onCheckedChange = onUseZhLayoutChange,
)
- TinySwitchSettingItem(
- title = stringResource(R.string.show_brightness_view),
- checked = preferences.showBrightnessView,
- onCheckedChange = onShowBrightnessViewChange,
- )
TinySwitchSettingItem(
title = stringResource(R.string.use_underline),
checked = preferences.useUnderline,
diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/ReadStyleSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/ReadStyleSheet.kt
index 2cb8d2a7e..20435348f 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/sheet/ReadStyleSheet.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/ReadStyleSheet.kt
@@ -10,7 +10,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.ReadBookIntent
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 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 = emptyMap(),
- bottomBarButtons: List = 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
fun ReadStyleContent(
onOpenPaddingConfig: () -> Unit,
@@ -87,6 +33,7 @@ fun ReadStyleContent(
onOpenTextTitle: () -> Unit,
onOpenFontSelect: () -> Unit,
onToggleDayNight: () -> Unit,
+ onPageChanged: (Int) -> Unit = {},
readMenuCustomIcons: Map = emptyMap(),
bottomBarButtons: List = emptyList(),
modifier: Modifier = Modifier,
@@ -100,6 +47,7 @@ fun ReadStyleContent(
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.currentPage }.collect { page ->
currentPage = page
+ onPageChanged(page)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/SystemMenuPage.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/SystemMenuPage.kt
index 5a5ddafba..e5cae7682 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/sheet/SystemMenuPage.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/SystemMenuPage.kt
@@ -340,6 +340,31 @@ private fun GlobalMenuTab(
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 {
mutableStateOf(preferences.readMenuBorderWidth > 0)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/TextTitleSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/TextTitleSheet.kt
index bafccfceb..24001eedc 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/sheet/TextTitleSheet.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/TextTitleSheet.kt
@@ -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.config.readConfig.ReadConfig
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.TinyColorSettingItem
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_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
fun ReadStyleTextTitleContent(
onOpenShadowSet: () -> Unit,
diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
deleted file mode 100644
index a010ce75e..000000000
--- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
+++ /dev/null
@@ -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() }
- )
- }
- }
-}
diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentResult.kt
new file mode 100644
index 000000000..c996e410b
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentResult.kt
@@ -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,
+ val index: Int,
+ val query: String,
+ )
+
+ private val _results = MutableSharedFlow(replay = 1)
+ val results = _results.asSharedFlow()
+
+ fun emitResult(result: Result) {
+ _results.tryEmit(result)
+ }
+
+ fun resetReplayCache() {
+ _results.resetReplayCache()
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
index f3672607b..57f924014 100644
--- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
@@ -1,8 +1,5 @@
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.AnimatedVisibility
import androidx.compose.foundation.clickable
@@ -81,8 +78,6 @@ fun SearchContentScreen(
onBack: () -> Unit,
viewModel: SearchContentViewModel = koinViewModel()
) {
- val activity = LocalActivity.current
-
val uiState by viewModel.uiState.collectAsState()
val searchQuery by viewModel.searchQuery.collectAsState()
val replaceEnabled by viewModel.replaceEnabled.collectAsState()
@@ -259,13 +254,8 @@ fun SearchContentScreen(
result = result,
isCurrentChapter = result.chapterIndex == durChapterIndex,
onClick = {
- viewModel.onSearchResultClick(result) { key ->
- val intent = Intent().apply {
- putExtra("key", key)
- putExtra("index", index)
- }
- activity?.setResult(Activity.RESULT_OK, intent)
- activity?.finish()
+ if (viewModel.onSearchResultClick(result)) {
+ onBack()
}
}
)
diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt
index 7e28c808a..b7ebc2465 100644
--- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt
@@ -1,16 +1,12 @@
package io.legado.app.ui.book.searchContent
-import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchContentHistory
import io.legado.app.data.repository.BookRepository
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.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -42,15 +38,13 @@ sealed interface SearchContentState {
class SearchContentViewModel(
- savedStateHandle: SavedStateHandle,
+ val bookUrl: String,
+ initialSearchWord: String?,
+ private val searchResultIndex: Int,
private val bookRepository: BookRepository,
private val searchContentRepository: SearchContentRepository
) : ViewModel() {
- val bookUrl: String = savedStateHandle.get("bookUrl") ?: ""
- private val initialSearchWord: String? = savedStateHandle.get("searchWord")
- private val searchResultIndex: Int = savedStateHandle.get("searchResultIndex") ?: 0
-
private val _searchQuery = MutableStateFlow(initialSearchWord ?: "")
val searchQuery = _searchQuery.asStateFlow()
@@ -189,12 +183,18 @@ class SearchContentViewModel(
hasAutoScrolled = true
}
- fun onSearchResultClick(searchResult: SearchResult, onSuccess: (key: Long) -> Unit) {
+ fun onSearchResultClick(searchResult: SearchResult): Boolean {
stopSearch()
- postEvent(EventBus.SEARCH_RESULT, uiState.value.searchResults)
- val key = System.currentTimeMillis()
- IntentData.put("searchResult$key", searchResult)
- IntentData.put("searchResultList$key", uiState.value.searchResults)
- onSuccess(key)
+ val results = uiState.value.searchResults
+ val index = results.indexOf(searchResult)
+ if (index < 0) return false
+ SearchContentResult.emitResult(
+ SearchContentResult.Result(
+ searchResults = results,
+ index = index,
+ query = searchResult.query
+ )
+ )
+ return true
}
}
diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt
index c41800a3c..202e76aa1 100644
--- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt
+++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt
@@ -1,10 +1,139 @@
package io.legado.app.ui.config.readConfig
+import io.legado.app.BuildConfig
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.themeConfig.ThemeConfig
+import io.legado.app.utils.isNightMode
+import io.legado.app.utils.sysConfiguration
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(
PreferKey.tocUiUseReplace,
false
@@ -82,7 +211,22 @@ object ReadConfig {
var showBrightnessView by prefDelegate(
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(
@@ -180,6 +324,73 @@ object ReadConfig {
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(
PreferKey.prevKeys,
""
diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt
index 90190ce80..df1d831d9 100644
--- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt
+++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt
@@ -14,7 +14,9 @@ data class ReadConfigUiState(
val textBottomJustify: Boolean = true,
val adaptSpecialStyle: Boolean = true,
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 readSliderMode: String = "0",
val doubleHorizontalPage: String = "0",
@@ -53,7 +55,8 @@ sealed interface ReadConfigIntent {
data class TextBottomJustifyChanged(val value: Boolean) : ReadConfigIntent
data class AdaptSpecialStyleChanged(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 ReadSliderModeChanged(val value: String) : ReadConfigIntent
data class DoubleHorizontalPageChanged(val value: String) : ReadConfigIntent
diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt
index 832741c7b..32970f322 100644
--- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt
@@ -18,13 +18,13 @@ import io.legado.app.R
import io.legado.app.ui.theme.adaptiveContentPadding
import io.legado.app.ui.widget.components.AppScaffold
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.DropdownListSettingItem
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.topbar.GlassMediumFlexibleTopAppBar
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 org.koin.androidx.compose.koinViewModel
@@ -173,14 +173,28 @@ fun ReadConfigScreen(
}
)
- SwitchSettingItem(
+ DropdownListSettingItem(
title = stringResource(R.string.show_brightness_view),
- checked = state.showBrightnessView,
- onCheckedChange = {
+ selectedValue = state.showBrightnessView,
+ displayEntries = stringArrayResource(R.array.brightness_bar_mode_title),
+ entryValues = stringArrayResource(R.array.brightness_bar_mode_value),
+ onValueChange = {
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(
title = stringResource(R.string.use_underline),
checked = state.useUnderline,
diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt
index 3f1fe9a0d..7d939ed43 100644
--- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt
@@ -92,6 +92,10 @@ class ReadConfigViewModel(
readSettingsRepository.setShowBrightnessView(intent.value)
}
+ is ReadConfigIntent.BrightnessVwPosChanged -> {
+ readSettingsRepository.setBrightnessVwPos(intent.value)
+ }
+
is ReadConfigIntent.UseUnderlineChanged -> {
readSettingsRepository.setUseUnderline(intent.value)
}
@@ -209,6 +213,8 @@ class ReadConfigViewModel(
adaptSpecialStyle = adaptSpecialStyle,
useZhLayout = useZhLayout,
showBrightnessView = showBrightnessView,
+ brightnessVwPos = brightnessVwPos,
+ brightnessAuto = brightnessAuto,
useUnderline = useUnderline,
readSliderMode = readSliderMode,
doubleHorizontalPage = doubleHorizontalPage,
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt
index edc4f2594..545ce7448 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt
@@ -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.SearchScreen
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.config.ConfigNavScreen
import io.legado.app.ui.config.backupConfig.BackupConfigScreen
import io.legado.app.ui.config.coverConfig.CoverConfigScreen
import io.legado.app.ui.config.customTheme.CustomThemeScreen
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.readConfig.ReadConfigScreen
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
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.rss.article.MainRouteRssSort
import io.legado.app.ui.rss.article.RssSortRouteScreen
@@ -69,6 +71,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.androidx.compose.koinViewModel
+import org.koin.core.parameter.parametersOf
@OptIn(ExperimentalSharedTransitionApi::class)
fun MainActivity.mainEntryProvider(
@@ -294,6 +297,15 @@ fun MainActivity.mainEntryProvider(
host = controller,
controller = controller,
onEffectsReady = { effectsReady.complete(Unit) },
+ onOpenSearch = { word, bookUrl ->
+ onNavigateToRoute(
+ MainRouteSearchContent(
+ bookUrl = bookUrl,
+ searchWord = word,
+ searchResultIndex = readBookViewModel.uiState.value.searchResultIndex
+ )
+ )
+ },
)
DisposableEffect(controller, lifecycleOwner, route.readAloud) {
@@ -340,6 +352,16 @@ fun MainActivity.mainEntryProvider(
}
}
+ entry { route ->
+ val viewModel = koinViewModel(
+ parameters = { parametersOf(route) }
+ )
+ SearchContentScreen(
+ viewModel = viewModel,
+ onBack = { onNavigateBack() },
+ )
+ }
+
entry { route ->
val searchViewModel = koinViewModel()
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt b/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt
index 4de1d10d4..a35e80da3 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt
@@ -97,6 +97,13 @@ data class MainRouteExploreShow(
val exploreUrl: String?,
) : MainRoute
+@Serializable
+data class MainRouteSearchContent(
+ val bookUrl: String,
+ val searchWord: String? = null,
+ val searchResultIndex: Int = 0,
+) : MainRoute
+
@Serializable
data object MainRouteAbout : MainRoute
@@ -118,6 +125,7 @@ object MainRouteConst {
const val ROUTE_BOOK_CACHE_MANAGE = "book/cache/manage"
const val ROUTE_READ_BOOK = "book/read"
const val ROUTE_SEARCH = "search"
+ const val ROUTE_SEARCH_CONTENT = "book/searchContent"
const val ROUTE_BOOK_INFO = "book/info"
const val ROUTE_EXPLORE_SHOW = "explore/show"
const val ROUTE_RSS_SORT = "rss/sort"
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt
index b73997706..641febb00 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt
@@ -60,6 +60,10 @@ object MainNavigator {
}
}
+ is MainRouteSearchContent -> {
+ backStack.add(route)
+ }
+
is MainRouteSearch -> {
if (
currentRoute == MainRouteHome ||
diff --git a/app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt b/app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt
index 3aa74c3f9..3940f9f39 100644
--- a/app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt
@@ -11,7 +11,7 @@ import android.text.StaticLayout
import android.util.AttributeSet
import androidx.annotation.ColorInt
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.recordIfNeededThenDraw
import io.legado.app.utils.dpToPx
@@ -75,7 +75,7 @@ class BatteryViewOrgin @JvmOverloads constructor(
}
override fun onDraw(canvas: Canvas) {
- if (AppConfig.optimizeRender) {
+ if (ReadConfig.optimizeRender) {
canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
super.onDraw(this)
drawBattery(this)
@@ -119,4 +119,4 @@ class BatteryViewOrgin @JvmOverloads constructor(
canvasRecorder?.invalidate()
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/legado/app/ui/widget/components/AppVerticalSlider.kt b/app/src/main/java/io/legado/app/ui/widget/components/AppVerticalSlider.kt
new file mode 100644
index 000000000..36847c76c
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/components/AppVerticalSlider.kt
@@ -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 = 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,
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/widget/components/FontSelectGrid.kt b/app/src/main/java/io/legado/app/ui/widget/components/FontSelectGrid.kt
index cea27be18..e15dda904 100644
--- a/app/src/main/java/io/legado/app/ui/widget/components/FontSelectGrid.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/components/FontSelectGrid.kt
@@ -143,14 +143,14 @@ private fun FontItem(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
- .background(LegadoTheme.colorScheme.surfaceVariant)
+ .background(LegadoTheme.colorScheme.surface)
.clickable(onClick = onClick)
.padding(12.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.height(48.dp)
) {
- val context = LocalContext.current
AndroidView(
factory = { ctx ->
android.widget.TextView(ctx).apply {
@@ -178,7 +178,7 @@ private fun FontItem(
Icon(
Icons.Default.Check,
contentDescription = null,
- tint = MaterialTheme.colorScheme.primary,
+ tint = LegadoTheme.colorScheme.primary,
modifier = Modifier.size(18.dp),
)
}
diff --git a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt
index f6c550372..0f630bcf5 100644
--- a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt
+++ b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt
@@ -1,7 +1,7 @@
package io.legado.app.utils.canvasrecorder
import android.os.Build
-import io.legado.app.help.config.AppConfig
+import io.legado.app.ui.config.readConfig.ReadConfig
object CanvasRecorderFactory {
@@ -12,7 +12,7 @@ object CanvasRecorderFactory {
// issue 3868
fun create(locked: Boolean = false): CanvasRecorder {
val impl = when {
- !AppConfig.optimizeRender -> CanvasRecorderImpl()
+ !ReadConfig.optimizeRender -> CanvasRecorderImpl()
atLeastApi29 -> CanvasRecorderApi29Impl()
atLeastApi24 -> CanvasRecorderApi23Impl()
else -> CanvasRecorderImpl()
diff --git a/app/src/main/res/layout/activity_search_content.xml b/app/src/main/res/layout/activity_search_content.xml
deleted file mode 100644
index 8293b420a..000000000
--- a/app/src/main/res/layout/activity_search_content.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 6593850a7..dfe919370 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -907,6 +907,13 @@
格式不对
错误
显示亮度调节控件
+ 关闭
+ 横排
+ 竖排
+ 亮度条位置
+ 左侧
+ 右侧
+ 跟随系统
语言
导入订阅源
您的支持是我更新的动力
diff --git a/app/src/main/res/values/array_values.xml b/app/src/main/res/values/array_values.xml
index ebb0acf40..32d2b7dd4 100644
--- a/app/src/main/res/values/array_values.xml
+++ b/app/src/main/res/values/array_values.xml
@@ -73,6 +73,17 @@
- chapter
+
+ - 0
+ - 1
+ - 2
+
+
+
+ - 0
+ - 1
+
+
- auto
- zh
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 67ab13d29..f29c23c4e 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -147,6 +147,17 @@
- @string/adjust_chapter_index
+
+ - @string/brightness_bar_off
+ - @string/brightness_bar_horizontal
+ - @string/brightness_bar_vertical
+
+
+
+ - @string/brightness_bar_left
+ - @string/brightness_bar_right
+
+
- iconMain
- iconWhite
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c6dd9e280..d470697f2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -932,6 +932,13 @@
Wrong format
Error
Show Brightness Widget
+ Off
+ Horizontal
+ Vertical
+ Brightness Bar Position
+ Left
+ Right
+ Follow System
Language
Import rss source
Your donation makes this app better
diff --git a/app/src/main/res/xml/pref_config_read.xml b/app/src/main/res/xml/pref_config_read.xml
index cf1b38d6a..6158b7f53 100644
--- a/app/src/main/res/xml/pref_config_read.xml
+++ b/app/src/main/res/xml/pref_config_read.xml
@@ -93,10 +93,12 @@
android:title="@string/use_zh_layout"
app:isBottomBackground="true" />
-