Retry thumbnails loading after network state changed

This commit is contained in:
Koitharu
2025-06-22 13:28:44 +03:00
parent fe59a13218
commit aeee782512
2 changed files with 42 additions and 2 deletions

View File

@@ -7,6 +7,7 @@ import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.core.content.withStyledAttributes
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope
import coil3.ImageLoader
import coil3.asImage
import coil3.request.Disposable
@@ -25,10 +26,14 @@ import coil3.size.ViewSizeResolver
import coil3.util.CoilUtils
import com.google.android.material.imageview.ShapeableImageView
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.ext.decodeRegion
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.isNetworkError
import java.util.LinkedList
import javax.inject.Inject
@@ -42,6 +47,9 @@ open class CoilImageView @JvmOverloads constructor(
@Inject
lateinit var coil: ImageLoader
@Inject
lateinit var networkState: NetworkState
var allowRgb565: Boolean = false
var useExistingDrawable: Boolean = false
var decodeRegion: Boolean = false
@@ -54,9 +62,13 @@ open class CoilImageView @JvmOverloads constructor(
private var currentRequest: Disposable? = null
private var currentImageData: Any = NullRequestData
private var networkWaitingJob: Job? = null
private var listeners: MutableList<ImageRequest.Listener>? = null
val isFailed: Boolean
get() = CoilUtils.result(this) is ErrorResult
init {
context.withStyledAttributes(attrs, R.styleable.CoilImageView, defStyleAttr) {
allowRgb565 = getBoolean(R.styleable.CoilImageView_allowRgb565, allowRgb565)
@@ -81,6 +93,9 @@ open class CoilImageView @JvmOverloads constructor(
override fun onError(request: ImageRequest, result: ErrorResult) {
super.onError(request, result)
listeners?.forEach { it.onError(request, result) }
if (result.throwable.isNetworkError()) {
waitForNetwork()
}
}
override fun onStart(request: ImageRequest) {
@@ -115,17 +130,27 @@ open class CoilImageView @JvmOverloads constructor(
)
fun disposeImage() {
networkWaitingJob?.cancel()
networkWaitingJob = null
CoilUtils.dispose(this)
currentRequest = null
currentImageData = NullRequestData
setImageDrawable(null)
}
protected fun enqueueRequest(request: ImageRequest): Disposable {
fun reload() {
CoilUtils.result(this)?.let { result ->
enqueueRequest(result.request, force = true)
}
}
protected fun enqueueRequest(request: ImageRequest, force: Boolean = false): Disposable {
val previous = currentRequest
if (currentImageData == request.data && previous?.job?.isCancelled == false) {
if (!force && currentImageData == request.data && previous?.job?.isCancelled == false && !isFailed) {
return previous
}
networkWaitingJob?.cancel()
networkWaitingJob = null
currentImageData = request.data
return coil.enqueue(request).also { currentRequest = it }
}
@@ -175,4 +200,17 @@ open class CoilImageView @JvmOverloads constructor(
} else {
Scale.FIT
}
private fun waitForNetwork() {
if (networkWaitingJob?.isActive == true || networkState.isOnline()) {
return
}
networkWaitingJob?.cancel()
networkWaitingJob = findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
networkState.awaitForConnection()
if (isFailed) {
reload()
}
}
}
}

View File

@@ -45,6 +45,7 @@ import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
import org.koitharu.kotatsu.scrobbling.common.domain.ScrobblerAuthRequiredException
import java.io.File
import java.net.ConnectException
import java.net.HttpURLConnection
import java.net.NoRouteToHostException
import java.net.SocketException
import java.net.SocketTimeoutException
@@ -216,6 +217,7 @@ fun Throwable.isNetworkError(): Boolean {
|| this is SocketTimeoutException
|| this is StreamResetException
|| this is SocketException
|| this is HttpException && response.code == HttpURLConnection.HTTP_GATEWAY_TIMEOUT
}
fun Throwable.report(silent: Boolean = false) {