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 c5ecf3b0a..592a8a08f 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 @@ -22,11 +22,9 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.system.logcat import tachiyomi.decoder.ImageDecoder import java.io.BufferedInputStream @@ -77,12 +75,6 @@ class PagerPageHolder( */ private var extraLoadJob: Job? = null - /** - * Subscription used to read the header of the image. This is needed in order to instantiate - * the appropiate image view depending if the image is animated (GIF). - */ - private var readImageHeaderSubscription: Subscription? = null - init { addView(progressIndicator) loadJob = scope.launch { loadPageAndProcessStatus(1) } @@ -99,7 +91,6 @@ class PagerPageHolder( loadJob = null extraLoadJob?.cancel() extraLoadJob = null - unsubscribeReadImageHeader() } /** @@ -136,14 +127,6 @@ class PagerPageHolder( } } - /** - * Unsubscribes from the read image header subscription. - */ - private fun unsubscribeReadImageHeader() { - readImageHeaderSubscription?.unsubscribe() - readImageHeaderSubscription = null - } - /** * Called when the page is queued. */ @@ -171,7 +154,7 @@ class PagerPageHolder( /** * Called when the page is ready. */ - private fun setImage() { + private suspend fun setImage() { if (extraPage == null) { progressIndicator.setProgress(0) } else { @@ -179,58 +162,52 @@ class PagerPageHolder( } errorLayout?.root?.isVisible = false - unsubscribeReadImageHeader() val streamFn = page.stream ?: return val streamFn2 = extraPage?.stream - readImageHeaderSubscription = Observable - .fromCallable { - val stream = streamFn().buffered(16) + + val (bais, isAnimated, background) = withIOContext { + streamFn().buffered(16).use { stream -> // SY --> - val stream2 = if (extraPage != null) streamFn2?.invoke()?.buffered(16) else null - val itemStream = if (viewer.config.dualPageSplit) { - process(item.first, stream) - } else { - mergePages(stream, stream2) - } - // SY <-- - val bais = ByteArrayInputStream(itemStream.readBytes()) - try { - val isAnimated = ImageUtil.isAnimatedAndSupported(bais) - bais.reset() - val background = if (!isAnimated && viewer.config.automaticBackground) { - ImageUtil.chooseBackground(context, bais) + (if (extraPage != null) streamFn2?.invoke()?.buffered(16) else null).use { stream2 -> + if (viewer.config.dualPageSplit) { + process(item.first, stream) } else { - null - } - bais.reset() - Triple(bais, isAnimated, background) - } finally { - stream.close() - itemStream.close() - } - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { (bais, isAnimated, background) -> - 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 + mergePages(stream, stream2) + }.use { itemStream -> + // SY <-- + 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) } } } - .subscribe({}, {}) + } + 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 + } + } + } } private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonBaseHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonBaseHolder.kt index 394f44d48..cbded3e7a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonBaseHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonBaseHolder.kt @@ -4,7 +4,6 @@ import android.content.Context import android.view.View import android.view.ViewGroup.LayoutParams import androidx.recyclerview.widget.RecyclerView -import rx.Subscription abstract class WebtoonBaseHolder( view: View, @@ -21,21 +20,6 @@ abstract class WebtoonBaseHolder( */ open fun recycle() {} - /** - * Adds a subscription to a list of subscriptions that will automatically unsubscribe when the - * activity or the reader is destroyed. - */ - protected fun addSubscription(subscription: Subscription?) { - viewer.subscriptions.add(subscription) - } - - /** - * Removes a subscription from the list of subscriptions. - */ - protected fun removeSubscription(subscription: Subscription?) { - subscription?.let { viewer.subscriptions.remove(it) } - } - /** * Extension method to set layout params to wrap content on this view. */ 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 eed0bb6d0..1ca082154 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 @@ -25,11 +25,10 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers +import kotlinx.coroutines.suspendCancellableCoroutine import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.util.lang.withUIContext import java.io.BufferedInputStream import java.io.InputStream @@ -79,12 +78,6 @@ class WebtoonPageHolder( */ private var loadJob: Job? = null - /** - * Subscription used to read the header of the image. This is needed in order to instantiate - * the appropriate image view depending if the image is animated (GIF). - */ - private var readImageHeaderSubscription: Subscription? = null - init { refreshLayoutParams() @@ -121,7 +114,6 @@ class WebtoonPageHolder( override fun recycle() { loadJob?.cancel() loadJob = null - unsubscribeReadImageHeader() removeErrorLayout() frame.recycle() @@ -159,14 +151,6 @@ class WebtoonPageHolder( } } - /** - * Unsubscribes from the read image header subscription. - */ - private fun unsubscribeReadImageHeader() { - removeSubscription(readImageHeaderSubscription) - readImageHeaderSubscription = null - } - /** * Called when the page is queued. */ @@ -197,40 +181,34 @@ class WebtoonPageHolder( /** * Called when the page is ready. */ - private fun setImage() { + private suspend fun setImage() { progressIndicator.setProgress(0) removeErrorLayout() - unsubscribeReadImageHeader() val streamFn = page?.stream ?: return - var openStream: InputStream? = null - readImageHeaderSubscription = Observable - .fromCallable { - val stream = streamFn().buffered(16) - openStream = process(stream) + val (openStream, isAnimated) = withIOContext { + val stream = streamFn().buffered(16) + val openStream = process(stream) - ImageUtil.isAnimatedAndSupported(stream) - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { isAnimated -> - frame.setImage( - openStream!!, - isAnimated, - ReaderPageImageView.Config( - zoomDuration = viewer.config.doubleTapAnimDuration, - minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH, - cropBorders = (viewer.config.imageCropBorders && viewer.isContinuous) || (viewer.config.continuousCropBorders && !viewer.isContinuous), - ), - ) - } - // Keep the Rx stream alive to close the input stream only when unsubscribed - .flatMap { Observable.never() } - .doOnUnsubscribe { openStream?.close() } - .subscribe({}, {}) - - addSubscription(readImageHeaderSubscription) + 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 && viewer.isContinuous) || (viewer.config.continuousCropBorders && !viewer.isContinuous), + ), + ) + } + // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { openStream.close() } + } } private fun process(imageStream: BufferedInputStream): InputStream { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 53e0a0271..a688315b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel -import rx.subscriptions.CompositeSubscription import tachiyomi.core.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -75,11 +74,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr /* [EXH] private */ var currentPage: Any? = null - /** - * Subscriptions to keep while this viewer is used. - */ - val subscriptions = CompositeSubscription() - private val threshold: Int = Injekt.get() .readerHideThreshold() @@ -203,7 +197,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr override fun destroy() { super.destroy() scope.cancel() - subscriptions.unsubscribe() } /**