Track Page progress with StateFlow (#8749)
* Update ReaderProgressIndicator documentation
ReaderProgressIndicator is not always determinate (cc554530
, #5605).
* Track Page progress with StateFlow
This commit is contained in:
parent
e20c66b156
commit
593172f891
4 changed files with 54 additions and 50 deletions
|
@ -11,10 +11,9 @@ import androidx.annotation.IntRange
|
|||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
|
||||
/**
|
||||
* A wrapper for [CircularProgressIndicator] that always rotates while being determinate.
|
||||
* A wrapper for [CircularProgressIndicator] that always rotates.
|
||||
*
|
||||
* By always rotating we give the feedback to the user that the application isn't 'stuck',
|
||||
* and by making it determinate the user also approximately knows how much the operation will take.
|
||||
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
|
||||
*/
|
||||
class ReaderProgressIndicator @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
|
|
@ -13,8 +13,13 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
|||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
|
@ -22,7 +27,6 @@ import rx.schedulers.Schedulers
|
|||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* View of the ViewPager that contains a page of a chapter.
|
||||
|
@ -54,15 +58,17 @@ class PagerPageHolder(
|
|||
*/
|
||||
private var errorLayout: ReaderErrorBinding? = null
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* Subscription for status changes of the page.
|
||||
*/
|
||||
private var statusSubscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* Subscription for progress changes of the page.
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressSubscription: Subscription? = null
|
||||
private var progressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription used to read the header of the image. This is needed in order to instantiate
|
||||
|
@ -81,7 +87,7 @@ class PagerPageHolder(
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
unsubscribeStatus()
|
||||
unsubscribeReadImageHeader()
|
||||
}
|
||||
|
@ -100,18 +106,11 @@ class PagerPageHolder(
|
|||
.subscribe { processStatus(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the progress of the page and updates view.
|
||||
*/
|
||||
private fun observeProgress() {
|
||||
progressSubscription?.unsubscribe()
|
||||
|
||||
progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||
.map { page.progress }
|
||||
.distinctUntilChanged()
|
||||
.onBackpressureLatest()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { value -> progressIndicator.setProgress(value) }
|
||||
private fun launchProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = scope.launchUI {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,16 +123,16 @@ class PagerPageHolder(
|
|||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
observeProgress()
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
}
|
||||
Page.State.READY -> {
|
||||
setImage()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,12 +145,9 @@ class PagerPageHolder(
|
|||
statusSubscription = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from the progress subscription.
|
||||
*/
|
||||
private fun unsubscribeProgress() {
|
||||
progressSubscription?.unsubscribe()
|
||||
progressSubscription = null
|
||||
private fun cancelProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,15 +18,19 @@ import eu.kanade.tachiyomi.ui.reader.model.StencilPage
|
|||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Holder of the webtoon reader for a single page of a chapter.
|
||||
|
@ -67,15 +71,17 @@ class WebtoonPageHolder(
|
|||
*/
|
||||
private var page: ReaderPage? = null
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* Subscription for status changes of the page.
|
||||
*/
|
||||
private var statusSubscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* Subscription for progress changes of the page.
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressSubscription: Subscription? = null
|
||||
private var progressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription used to read the header of the image. This is needed in order to instantiate
|
||||
|
@ -117,7 +123,7 @@ class WebtoonPageHolder(
|
|||
*/
|
||||
override fun recycle() {
|
||||
unsubscribeStatus()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
unsubscribeReadImageHeader()
|
||||
|
||||
removeErrorLayout()
|
||||
|
@ -145,19 +151,14 @@ class WebtoonPageHolder(
|
|||
/**
|
||||
* Observes the progress of the page and updates view.
|
||||
*/
|
||||
private fun observeProgress() {
|
||||
unsubscribeProgress()
|
||||
private fun launchProgressJob() {
|
||||
cancelProgressJob()
|
||||
|
||||
val page = page ?: return
|
||||
|
||||
progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||
.map { page.progress }
|
||||
.distinctUntilChanged()
|
||||
.onBackpressureLatest()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { value -> progressIndicator.setProgress(value) }
|
||||
|
||||
addSubscription(progressSubscription)
|
||||
progressJob = scope.launchUI {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,16 +171,16 @@ class WebtoonPageHolder(
|
|||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
observeProgress()
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
}
|
||||
Page.State.READY -> {
|
||||
setImage()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,9 +196,9 @@ class WebtoonPageHolder(
|
|||
/**
|
||||
* Unsubscribes from the progress subscription.
|
||||
*/
|
||||
private fun unsubscribeProgress() {
|
||||
removeSubscription(progressSubscription)
|
||||
progressSubscription = null
|
||||
private fun cancelProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.source.model
|
|||
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.network.ProgressListener
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import rx.subjects.Subject
|
||||
|
@ -26,8 +28,14 @@ open class Page(
|
|||
}
|
||||
|
||||
@Transient
|
||||
@Volatile
|
||||
var progress: Int = 0
|
||||
private val _progressFlow = MutableStateFlow(0)
|
||||
@Transient
|
||||
val progressFlow = _progressFlow.asStateFlow()
|
||||
var progress: Int
|
||||
get() = _progressFlow.value
|
||||
set(value) {
|
||||
_progressFlow.value = value
|
||||
}
|
||||
|
||||
@Transient
|
||||
var statusSubject: Subject<State, State>? = null
|
||||
|
|
Reference in a new issue