Merge pull request #3731 from gedoor/master

Sync changes from branch master
This commit is contained in:
Xwite authored and GitHub committed 2024-02-19 14:24:10 +08:00
commit 5975156a58
64 files changed
+1745 -867

No files matched your search

+3
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: 简繁转化
url: https://github.com/liuyueyi/quick-chinese-transfer/issues/new
about: 简繁转化问题请优先到quick-chinese-transfer反馈
- name: 讨论 / Discussions - name: 讨论 / Discussions
url: https://github.com/gedoor/legado/discussions url: https://github.com/gedoor/legado/discussions
about: Please ask and answer questions here. about: Please ask and answer questions here.
+16 -2
View File
@@ -78,6 +78,7 @@ jobs:
uses: gradle/actions/setup-gradle@v3 uses: gradle/actions/setup-gradle@v3
- name: Build With Gradle - name: Build With Gradle
continue-on-error: true
run: | run: |
if [ ${{ env.type }} == 'release' ]; then if [ ${{ env.type }} == 'release' ]; then
typeName="原包名" typeName="原包名"
@@ -91,15 +92,22 @@ jobs:
echo "开始${{ env.product }}$typeName构建" echo "开始${{ env.product }}$typeName构建"
chmod +x gradlew chmod +x gradlew
./gradlew assemble${{ env.product }}release --build-cache --parallel --daemon --warning-mode all ./gradlew assemble${{ env.product }}release --build-cache --parallel --daemon --warning-mode all
echo "修改文件名" - name: Rename outputs apks And Move files
run: |
echo "修改APK文件名"
mkdir -p ${{ github.workspace }}/apk/ mkdir -p ${{ github.workspace }}/apk/
mkdir -p ${{ github.workspace }}/mapping/
for file in `ls ${{ github.workspace }}/app/build/outputs/apk/*/*/*.apk`; do for file in `ls ${{ github.workspace }}/app/build/outputs/apk/*/*/*.apk`; do
mv "$file" ${{ github.workspace }}/apk/legado_${{ env.product }}_${{ env.VERSIONL }}_$typeName.apk mv "$file" ${{ github.workspace }}/apk/legado_${{ env.product }}_${{ env.VERSIONL }}_$typeName.apk
done done
echo "移动mapping文件"
mkdir -p ${{ github.workspace }}/mapping/
for file in `ls ${{ github.workspace }}/app/build/outputs/mapping/*/mapping.txt`; do for file in `ls ${{ github.workspace }}/app/build/outputs/mapping/*/mapping.txt`; do
mv "$file" ${{ github.workspace }}/mapping/mapping.txt mv "$file" ${{ github.workspace }}/mapping/mapping.txt
done done
echo "移动missing_rules.txt文件"
for file in `ls ${{ github.workspace }}/app/build/outputs/mapping/*/missing_rules.txt`; do
mv "$file" ${{ github.workspace }}/mapping/missing_rules.txt
done
- name: Upload App To Artifact - name: Upload App To Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@@ -112,6 +120,12 @@ jobs:
name: legado.${{ env.product }}.${{ env.type }}.mapping name: legado.${{ env.product }}.${{ env.type }}.mapping
path: ${{ github.workspace }}/mapping/mapping.txt path: ${{ github.workspace }}/mapping/mapping.txt
- name: Upload Missing Rules File To Artifact
uses: actions/upload-artifact@v4
with:
name: legado.${{ env.product }}.${{ env.type }}.mapping.missing_rules
path: ${{ github.workspace }}/mapping/missing_rules.txt
lanzou: lanzou:
needs: [ prepare, build ] needs: [ prepare, build ]
if: ${{ github.event_name != 'pull_request' && needs.prepare.outputs.lanzou == 'yes' }} if: ${{ github.event_name != 'pull_request' && needs.prepare.outputs.lanzou == 'yes' }}
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
with: with:
node-version: 16 node-version: 16
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v3
name: Install pnpm name: Install pnpm
id: pnpm-install id: pnpm-install
with: with:
+9
View File
@@ -473,6 +473,15 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove" />
</provider>
<meta-data <meta-data
android:name="channel" android:name="channel"
@@ -117,7 +117,6 @@ object PreferKey {
const val welcomeShowIconDark = "welcomeShowIconDark" const val welcomeShowIconDark = "welcomeShowIconDark"
const val pageTouchSlop = "pageTouchSlop" const val pageTouchSlop = "pageTouchSlop"
const val showAddToShelfAlert = "showAddToShelfAlert" const val showAddToShelfAlert = "showAddToShelfAlert"
const val asyncLoadImage = "asyncLoadImage"
const val ignoreAudioFocus = "ignoreAudioFocus" const val ignoreAudioFocus = "ignoreAudioFocus"
const val parallelExportBook = "parallelExportBook" const val parallelExportBook = "parallelExportBook"
const val progressBarBehavior = "progressBarBehavior" const val progressBarBehavior = "progressBarBehavior"
@@ -0,0 +1,6 @@
package io.legado.app.help
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
val globalExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
@@ -137,6 +137,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
} }
} }
val textSelectAble: Boolean
get() = appCtx.getPrefBoolean(PreferKey.textSelectAble, true)
val isTransparentStatusBar: Boolean val isTransparentStatusBar: Boolean
get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true) get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true)
@@ -457,8 +460,6 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true) val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
val asyncLoadImage get() = appCtx.getPrefBoolean(PreferKey.asyncLoadImage, false)
val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false) val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false)
val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true) val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true)
@@ -567,7 +567,6 @@ object ReadBookConfig {
textColorInt = color textColorInt = color
} }
} }
ChapterProvider.upStyle()
} }
fun curTextColor(): Int { fun curTextColor(): Int {
@@ -163,7 +163,22 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
.apply() .apply()
} }
companion object { companion object : SharedPreferences.OnSharedPreferenceChangeListener {
init {
prefs(appCtx).registerOnSharedPreferenceChangeListener(this)
}
var accentColor = accentColor()
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
key: String?
) {
when (key) {
ThemeStorePrefKeys.KEY_ACCENT_COLOR -> accentColor = accentColor()
}
}
fun editTheme(context: Context): ThemeStore { fun editTheme(context: Context): ThemeStore {
return ThemeStore(context) return ThemeStore(context)
@@ -2,13 +2,10 @@ package io.legado.app.model
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Build
import android.util.Size import android.util.Size
import androidx.collection.LruCache import androidx.collection.LruCache
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.constant.AppLog.putDebug import io.legado.app.constant.AppLog.putDebug
import io.legado.app.constant.PageAnim
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
@@ -16,10 +13,8 @@ import io.legado.app.help.book.BookHelp
import io.legado.app.help.book.isEpub import io.legado.app.help.book.isEpub
import io.legado.app.help.book.isPdf import io.legado.app.help.book.isPdf
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.localBook.EpubFile import io.legado.app.model.localBook.EpubFile
import io.legado.app.model.localBook.PdfFile import io.legado.app.model.localBook.PdfFile
import io.legado.app.utils.BitmapCache
import io.legado.app.utils.BitmapUtils import io.legado.app.utils.BitmapUtils
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.SvgUtils import io.legado.app.utils.SvgUtils
@@ -29,8 +24,6 @@ import kotlinx.coroutines.withContext
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.min
object ImageProvider { object ImageProvider {
@@ -50,12 +43,6 @@ object ImageProvider {
} }
return AppConfig.bitmapCacheSize * M return AppConfig.bitmapCacheSize * M
} }
private val maxCacheSize = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
min(128 * M, Runtime.getRuntime().maxMemory().toInt())
} else {
256 * M
}
private val asyncLoadingImages = ConcurrentHashMap.newKeySet<String>()
val bitmapLruCache = object : LruCache<String, Bitmap>(cacheSize) { val bitmapLruCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(filePath: String, bitmap: Bitmap): Int { override fun sizeOf(filePath: String, bitmap: Bitmap): Int {
@@ -70,8 +57,7 @@ object ImageProvider {
) { ) {
//错误图片不能释放,占位用,防止一直重复获取图片 //错误图片不能释放,占位用,防止一直重复获取图片
if (oldBitmap != errorBitmap) { if (oldBitmap != errorBitmap) {
BitmapCache.add(oldBitmap) oldBitmap.recycle()
//oldBitmap.recycle()
//putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath") //putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
//putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes") //putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
} }
@@ -160,9 +146,8 @@ object ImageProvider {
book: Book, book: Book,
src: String, src: String,
width: Int, width: Int,
height: Int? = null, height: Int? = null
block: (() -> Unit)? = null ): Bitmap {
): Bitmap? {
//src为空白时 可能被净化替换掉了 或者规则失效 //src为空白时 可能被净化替换掉了 或者规则失效
if (book.getUseReplaceRule() && src.isBlank()) { if (book.getUseReplaceRule() && src.isBlank()) {
book.setUseReplaceRule(false) book.setUseReplaceRule(false)
@@ -174,32 +159,6 @@ object ImageProvider {
//bitmapLruCache的key同一改成缓存文件的路径 //bitmapLruCache的key同一改成缓存文件的路径
val cacheBitmap = getNotRecycled(vFile.absolutePath) val cacheBitmap = getNotRecycled(vFile.absolutePath)
if (cacheBitmap != null) return cacheBitmap if (cacheBitmap != null) return cacheBitmap
if (height != null && AppConfig.asyncLoadImage && ReadBook.pageAnim() == PageAnim.scrollPageAnim) {
if (asyncLoadingImages.contains(vFile.absolutePath)) {
return null
}
asyncLoadingImages.add(vFile.absolutePath)
Coroutine.async {
BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap))
}.onSuccess {
bitmapLruCache.run {
if (maxSize() < maxCacheSize && size() + it.byteCount > maxSize() && putCount() - evictionCount() < 5) {
resize(min(maxCacheSize, maxSize() + it.byteCount))
AppLog.put("图片缓存太小,自动扩增至${(maxSize() / M)}MB。")
}
}
bitmapLruCache.put(vFile.absolutePath, it)
}.onError {
//错误图片占位,防止重复获取
bitmapLruCache.put(vFile.absolutePath, errorBitmap)
}.onFinally {
asyncLoadingImages.remove(vFile.absolutePath)
block?.invoke()
}
return null
}
return kotlin.runCatching { return kotlin.runCatching {
val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
?: SvgUtils.createBitmap(vFile.absolutePath, width, height) ?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
@@ -212,4 +171,8 @@ object ImageProvider {
}.getOrDefault(errorBitmap) }.getOrDefault(errorBitmap)
} }
fun clear() {
bitmapLruCache.evictAll()
}
} }
@@ -14,6 +14,7 @@ import io.legado.app.help.book.isLocal
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.globalExecutor
import io.legado.app.model.localBook.TextFile import io.legado.app.model.localBook.TextFile
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
@@ -63,6 +64,7 @@ object ReadBook : CoroutineScope by MainScope() {
val downloadFailChapters = hashMapOf<Int, Int>() val downloadFailChapters = hashMapOf<Int, Int>()
var contentProcessor: ContentProcessor? = null var contentProcessor: ContentProcessor? = null
val downloadScope = CoroutineScope(SupervisorJob() + IO) val downloadScope = CoroutineScope(SupervisorJob() + IO)
val executor = globalExecutor
//暂时保存跳转前进度 //暂时保存跳转前进度
fun saveCurrentBookProcess() { fun saveCurrentBookProcess() {
@@ -158,10 +160,10 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun upReadTime() { fun upReadTime() {
executor.execute {
if (!AppConfig.enableReadRecord) { if (!AppConfig.enableReadRecord) {
return return@execute
} }
Coroutine.async(executeContext = IO) {
readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime
readStartTime = System.currentTimeMillis() readStartTime = System.currentTimeMillis()
readRecord.lastRead = System.currentTimeMillis() readRecord.lastRead = System.currentTimeMillis()
@@ -204,7 +206,7 @@ object ReadBook : CoroutineScope by MainScope() {
return hasPrevPage return hasPrevPage
} }
fun moveToNextChapter(upContent: Boolean): Boolean { fun moveToNextChapter(upContent: Boolean, upContentInPlace: Boolean = true): Boolean {
if (durChapterIndex < chapterSize - 1) { if (durChapterIndex < chapterSize - 1) {
durChapterPos = 0 durChapterPos = 0
durChapterIndex++ durChapterIndex++
@@ -213,11 +215,11 @@ object ReadBook : CoroutineScope by MainScope() {
nextTextChapter = null nextTextChapter = null
if (curTextChapter == null) { if (curTextChapter == null) {
AppLog.putDebug("moveToNextChapter-章节未加载,开始加载") AppLog.putDebug("moveToNextChapter-章节未加载,开始加载")
callBack?.upContent() if (upContentInPlace) callBack?.upContent(resetPageOffset = false)
loadContent(durChapterIndex, upContent, resetPageOffset = false) loadContent(durChapterIndex, upContent, resetPageOffset = false)
} else if (upContent) { } else if (upContent && upContentInPlace) {
AppLog.putDebug("moveToNextChapter-章节已加载,刷新视图") AppLog.putDebug("moveToNextChapter-章节已加载,刷新视图")
callBack?.upContent() callBack?.upContent(resetPageOffset = false)
} }
loadContent(durChapterIndex.plus(1), upContent, false) loadContent(durChapterIndex.plus(1), upContent, false)
saveRead() saveRead()
@@ -233,7 +235,8 @@ object ReadBook : CoroutineScope by MainScope() {
fun moveToPrevChapter( fun moveToPrevChapter(
upContent: Boolean, upContent: Boolean,
toLast: Boolean = true toLast: Boolean = true,
upContentInPlace: Boolean = true
): Boolean { ): Boolean {
if (durChapterIndex > 0) { if (durChapterIndex > 0) {
durChapterPos = if (toLast) prevTextChapter?.lastReadLength ?: Int.MAX_VALUE else 0 durChapterPos = if (toLast) prevTextChapter?.lastReadLength ?: Int.MAX_VALUE else 0
@@ -242,10 +245,10 @@ object ReadBook : CoroutineScope by MainScope() {
curTextChapter = prevTextChapter curTextChapter = prevTextChapter
prevTextChapter = null prevTextChapter = null
if (curTextChapter == null) { if (curTextChapter == null) {
callBack?.upContent() if (upContentInPlace) callBack?.upContent(resetPageOffset = false)
loadContent(durChapterIndex, upContent, resetPageOffset = false) loadContent(durChapterIndex, upContent, resetPageOffset = false)
} else if (upContent) { } else if (upContent && upContentInPlace) {
callBack?.upContent() callBack?.upContent(resetPageOffset = false)
} }
loadContent(durChapterIndex.minus(1), upContent, false) loadContent(durChapterIndex.minus(1), upContent, false)
saveRead() saveRead()
@@ -267,6 +270,16 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun setPageIndex(index: Int) { fun setPageIndex(index: Int) {
val textChapter = curTextChapter
if (textChapter != null) {
val pageIndex = durPageIndex
if (index > pageIndex) {
textChapter.getPage(index - 2)?.recycleRecorders()
}
if (index < pageIndex) {
textChapter.getPage(index + 3)?.recycleRecorders()
}
}
durChapterPos = curTextChapter?.getReadLength(index) ?: index durChapterPos = curTextChapter?.getReadLength(index) ?: index
saveRead(true) saveRead(true)
curPageChanged(true) curPageChanged(true)
@@ -525,8 +538,8 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun saveRead(pageChanged: Boolean = false) { fun saveRead(pageChanged: Boolean = false) {
Coroutine.async(executeContext = IO) { executor.execute {
val book = book ?: return@async val book = book ?: return@execute
book.lastCheckCount = 0 book.lastCheckCount = 0
book.durChapterTime = System.currentTimeMillis() book.durChapterTime = System.currentTimeMillis()
val chapterChanged = book.durChapterIndex != durChapterIndex val chapterChanged = book.durChapterIndex != durChapterIndex
@@ -549,14 +562,16 @@ object ReadBook : CoroutineScope by MainScope() {
*/ */
private fun preDownload() { private fun preDownload() {
if (book?.isLocal == true) return if (book?.isLocal == true) return
executor.execute {
if (AppConfig.preDownloadNum < 2) { if (AppConfig.preDownloadNum < 2) {
return return@execute
} }
preDownloadTask?.cancel() preDownloadTask?.cancel()
preDownloadTask = Coroutine.async(executeContext = IO) { preDownloadTask = Coroutine.async(executeContext = IO) {
//预下载 //预下载
launch { launch {
val maxChapterIndex = min(durChapterIndex + AppConfig.preDownloadNum, chapterSize) val maxChapterIndex =
min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
for (i in durChapterIndex.plus(2)..maxChapterIndex) { for (i in durChapterIndex.plus(2)..maxChapterIndex) {
if (downloadedChapters.contains(i)) continue if (downloadedChapters.contains(i)) continue
if ((downloadFailChapters[i] ?: 0) >= 3) continue if ((downloadFailChapters[i] ?: 0) >= 3) continue
@@ -573,6 +588,7 @@ object ReadBook : CoroutineScope by MainScope() {
} }
} }
} }
}
/** /**
* 注册回调 * 注册回调
@@ -595,6 +611,7 @@ object ReadBook : CoroutineScope by MainScope() {
coroutineContext.cancelChildren() coroutineContext.cancelChildren()
downloadedChapters.clear() downloadedChapters.clear()
downloadFailChapters.clear() downloadFailChapters.clear()
ImageProvider.clear()
} }
interface CallBack { interface CallBack {
@@ -197,7 +197,7 @@ class AnalyzeRule(
} }
if (sourceRule.replaceRegex.isNotEmpty() && result is List<*>) { if (sourceRule.replaceRegex.isNotEmpty() && result is List<*>) {
val newList = ArrayList<String>() val newList = ArrayList<String>()
for (item in result as List<*>) { for (item in result) {
newList.add(replaceRegex(item.toString(), sourceRule)) newList.add(replaceRegex(item.toString(), sourceRule))
} }
result = newList result = newList
@@ -210,12 +210,12 @@ class AnalyzeRule(
} }
if (result == null) return null if (result == null) return null
if (result is String) { if (result is String) {
result = (result as String).split("\n") result = result.split("\n")
} }
if (isUrl) { if (isUrl) {
val urlList = ArrayList<String>() val urlList = ArrayList<String>()
if (result is List<*>) { if (result is List<*>) {
for (url in result as List<*>) { for (url in result) {
val absoluteURL = NetworkUtils.getAbsoluteURL(redirectUrl, url.toString()) val absoluteURL = NetworkUtils.getAbsoluteURL(redirectUrl, url.toString())
if (absoluteURL.isNotEmpty() && !urlList.contains(absoluteURL)) { if (absoluteURL.isNotEmpty() && !urlList.contains(absoluteURL)) {
urlList.add(absoluteURL) urlList.add(absoluteURL)
@@ -91,7 +91,7 @@ class HttpReadAloudService : BaseReadAloudService(),
playIndexJob?.cancel() playIndexJob?.cancel()
} }
private fun playNext() { private fun updateNextPos() {
readAloudNumber += contentList[nowSpeak].length + 1 - paragraphStartPos readAloudNumber += contentList[nowSpeak].length + 1 - paragraphStartPos
paragraphStartPos = 0 paragraphStartPos = 0
if (nowSpeak < contentList.lastIndex) { if (nowSpeak < contentList.lastIndex) {
@@ -355,14 +355,14 @@ class HttpReadAloudService : BaseReadAloudService(),
Player.STATE_ENDED -> { Player.STATE_ENDED -> {
// 结束 // 结束
playErrorNo = 0 playErrorNo = 0
playNext() updateNextPos()
} }
} }
} }
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) return if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) return
playNext() updateNextPos()
upPlayPos() upPlayPos()
} }
@@ -375,7 +375,8 @@ class HttpReadAloudService : BaseReadAloudService(),
AppLog.put("朗读连续5次错误, 最后一次错误代码(${error.localizedMessage})", error) AppLog.put("朗读连续5次错误, 最后一次错误代码(${error.localizedMessage})", error)
ReadAloud.pause(this) ReadAloud.pause(this)
} else { } else {
playNext() updateNextPos()
exoPlayer.seekToNextMediaItem()
} }
} }
@@ -58,6 +58,12 @@ abstract class BaseReadBookActivity :
override val binding by viewBinding(ActivityBookReadBinding::inflate) override val binding by viewBinding(ActivityBookReadBinding::inflate)
override val viewModel by viewModels<ReadBookViewModel>() override val viewModel by viewModels<ReadBookViewModel>()
var bottomDialog = 0 var bottomDialog = 0
set(value) {
if (field != value) {
field = value
onBottomDialogChange()
}
}
private val selectBookFolderResult = registerForActivityResult(HandleFileContract()) { private val selectBookFolderResult = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
ReadBook.book?.let { book -> ReadBook.book?.let { book ->
@@ -94,6 +100,21 @@ abstract class BaseReadBookActivity :
} }
} }
private fun onBottomDialogChange() {
when (bottomDialog) {
0 -> onMenuHide()
1 -> onMenuShow()
}
}
open fun onMenuShow() {
}
open fun onMenuHide() {
}
fun showPaddingConfig() { fun showPaddingConfig() {
showDialogFragment<PaddingConfigDialog>() showDialogFragment<PaddingConfigDialog>()
} }
@@ -6,6 +6,7 @@ import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Looper
import android.view.Gravity import android.view.Gravity
import android.view.InputDevice import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
@@ -76,7 +77,7 @@ import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_DIVIDER_C
import io.legado.app.ui.book.read.page.ContentTextView import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.ReadView import io.legado.app.ui.book.read.page.ReadView
import io.legado.app.ui.book.read.page.entities.PageDirection import io.legado.app.ui.book.read.page.entities.PageDirection
import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.searchContent.SearchContentActivity import io.legado.app.ui.book.searchContent.SearchContentActivity
import io.legado.app.ui.book.searchContent.SearchResult import io.legado.app.ui.book.searchContent.SearchResult
import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity
@@ -95,8 +96,8 @@ import io.legado.app.utils.ACache
import io.legado.app.utils.Debounce import io.legado.app.utils.Debounce
import io.legado.app.utils.LogUtils import io.legado.app.utils.LogUtils
import io.legado.app.utils.StartActivityContract import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.SyncedRenderer
import io.legado.app.utils.applyOpenTint import io.legado.app.utils.applyOpenTint
import io.legado.app.utils.buildMainHandler
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
import io.legado.app.utils.hexString import io.legado.app.utils.hexString
@@ -191,7 +192,6 @@ class ReadBookActivity : BaseReadBookActivity(),
} }
private var menu: Menu? = null private var menu: Menu? = null
private var backupJob: Job? = null private var backupJob: Job? = null
private var keepScreenJon: Job? = null
private var tts: TTS? = null private var tts: TTS? = null
val textActionMenu: TextActionMenu by lazy { val textActionMenu: TextActionMenu by lazy {
TextActionMenu(this, this) TextActionMenu(this, this)
@@ -201,8 +201,7 @@ class ReadBookActivity : BaseReadBookActivity(),
} }
override val isInitFinish: Boolean get() = viewModel.isInitFinish override val isInitFinish: Boolean get() = viewModel.isInitFinish
override val isScroll: Boolean get() = binding.readView.isScroll override val isScroll: Boolean get() = binding.readView.isScroll
override var autoPageProgress = 0 private val isAutoPage get() = binding.readView.isAutoPage
override var isAutoPage = false
override var isShowingSearchResult = false override var isShowingSearchResult = false
override var isSelectingSearchResult = false override var isSelectingSearchResult = false
set(value) { set(value) {
@@ -211,16 +210,17 @@ class ReadBookActivity : BaseReadBookActivity(),
private val timeBatteryReceiver = TimeBatteryReceiver() private val timeBatteryReceiver = TimeBatteryReceiver()
private var screenTimeOut: Long = 0 private var screenTimeOut: Long = 0
private var loadStates: Boolean = false private var loadStates: Boolean = false
override val pageFactory: TextPageFactory get() = binding.readView.pageFactory override val pageFactory get() = binding.readView.pageFactory
override val pageDelegate get() = binding.readView.pageDelegate
override val headerHeight: Int get() = binding.readView.curPage.headerHeight override val headerHeight: Int get() = binding.readView.curPage.headerHeight
private val menuLayoutIsVisible get() = bottomDialog > 0 || binding.readMenu.isVisible private val menuLayoutIsVisible get() = bottomDialog > 0 || binding.readMenu.isVisible
private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } } private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } }
private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } } private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } }
private var bookChanged = false private var bookChanged = false
private var pageChanged = false private var pageChanged = false
private var reloadContent = false private val handler by lazy { buildMainHandler() }
private val autoPageRenderer by lazy { SyncedRenderer { doAutoPage(it) } } private val screenOffRunnable by lazy { Runnable { keepScreenOn(false) } }
private var autoPageScrollOffset = 0.0 private val executor = ReadBook.executor
//恢复跳转前进度对话框的交互结果 //恢复跳转前进度对话框的交互结果
private var confirmRestoreProcess: Boolean? = null private var confirmRestoreProcess: Boolean? = null
@@ -264,23 +264,9 @@ class ReadBookActivity : BaseReadBookActivity(),
override fun onPostCreate(savedInstanceState: Bundle?) { override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState) super.onPostCreate(savedInstanceState)
viewModel.initData(intent) { Looper.myQueue().addIdleHandler {
initDataSuccess() viewModel.initData(intent) { upMenu() }
} false
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
viewModel.initData(intent ?: return) {
initDataSuccess()
}
}
private fun initDataSuccess() {
upMenu()
if (reloadContent) {
reloadContent = false
ReadBook.loadContent(resetPageOffset = false)
} }
} }