|
|
@@ -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()
|