Fix concurrent chapters loading in reader

This commit is contained in:
Koitharu
2023-05-17 16:17:18 +03:00
parent 2e0eb5de54
commit 51ff1ff7b7
5 changed files with 53 additions and 12 deletions

View File

@@ -15,8 +15,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdkVersion 21
targetSdkVersion 33
versionCode 544
versionName '5.1'
versionCode 545
versionName '5.1.1'
generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -83,7 +83,7 @@ dependencies {
}
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.21'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.core:core-ktx:1.10.1'
@@ -140,17 +140,17 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20230227'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5'
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.0'
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
androidTestImplementation 'androidx.room:room-testing:2.5.1'
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.14.0'
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0'
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.46.1'
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.46.1'

View File

@@ -8,19 +8,22 @@ abstract class BoundsScrollListener(private val offsetTop: Int, private val offs
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.hasPendingAdapterUpdates()) {
return
}
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (firstVisibleItemPosition == RecyclerView.NO_POSITION) {
return
}
if (firstVisibleItemPosition <= offsetTop) {
onScrolledToStart(recyclerView)
}
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom) {
onScrolledToEnd(recyclerView)
}
if (firstVisibleItemPosition <= offsetTop) {
onScrolledToStart(recyclerView)
}
}
abstract fun onScrolledToStart(recyclerView: RecyclerView)

View File

@@ -0,0 +1,30 @@
package org.koitharu.kotatsu.base.ui.list
import androidx.recyclerview.widget.RecyclerView
class ScrollListenerInvalidationObserver(
private val recyclerView: RecyclerView,
private val scrollListener: RecyclerView.OnScrollListener,
) : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
invalidateScroll()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount)
invalidateScroll()
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
super.onItemRangeRemoved(positionStart, itemCount)
invalidateScroll()
}
private fun invalidateScroll() {
recyclerView.post {
scrollListener.onScrolled(recyclerView, 0, 0)
}
}
}

View File

@@ -252,6 +252,7 @@ class ReaderViewModel @Inject constructor(
val prevJob = stateChangeJob
stateChangeJob = launchJob(Dispatchers.Default) {
prevJob?.cancelAndJoin()
loadingJob?.join()
val pages = content.value?.pages ?: return@launchJob
pages.getOrNull(position)?.let { page ->
currentState.update { cs ->
@@ -263,12 +264,12 @@ class ReaderViewModel @Inject constructor(
return@launchJob
}
ensureActive()
if (position <= BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.first().chapterId, isNext = false)
}
if (position >= pages.lastIndex - BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.last().chapterId, isNext = true)
}
if (position <= BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.first().chapterId, isNext = false)
}
if (pageLoader.isPrefetchApplicable()) {
pageLoader.prefetch(pages.trySublist(position + 1, position + PREFETCH_LIMIT))
}
@@ -348,7 +349,9 @@ class ReaderViewModel @Inject constructor(
@AnyThread
private fun loadPrevNextChapter(currentId: Long, isNext: Boolean) {
val prevJob = loadingJob
loadingJob = launchLoadingJob(Dispatchers.Default) {
prevJob?.join()
chaptersLoader.loadPrevNextChapter(mangaData.requireValue(), currentId, isNext)
content.emitValue(ReaderContent(chaptersLoader.snapshot(), null))
}

View File

@@ -54,6 +54,11 @@ var RecyclerView.firstVisibleItemPosition: Int
}
}
val RecyclerView.visibleItemCount: Int
get() = (layoutManager as? LinearLayoutManager)?.run {
findLastVisibleItemPosition() - findFirstVisibleItemPosition()
} ?: 0
fun View.hasGlobalPoint(x: Int, y: Int): Boolean {
if (visibility != View.VISIBLE) {
return false