优化
This commit is contained in:
@@ -136,6 +136,8 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
TextFile.clear()
|
||||
synchronized(this) {
|
||||
loadingChapters.clear()
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -954,8 +956,6 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
preDownloadTask?.cancel()
|
||||
downloadScope.coroutineContext.cancelChildren()
|
||||
coroutineContext.cancelChildren()
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
ImageProvider.clear()
|
||||
if (!CacheBookService.isRun) {
|
||||
CacheBook.close()
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.help.globalExecutor
|
||||
import io.legado.app.model.webBook.WebBook
|
||||
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.ReaderLoading
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
@@ -50,19 +51,23 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
var chapterSize = 0//总章节
|
||||
var durChapterImageCount = 0
|
||||
var durChapterPos = 0
|
||||
var prevMangaChapter: MangaChapter? = null
|
||||
var curMangaChapter: MangaChapter? = null
|
||||
var nextMangaChapter: MangaChapter? = null
|
||||
var bookSource: BookSource? = null
|
||||
var chapterTitle: String = ""
|
||||
var readStartTime: Long = System.currentTimeMillis()
|
||||
private val readRecord = ReadRecord()
|
||||
private val loadingChapters = arrayListOf<Int>()
|
||||
var simulatedChapterSize = 0
|
||||
var mCallback: Callback? = null
|
||||
var gameOver = false
|
||||
var preDownloadTask: Job? = null
|
||||
val downloadedChapters = hashSetOf<Int>()
|
||||
val downloadFailChapters = hashMapOf<Int, Int>()
|
||||
val downloadScope = CoroutineScope(SupervisorJob() + IO)
|
||||
var rateLimiter = ConcurrentRateLimiter(null)
|
||||
val durChapterAbsPos get() = (prevMangaChapter?.contents?.size ?: 0) + durChapterPos + 1
|
||||
val mangaContents get() = buildContentList()
|
||||
val hasNextChapter get() = durChapterIndex < simulatedChapterSize - 1
|
||||
|
||||
fun saveRead(pageChanged: Boolean = false) {
|
||||
executor.execute {
|
||||
@@ -115,9 +120,12 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
}
|
||||
durChapterIndex = book.durChapterIndex
|
||||
durChapterPos = book.durChapterPos
|
||||
clearMangaChapter()
|
||||
upWebBook(book)
|
||||
synchronized(this) {
|
||||
loadingChapters.clear()
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +138,11 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMangaChapter() {
|
||||
prevMangaChapter = null
|
||||
curMangaChapter = null
|
||||
nextMangaChapter = null
|
||||
}
|
||||
|
||||
//每次切换章节更新阅读记录
|
||||
fun upReadTime() {
|
||||
@@ -158,12 +171,6 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
}
|
||||
}
|
||||
|
||||
fun loading(index: Int): Boolean {
|
||||
synchronized(this) {
|
||||
return loadingChapters.contains(index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正文
|
||||
*/
|
||||
@@ -188,11 +195,25 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
|
||||
fun loadContent() {
|
||||
loadContent(durChapterIndex)
|
||||
loadContent(durChapterIndex - 1)
|
||||
loadContent(durChapterIndex + 1)
|
||||
}
|
||||
|
||||
fun loadContent(
|
||||
index: Int,
|
||||
) {
|
||||
fun loadOrUpContent() {
|
||||
if (curMangaChapter == null) {
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent(true)
|
||||
}
|
||||
if (nextMangaChapter == null) {
|
||||
loadContent(durChapterIndex + 1)
|
||||
}
|
||||
if (prevMangaChapter == null) {
|
||||
loadContent(durChapterIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContent(index: Int) {
|
||||
if (chapterChanged) {
|
||||
removeLoading(index)
|
||||
}
|
||||
@@ -225,25 +246,20 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
/**
|
||||
* 取消注册回调
|
||||
*/
|
||||
fun unregister() {
|
||||
fun unregister(cb: Callback) {
|
||||
if (mCallback === cb) {
|
||||
mCallback = null
|
||||
}
|
||||
inBookshelf = false
|
||||
tocChanged = false
|
||||
chapterChanged = false
|
||||
book = null
|
||||
durChapterIndex = 0
|
||||
chapterSize = 0
|
||||
durChapterPos = 0
|
||||
durChapterImageCount = 0
|
||||
bookSource = null
|
||||
chapterTitle = ""
|
||||
loadingChapters.clear()
|
||||
simulatedChapterSize = 0
|
||||
mCallback = null
|
||||
gameOver = false
|
||||
preDownloadTask?.cancel()
|
||||
preDownloadTask = null
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
downloadScope.coroutineContext.cancelChildren()
|
||||
coroutineContext.cancelChildren()
|
||||
}
|
||||
@@ -256,122 +272,119 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
content: String?,
|
||||
book: Book,
|
||||
) {
|
||||
chapterTitle = chapter.title
|
||||
removeLoading(chapter.index)
|
||||
if (chapter.index !in durChapterIndex - 1..durChapterIndex + 1) {
|
||||
return
|
||||
}
|
||||
if (content == null) {
|
||||
mCallback?.loadFail("加载内容失败")
|
||||
return
|
||||
}
|
||||
if (content.isNotEmpty()) {
|
||||
val list = flow {
|
||||
val matcher = AppPattern.imgPattern.matcher(content)
|
||||
while (matcher.find()) {
|
||||
val src = matcher.group(1) ?: continue
|
||||
val mSrc = NetworkUtils.getAbsoluteURL(chapter.url, src)
|
||||
emit(mSrc)
|
||||
when (chapter.index - durChapterIndex) {
|
||||
0 -> {
|
||||
if (content == null) {
|
||||
mCallback?.loadFail("加载内容失败")
|
||||
return
|
||||
}
|
||||
}.distinctUntilChanged().mapIndexed { index, src ->
|
||||
MangaContent(
|
||||
chapterSize = chapterSize,
|
||||
mChapterIndex = durChapterIndex,
|
||||
nextChapterIndex = durChapterIndex.plus(1),
|
||||
mImageUrl = src,
|
||||
mDurChapterPos = index,
|
||||
mChapterName = chapterTitle
|
||||
)
|
||||
}.toList().apply {
|
||||
this.forEach {
|
||||
it.mDurChapterCount = this.size
|
||||
if (content.isEmpty()) {
|
||||
mCallback?.loadFail("正文内容为空")
|
||||
return
|
||||
}
|
||||
durChapterImageCount = this.size
|
||||
val mangaChapter = getManageChapter(chapter, content)
|
||||
if (mangaChapter.imageCount == 0) {
|
||||
mCallback?.loadFail("正文没有图片")
|
||||
return
|
||||
}
|
||||
durChapterImageCount = mangaChapter.imageCount
|
||||
curMangaChapter = mangaChapter
|
||||
mCallback?.upContent(true)
|
||||
mCallback?.contentLoadFinish()
|
||||
}
|
||||
|
||||
-1 -> {
|
||||
if (content == null || content.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val mangaChapter = getManageChapter(chapter, content)
|
||||
if (mangaChapter.imageCount == 0) {
|
||||
return
|
||||
}
|
||||
prevMangaChapter = mangaChapter
|
||||
mCallback?.upContent()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
if (content == null || content.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val mangaChapter = getManageChapter(chapter, content)
|
||||
if (mangaChapter.imageCount == 0) {
|
||||
return
|
||||
}
|
||||
nextMangaChapter = mangaChapter
|
||||
mCallback?.upContent()
|
||||
}
|
||||
val contentList = mutableListOf<Any>()
|
||||
contentList.add(
|
||||
ReaderLoading(
|
||||
durChapterIndex,
|
||||
"阅读 ${chapter.title}",
|
||||
mNextChapterIndex = durChapterIndex.plus(1)
|
||||
)
|
||||
)
|
||||
contentList.addAll(list)
|
||||
contentList.add(
|
||||
ReaderLoading(
|
||||
durChapterIndex,
|
||||
"已读完 ${chapter.title}",
|
||||
mNextChapterIndex = durChapterIndex.plus(1)
|
||||
)
|
||||
)
|
||||
mCallback?.loadContentFinish(contentList)
|
||||
}
|
||||
mCallback?.loadComplete()
|
||||
}
|
||||
|
||||
private fun buildContentList(): List<Any> {
|
||||
val list = arrayListOf<Any>()
|
||||
prevMangaChapter?.let {
|
||||
list.addAll(it.contents)
|
||||
}
|
||||
curMangaChapter?.let {
|
||||
list.addAll(it.contents)
|
||||
}
|
||||
nextMangaChapter?.let {
|
||||
list.addAll(it.contents)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载下一章
|
||||
*/
|
||||
fun moveToNextChapter(index: Int) {
|
||||
|
||||
if (loading(index)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (index > chapterSize - 1) {
|
||||
upToc(index)
|
||||
return
|
||||
}
|
||||
fun moveToNextChapter(): Boolean {
|
||||
if (durChapterIndex < simulatedChapterSize - 1) {
|
||||
durChapterPos = 0
|
||||
durChapterIndex = index
|
||||
durChapterIndex++
|
||||
prevMangaChapter = curMangaChapter
|
||||
curMangaChapter = nextMangaChapter
|
||||
nextMangaChapter = null
|
||||
if (curMangaChapter == null) {
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent(true)
|
||||
}
|
||||
loadContent(durChapterIndex + 1)
|
||||
saveRead()
|
||||
loadContent(durChapterIndex)
|
||||
AppLog.putDebug("moveToNextChapter-curPageChanged()")
|
||||
curPageChanged()
|
||||
return true
|
||||
} else {
|
||||
AppLog.putDebug("跳转下一章失败,没有下一章")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun moveToPrevChapter(): Boolean {
|
||||
if (durChapterIndex > 0) {
|
||||
durChapterIndex--
|
||||
nextMangaChapter = curMangaChapter
|
||||
curMangaChapter = prevMangaChapter
|
||||
prevMangaChapter = null
|
||||
if (curMangaChapter == null) {
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent(true)
|
||||
}
|
||||
loadContent(durChapterIndex - 1)
|
||||
saveRead()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun curPageChanged() {
|
||||
upReadTime()
|
||||
preDownload()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun upToc(index: Int) {
|
||||
val bookSource = bookSource ?: return
|
||||
val book = book ?: return
|
||||
if (!book.canUpdate) return
|
||||
if (System.currentTimeMillis() - book.lastCheckTime < 600000) {
|
||||
mCallback?.noData()
|
||||
return
|
||||
}
|
||||
book.lastCheckTime = System.currentTimeMillis()
|
||||
WebBook.getChapterList(this, bookSource, book).onSuccess(IO) { cList ->
|
||||
if (book.bookUrl == ReadManga.book?.bookUrl
|
||||
&& cList.size > chapterSize
|
||||
) {
|
||||
appDb.bookChapterDao.delByBook(book.bookUrl)
|
||||
appDb.bookChapterDao.insert(*cList.toTypedArray())
|
||||
saveRead()
|
||||
durChapterPos = 0
|
||||
durChapterIndex = index
|
||||
chapterSize = cList.size
|
||||
simulatedChapterSize = book.simulatedTotalChapterNum()
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
durChapterIndex = durChapterIndex.minus(1)
|
||||
saveRead()
|
||||
gameOver = true
|
||||
mCallback?.noData()
|
||||
}
|
||||
}.onError {
|
||||
mCallback?.noData()
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadNetworkContent(
|
||||
bookSource: BookSource,
|
||||
scope: CoroutineScope,
|
||||
@@ -480,7 +493,6 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步阅读进度
|
||||
* 如果当前进度快于服务器进度或者没有进度进行上传,如果慢与服务器进度则执行传入动作
|
||||
@@ -536,11 +548,55 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getManageChapter(chapter: BookChapter, content: String): MangaChapter {
|
||||
val list = flow {
|
||||
val matcher = AppPattern.imgPattern.matcher(content)
|
||||
while (matcher.find()) {
|
||||
val src = matcher.group(1) ?: continue
|
||||
val mSrc = NetworkUtils.getAbsoluteURL(chapter.url, src)
|
||||
emit(mSrc)
|
||||
}
|
||||
}.distinctUntilChanged().mapIndexed { index, src ->
|
||||
MangaContent(
|
||||
chapterSize = chapterSize,
|
||||
mChapterIndex = chapter.index,
|
||||
nextChapterIndex = chapter.index.plus(1),
|
||||
mImageUrl = src,
|
||||
mDurChapterPos = index,
|
||||
mChapterName = chapter.title
|
||||
)
|
||||
}.toList()
|
||||
|
||||
val imageCount = list.size
|
||||
|
||||
list.forEach {
|
||||
it.mDurChapterImageCount = imageCount
|
||||
}
|
||||
|
||||
val contentList = mutableListOf<Any>()
|
||||
contentList.add(
|
||||
ReaderLoading(
|
||||
chapter.index,
|
||||
"阅读 ${chapter.title}",
|
||||
mNextChapterIndex = chapter.index.plus(1)
|
||||
)
|
||||
)
|
||||
contentList.addAll(list)
|
||||
contentList.add(
|
||||
ReaderLoading(
|
||||
chapter.index,
|
||||
"已读完 ${chapter.title}",
|
||||
mNextChapterIndex = chapter.index.plus(1)
|
||||
)
|
||||
)
|
||||
|
||||
return MangaChapter(chapter, contentList, imageCount)
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun loadContentFinish(list: MutableList<Any>)
|
||||
fun loadComplete()
|
||||
fun upContent(finish: Boolean = false)
|
||||
fun contentLoadFinish()
|
||||
fun loadFail(msg: String)
|
||||
fun noData()
|
||||
fun adjustProgress()
|
||||
fun sureNewProgress(progress: BookProgress)
|
||||
}
|
||||
|
||||
@@ -39,13 +39,12 @@ import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.storage.Backup
|
||||
import io.legado.app.lib.dialogs.alert
|
||||
import io.legado.app.model.ReadManga
|
||||
import io.legado.app.ui.book.manga.entities.MangaContent
|
||||
import io.legado.app.ui.book.manga.entities.ReaderLoading
|
||||
import io.legado.app.receiver.NetworkChangedListener
|
||||
import io.legado.app.ui.book.changesource.ChangeBookSourceDialog
|
||||
import io.legado.app.ui.book.info.BookInfoActivity
|
||||
import io.legado.app.ui.book.manga.config.MangaFooterConfig
|
||||
import io.legado.app.ui.book.manga.config.MangaFooterSettingDialog
|
||||
import io.legado.app.ui.book.manga.entities.MangaContent
|
||||
import io.legado.app.ui.book.manga.recyclerview.MangaAdapter
|
||||
import io.legado.app.ui.book.read.MangaMenu
|
||||
import io.legado.app.ui.book.read.ReadBookActivity.Companion.RESULT_DELETED
|
||||
@@ -64,9 +63,12 @@ import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.toggleStatusBar
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import io.legado.app.utils.visible
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>(),
|
||||
class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewModel>(),
|
||||
ReadManga.Callback, ChangeBookSourceDialog.CallBack, MangaMenu.CallBack {
|
||||
|
||||
private val mLayoutManager by lazy {
|
||||
@@ -140,7 +142,11 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
}
|
||||
}
|
||||
override val binding by viewBinding(ActivityMangaBinding::inflate)
|
||||
override val viewModel by viewModels<MangaViewModel>()
|
||||
override val viewModel by viewModels<ReadMangaViewModel>()
|
||||
private val loadingViewVisible get() = binding.flLoading.isVisible
|
||||
private val df by lazy {
|
||||
DecimalFormat("0.0%")
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
upLayoutInDisplayCutoutMode()
|
||||
@@ -154,15 +160,15 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
binding.tvRetry.setOnClickListener {
|
||||
binding.llLoading.isVisible = true
|
||||
binding.llRetry.isGone = true
|
||||
ReadManga.loadContent()
|
||||
ReadManga.loadOrUpContent()
|
||||
}
|
||||
|
||||
mAdapter.addFooterView {
|
||||
ViewLoadMoreBinding.bind(loadMoreView)
|
||||
}
|
||||
loadMoreView.setOnClickListener {
|
||||
if (!loadMoreView.isLoading && !ReadManga.gameOver) {
|
||||
scrollToBottom(true, ReadManga.durChapterIndex)
|
||||
if (!loadMoreView.isLoading && ReadManga.hasNextChapter) {
|
||||
loadMoreView.startLoad()
|
||||
ReadManga.loadOrUpContent()
|
||||
}
|
||||
}
|
||||
loadMoreView.gone()
|
||||
@@ -175,11 +181,11 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
observeEvent<MangaFooterConfig>(EventBus.UP_MANGA_CONFIG) {
|
||||
mMangaFooterConfig = it
|
||||
upInfoBar(
|
||||
ReadManga.durChapterIndex.plus(1),
|
||||
ReadManga.durChapterIndex,
|
||||
ReadManga.chapterSize,
|
||||
ReadManga.durChapterPos.plus(1),
|
||||
ReadManga.durChapterPos,
|
||||
ReadManga.durChapterImageCount,
|
||||
ReadManga.chapterTitle
|
||||
ReadManga.curMangaChapter?.chapter?.title ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -197,28 +203,28 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
singlePagerScroller(AppConfig.enableMangaHorizontalScroll)
|
||||
disabledClickScroller(AppConfig.disableClickScroll)
|
||||
disableMangaScaling(AppConfig.disableMangaScale)
|
||||
setPreScrollListener { _, dx, dy, position ->
|
||||
if ((dy > 0 || dx > 0) && position + 2 > mAdapter.getCurrentList().size - 3) {
|
||||
if (mAdapter.getCurrentList().last() is ReaderLoading) {
|
||||
val nextIndex =
|
||||
(mAdapter.getCurrentList().last() as ReaderLoading).mNextChapterIndex
|
||||
if (nextIndex != -1) {
|
||||
scrollToBottom(false, nextIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setNestedPreScrollListener { _, _, _, position ->
|
||||
setPreScrollListener { _, _, _, position ->
|
||||
if (mAdapter.isNotEmpty()) {
|
||||
val content = mAdapter.getItem(position)
|
||||
if (content is MangaContent) {
|
||||
ReadManga.durChapterIndex = content.mChapterIndex
|
||||
ReadManga.durChapterPos = content.mDurChapterPos
|
||||
if (ReadManga.durChapterIndex < content.mChapterIndex) {
|
||||
ReadManga.moveToNextChapter()
|
||||
if (ReadManga.hasNextChapter) {
|
||||
loadMoreView.startLoad()
|
||||
} else {
|
||||
loadMoreView.noMore("暂无章节了!")
|
||||
}
|
||||
} else if (ReadManga.durChapterIndex > content.mChapterIndex) {
|
||||
ReadManga.moveToPrevChapter()
|
||||
} else {
|
||||
ReadManga.durChapterPos = content.mDurChapterPos
|
||||
ReadManga.curPageChanged()
|
||||
}
|
||||
upInfoBar(
|
||||
content.mChapterIndex + 1,
|
||||
content.mChapterIndex,
|
||||
content.chapterSize,
|
||||
content.mDurChapterPos + 1,
|
||||
content.mDurChapterCount,
|
||||
content.mDurChapterPos,
|
||||
content.mDurChapterImageCount,
|
||||
content.mChapterName
|
||||
)
|
||||
}
|
||||
@@ -244,13 +250,6 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
viewModel.initData(intent)
|
||||
}
|
||||
|
||||
private fun scrollToBottom(forceLoad: Boolean = false, index: Int) {
|
||||
if ((loadMoreView.hasMore && !loadMoreView.isLoading) && !ReadManga.gameOver || forceLoad) {
|
||||
loadMoreView.hasMore()
|
||||
ReadManga.moveToNextChapter(index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
Looper.myQueue().addIdleHandler {
|
||||
@@ -260,41 +259,47 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
justInitData = true
|
||||
}
|
||||
|
||||
override fun loadContentFinish(list: MutableList<Any>) {
|
||||
override fun upContent(finish: Boolean) {
|
||||
lifecycleScope.launch {
|
||||
setTitle(ReadManga.book?.name)
|
||||
val isEmpty = mAdapter.isEmpty()
|
||||
val list = withContext(IO) { ReadManga.mangaContents }
|
||||
mAdapter.submitList(list) {
|
||||
if (isEmpty) {
|
||||
if (loadingViewVisible && finish) {
|
||||
binding.infobar.isVisible = true
|
||||
upInfoBar(
|
||||
ReadManga.durChapterIndex.plus(1),
|
||||
ReadManga.durChapterIndex,
|
||||
ReadManga.chapterSize,
|
||||
ReadManga.durChapterPos.plus(1),
|
||||
ReadManga.durChapterPos,
|
||||
ReadManga.durChapterImageCount,
|
||||
ReadManga.chapterTitle
|
||||
ReadManga.curMangaChapter!!.chapter.title
|
||||
)
|
||||
binding.mRecyclerManga.scrollToPosition(ReadManga.durChapterAbsPos)
|
||||
binding.flLoading.isGone = true
|
||||
loadMoreView.visible()
|
||||
}
|
||||
|
||||
if (ReadManga.durChapterPos + 2 > mAdapter.getCurrentList().size - 3) {
|
||||
val nextIndex =
|
||||
(mAdapter.getCurrentList().last() as ReaderLoading).mNextChapterIndex
|
||||
scrollToBottom(index = nextIndex)
|
||||
} else {
|
||||
binding.mRecyclerManga.scrollToPosition(ReadManga.durChapterPos)
|
||||
if (finish) {
|
||||
loadMoreView.stopLoad()
|
||||
if (!ReadManga.hasNextChapter) {
|
||||
loadMoreView.noMore("暂无章节了!")
|
||||
}
|
||||
}
|
||||
|
||||
if (ReadManga.chapterChanged) {
|
||||
binding.mRecyclerManga.scrollToPosition(ReadManga.durChapterPos)
|
||||
binding.mRecyclerManga.scrollToPosition(ReadManga.durChapterAbsPos)
|
||||
}
|
||||
|
||||
ReadManga.chapterChanged = false
|
||||
loadMoreView.visible()
|
||||
loadMoreView.stopLoad()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun contentLoadFinish() {
|
||||
lifecycleScope.launch {
|
||||
loadMoreView.stopLoad()
|
||||
}
|
||||
}
|
||||
|
||||
private fun upInfoBar(
|
||||
chapterIndex: Int,
|
||||
chapterSize: Int,
|
||||
@@ -315,21 +320,35 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
if (!hidePageNumberLabel) {
|
||||
mLabelBuilder.append(getString(R.string.manga_check_page_number))
|
||||
}
|
||||
mLabelBuilder.append("${chapterPos}/${chapterImageCount}").append(" ")
|
||||
mLabelBuilder.append("${chapterPos + 1}/${chapterImageCount}").append(" ")
|
||||
}
|
||||
|
||||
if (!hideChapter) {
|
||||
if (!hideChapterLabel) {
|
||||
mLabelBuilder.append(getString(R.string.manga_check_chapter))
|
||||
}
|
||||
mLabelBuilder.append("${chapterIndex}/${chapterSize}").append(" ")
|
||||
mLabelBuilder.append("${chapterIndex + 1}/${chapterSize}").append(" ")
|
||||
}
|
||||
|
||||
if (!hideProgressRatio) {
|
||||
if (!hideProgressRatioLabel) {
|
||||
mLabelBuilder.append(getString(R.string.manga_check_progress))
|
||||
}
|
||||
mLabelBuilder.append("${chapterIndex.div(chapterSize).times(100)}%")
|
||||
val percent = if (chapterSize == 0 || chapterImageCount == 0 && chapterIndex == 0) {
|
||||
"0.0%"
|
||||
} else if (chapterImageCount == 0) {
|
||||
df.format((chapterIndex + 1.0f) / chapterSize.toDouble())
|
||||
} else {
|
||||
var percent =
|
||||
df.format(
|
||||
chapterIndex * 1.0f / chapterSize + 1.0f / chapterSize * (chapterPos + 1) / chapterImageCount.toDouble()
|
||||
)
|
||||
if (percent == "100.0%" && (chapterIndex + 1 != chapterSize || chapterPos + 1 != chapterImageCount)) {
|
||||
percent = "99.9%"
|
||||
}
|
||||
percent
|
||||
}
|
||||
mLabelBuilder.append(percent)
|
||||
}
|
||||
}
|
||||
binding.infobar.update(
|
||||
@@ -366,15 +385,9 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
stopAutoPage()
|
||||
}
|
||||
|
||||
override fun loadComplete() {
|
||||
lifecycleScope.launch {
|
||||
binding.flLoading.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadFail(msg: String) {
|
||||
lifecycleScope.launch {
|
||||
if (mAdapter.isEmpty() || ReadManga.chapterChanged) {
|
||||
if (loadingViewVisible) {
|
||||
binding.llLoading.isGone = true
|
||||
binding.llRetry.isVisible = true
|
||||
binding.tvMsg.text = msg
|
||||
@@ -384,12 +397,6 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
}
|
||||
}
|
||||
|
||||
override fun noData() {
|
||||
lifecycleScope.launch {
|
||||
loadMoreView.noMore("暂无章节了!")
|
||||
}
|
||||
}
|
||||
|
||||
override fun adjustProgress() {
|
||||
if (ReadManga.chapterChanged) {
|
||||
binding.mRecyclerManga.scrollToPosition(ReadManga.durChapterPos)
|
||||
@@ -398,7 +405,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, MangaViewModel>()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
ReadManga.unregister()
|
||||
ReadManga.unregister(this)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.onEmpty
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.take
|
||||
|
||||
class MangaViewModel(application: Application) : BaseViewModel(application) {
|
||||
class ReadMangaViewModel(application: Application) : BaseViewModel(application) {
|
||||
|
||||
private var changeSourceCoroutine: Coroutine<*>? = null
|
||||
|
||||
@@ -84,7 +84,12 @@ class MangaViewModel(application: Application) : BaseViewModel(application) {
|
||||
ensureChapterExist()
|
||||
|
||||
//开始加载内容
|
||||
ReadManga.loadContent()
|
||||
if (!isSameBook) {
|
||||
ReadManga.loadContent()
|
||||
} else {
|
||||
ReadManga.loadOrUpContent()
|
||||
}
|
||||
|
||||
|
||||
//自动换源
|
||||
if (!book.isLocal && ReadManga.bookSource == null) {
|
||||
@@ -224,7 +229,7 @@ class MangaViewModel(application: Application) : BaseViewModel(application) {
|
||||
ReadManga.durChapterIndex = index
|
||||
ReadManga.durChapterPos = durChapterPos
|
||||
ReadManga.saveRead()
|
||||
ReadManga.loadContent(index)
|
||||
ReadManga.loadContent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.legado.app.ui.book.manga.entities
|
||||
|
||||
import io.legado.app.data.entities.BookChapter
|
||||
|
||||
data class MangaChapter(
|
||||
val chapter: BookChapter,
|
||||
val contents: List<Any>,
|
||||
val imageCount: Int
|
||||
)
|
||||
@@ -6,6 +6,6 @@ data class MangaContent(
|
||||
var nextChapterIndex: Int = 0,//下一章
|
||||
val mImageUrl: String = "",//当前URL
|
||||
var mDurChapterPos: Int = 0,//当前章节位置
|
||||
var mDurChapterCount: Int = 0,//当前章节内容总数
|
||||
var mDurChapterImageCount: Int = 0,//当前章节内容总数
|
||||
var mChapterName: String = "",//章节名称
|
||||
)
|
||||
@@ -67,17 +67,8 @@ class MangaAdapter(private val context: Context) :
|
||||
fun isNotEmpty() = !isEmpty()
|
||||
|
||||
//全部替换数据
|
||||
fun submitList(contents: MutableList<Any>, runnable: Runnable) {
|
||||
val list = if (ReadManga.chapterChanged) {
|
||||
contents
|
||||
} else {
|
||||
val currentList = mDiffer.currentList.toMutableList()
|
||||
currentList.addAll(contents)
|
||||
currentList
|
||||
}
|
||||
mDiffer.submitList(list) {
|
||||
runnable.run()
|
||||
}
|
||||
fun submitList(contents: List<Any>, runnable: Runnable? = null) {
|
||||
mDiffer.submitList(contents, runnable)
|
||||
}
|
||||
|
||||
inner class PageViewHolder(binding: BookComicRvBinding) :
|
||||
@@ -94,13 +85,13 @@ class MangaAdapter(private val context: Context) :
|
||||
binding.retry.setOnClickListener {
|
||||
val item = mDiffer.currentList[layoutPosition]
|
||||
if (item is MangaContent) {
|
||||
loadImageWithRetry(item.mImageUrl, isHorizontal)
|
||||
loadImageWithRetry(item.mImageUrl, isHorizontal, mDiffer.currentList.size == 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBind(item: MangaContent) {
|
||||
loadImageWithRetry(item.mImageUrl, isHorizontal)
|
||||
loadImageWithRetry(item.mImageUrl, isHorizontal, mDiffer.currentList.size == 3)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ open class MangaVH<VB : ViewBinding>(val binding: VB, private val context: Conte
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun loadImageWithRetry(imageUrl: String, isHorizontal: Boolean) {
|
||||
fun loadImageWithRetry(imageUrl: String, isHorizontal: Boolean, singleImage: Boolean) {
|
||||
mFlProgress.isVisible = true
|
||||
mLoading.isVisible = true
|
||||
mRetry?.isGone = true
|
||||
@@ -95,7 +95,11 @@ open class MangaVH<VB : ViewBinding>(val binding: VB, private val context: Conte
|
||||
mFlProgress.isGone = true
|
||||
if (!isHorizontal) {
|
||||
itemView.updateLayoutParams<ViewGroup.LayoutParams> {
|
||||
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
height = if (singleImage) {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
} else {
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mImage.updateLayoutParams<FrameLayout.LayoutParams> {
|
||||
|
||||
@@ -4,11 +4,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
fun RecyclerView.findCenterViewPosition(): Int {
|
||||
return getChildAdapterPosition(findChildViewUnder(width / 2f, height / 2f) ?: return RecyclerView.NO_POSITION)
|
||||
return getChildAdapterPosition(
|
||||
findChildViewUnder(width / 2f, height / 2f) ?: return RecyclerView.NO_POSITION
|
||||
)
|
||||
}
|
||||
|
||||
fun RecyclerView.findViewPosition(x: Float, y: Float): Int {
|
||||
return getChildAdapterPosition(findChildViewUnder(x, y) ?: return RecyclerView.NO_POSITION)
|
||||
return getChildAdapterPosition(findChildViewUnder(x, y) ?: return RecyclerView.NO_POSITION)
|
||||
}
|
||||
|
||||
fun RecyclerView.findFirstVisibleViewPosition(): Int {
|
||||
@@ -21,7 +23,7 @@ fun RecyclerView.findFirstVisibleViewPosition(): Int {
|
||||
|
||||
fun RecyclerView.findLastVisibleViewPosition(): Int {
|
||||
var pos = -1
|
||||
if ( layoutManager is LinearLayoutManager) {
|
||||
if (layoutManager is LinearLayoutManager) {
|
||||
pos = (layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
|
||||
}
|
||||
return pos
|
||||
|
||||
Reference in New Issue
Block a user