Fix hiding page loading indicator (close #1357)

master
Koitharu 1 year ago
parent 92f6221ba0
commit fe5d37f45e
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -1,28 +0,0 @@
package org.koitharu.kotatsu.core.util
import android.view.View
import android.view.ViewTreeObserver
/**
* ProgressIndicator become INVISIBLE instead of GONE by hide() call.
* It`s final so we need this workaround
*/
class GoneOnInvisibleListener(
private val view: View,
) : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (view.visibility == View.INVISIBLE) {
view.visibility = View.GONE
}
}
fun attach() {
view.viewTreeObserver.addOnGlobalLayoutListener(this)
onGlobalLayout()
}
fun detach() {
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}

@ -10,7 +10,6 @@ import androidx.appcompat.widget.ActionMenuView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.descendants import androidx.core.view.descendants
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -155,9 +154,9 @@ fun TabLayout.setTabsEnabled(enabled: Boolean) {
fun BaseProgressIndicator<*>.showOrHide(value: Boolean) { fun BaseProgressIndicator<*>.showOrHide(value: Boolean) {
if (value) { if (value) {
if (!isVisible) show() show()
} else { } else {
if (isVisible) hide() hide()
} }
} }

@ -1,6 +1,9 @@
package org.koitharu.kotatsu.reader.ui.pager package org.koitharu.kotatsu.reader.ui.pager
import android.content.ComponentCallbacks2
import android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.view.View import android.view.View
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.core.view.isGone import androidx.core.view.isGone
@ -21,7 +24,6 @@ import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.isLowRamDevice import org.koitharu.kotatsu.core.util.ext.isLowRamDevice
import org.koitharu.kotatsu.core.util.ext.isSerializable import org.koitharu.kotatsu.core.util.ext.isSerializable
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.showOrHide
import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding
import org.koitharu.kotatsu.parsers.util.ifZero import org.koitharu.kotatsu.parsers.util.ifZero
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
@ -37,7 +39,7 @@ abstract class BasePageHolder<B : ViewBinding>(
networkState: NetworkState, networkState: NetworkState,
exceptionResolver: ExceptionResolver, exceptionResolver: ExceptionResolver,
lifecycleOwner: LifecycleOwner, lifecycleOwner: LifecycleOwner,
) : LifecycleAwareViewHolder(binding.root, lifecycleOwner), DefaultOnImageEventListener { ) : LifecycleAwareViewHolder(binding.root, lifecycleOwner), DefaultOnImageEventListener, ComponentCallbacks2 {
protected val viewModel = PageViewModel( protected val viewModel = PageViewModel(
loader = loader, loader = loader,
@ -82,9 +84,12 @@ abstract class BasePageHolder<B : ViewBinding>(
@CallSuper @CallSuper
protected open fun onConfigChanged(settings: ReaderSettings) { protected open fun onConfigChanged(settings: ReaderSettings) {
settings.applyBackground(itemView) settings.applyBackground(itemView)
if (viewModel.state.value is PageState.Shown) { if (settings.applyBitmapConfig(ssiv)) {
reloadImage()
} else if (viewModel.state.value is PageState.Shown) {
onReady() onReady()
} }
ssiv.applyDownSampling(isResumed())
} }
fun reloadImage() { fun reloadImage() {
@ -103,7 +108,7 @@ abstract class BasePageHolder<B : ViewBinding>(
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
context.registerComponentCallbacks(viewModel) context.registerComponentCallbacks(this)
viewModel.state.observe(this, ::onStateChanged) viewModel.state.observe(this, ::onStateChanged)
viewModel.settingsProducer.observe(this, ::onConfigChanged) viewModel.settingsProducer.observe(this, ::onConfigChanged)
} }
@ -122,7 +127,7 @@ abstract class BasePageHolder<B : ViewBinding>(
} }
override fun onDestroy() { override fun onDestroy() {
context.unregisterComponentCallbacks(viewModel) context.unregisterComponentCallbacks(this)
super.onDestroy() super.onDestroy()
} }
@ -136,9 +141,18 @@ abstract class BasePageHolder<B : ViewBinding>(
ssiv.recycle() ssiv.recycle()
} }
override fun onTrimMemory(level: Int) {
// TODO
}
override fun onConfigurationChanged(newConfig: Configuration) = Unit
@Deprecated("Deprecated in Java")
final override fun onLowMemory() = onTrimMemory(TRIM_MEMORY_COMPLETE)
protected open fun onStateChanged(state: PageState) { protected open fun onStateChanged(state: PageState) {
bindingInfo.layoutError.isVisible = state is PageState.Error bindingInfo.layoutError.isVisible = state is PageState.Error
bindingInfo.progressBar.showOrHide(!state.isFinalState()) bindingInfo.progressBar.isGone = state.isFinalState()
bindingInfo.textViewStatus.isGone = state.isFinalState() bindingInfo.textViewStatus.isGone = state.isFinalState()
val progress = (state as? PageState.Loading)?.progress ?: -1 val progress = (state as? PageState.Loading)?.progress ?: -1
if (progress in 0..100) { if (progress in 0..100) {

@ -30,17 +30,12 @@ open class PageHolder(
networkState = networkState, networkState = networkState,
exceptionResolver = exceptionResolver, exceptionResolver = exceptionResolver,
lifecycleOwner = owner, lifecycleOwner = owner,
), ), ZoomControl.ZoomControlListener {
ZoomControl.ZoomControlListener {
override val ssiv = binding.ssiv override val ssiv = binding.ssiv
override fun onConfigChanged(settings: ReaderSettings) { override fun onConfigChanged(settings: ReaderSettings) {
super.onConfigChanged(settings) super.onConfigChanged(settings)
if (settings.applyBitmapConfig(binding.ssiv)) {
reloadImage()
}
binding.ssiv.applyDownSampling(isResumed())
binding.textViewNumber.isVisible = settings.isPagesNumbersEnabled binding.textViewNumber.isVisible = settings.isPagesNumbersEnabled
} }
@ -50,11 +45,6 @@ open class PageHolder(
binding.textViewNumber.text = (data.index + 1).toString() binding.textViewNumber.text = (data.index + 1).toString()
} }
override fun onRecycled() {
super.onRecycled()
binding.ssiv.recycle()
}
override fun onReady() { override fun onReady() {
binding.ssiv.maxScale = 2f * maxOf( binding.ssiv.maxScale = 2f * maxOf(
binding.ssiv.width / binding.ssiv.sWidth.toFloat(), binding.ssiv.width / binding.ssiv.sWidth.toFloat(),

@ -1,7 +1,5 @@
package org.koitharu.kotatsu.reader.ui.pager.vm package org.koitharu.kotatsu.reader.ui.pager.vm
import android.content.ComponentCallbacks2
import android.content.res.Configuration
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
@ -20,6 +18,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import okio.IOException import okio.IOException
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
@ -35,7 +34,7 @@ class PageViewModel(
private val networkState: NetworkState, private val networkState: NetworkState,
private val exceptionResolver: ExceptionResolver, private val exceptionResolver: ExceptionResolver,
private val isWebtoon: Boolean, private val isWebtoon: Boolean,
) : DefaultOnImageEventListener, ComponentCallbacks2 { ) : DefaultOnImageEventListener {
private val scope = loader.loaderScope + Dispatchers.Main.immediate private val scope = loader.loaderScope + Dispatchers.Main.immediate
private var job: Job? = null private var job: Job? = null
@ -43,12 +42,6 @@ class PageViewModel(
val state = MutableStateFlow<PageState>(PageState.Empty) val state = MutableStateFlow<PageState>(PageState.Empty)
init {
scope.launch(Dispatchers.Main) { // the same as post() -- wait until child fields init
// callback.onConfigChanged()
}
}
fun isLoading() = job?.isActive == true fun isLoading() = job?.isActive == true
fun onBind(page: MangaPage) { fun onBind(page: MangaPage) {
@ -64,13 +57,14 @@ class PageViewModel(
job = scope.launch { job = scope.launch {
prevJob?.cancelAndJoin() prevJob?.cancelAndJoin()
val e = (state.value as? PageState.Error)?.error val e = (state.value as? PageState.Error)?.error
if (e != null && ExceptionResolver.Companion.canResolve(e)) { if (e != null && ExceptionResolver.canResolve(e)) {
if (!isFromUser) { if (isFromUser) {
return@launch exceptionResolver.resolve(e)
} }
exceptionResolver.resolve(e)
} }
doLoad(page, force = true) withContext(Dispatchers.Default) {
doLoad(page, force = true)
}
} }
} }
@ -86,8 +80,12 @@ class PageViewModel(
} }
override fun onImageLoaded() { override fun onImageLoaded() {
state.update { state.update { currentState ->
if (it is PageState.Loaded) PageState.Shown(it.source, it.isConverted) else it if (currentState is PageState.Loaded) {
PageState.Shown(currentState.source, currentState.isConverted)
} else {
currentState
}
} }
} }
@ -109,18 +107,9 @@ class PageViewModel(
} }
} }
override fun onConfigurationChanged(newConfig: Configuration) = Unit
@Suppress("OVERRIDE_DEPRECATION")
override fun onLowMemory() = Unit
override fun onTrimMemory(level: Int) {
// callback.onTrimMemory()
}
private fun tryConvert(uri: Uri, e: Exception) { private fun tryConvert(uri: Uri, e: Exception) {
val prevJob = job val prevJob = job
job = scope.launch { job = scope.launch(Dispatchers.Default) {
prevJob?.join() prevJob?.join()
state.value = PageState.Converting() state.value = PageState.Converting()
try { try {
@ -143,7 +132,7 @@ class PageViewModel(
@WorkerThread @WorkerThread
private suspend fun doLoad(data: MangaPage, force: Boolean) = coroutineScope { private suspend fun doLoad(data: MangaPage, force: Boolean) = coroutineScope {
state.value = PageState.Loading(null/* TODO */, -1) state.value = PageState.Loading(null, -1)
val previewJob = launch { val previewJob = launch {
val preview = loader.loadPreview(data) ?: return@launch val preview = loader.loadPreview(data) ?: return@launch
state.update { state.update {

@ -1,9 +1,9 @@
package org.koitharu.kotatsu.reader.ui.pager.webtoon package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.view.View
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.GoneOnInvisibleListener
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@ -28,29 +28,9 @@ class WebtoonHolder(
override val ssiv = binding.ssiv override val ssiv = binding.ssiv
private var scrollToRestore = 0 private var scrollToRestore = 0
private val goneOnInvisibleListener = GoneOnInvisibleListener(bindingInfo.progressBar)
override fun onConfigChanged(settings: ReaderSettings) { init {
super.onConfigChanged(settings) bindingInfo.progressBar.setVisibilityAfterHide(View.GONE)
if (settings.applyBitmapConfig(binding.ssiv)) {
reloadImage()
}
binding.ssiv.applyDownSampling(isResumed())
}
override fun onRecycled() {
super.onRecycled()
binding.ssiv.recycle()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
goneOnInvisibleListener.attach()
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
goneOnInvisibleListener.detach()
} }
override fun onReady() { override fun onReady() {

Loading…
Cancel
Save