This commit is contained in:
Horis
2024-07-28 21:01:11 +08:00
parent 79e377aeee
commit 9d98d1a588
4 changed files with 198 additions and 13 deletions

View File

@@ -16,6 +16,7 @@ import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService
import io.legado.app.utils.postEvent
import io.legado.app.utils.startService
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers.IO
@@ -308,6 +309,31 @@ object CacheBook {
}.start()
}
suspend fun downloadAwait(chapter: BookChapter): String {
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
synchronized(this) {
onDownloadSet.add(chapter.index)
waitDownloadSet.remove(chapter.index)
}
try {
val content = WebBook.getContentAwait(bookSource, book, chapter)
onSuccess(chapter)
ReadBook.downloadedChapters.add(chapter.index)
ReadBook.downloadFailChapters.remove(chapter.index)
return content
} catch (e: Exception) {
if (e is CancellationException) {
onCancel(chapter.index)
}
onError(chapter, e)
ReadBook.downloadFailChapters[chapter.index] =
(ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1
return "获取正文失败\n${e.localizedMessage}"
} finally {
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
}
}
@Synchronized
fun download(
scope: CoroutineScope,

View File

@@ -37,6 +37,7 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import splitties.init.appCtx
import kotlin.math.max
import kotlin.math.min
@@ -55,8 +56,20 @@ object ReadBook : CoroutineScope by MainScope() {
var isLocalBook = true
var chapterChanged = false
var prevTextChapter: TextChapter? = null
set(value) {
field?.cancelLayout()
field = value
}
var curTextChapter: TextChapter? = null
set(value) {
field?.cancelLayout()
field = value
}
var nextTextChapter: TextChapter? = null
set(value) {
field?.cancelLayout()
field = value
}
var bookSource: BookSource? = null
var msg: String? = null
private val loadingChapters = arrayListOf<Int>()
@@ -164,7 +177,7 @@ object ReadBook : CoroutineScope by MainScope() {
fun setProgress(progress: BookProgress) {
if (progress.durChapterIndex < chapterSize &&
(durChapterIndex != progress.durChapterIndex
|| durChapterPos != progress.durChapterPos)
|| durChapterPos != progress.durChapterPos)
) {
durChapterIndex = progress.durChapterIndex
durChapterPos = progress.durChapterPos
@@ -175,9 +188,6 @@ object ReadBook : CoroutineScope by MainScope() {
}
fun clearTextChapter() {
prevTextChapter?.cancelLayout()
curTextChapter?.cancelLayout()
nextTextChapter?.cancelLayout()
prevTextChapter = null
curTextChapter = null
nextTextChapter = null
@@ -245,7 +255,6 @@ object ReadBook : CoroutineScope by MainScope() {
if (durChapterIndex < simulatedChapterSize - 1) {
durChapterPos = 0
durChapterIndex++
prevTextChapter?.cancelLayout()
prevTextChapter = curTextChapter
curTextChapter = nextTextChapter
nextTextChapter = null
@@ -269,6 +278,36 @@ object ReadBook : CoroutineScope by MainScope() {
}
}
suspend fun moveToNextChapterAwait(
upContent: Boolean,
upContentInPlace: Boolean = true
): Boolean {
if (durChapterIndex < simulatedChapterSize - 1) {
durChapterPos = 0
durChapterIndex++
prevTextChapter = curTextChapter
curTextChapter = nextTextChapter
nextTextChapter = null
if (curTextChapter == null) {
AppLog.putDebug("moveToNextChapter-章节未加载,开始加载")
if (upContentInPlace) callBack?.upContentAwait()
loadContentAwait(durChapterIndex, upContent, resetPageOffset = false)
} else if (upContent && upContentInPlace) {
AppLog.putDebug("moveToNextChapter-章节已加载,刷新视图")
callBack?.upContentAwait()
}
loadContent(durChapterIndex.plus(1), upContent, false)
saveRead()
callBack?.upMenuView()
AppLog.putDebug("moveToNextChapter-curPageChanged()")
curPageChanged()
return true
} else {
AppLog.putDebug("跳转下一章失败,没有下一章")
return false
}
}
fun moveToPrevChapter(
upContent: Boolean,
toLast: Boolean = true,
@@ -277,7 +316,6 @@ object ReadBook : CoroutineScope by MainScope() {
if (durChapterIndex > 0) {
durChapterPos = if (toLast) prevTextChapter?.lastReadLength ?: Int.MAX_VALUE else 0
durChapterIndex--
nextTextChapter?.cancelLayout()
nextTextChapter = curTextChapter
curTextChapter = prevTextChapter
prevTextChapter = null
@@ -467,6 +505,27 @@ object ReadBook : CoroutineScope by MainScope() {
}
}
suspend fun loadContentAwait(
index: Int,
upContent: Boolean = true,
resetPageOffset: Boolean = false,
success: (() -> Unit)? = null
) = withContext(IO) {
if (addLoading(index)) {
try {
val book = book!!
val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, index)!!
val content = BookHelp.getContent(book, chapter) ?: downloadAwait(chapter)
contentLoadFinishAwait(book, chapter, content, upContent, resetPageOffset)
success?.invoke()
} catch (e: Exception) {
AppLog.put("加载正文出错\n${e.localizedMessage}")
} finally {
removeLoading(index)
}
}
}
/**
* 下载正文
*/
@@ -519,6 +578,17 @@ object ReadBook : CoroutineScope by MainScope() {
}
}
private suspend fun downloadAwait(chapter: BookChapter): String {
val book = book!!
val bookSource = bookSource
if (bookSource != null) {
return CacheBook.getOrCreate(bookSource, book).downloadAwait(chapter)
} else {
val msg = if (book.isLocal) "无内容" else "没有书源"
return "加载正文失败\n$msg"
}
}
private fun addLoading(index: Int): Boolean {
synchronized(this) {
if (loadingChapters.contains(index)) return false
@@ -561,7 +631,6 @@ object ReadBook : CoroutineScope by MainScope() {
)
when (val offset = chapter.index - durChapterIndex) {
0 -> {
curTextChapter?.cancelLayout()
curTextChapter = textChapter
callBack?.upMenuView()
var available = false
@@ -586,14 +655,12 @@ object ReadBook : CoroutineScope by MainScope() {
}
-1 -> {
prevTextChapter?.cancelLayout()
prevTextChapter = textChapter
textChapter.layoutChannel.receiveAsFlow().collect()
if (upContent) callBack?.upContent(offset, resetPageOffset)
}
1 -> {
nextTextChapter?.cancelLayout()
nextTextChapter = textChapter
for (page in textChapter.layoutChannel) {
if (page.index > 1) {
@@ -613,6 +680,77 @@ object ReadBook : CoroutineScope by MainScope() {
}
}
suspend fun contentLoadFinishAwait(
book: Book,
chapter: BookChapter,
content: String,
upContent: Boolean = true,
resetPageOffset: Boolean
) {
removeLoading(chapter.index)
if (chapter.index !in durChapterIndex - 1..durChapterIndex + 1) {
return
}
kotlin.runCatching {
val contentProcessor = ContentProcessor.get(book.name, book.origin)
val displayTitle = chapter.getDisplayTitle(
contentProcessor.getTitleReplaceRules(),
book.getUseReplaceRule()
)
val contents = contentProcessor
.getContent(book, chapter, content, includeTitle = false)
val textChapter = ChapterProvider.getTextChapterAsync(
this@ReadBook, book, chapter, displayTitle, contents, simulatedChapterSize
)
when (val offset = chapter.index - durChapterIndex) {
0 -> {
curTextChapter = textChapter
callBack?.upMenuView()
var available = false
for (page in textChapter.layoutChannel) {
val index = page.index
if (!available && page.containPos(durChapterPos)) {
if (upContent) {
callBack?.upContent(offset, resetPageOffset)
}
available = true
}
if (upContent && isScroll) {
if (max(index - 3, 0) < durPageIndex) {
callBack?.upContent(offset, false)
}
}
callBack?.onLayoutPageCompleted(index, page)
}
if (upContent) callBack?.upContent(offset, !available && resetPageOffset)
curPageChanged()
callBack?.contentLoadFinish()
}
-1 -> {
prevTextChapter = textChapter
textChapter.layoutChannel.receiveAsFlow().collect()
if (upContent) callBack?.upContent(offset, resetPageOffset)
}
1 -> {
nextTextChapter = textChapter
for (page in textChapter.layoutChannel) {
if (page.index > 1) {
continue
}
if (upContent) callBack?.upContent(offset, resetPageOffset)
}
}
}
return
}.onFailure {
AppLog.put("ChapterProvider ERROR", it)
appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
}
}
@Synchronized
fun upToc() {
val bookSource = bookSource ?: return
@@ -718,7 +856,6 @@ object ReadBook : CoroutineScope by MainScope() {
downloadedChapters.clear()
downloadFailChapters.clear()
ImageProvider.clear()
curTextChapter?.cancelLayout()
}
interface CallBack : LayoutProgressListener {
@@ -732,6 +869,12 @@ object ReadBook : CoroutineScope by MainScope() {
success: (() -> Unit)? = null
)
suspend fun upContentAwait(
relativePosition: Int = 0,
resetPageOffset: Boolean = true,
success: (() -> Unit)? = null
)
fun pageChanged()
fun contentLoadFinish()

View File

@@ -123,6 +123,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.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
@@ -798,7 +799,9 @@ class ReadBookActivity : BaseReadBookActivity(),
override fun onMenuItemSelected(itemId: Int): Boolean {
when (itemId) {
R.id.menu_aloud -> when (AppConfig.contentSelectSpeakMod) {
1 -> binding.readView.aloudStartSelect()
1 -> lifecycleScope.launch {
binding.readView.aloudStartSelect()
}
else -> speak(binding.readView.getSelectText())
}
@@ -965,6 +968,19 @@ class ReadBookActivity : BaseReadBookActivity(),
}
}
override suspend fun upContentAwait(
relativePosition: Int,
resetPageOffset: Boolean,
success: (() -> Unit)?
) = withContext(Main.immediate) {
binding.readView.cancelSelect()
binding.readView.upContent(relativePosition, resetPageOffset)
if (relativePosition == 0) {
upSeekBarProgress()
}
loadStates = false
}
override fun upPageAnim(upRecorder: Boolean) {
lifecycleScope.launch {
binding.readView.upPageAnim(upRecorder)

View File

@@ -622,14 +622,14 @@ class ReadView(context: Context, attrs: AttributeSet) :
/**
* 从选择位置开始朗读
*/
fun aloudStartSelect() {
suspend fun aloudStartSelect() {
val selectStartPos = curPage.selectStartPos
var pagePos = selectStartPos.relativePagePos
val line = selectStartPos.lineIndex
val column = selectStartPos.columnIndex
while (pagePos > 0) {
if (!ReadBook.moveToNextPage()) {
ReadBook.moveToNextChapter(false)
ReadBook.moveToNextChapterAwait(false)
}
pagePos--
}