From 3ea026e3116a77fd58bf656e1ecdb5e1ab6de28a Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 6 Jan 2024 18:15:17 -0500 Subject: [PATCH] Avoid hard crash if cached image file was already deleted Closes #9720 --- .../ui/reader/viewer/pager/PagerPageHolder.kt | 67 +++++++++++-------- .../viewer/webtoon/WebtoonPageHolder.kt | 51 ++++++++------ 2 files changed, 68 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt index 21424cbcf..2c81f1387 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt @@ -17,10 +17,12 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import logcat.LogPriority import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.util.system.logcat import java.io.BufferedInputStream import java.io.ByteArrayInputStream import java.io.InputStream @@ -136,40 +138,47 @@ class PagerPageHolder( val streamFn = page.stream ?: return - val (bais, isAnimated, background) = withIOContext { - streamFn().buffered(16).use { stream -> - process(item, stream).use { itemStream -> - val bais = ByteArrayInputStream(itemStream.readBytes()) - val isAnimated = ImageUtil.isAnimatedAndSupported(bais) - bais.reset() - val background = if (!isAnimated && viewer.config.automaticBackground) { - ImageUtil.chooseBackground(context, bais) - } else { - null + try { + val (bais, isAnimated, background) = withIOContext { + streamFn().buffered(16).use { stream -> + process(item, stream).use { itemStream -> + val bais = ByteArrayInputStream(itemStream.readBytes()) + val isAnimated = ImageUtil.isAnimatedAndSupported(bais) + bais.reset() + val background = if (!isAnimated && viewer.config.automaticBackground) { + ImageUtil.chooseBackground(context, bais) + } else { + null + } + bais.reset() + Triple(bais, isAnimated, background) } - bais.reset() - Triple(bais, isAnimated, background) } } - } - withUIContext { - bais.use { - setImage( - it, - isAnimated, - Config( - zoomDuration = viewer.config.doubleTapAnimDuration, - minimumScaleType = viewer.config.imageScaleType, - cropBorders = viewer.config.imageCropBorders, - zoomStartPosition = viewer.config.imageZoomType, - landscapeZoom = viewer.config.landscapeZoom, - ), - ) - if (!isAnimated) { - pageBackground = background + withUIContext { + bais.use { + setImage( + it, + isAnimated, + Config( + zoomDuration = viewer.config.doubleTapAnimDuration, + minimumScaleType = viewer.config.imageScaleType, + cropBorders = viewer.config.imageCropBorders, + zoomStartPosition = viewer.config.imageZoomType, + landscapeZoom = viewer.config.landscapeZoom, + ), + ) + if (!isAnimated) { + pageBackground = background + } } + removeErrorLayout() + } + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) + withUIContext { + setError() } - removeErrorLayout() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index b0f2fae72..b491a7363 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -23,10 +23,12 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.suspendCancellableCoroutine +import logcat.LogPriority import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.util.system.logcat import java.io.BufferedInputStream import java.io.InputStream @@ -184,28 +186,35 @@ class WebtoonPageHolder( val streamFn = page?.stream ?: return - val (openStream, isAnimated) = withIOContext { - val stream = streamFn().buffered(16) - val openStream = process(stream) + try { + val (openStream, isAnimated) = withIOContext { + val stream = streamFn().buffered(16) + val openStream = process(stream) - val isAnimated = ImageUtil.isAnimatedAndSupported(stream) - Pair(openStream, isAnimated) - } - withUIContext { - frame.setImage( - openStream, - isAnimated, - ReaderPageImageView.Config( - zoomDuration = viewer.config.doubleTapAnimDuration, - minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH, - cropBorders = viewer.config.imageCropBorders, - ), - ) - removeErrorLayout() - } - // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled - suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { openStream.close() } + val isAnimated = ImageUtil.isAnimatedAndSupported(stream) + Pair(openStream, isAnimated) + } + withUIContext { + frame.setImage( + openStream, + isAnimated, + ReaderPageImageView.Config( + zoomDuration = viewer.config.doubleTapAnimDuration, + minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH, + cropBorders = viewer.config.imageCropBorders, + ), + ) + removeErrorLayout() + } + // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { openStream.close() } + } + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) + withUIContext { + setError() + } } }