Fix recycled item's height being 0 in webtoon mode

Refactor reader page holder and miscellaneous cleanups
This commit is contained in:
FooIbar committed 2024-03-22 22:10:00 +08:00
1 parent 73afe05a18
commit 1e97407936
5 files changed
+45 -188

No files matched your search

@@ -1336,8 +1336,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
}
/**
* Add a listener allowing notification of load and error events. Extend [DefaultOnImageEventListener]
* to simplify implementation.
* Add a listener allowing notification of load and error events.
*
* @param onImageEventListener an [OnImageEventListener] instance.
*/
@@ -1391,7 +1390,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
/**
* An event listener, allowing subclasses and activities to be notified of significant events.
*/
interface OnImageEventListener {
fun interface OnImageEventListener {
/**
* Called when the dimensions of the image and view are known, and either a preview image,
* the full size image, or base layer tiles are loaded. This indicates the scale and translate
@@ -1399,16 +1398,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
* graphic, or inform a subclass that it is safe to draw overlays.
*/
fun onReady()
/**
* Indicates an error initiliasing the decoder when using a tiling, or when loading the full
* size bitmap when tiling is disabled. This method cannot be relied upon; certain encoding
* types of supported image formats can result in corrupt or blank images being loaded and
* displayed with no detectable error.
*
* @param e The exception thrown. This error is also logged by the view.
*/
fun onImageLoadError(e: Exception)
}
/**
@@ -1467,14 +1456,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
private data class ScaleAndTranslate(var scale: Float, val vTranslate: PointF)
/**
* Default implementation of [OnImageEventListener] for extension. This does nothing in any method.
*/
open class DefaultOnImageEventListener : OnImageEventListener {
override fun onReady() {}
override fun onImageLoadError(e: Exception) {}
}
/**
* Builder class used to set additional options for a scale animation. Create an instance using [.animateScale],
* then set your options and call [.start].
@@ -53,9 +53,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
private var config: Config? = null
var onImageLoaded: (() -> Unit)? = null
var onImageLoadError: (() -> Unit)? = null
var onScaleChanged: ((newScale: Float) -> Unit)? = null
var onViewClicked: (() -> Unit)? = null
/**
* For automatic background. Will be set as background color when [onImageLoaded] is called.
@@ -68,40 +66,22 @@ open class ReaderPageImageView @JvmOverloads constructor(
background = pageBackground
}
@CallSuper
open fun onImageLoadError() {
onImageLoadError?.invoke()
}
@CallSuper
open fun onScaleChanged(newScale: Float) {
onScaleChanged?.invoke(newScale)
}
@CallSuper
open fun onViewClicked() {
onViewClicked?.invoke()
}
open fun onPageSelected(forward: Boolean) {
with(pageView as? SubsamplingScaleImageView) {
if (this == null) return
if (isReady) {
landscapeZoom(forward)
} else {
setOnImageEventListener(
object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
override fun onReady() {
setupZoom(config)
landscapeZoom(forward)
this@ReaderPageImageView.onImageLoaded()
}
override fun onImageLoadError(e: Exception) {
onImageLoadError()
}
},
)
setOnImageEventListener {
setupZoom(config)
landscapeZoom(forward)
this@ReaderPageImageView.onImageLoaded()
}
}
}
}
@@ -221,7 +201,6 @@ open class ReaderPageImageView @JvmOverloads constructor(
}
},
)
setOnClickListener { this@ReaderPageImageView.onViewClicked() }
}
addView(pageView, MATCH_PARENT, MATCH_PARENT)
}
@@ -248,19 +227,11 @@ open class ReaderPageImageView @JvmOverloads constructor(
setMinimumScaleType(config.minimumScaleType)
setMinimumDpi(1) // Just so that very small image will be fit for initial load
setCropBorders(config.cropBorders)
setOnImageEventListener(
object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
override fun onReady() {
setupZoom(config)
if (isVisibleOnScreen()) landscapeZoom(true)
this@ReaderPageImageView.onImageLoaded()
}
override fun onImageLoadError(e: Exception) {
this@ReaderPageImageView.onImageLoadError()
}
},
)
setOnImageEventListener {
setupZoom(config)
if (isVisibleOnScreen()) landscapeZoom(true)
this@ReaderPageImageView.onImageLoaded()
}
setImage(image, bounds)
isVisible = true
@@ -292,11 +263,6 @@ open class ReaderPageImageView @JvmOverloads constructor(
}
return true
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
this@ReaderPageImageView.onViewClicked()
return super.onSingleTapConfirmed(e)
}
},
)
setOnScaleChangeListener { _, _, _ ->
@@ -2,15 +2,16 @@ package eu.kanade.tachiyomi.ui.reader.viewer
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import androidx.annotation.IntRange
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.view.isVisible
@@ -23,7 +24,6 @@ class ReaderProgressIndicator @JvmOverloads constructor(
) : AbstractComposeView(context, attrs, defStyleAttr) {
init {
layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER)
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
}
@@ -33,7 +33,9 @@ class ReaderProgressIndicator @JvmOverloads constructor(
@Composable
override fun Content() {
EhTheme {
if (visible) CombinedCircularProgressIndicator(progress = progress)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
if (visible) CombinedCircularProgressIndicator(progress = progress)
}
}
}
@@ -47,6 +49,12 @@ class ReaderProgressIndicator @JvmOverloads constructor(
isVisible = false
}
fun reset() {
progress = 0f
visible = false
isVisible = true
}
fun setProgress(@IntRange(from = 0, to = 100) progress: Int) {
this.progress = progress / 100f
}
@@ -49,11 +49,6 @@ class PagerPageHolder(
*/
private var statusJob: Job? = null
/**
* Job for progress changes of the page.
*/
private var progressJob: Job? = null
init {
addView(progressIndicator)
statusJob = scope.launch(Dispatchers.Main) {
@@ -69,39 +64,25 @@ class PagerPageHolder(
@SuppressLint("ClickableViewAccessibility")
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
cancelProgressJob()
unsubscribeStatus()
progressIndicator.hide()
}
private fun launchProgressJob() {
progressJob?.cancel()
progressJob = scope.launch(Dispatchers.Main) {
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
}
}
/**
* Called when the status of the page changes.
*
* @param status the new status of the page.
*/
private fun processStatus(status: Page.State) {
private suspend fun processStatus(status: Page.State) {
when (status) {
Page.State.QUEUE -> setQueued()
Page.State.LOAD_PAGE -> setLoading()
Page.State.DOWNLOAD_IMAGE -> {
launchProgressJob()
setDownloading()
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
}
Page.State.READY -> {
page.image?.let { setImage(it) }
cancelProgressJob()
}
Page.State.ERROR -> {
setError()
cancelProgressJob()
}
Page.State.READY -> page.image?.let { setImage(it) } ?: setError()
Page.State.ERROR -> setError()
}
}
@@ -113,11 +94,6 @@ class PagerPageHolder(
statusJob = null
}
private fun cancelProgressJob() {
progressJob?.cancel()
progressJob = null
}
/**
* Called when the page is queued.
*/
@@ -149,8 +125,6 @@ class PagerPageHolder(
*/
private fun setImage(image: Image) {
progressIndicator.setProgress(0)
progressIndicator.hide()
removeErrorLayout()
setImage(
image,
Config(
@@ -177,15 +151,7 @@ class PagerPageHolder(
override fun onImageLoaded() {
super.onImageLoaded()
progressIndicator.hide()
}
/**
* Called when an image fails to decode.
*/
override fun onImageLoadError() {
super.onImageLoadError()
progressIndicator.hide()
showErrorLayout()
removeErrorLayout()
}
/**
@@ -1,11 +1,9 @@
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.content.res.Resources
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import androidx.core.view.isVisible
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.hippo.ehviewer.image.Image
import eu.kanade.tachiyomi.source.model.Page
@@ -34,14 +32,10 @@ class WebtoonPageHolder(
/**
* Loading progress bar to indicate the current progress.
* Needed to keep a minimum height size of the holder, otherwise the adapter would create more
* views to fill the screen, which is not wanted.
*/
private val progressIndicator = createProgressIndicator()
/**
* Progress bar container. Needed to keep a minimum height size of the holder, otherwise the
* adapter would create more views to fill the screen, which is not wanted.
*/
private lateinit var progressContainer: ViewGroup
private val progressIndicator = ReaderProgressIndicator(context)
/**
* Error layout to show when the image fails to load.
@@ -57,7 +51,7 @@ class WebtoonPageHolder(
/**
* Page of a chapter.
*/
private var page: ReaderPage? = null
private lateinit var page: ReaderPage
private val scope = CoroutineScope(Dispatchers.IO)
@@ -66,16 +60,11 @@ class WebtoonPageHolder(
*/
private var statusJob: Job? = null
/**
* Job for progress changes of the page.
*/
private var progressJob: Job? = null
init {
frame.addView(progressIndicator, MATCH_PARENT, defaultHeight)
refreshLayoutParams()
frame.onImageLoaded = { onImageDecoded() }
frame.onImageLoadError = { onImageDecodeError() }
frame.onScaleChanged = { viewer.activity.hideMenu() }
}
@@ -110,25 +99,11 @@ class WebtoonPageHolder(
*/
override fun recycle() {
statusJob?.cancel()
cancelProgressJob()
progressIndicator.hide()
statusJob = null
removeErrorLayout()
frame.recycle()
progressIndicator.setProgress(0)
}
/**
* Observes the progress of the page and updates view.
*/
private fun launchProgressJob() {
cancelProgressJob()
val page = page ?: return
progressJob = scope.launch(Dispatchers.Main) {
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
}
progressIndicator.reset()
}
/**
@@ -136,38 +111,23 @@ class WebtoonPageHolder(
*
* @param status the new status of the page.
*/
private fun processStatus(status: Page.State) {
private suspend fun processStatus(status: Page.State) {
when (status) {
Page.State.QUEUE -> setQueued()
Page.State.LOAD_PAGE -> setLoading()
Page.State.DOWNLOAD_IMAGE -> {
launchProgressJob()
setDownloading()
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
}
Page.State.READY -> {
page?.image?.let { setImage(it) }
cancelProgressJob()
}
Page.State.ERROR -> {
setError()
cancelProgressJob()
}
Page.State.READY -> page.image?.let { setImage(it) } ?: setError()
Page.State.ERROR -> setError()
}
}
/**
* Unsubscribes from the progress subscription.
*/
private fun cancelProgressJob() {
progressJob?.cancel()
progressJob = null
}
/**
* Called when the page is queued.
*/
private fun setQueued() {
progressContainer.isVisible = true
progressIndicator.setProgress(0)
progressIndicator.show()
removeErrorLayout()
@@ -178,7 +138,6 @@ class WebtoonPageHolder(
* Called when the page is loading.
*/
private fun setLoading() {
progressContainer.isVisible = true
progressIndicator.show()
removeErrorLayout()
}
@@ -187,7 +146,6 @@ class WebtoonPageHolder(
* Called when the page is downloading
*/
private fun setDownloading() {
progressContainer.isVisible = true
progressIndicator.show()
removeErrorLayout()
}
@@ -197,8 +155,6 @@ class WebtoonPageHolder(
*/
private fun setImage(drawable: Image) {
progressIndicator.setProgress(0)
progressIndicator.hide()
removeErrorLayout()
frame.setImage(
drawable,
ReaderPageImageView.Config(
@@ -214,7 +170,6 @@ class WebtoonPageHolder(
*/
private fun setError() {
progressIndicator.hide()
progressContainer.isVisible = false
initErrorLayout()
}
@@ -222,27 +177,8 @@ class WebtoonPageHolder(
* Called when the image is decoded and going to be displayed.
*/
private fun onImageDecoded() {
progressContainer.isVisible = false
}
/**
* Called when the image fails to decode.
*/
private fun onImageDecodeError() {
progressContainer.isVisible = false
initErrorLayout()
}
/**
* Creates a new progress bar.
*/
private fun createProgressIndicator(): ReaderProgressIndicator {
progressContainer = FrameLayout(context)
frame.addView(progressContainer, MATCH_PARENT, defaultHeight)
val progress = ReaderProgressIndicator(context)
progressContainer.addView(progress)
return progress
progressIndicator.hide()
removeErrorLayout()
}
/**
@@ -250,8 +186,8 @@ class WebtoonPageHolder(
*/
private fun initErrorLayout() {
if (errorLayout == null) {
errorLayout = ReaderErrorLayout(context, page!!.errorMsg) {
viewer.activity.retryPage(page!!.index)
errorLayout = ReaderErrorLayout(context, page.errorMsg) {
viewer.activity.retryPage(page.index)
}.also {
frame.addView(it)
}