ReaderProgressIndicator: Convert to Compose (#9574)

This commit is contained in:
Ivan Iskandar 2023-06-04 00:11:41 +07:00 committed by GitHub
parent 0d96791a84
commit 39e4568460
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 81 deletions

View file

@ -15,7 +15,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.view.Gravity
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
@ -24,7 +23,6 @@ import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.FrameLayout
import android.widget.Toast
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
@ -36,7 +34,6 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.internal.ToolbarUtils
@ -69,7 +66,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.preference.toggle
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.toShareIntent
@ -660,12 +656,7 @@ class ReaderActivity : BaseActivity() {
supportActionBar?.title = manga.title
val loadingIndicatorContext = createReaderThemeContext()
loadingIndicator = ReaderProgressIndicator(loadingIndicatorContext).apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER
}
}
loadingIndicator = ReaderProgressIndicator(this)
binding.readerContainer.addView(loadingIndicator)
startPostponedEnterTransition()

View file

@ -2,13 +2,21 @@ 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.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import android.widget.FrameLayout
import androidx.annotation.IntRange
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.view.isVisible
import com.google.android.material.progressindicator.CircularProgressIndicator
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.components.CombinedCircularProgressIndicator
/**
* A wrapper for [CircularProgressIndicator] that always rotates.
@ -19,76 +27,31 @@ class ReaderProgressIndicator @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr) {
private val indicator: CircularProgressIndicator
private val rotateAnimation by lazy {
RotateAnimation(
0F,
360F,
Animation.RELATIVE_TO_SELF,
0.5F,
Animation.RELATIVE_TO_SELF,
0.5F,
).apply {
interpolator = LinearInterpolator()
repeatCount = Animation.INFINITE
duration = 4000
}
}
) : AbstractComposeView(context, attrs, defStyleAttr) {
init {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
indicator = CircularProgressIndicator(context)
indicator.max = 100
indicator.isIndeterminate = true
addView(indicator)
layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER)
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
updateRotateAnimation()
}
private var progress by mutableFloatStateOf(0f)
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
updateRotateAnimation()
@Composable
override fun Content() {
TachiyomiTheme {
CombinedCircularProgressIndicator(progress = progress)
}
}
fun show() {
indicator.show()
updateRotateAnimation()
isVisible = true
}
fun hide() {
indicator.hide()
updateRotateAnimation()
isVisible = false
}
/**
* Sets the current indicator progress to the specified value.
*
* @param progress Indicator will be set indeterminate if this value is 0
*/
fun setProgress(@IntRange(from = 0, to = 100) progress: Int, animated: Boolean = true) {
if (progress > 0) {
indicator.setProgressCompat(progress, animated)
} else if (!indicator.isIndeterminate) {
indicator.hide()
indicator.isIndeterminate = true
indicator.show()
}
updateRotateAnimation()
}
private fun updateRotateAnimation() {
if (isAttachedToWindow && indicator.isShown && !indicator.isIndeterminate) {
if (animation == null) {
startAnimation(rotateAnimation)
}
} else {
clearAnimation()
}
fun setProgress(@IntRange(from = 0, to = 100) progress: Int) {
this.progress = progress / 100f
}
}

View file

@ -2,10 +2,8 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint
import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
@ -46,11 +44,7 @@ class PagerPageHolder(
/**
* Loading progress bar to indicate the current progress.
*/
private val progressIndicator: ReaderProgressIndicator = ReaderProgressIndicator(readerThemedContext).apply {
updateLayoutParams<LayoutParams> {
gravity = Gravity.CENTER
}
}
private val progressIndicator: ReaderProgressIndicator = ReaderProgressIndicator(readerThemedContext)
/**
* Error layout to show when the image fails to load.

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.content.res.Resources
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
@ -119,7 +118,7 @@ class WebtoonPageHolder(
removeErrorLayout()
frame.recycle()
progressIndicator.setProgress(0, animated = false)
progressIndicator.setProgress(0)
}
/**
@ -288,7 +287,6 @@ class WebtoonPageHolder(
val progress = ReaderProgressIndicator(context).apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER_HORIZONTAL
updateMargins(top = parentHeight / 4)
}
}

View file

@ -0,0 +1,110 @@
package tachiyomi.presentation.core.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.tooling.preview.Preview
/**
* A combined [CircularProgressIndicator] that always rotates.
*
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
*/
@Composable
fun CombinedCircularProgressIndicator(
progress: Float,
) {
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
label = "progress",
)
AnimatedContent(
targetState = progress == 0f,
transitionSpec = { fadeIn() togetherWith fadeOut() },
label = "progressState",
) { indeterminate ->
if (indeterminate) {
// Indeterminate
CircularProgressIndicator()
} else {
// Determinate
val infiniteTransition = rememberInfiniteTransition(label = "infiniteRotation")
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
repeatMode = RepeatMode.Restart,
),
label = "rotation",
)
CircularProgressIndicator(
progress = animatedProgress,
modifier = Modifier.rotate(rotation),
)
}
}
}
@Preview
@Composable
private fun CombinedCircularProgressIndicatorPreview() {
var progress by remember { mutableFloatStateOf(0f) }
MaterialTheme {
Scaffold(
bottomBar = {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
progress = when (progress) {
0f -> 0.15f
0.15f -> 0.25f
0.25f -> 0.5f
0.5f -> 0.75f
0.75f -> 0.95f
else -> 0f
}
},
) {
Text("change")
}
},
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.padding(it),
) {
CombinedCircularProgressIndicator(progress = progress)
}
}
}
}