From aeee782512050d7553cc25d2da85c3f51b25fb81 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 22 Jun 2025 13:28:44 +0300 Subject: [PATCH] Retry thumbnails loading after network state changed --- .../kotatsu/core/image/CoilImageView.kt | 42 ++++++++++++++++++- .../kotatsu/core/util/ext/Throwable.kt | 2 + 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CoilImageView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CoilImageView.kt index fd17f2de6..452ce1c5d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CoilImageView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CoilImageView.kt @@ -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? = 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() + } + } + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 7447209cd..b07268125 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -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) {