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 a9f63775e2..6cc0e4b416 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 @@ -20,11 +20,9 @@ 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 tachiyomi.core.util.lang.launchIO +import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.util.lang.withUIContext import java.io.BufferedInputStream import java.io.ByteArrayInputStream import java.io.InputStream @@ -66,12 +64,6 @@ class PagerPageHolder( */ private var loadJob: 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() } @@ -85,7 +77,6 @@ class PagerPageHolder( super.onDetachedFromWindow() loadJob?.cancel() loadJob = null - unsubscribeReadImageHeader() } /** @@ -119,14 +110,6 @@ class PagerPageHolder( } } - /** - * Unsubscribes from the read image header subscription. - */ - private fun unsubscribeReadImageHeader() { - readImageHeaderSubscription?.unsubscribe() - readImageHeaderSubscription = null - } - /** * Called when the page is queued. */ @@ -154,19 +137,16 @@ class PagerPageHolder( /** * Called when the page is ready. */ - private fun setImage() { + private suspend fun setImage() { progressIndicator.setProgress(0) errorLayout?.root?.isVisible = false - unsubscribeReadImageHeader() val streamFn = page.stream ?: return - readImageHeaderSubscription = Observable - .fromCallable { - val stream = streamFn().buffered(16) - val itemStream = process(item, stream) - val bais = ByteArrayInputStream(itemStream.readBytes()) - 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) { @@ -176,32 +156,27 @@ class PagerPageHolder( } 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 - } + } + 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 } } - .subscribe({}, {}) + } } 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 394f44d484..cbded3e7a2 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 9112fc559b..fda21b8a24 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, - ), - ) - } - // 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, + ), + ) + } + // 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 e25aab8a72..037d64e316 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 @@ -74,11 +73,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr */ private var currentPage: Any? = null - /** - * Subscriptions to keep while this viewer is used. - */ - val subscriptions = CompositeSubscription() - private val threshold: Int = Injekt.get() .readerHideThreshold() @@ -196,7 +190,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr override fun destroy() { super.destroy() scope.cancel() - subscriptions.unsubscribe() } /**