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