This commit is contained in:
Horis
2025-03-15 22:36:05 +08:00
parent 0d8bbbfe60
commit 6f875ac04d
7 changed files with 105 additions and 118 deletions
@@ -21,8 +21,8 @@ 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.MangaContentData
import io.legado.app.ui.book.manga.entities.MangaPage
import io.legado.app.ui.book.manga.entities.ReaderLoading
import io.legado.app.utils.mapIndexed
import kotlinx.coroutines.CoroutineScope
@@ -67,7 +67,6 @@ object ReadManga : CoroutineScope by MainScope() {
var rateLimiter = ConcurrentRateLimiter(null)
val mangaContents get() = buildContentList()
val hasNextChapter get() = durChapterIndex < simulatedChapterSize - 1
val mSeekParPos = mutableMapOf<Int, MutableMap<Int, Int>>()
fun resetData(book: Book) {
ReadManga.book = book
@@ -260,14 +259,18 @@ object ReadManga : CoroutineScope by MainScope() {
/**
* 加载下一章
*/
fun moveToNextChapter(startLoad: () -> Unit): Boolean {
fun moveToNextChapter(toFirst: Boolean = false): Boolean {
if (durChapterIndex < simulatedChapterSize - 1) {
if (toFirst) {
mCallback?.showLoading()
durChapterPos = 0
}
durChapterIndex++
prevMangaChapter = curMangaChapter
curMangaChapter = nextMangaChapter
nextMangaChapter = null
if (curMangaChapter == null) {
startLoad.invoke()
mCallback?.startLoad()
loadContent(durChapterIndex)
} else {
mCallback?.upContent()
@@ -283,8 +286,12 @@ object ReadManga : CoroutineScope by MainScope() {
}
}
fun moveToPrevChapter(): Boolean {
fun moveToPrevChapter(toFirst: Boolean = false): Boolean {
if (durChapterIndex > 0) {
if (toFirst) {
mCallback?.showLoading()
durChapterPos = 0
}
durChapterIndex--
nextMangaChapter = curMangaChapter
curMangaChapter = prevMangaChapter
@@ -561,8 +568,8 @@ object ReadManga : CoroutineScope by MainScope() {
private suspend fun getManageChapter(chapter: BookChapter, content: String): MangaChapter {
val list = BookHelp.flowImages(chapter, content)
.distinctUntilChanged().mapIndexed { index, src ->
MangaContent(
mChapterIndex = chapter.index,
MangaPage(
chapterIndex = chapter.index,
chapterSize = chapterSize,
mImageUrl = src,
index = index,
@@ -577,43 +584,18 @@ object ReadManga : CoroutineScope by MainScope() {
}
val contentList = mutableListOf<Any>()
contentList.add(ReaderLoading(chapter.index, "阅读 ${chapter.title}"))
contentList.add(ReaderLoading(chapter.index, -1, "阅读 ${chapter.title}"))
contentList.addAll(list)
contentList.add(ReaderLoading(chapter.index, "已读完 ${chapter.title}"))
contentList.add(ReaderLoading(chapter.index, imageCount, "已读完 ${chapter.title}"))
return MangaChapter(chapter, contentList, imageCount)
}
fun recordMangaPosition(dataList: MutableList<Any>) {
Coroutine.async {
var globalPosition = 0
val mangaList = mutableListOf<MangaContent>()
dataList.forEach {
if (it is MangaContent) {
mangaList.add(it)
}
}
dataList.groupBy { if (it is MangaContent) it.mChapterIndex else (it as ReaderLoading).mChapterIndex }
.forEach { (chapterIndex, items) ->
val itemMap = mutableMapOf<Int, Int>()
for (i in items.indices) {
val item = items[i]
if (item is MangaContent) {
itemMap[item.index] = globalPosition++
} else {
globalPosition++
}
}
mSeekParPos[chapterIndex] = itemMap
}
}
}
interface Callback {
fun upContent()
fun loadFail(msg: String)
fun sureNewProgress(progress: BookProgress)
fun showLoading()
fun startLoad()
}
}
@@ -46,7 +46,8 @@ import io.legado.app.ui.book.manga.config.MangaColorFilterConfig
import io.legado.app.ui.book.manga.config.MangaColorFilterDialog
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.entities.BaseMangaPage
import io.legado.app.ui.book.manga.entities.MangaPage
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
@@ -56,6 +57,8 @@ import io.legado.app.ui.widget.recycler.LoadMoreView
import io.legado.app.utils.GSON
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.fastBinarySearch
import io.legado.app.utils.findCenterViewPosition
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.gone
@@ -187,13 +190,8 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
override fun observeLiveBus() {
observeEvent<MangaFooterConfig>(EventBus.UP_MANGA_CONFIG) {
mMangaFooterConfig = it
upInfoBar(
ReadManga.durChapterIndex,
ReadManga.chapterSize,
ReadManga.durChapterPos,
ReadManga.durChapterImageCount,
ReadManga.curMangaChapter?.chapter?.title ?: ""
)
val item = mAdapter.getItem(binding.mRecyclerManga.findCenterViewPosition())
upInfoBar(item)
}
}
@@ -215,26 +213,18 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
disableMangaScaling(AppConfig.disableMangaScale)
setPreScrollListener { _, _, _, position ->
if (mAdapter.isNotEmpty()) {
val content = mAdapter.getItem(position)
if (content is MangaContent) {
if (ReadManga.durChapterIndex < content.mChapterIndex) {
ReadManga.moveToNextChapter {
loadMoreView.startLoad()
}
} else if (ReadManga.durChapterIndex > content.mChapterIndex) {
val item = mAdapter.getItem(position)
if (item is MangaPage) {
if (ReadManga.durChapterIndex < item.chapterIndex) {
ReadManga.moveToNextChapter()
} else if (ReadManga.durChapterIndex > item.chapterIndex) {
ReadManga.moveToPrevChapter()
} else {
ReadManga.durChapterPos = content.index
ReadManga.durChapterPos = item.index
ReadManga.curPageChanged()
}
binding.mangaMenu.upSeekBar(content.index, content.imageCount)
upInfoBar(
content.mChapterIndex,
content.chapterSize,
content.index,
content.imageCount,
content.mChapterName
)
binding.mangaMenu.upSeekBar(item.index, item.imageCount)
upInfoBar(item)
}
}
}
@@ -279,16 +269,9 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
val nextFinish = data.nextFinish
mAdapter.submitList(list) {
binding.mangaMenu.upSeekBar(ReadManga.durChapterPos, ReadManga.durChapterImageCount)
ReadManga.recordMangaPosition(mAdapter.getCurrentList())
if (loadingViewVisible && curFinish) {
binding.infobar.isVisible = true
upInfoBar(
ReadManga.durChapterIndex,
ReadManga.chapterSize,
ReadManga.durChapterPos,
ReadManga.durChapterImageCount,
ReadManga.curMangaChapter!!.chapter.title
)
upInfoBar(list[pos])
binding.mRecyclerManga.scrollToPosition(pos)
binding.flLoading.isGone = true
loadMoreView.visible()
@@ -307,13 +290,15 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
}
}
private fun upInfoBar(
chapterIndex: Int,
chapterSize: Int,
chapterPos: Int,
chapterImageCount: Int,
chapterName: String,
) {
private fun upInfoBar(page: Any?) {
if (page !is MangaPage) {
return
}
val chapterIndex = page.index
val chapterSize = page.chapterSize
val chapterPos = page.index
val imageCount = page.imageCount
val chapterName = page.mChapterName
mMangaFooterConfig.run {
mLabelBuilder.clear()
binding.infobar.isGone = hideFooter
@@ -327,7 +312,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
if (!hidePageNumberLabel) {
mLabelBuilder.append(getString(R.string.manga_check_page_number))
}
mLabelBuilder.append("${chapterPos + 1}/${chapterImageCount}").append(" ")
mLabelBuilder.append("${chapterPos + 1}/${imageCount}").append(" ")
}
if (!hideChapter) {
@@ -341,16 +326,16 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
if (!hideProgressRatioLabel) {
mLabelBuilder.append(getString(R.string.manga_check_progress))
}
val percent = if (chapterSize == 0 || chapterImageCount == 0 && chapterIndex == 0) {
val percent = if (chapterSize == 0 || imageCount == 0 && chapterIndex == 0) {
"0.0%"
} else if (chapterImageCount == 0) {
} else if (imageCount == 0) {
df.format((chapterIndex + 1.0f) / chapterSize.toDouble())
} else {
var percent =
df.format(
chapterIndex * 1.0f / chapterSize + 1.0f / chapterSize * (chapterPos + 1) / chapterImageCount.toDouble()
chapterIndex * 1.0f / chapterSize + 1.0f / chapterSize * (chapterPos + 1) / imageCount.toDouble()
)
if (percent == "100.0%" && (chapterIndex + 1 != chapterSize || chapterPos + 1 != chapterImageCount)) {
if (percent == "100.0%" && (chapterIndex + 1 != chapterSize || chapterPos + 1 != imageCount)) {
percent = "99.9%"
}
percent
@@ -432,6 +417,12 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
}
}
override fun startLoad() {
lifecycleScope.launch {
loadMoreView.startLoad()
}
}
override val oldBook: Book?
get() = ReadManga.book
@@ -540,10 +531,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
if (item.isChecked) LinearLayoutManager.HORIZONTAL else LinearLayoutManager.VERTICAL
singlePagerScroller(item.isChecked)
mAdapter.isHorizontal = item.isChecked
mAdapter.notifyItemRangeChanged(
ReadManga.durChapterPos.minus(2),
mAdapter.getCurrentList().size
)
mAdapter.notifyItemRangeChanged(0, mAdapter.itemCount)
}
R.id.menu_manga_color_filter -> {
@@ -567,12 +555,6 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
toggleSystemBar(menuIsVisible)
}
override fun moveToTargetIndex(isNext: Boolean) {
var targetIndex =
if (isNext) ReadManga.durChapterIndex + 1 else ReadManga.durChapterIndex - 1
viewModel.openChapter(if (isNext) targetIndex else targetIndex, 0)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val keyCode = event.keyCode
val action = event.action
@@ -756,10 +738,27 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
window.decorView.postInvalidate()
}
override fun seekValue(pos: Int) {
val mangaIndex = ReadManga.mSeekParPos[ReadManga.durChapterIndex]
if (mangaIndex != null && mangaIndex[pos] != null) {
binding.mRecyclerManga.scrollToPosition(mangaIndex[pos]!!)
override fun skipToPage(pos: Int) {
val durChapterIndex = ReadManga.durChapterIndex
val pos = mAdapter.getItems().fastBinarySearch {
val chapterIndex: Int
val pageIndex: Int
if (it is BaseMangaPage) {
chapterIndex = it.chapterIndex
pageIndex = it.index
} else {
error("unknown item type")
}
val delta = chapterIndex - durChapterIndex
if (delta != 0) {
delta
} else {
pageIndex - pos
}
}
if (pos > -1) {
binding.mRecyclerManga.scrollToPosition(pos)
upInfoBar(mAdapter.getItem(pos))
}
}
}
@@ -0,0 +1,6 @@
package io.legado.app.ui.book.manga.entities
interface BaseMangaPage {
val chapterIndex: Int
val index: Int
}
@@ -1,10 +1,10 @@
package io.legado.app.ui.book.manga.entities
data class MangaContent(
val mChapterIndex: Int = 0,//总章节位置
data class MangaPage(
override val chapterIndex: Int = 0,//总章节位置
val chapterSize: Int,//总章节数量
val mImageUrl: String = "",//当前URL
val index: Int = 0,//当前章节位置
override val index: Int = 0,//当前章节位置
var imageCount: Int = 0,//当前章节内容总数
val mChapterName: String = "",//章节名称
)
) : BaseMangaPage
@@ -1,7 +1,8 @@
package io.legado.app.ui.book.manga.entities
data class ReaderLoading(
val mChapterIndex: Int = 0,
override val chapterIndex: Int = 0,
override val index: Int = 0,
val mMessage: String? = null,
var mLoading: Boolean = false,
)
) : BaseMangaPage
@@ -26,7 +26,7 @@ import io.legado.app.help.glide.progress.ProgressManager
import io.legado.app.model.BookCover
import io.legado.app.model.ReadManga
import io.legado.app.ui.book.manga.config.MangaColorFilterConfig
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.utils.getCompatDrawable
import java.util.Collections
@@ -49,7 +49,7 @@ class MangaAdapter(private val context: Context) :
override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean {
return if (oldItem is ReaderLoading && newItem is ReaderLoading) {
newItem.mMessage == oldItem.mMessage
} else if (oldItem is MangaContent && newItem is MangaContent) {
} else if (oldItem is MangaPage && newItem is MangaPage) {
oldItem.mImageUrl == newItem.mImageUrl
} else false
}
@@ -57,7 +57,7 @@ class MangaAdapter(private val context: Context) :
override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean {
return if (oldItem is ReaderLoading && newItem is ReaderLoading) {
oldItem == newItem
} else if (oldItem is MangaContent && newItem is MangaContent) {
} else if (oldItem is MangaPage && newItem is MangaPage) {
oldItem == newItem
} else false
}
@@ -67,7 +67,7 @@ class MangaAdapter(private val context: Context) :
fun getItem(@IntRange(from = 0) position: Int) = mDiffer.currentList.getOrNull(position)
fun getCurrentList() = mDiffer.currentList
fun getItems() = mDiffer.currentList
fun isEmpty() = mDiffer.currentList.isEmpty()
@@ -91,7 +91,7 @@ class MangaAdapter(private val context: Context) :
)
binding.retry.setOnClickListener {
val item = mDiffer.currentList[layoutPosition]
if (item is MangaContent) {
if (item is MangaPage) {
loadImageWithRetry(
item.mImageUrl, isHorizontal, item.imageCount == 1
)
@@ -99,7 +99,7 @@ class MangaAdapter(private val context: Context) :
}
}
fun onBind(item: MangaContent) {
fun onBind(item: MangaPage) {
setImageColorFilter()
loadImageWithRetry(item.mImageUrl, isHorizontal, item.imageCount == 1)
}
@@ -155,7 +155,7 @@ class MangaAdapter(private val context: Context) :
override fun getItemViewType(position: Int): Int {
return when {
isFooter(position) -> TYPE_FOOTER_VIEW + position - getActualItemCount()
getItem(position) is MangaContent -> CONTENT_VIEW
getItem(position) is MangaPage -> CONTENT_VIEW
getItem(position) is ReaderLoading -> LOADING_VIEW
else -> error("Unknown view type!")
}
@@ -182,7 +182,7 @@ class MangaAdapter(private val context: Context) :
override fun onBindViewHolder(vh: RecyclerView.ViewHolder, position: Int) {
when (vh) {
is PageViewHolder -> vh.onBind(getItem(position) as MangaContent)
is PageViewHolder -> vh.onBind(getItem(position) as MangaPage)
is PageMoreViewHolder -> vh.onBind(getItem(position) as ReaderLoading)
}
}
@@ -202,7 +202,7 @@ class MangaAdapter(private val context: Context) :
/**
* 除去header和footer
*/
fun getActualItemCount() = getCurrentList().size
fun getActualItemCount() = getItems().size
@Synchronized
fun removeFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) {
@@ -216,13 +216,13 @@ class MangaAdapter(private val context: Context) :
}
override fun getPreloadItems(position: Int): MutableList<Any> {
if (getCurrentList().isEmpty()) return Collections.emptyList()
if (position >= getCurrentList().size) return Collections.emptyList()
return getCurrentList().subList(position, position + 1)
if (getItems().isEmpty()) return Collections.emptyList()
if (position >= getItems().size) return Collections.emptyList()
return getItems().subList(position, position + 1)
}
override fun getPreloadRequestBuilder(item: Any): RequestBuilder<*>? {
if (item is MangaContent) {
if (item is MangaPage) {
return BookCover.loadManga(
context,
item.mImageUrl,
@@ -236,15 +236,15 @@ class MangaMenu @JvmOverloads constructor(
tvChapterUrl.setOnLongClickListener(chapterViewLongClickListener)
tvNext.setOnClickListener {
callBack.moveToTargetIndex(true)
ReadManga.moveToNextChapter(true)
}
tvPre.setOnClickListener {
callBack.moveToTargetIndex(false)
ReadManga.moveToPrevChapter(true)
}
seekReadPage.setOnSeekBarChangeListener(object : SeekBarChangeListener {
override fun onStopTrackingTouch(seekBar: SeekBar) {
callBack.seekValue(seekBar.progress)
callBack.skipToPage(seekBar.progress)
}
})
}
@@ -259,8 +259,7 @@ class MangaMenu @JvmOverloads constructor(
interface CallBack {
fun openBookInfoActivity()
fun upSystemUiVisibility(menuIsVisible: Boolean)
fun moveToTargetIndex(isNext: Boolean)
fun seekValue(pos: Int)
fun skipToPage(pos: Int)
}
}