|
|
|
|
@ -1,13 +1,12 @@
|
|
|
|
|
package org.koitharu.kotatsu.core.ui.image
|
|
|
|
|
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.View.OnLayoutChangeListener
|
|
|
|
|
import android.view.ViewGroup
|
|
|
|
|
import android.view.ViewTreeObserver
|
|
|
|
|
import android.view.ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
import android.widget.ImageView
|
|
|
|
|
import coil3.size.Dimension
|
|
|
|
|
import coil3.size.Size
|
|
|
|
|
import coil3.size.ViewSizeResolver
|
|
|
|
|
import kotlinx.coroutines.CancellableContinuation
|
|
|
|
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
|
|
|
import kotlin.coroutines.resume
|
|
|
|
|
import kotlin.math.roundToInt
|
|
|
|
|
@ -20,31 +19,67 @@ class CoverSizeResolver(
|
|
|
|
|
) : ViewSizeResolver<ImageView> {
|
|
|
|
|
|
|
|
|
|
override suspend fun size(): Size {
|
|
|
|
|
// Fast path: the view is already measured.
|
|
|
|
|
getSize()?.let { return it }
|
|
|
|
|
return suspendCancellableCoroutine { cont ->
|
|
|
|
|
val layoutListener = LayoutListener(cont)
|
|
|
|
|
view.addOnLayoutChangeListener(layoutListener)
|
|
|
|
|
cont.invokeOnCancellation {
|
|
|
|
|
view.removeOnLayoutChangeListener(layoutListener)
|
|
|
|
|
|
|
|
|
|
// Slow path: wait for the view to be measured.
|
|
|
|
|
return suspendCancellableCoroutine { continuation ->
|
|
|
|
|
val viewTreeObserver = view.viewTreeObserver
|
|
|
|
|
|
|
|
|
|
val preDrawListener = object : OnPreDrawListener {
|
|
|
|
|
private var isResumed = false
|
|
|
|
|
|
|
|
|
|
override fun onPreDraw(): Boolean {
|
|
|
|
|
val size = getSize()
|
|
|
|
|
if (size != null) {
|
|
|
|
|
viewTreeObserver.removePreDrawListenerSafe(this)
|
|
|
|
|
|
|
|
|
|
if (!isResumed) {
|
|
|
|
|
isResumed = true
|
|
|
|
|
continuation.resume(size)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
viewTreeObserver.addOnPreDrawListener(preDrawListener)
|
|
|
|
|
|
|
|
|
|
continuation.invokeOnCancellation {
|
|
|
|
|
viewTreeObserver.removePreDrawListenerSafe(preDrawListener)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun getSize(): Size? {
|
|
|
|
|
val lp = view.layoutParams
|
|
|
|
|
var width = getDimension(lp.width, view.width, view.paddingLeft + view.paddingRight)
|
|
|
|
|
var height = getDimension(lp.height, view.height, view.paddingTop + view.paddingBottom)
|
|
|
|
|
if (width == null && height == null) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
if (height == null && width != null) {
|
|
|
|
|
height = Dimension((width.px * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).roundToInt())
|
|
|
|
|
} else if (width == null && height != null) {
|
|
|
|
|
width = Dimension((height.px * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).roundToInt())
|
|
|
|
|
var width = getWidth()
|
|
|
|
|
var height = getHeight()
|
|
|
|
|
when {
|
|
|
|
|
width == null && height == null -> {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
height == null && width != null -> {
|
|
|
|
|
height = Dimension((width.px * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).roundToInt())
|
|
|
|
|
}
|
|
|
|
|
width == null && height != null -> {
|
|
|
|
|
width = Dimension((height.px * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).roundToInt())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Size(checkNotNull(width), checkNotNull(height))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun getWidth() = getDimension(
|
|
|
|
|
paramSize = view.layoutParams?.width ?: -1,
|
|
|
|
|
viewSize = view.width,
|
|
|
|
|
paddingSize = if (subtractPadding) view.paddingLeft + view.paddingRight else 0
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private fun getHeight() = getDimension(
|
|
|
|
|
paramSize = view.layoutParams?.height ?: -1,
|
|
|
|
|
viewSize = view.height,
|
|
|
|
|
paddingSize = if (subtractPadding) view.paddingTop + view.paddingBottom else 0
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private fun getDimension(paramSize: Int, viewSize: Int, paddingSize: Int): Dimension.Pixels? {
|
|
|
|
|
if (paramSize == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
|
|
|
return null
|
|
|
|
|
@ -60,24 +95,11 @@ class CoverSizeResolver(
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private inner class LayoutListener(
|
|
|
|
|
private val continuation: CancellableContinuation<Size>,
|
|
|
|
|
) : OnLayoutChangeListener {
|
|
|
|
|
|
|
|
|
|
override fun onLayoutChange(
|
|
|
|
|
v: View,
|
|
|
|
|
left: Int,
|
|
|
|
|
top: Int,
|
|
|
|
|
right: Int,
|
|
|
|
|
bottom: Int,
|
|
|
|
|
oldLeft: Int,
|
|
|
|
|
oldTop: Int,
|
|
|
|
|
oldRight: Int,
|
|
|
|
|
oldBottom: Int,
|
|
|
|
|
) {
|
|
|
|
|
val size = getSize() ?: return
|
|
|
|
|
v.removeOnLayoutChangeListener(this)
|
|
|
|
|
continuation.resume(size)
|
|
|
|
|
private fun ViewTreeObserver.removePreDrawListenerSafe(victim: OnPreDrawListener) {
|
|
|
|
|
if (isAlive) {
|
|
|
|
|
removeOnPreDrawListener(victim)
|
|
|
|
|
} else {
|
|
|
|
|
view.viewTreeObserver.removeOnPreDrawListener(victim)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|