Surface image loading error in Reader (#1981)

(cherry picked from commit fefa8f84982b537ca930438f7976087844d5bb9c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
This commit is contained in:
AwkwardPeak7 2025-04-13 19:46:12 +05:00 committed by Jobobby04
parent 59887eed80
commit 746b1b051c
9 changed files with 49 additions and 32 deletions

View File

@ -383,7 +383,7 @@ class Downloader(
try { try {
page.imageUrl = download.source.getImageUrl(page) page.imageUrl = download.source.getImageUrl(page)
} catch (e: Throwable) { } catch (e: Throwable) {
page.status = Page.State.Error page.status = Page.State.Error(e)
} }
} }
@ -474,7 +474,7 @@ class Downloader(
if (e is CancellationException) throw e if (e is CancellationException) throw e
// Mark this page as error and allow to download the remaining // Mark this page as error and allow to download the remaining
page.progress = 0 page.progress = 0
page.status = Page.State.Error page.status = Page.State.Error(e)
notifier.onError(e.message, download.chapter.name, download.manga.title, download.manga.id) notifier.onError(e.message, download.chapter.name, download.manga.title, download.manga.id)
} }
} }

View File

@ -717,7 +717,7 @@ class ReaderActivity : BaseActivity() {
?.pages ?.pages
?.forEachIndexed { _, page -> ?.forEachIndexed { _, page ->
var shouldQueuePage = false var shouldQueuePage = false
if (page.status == Page.State.Error) { if (page.status is Page.State.Error) {
shouldQueuePage = true shouldQueuePage = true
} /*else if (page.status == Page.LOAD_PAGE || } /*else if (page.status == Page.LOAD_PAGE ||
page.status == Page.DOWNLOAD_IMAGE) { page.status == Page.DOWNLOAD_IMAGE) {
@ -758,7 +758,7 @@ class ReaderActivity : BaseActivity() {
return return
} }
if (curPage.status == Page.State.Error) { if (curPage.status is Page.State.Error) {
toast(SYMR.strings.eh_boost_page_errored) toast(SYMR.strings.eh_boost_page_errored)
} else if (curPage.status == Page.State.LoadPage || curPage.status == Page.State.DownloadImage) { } else if (curPage.status == Page.State.LoadPage || curPage.status == Page.State.DownloadImage) {
toast(SYMR.strings.eh_boost_page_downloading) toast(SYMR.strings.eh_boost_page_downloading)

View File

@ -697,7 +697,7 @@ class ReaderViewModel @JvmOverloads constructor(
readerChapter.requestedPage = pageIndex readerChapter.requestedPage = pageIndex
chapterPageIndex = pageIndex chapterPageIndex = pageIndex
if (!incognitoMode && page.status != Page.State.Error) { if (!incognitoMode && page.status is Page.State.Error) {
readerChapter.chapter.last_page_read = pageIndex readerChapter.chapter.last_page_read = pageIndex
// SY --> // SY -->

View File

@ -119,7 +119,7 @@ internal class HttpPageLoader(
} }
// Automatically retry failed pages when subscribed to this page // Automatically retry failed pages when subscribed to this page
if (page.status == Page.State.Error) { if (page.status is Page.State.Error) {
page.status = Page.State.Queue page.status = Page.State.Queue
} }
@ -144,7 +144,7 @@ internal class HttpPageLoader(
* Retries a page. This method is only called from user interaction on the viewer. * Retries a page. This method is only called from user interaction on the viewer.
*/ */
override fun retryPage(page: ReaderPage) { override fun retryPage(page: ReaderPage) {
if (page.status == Page.State.Error) { if (page.status is Page.State.Error) {
page.status = Page.State.Queue page.status = Page.State.Queue
} }
// EXH --> // EXH -->
@ -227,7 +227,7 @@ internal class HttpPageLoader(
page.stream = { chapterCache.getImageFile(imageUrl).inputStream() } page.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
page.status = Page.State.Ready page.status = Page.State.Ready
} catch (e: Throwable) { } catch (e: Throwable) {
page.status = Page.State.Error page.status = Page.State.Error(e)
if (e is CancellationException) { if (e is CancellationException) {
throw e throw e
} }

View File

@ -69,7 +69,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
private var config: Config? = null private var config: Config? = null
var onImageLoaded: (() -> Unit)? = null var onImageLoaded: (() -> Unit)? = null
var onImageLoadError: (() -> Unit)? = null var onImageLoadError: ((Throwable?) -> Unit)? = null
var onScaleChanged: ((newScale: Float) -> Unit)? = null var onScaleChanged: ((newScale: Float) -> Unit)? = null
var onViewClicked: (() -> Unit)? = null var onViewClicked: (() -> Unit)? = null
@ -85,8 +85,8 @@ open class ReaderPageImageView @JvmOverloads constructor(
} }
@CallSuper @CallSuper
open fun onImageLoadError() { open fun onImageLoadError(error: Throwable?) {
onImageLoadError?.invoke() onImageLoadError?.invoke(error)
} }
@CallSuper @CallSuper
@ -114,7 +114,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
} }
override fun onImageLoadError(e: Exception) { override fun onImageLoadError(e: Exception) {
onImageLoadError() onImageLoadError(e)
} }
}, },
) )
@ -290,7 +290,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
} }
override fun onImageLoadError(e: Exception) { override fun onImageLoadError(e: Exception) {
this@ReaderPageImageView.onImageLoadError() this@ReaderPageImageView.onImageLoadError(e)
} }
}, },
) )
@ -318,8 +318,10 @@ open class ReaderPageImageView @JvmOverloads constructor(
setImage(ImageSource.bitmap(image.bitmap)) setImage(ImageSource.bitmap(image.bitmap))
isVisible = true isVisible = true
}, },
onError = { )
onImageLoadError() .listener(
onError = { _, result ->
onImageLoadError(result.throwable)
}, },
) )
.size(ViewSizeResolver(this@ReaderPageImageView)) .size(ViewSizeResolver(this@ReaderPageImageView))
@ -395,8 +397,10 @@ open class ReaderPageImageView @JvmOverloads constructor(
isVisible = true isVisible = true
this@ReaderPageImageView.onImageLoaded() this@ReaderPageImageView.onImageLoaded()
}, },
onError = { )
this@ReaderPageImageView.onImageLoadError() .listener(
onError = { _, result ->
onImageLoadError(result.throwable)
}, },
) )
.crossfade(false) .crossfade(false)

View File

@ -5,6 +5,7 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.core.view.isVisible import androidx.core.view.isVisible
import eu.kanade.presentation.util.formattedMessage
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.InsertPage
@ -22,12 +23,14 @@ import kotlinx.coroutines.supervisorScope
import logcat.LogPriority import logcat.LogPriority
import okio.Buffer import okio.Buffer
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.decoder.ImageDecoder import tachiyomi.decoder.ImageDecoder
import tachiyomi.i18n.MR
import kotlin.math.max import kotlin.math.max
/** /**
@ -121,7 +124,7 @@ class PagerPageHolder(
} }
} }
Page.State.Ready -> setImage() Page.State.Ready -> setImage()
Page.State.Error -> setError() is Page.State.Error -> setError(state.error)
} }
} }
} }
@ -213,7 +216,7 @@ class PagerPageHolder(
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)
withUIContext { withUIContext {
setError() setError(e)
} }
} }
} }
@ -405,9 +408,9 @@ class PagerPageHolder(
/** /**
* Called when the page has an error. * Called when the page has an error.
*/ */
private fun setError() { private fun setError(error: Throwable?) {
progressIndicator?.hide() progressIndicator?.hide()
showErrorLayout() showErrorLayout(error)
} }
override fun onImageLoaded() { override fun onImageLoaded() {
@ -418,9 +421,9 @@ class PagerPageHolder(
/** /**
* Called when an image fails to decode. * Called when an image fails to decode.
*/ */
override fun onImageLoadError() { override fun onImageLoadError(error: Throwable?) {
super.onImageLoadError() super.onImageLoadError(error)
setError() setError(error)
} }
/** /**
@ -431,7 +434,7 @@ class PagerPageHolder(
viewer.activity.hideMenu() viewer.activity.hideMenu()
} }
private fun showErrorLayout(): ReaderErrorBinding { private fun showErrorLayout(error: Throwable?): ReaderErrorBinding {
if (errorLayout == null) { if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true) errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true)
errorLayout?.actionRetry?.viewer = viewer errorLayout?.actionRetry?.viewer = viewer
@ -452,6 +455,9 @@ class PagerPageHolder(
} }
} }
errorLayout?.errorMessage?.text = with(context) { error?.formattedMessage }
?: context.stringResource(MR.strings.decode_image_error)
errorLayout?.root?.isVisible = true errorLayout?.root?.isVisible = true
return errorLayout!! return errorLayout!!
} }

View File

@ -10,6 +10,7 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins import androidx.core.view.updateMargins
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import eu.kanade.presentation.util.formattedMessage
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
@ -25,11 +26,13 @@ import kotlinx.coroutines.supervisorScope
import logcat.LogPriority import logcat.LogPriority
import okio.Buffer import okio.Buffer
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.i18n.MR
/** /**
* Holder of the webtoon reader for a single page of a chapter. * Holder of the webtoon reader for a single page of a chapter.
@ -81,7 +84,7 @@ class WebtoonPageHolder(
refreshLayoutParams() refreshLayoutParams()
frame.onImageLoaded = { onImageDecoded() } frame.onImageLoaded = { onImageDecoded() }
frame.onImageLoadError = { setError() } frame.onImageLoadError = { error -> setError(error) }
frame.onScaleChanged = { viewer.activity.hideMenu() } frame.onScaleChanged = { viewer.activity.hideMenu() }
} }
@ -145,7 +148,7 @@ class WebtoonPageHolder(
} }
} }
Page.State.Ready -> setImage() Page.State.Ready -> setImage()
Page.State.Error -> setError() is Page.State.Error -> setError(state.error)
} }
} }
} }
@ -209,7 +212,7 @@ class WebtoonPageHolder(
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)
withUIContext { withUIContext {
setError() setError(e)
} }
} }
} }
@ -243,9 +246,9 @@ class WebtoonPageHolder(
/** /**
* Called when the page has an error. * Called when the page has an error.
*/ */
private fun setError() { private fun setError(error: Throwable?) {
progressContainer.isVisible = false progressContainer.isVisible = false
initErrorLayout() initErrorLayout(error)
} }
/** /**
@ -275,7 +278,7 @@ class WebtoonPageHolder(
/** /**
* Initializes a button to retry pages. * Initializes a button to retry pages.
*/ */
private fun initErrorLayout(): ReaderErrorBinding { private fun initErrorLayout(error: Throwable?): ReaderErrorBinding {
if (errorLayout == null) { if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true) errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true)
errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt()) errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt())
@ -295,6 +298,9 @@ class WebtoonPageHolder(
} }
} }
errorLayout?.errorMessage?.text = with(context) { error?.formattedMessage }
?: context.stringResource(MR.strings.decode_image_error)
return errorLayout!! return errorLayout!!
} }

View File

@ -7,6 +7,7 @@
android:gravity="center"> android:gravity="center">
<TextView <TextView
android:id="@+id/error_message"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"

View File

@ -54,6 +54,6 @@ open class Page(
data object LoadPage : State data object LoadPage : State
data object DownloadImage : State data object DownloadImage : State
data object Ready : State data object Ready : State
data object Error : State data class Error(val error: Throwable) : State
} }
} }