Merge branch 'feature/percent' into devel
commit
6e324fd5ab
@ -1,12 +1,13 @@
|
||||
package org.koitharu.kotatsu.history.data
|
||||
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import java.util.*
|
||||
|
||||
fun HistoryEntity.toMangaHistory() = MangaHistory(
|
||||
createdAt = Date(createdAt),
|
||||
updatedAt = Date(updatedAt),
|
||||
chapterId = chapterId,
|
||||
page = page,
|
||||
scroll = scroll.toInt()
|
||||
scroll = scroll.toInt(),
|
||||
percent = percent,
|
||||
)
|
||||
@ -0,0 +1,151 @@
|
||||
package org.koitharu.kotatsu.history.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ReadingProgressDrawable(
|
||||
context: Context,
|
||||
@StyleRes styleResId: Int,
|
||||
) : Drawable() {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val checkDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_check)
|
||||
private val lineColor: Int
|
||||
private val outlineColor: Int
|
||||
private val backgroundColor: Int
|
||||
private val textColor: Int
|
||||
private val textPattern = context.getString(R.string.percent_string_pattern)
|
||||
private val textBounds = Rect()
|
||||
private val tempRect = Rect()
|
||||
private val hasBackground: Boolean
|
||||
private val hasOutline: Boolean
|
||||
private val hasText: Boolean
|
||||
private val desiredHeight: Int
|
||||
private val desiredWidth: Int
|
||||
private val autoFitTextSize: Boolean
|
||||
|
||||
var progress: Float = PROGRESS_NONE
|
||||
set(value) {
|
||||
field = value
|
||||
text = textPattern.format((value * 100f).toInt().toString())
|
||||
paint.getTextBounds(text, 0, text.length, textBounds)
|
||||
invalidateSelf()
|
||||
}
|
||||
private var text = ""
|
||||
|
||||
init {
|
||||
val ta = context.obtainStyledAttributes(styleResId, R.styleable.ProgressDrawable)
|
||||
desiredHeight = ta.getDimensionPixelSize(R.styleable.ProgressDrawable_android_height, -1)
|
||||
desiredWidth = ta.getDimensionPixelSize(R.styleable.ProgressDrawable_android_width, -1)
|
||||
autoFitTextSize = ta.getBoolean(R.styleable.ProgressDrawable_autoFitTextSize, false)
|
||||
lineColor = ta.getColor(R.styleable.ProgressDrawable_android_strokeColor, Color.BLACK)
|
||||
outlineColor = ta.getColor(R.styleable.ProgressDrawable_outlineColor, Color.TRANSPARENT)
|
||||
backgroundColor = ColorUtils.setAlphaComponent(
|
||||
ta.getColor(R.styleable.ProgressDrawable_android_fillColor, Color.TRANSPARENT),
|
||||
(255 * ta.getFloat(R.styleable.ProgressDrawable_android_fillAlpha, 0f)).toInt(),
|
||||
)
|
||||
textColor = ta.getColor(R.styleable.ProgressDrawable_android_textColor, lineColor)
|
||||
paint.strokeCap = Paint.Cap.ROUND
|
||||
paint.textAlign = Paint.Align.CENTER
|
||||
paint.textSize = ta.getDimension(R.styleable.ProgressDrawable_android_textSize, paint.textSize)
|
||||
paint.strokeWidth = ta.getDimension(R.styleable.ProgressDrawable_strokeWidth, 1f)
|
||||
ta.recycle()
|
||||
hasBackground = Color.alpha(backgroundColor) != 0
|
||||
hasOutline = Color.alpha(outlineColor) != 0
|
||||
hasText = Color.alpha(textColor) != 0 && paint.textSize > 0
|
||||
checkDrawable?.setTint(textColor)
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
super.onBoundsChange(bounds)
|
||||
if (autoFitTextSize) {
|
||||
val innerWidth = bounds.width() - (paint.strokeWidth * 2f)
|
||||
paint.textSize = getTextSizeForWidth(innerWidth, "100%")
|
||||
paint.getTextBounds(text, 0, text.length, textBounds)
|
||||
invalidateSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (progress < 0f) {
|
||||
return
|
||||
}
|
||||
val cx = bounds.exactCenterX()
|
||||
val cy = bounds.exactCenterY()
|
||||
val radius = minOf(bounds.width(), bounds.height()) / 2f
|
||||
if (hasBackground) {
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = backgroundColor
|
||||
canvas.drawCircle(cx, cy, radius, paint)
|
||||
}
|
||||
val innerRadius = radius - paint.strokeWidth / 2f
|
||||
paint.style = Paint.Style.STROKE
|
||||
if (hasOutline) {
|
||||
paint.color = outlineColor
|
||||
canvas.drawCircle(cx, cy, innerRadius, paint)
|
||||
}
|
||||
paint.color = lineColor
|
||||
canvas.drawArc(
|
||||
cx - innerRadius,
|
||||
cy - innerRadius,
|
||||
cx + innerRadius,
|
||||
cy + innerRadius,
|
||||
-90f,
|
||||
360f * progress,
|
||||
false,
|
||||
paint,
|
||||
)
|
||||
if (hasText) {
|
||||
if (checkDrawable != null && progress >= 1f - Math.ulp(progress)) {
|
||||
tempRect.set(bounds)
|
||||
tempRect *= 0.6
|
||||
checkDrawable.bounds = tempRect
|
||||
checkDrawable.draw(canvas)
|
||||
} else {
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = textColor
|
||||
val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom
|
||||
canvas.drawText(text, cx, ty, paint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
paint.alpha = alpha
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
paint.colorFilter = colorFilter
|
||||
}
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOpacity() = PixelFormat.TRANSLUCENT
|
||||
|
||||
override fun getIntrinsicHeight() = desiredHeight
|
||||
|
||||
override fun getIntrinsicWidth() = desiredWidth
|
||||
|
||||
private fun getTextSizeForWidth(width: Float, text: String): Float {
|
||||
val testTextSize = 48f
|
||||
paint.textSize = testTextSize
|
||||
paint.getTextBounds(text, 0, text.length, tempRect)
|
||||
return testTextSize * width / tempRect.width()
|
||||
}
|
||||
|
||||
private operator fun Rect.timesAssign(factor: Double) {
|
||||
val newWidth = (width() * factor).roundToInt()
|
||||
val newHeight = (height() * factor).roundToInt()
|
||||
inset(
|
||||
(width() - newWidth) / 2,
|
||||
(height() - newHeight) / 2,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
package org.koitharu.kotatsu.history.ui.util
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Outline
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StyleRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
|
||||
class ReadingProgressView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@AttrRes defStyleAttr: Int = 0,
|
||||
) : View(context, attrs, defStyleAttr), ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
|
||||
|
||||
private var percentAnimator: ValueAnimator? = null
|
||||
private val animationDuration = context.resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
|
||||
|
||||
@StyleRes
|
||||
private val drawableStyle: Int
|
||||
|
||||
var percent: Float
|
||||
get() = peekProgressDrawable()?.progress ?: PROGRESS_NONE
|
||||
set(value) {
|
||||
cancelAnimation()
|
||||
getProgressDrawable().progress = value
|
||||
}
|
||||
|
||||
init {
|
||||
val ta = context.obtainStyledAttributes(attrs, R.styleable.ReadingProgressView, defStyleAttr, 0)
|
||||
drawableStyle = ta.getResourceId(R.styleable.ReadingProgressView_progressStyle, R.style.ProgressDrawable)
|
||||
ta.recycle()
|
||||
outlineProvider = OutlineProvider()
|
||||
if (isInEditMode) {
|
||||
percent = 0.27f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
percentAnimator?.run {
|
||||
if (isRunning) end()
|
||||
}
|
||||
percentAnimator = null
|
||||
}
|
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
val p = animation.animatedValue as Float
|
||||
getProgressDrawable().progress = p
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator?) = Unit
|
||||
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
if (percentAnimator === animation) {
|
||||
percentAnimator = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animation: Animator?) = Unit
|
||||
|
||||
override fun onAnimationRepeat(animation: Animator?) = Unit
|
||||
|
||||
fun setPercent(value: Float, animate: Boolean) {
|
||||
val currentDrawable = peekProgressDrawable()
|
||||
if (!animate || currentDrawable == null || value == PROGRESS_NONE) {
|
||||
percent = value
|
||||
return
|
||||
}
|
||||
percentAnimator?.cancel()
|
||||
percentAnimator = ValueAnimator.ofFloat(
|
||||
currentDrawable.progress.coerceAtLeast(0f),
|
||||
value
|
||||
).apply {
|
||||
duration = animationDuration
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
addUpdateListener(this@ReadingProgressView)
|
||||
addListener(this@ReadingProgressView)
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelAnimation() {
|
||||
percentAnimator?.cancel()
|
||||
percentAnimator = null
|
||||
}
|
||||
|
||||
private fun peekProgressDrawable(): ReadingProgressDrawable? {
|
||||
return background as? ReadingProgressDrawable
|
||||
}
|
||||
|
||||
private fun getProgressDrawable(): ReadingProgressDrawable {
|
||||
var d = peekProgressDrawable()
|
||||
if (d != null) {
|
||||
return d
|
||||
}
|
||||
d = ReadingProgressDrawable(context, drawableStyle)
|
||||
background = d
|
||||
return d
|
||||
}
|
||||
|
||||
private class OutlineProvider : ViewOutlineProvider() {
|
||||
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setOval(0, 0, view.width, view.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
package org.koitharu.kotatsu.list.domain
|
||||
|
||||
fun interface CountersProvider {
|
||||
interface ListExtraProvider {
|
||||
|
||||
suspend fun getCounter(mangaId: Long): Int
|
||||
|
||||
suspend fun getProgress(mangaId: Long): Float
|
||||
}
|
||||
Loading…
Reference in New Issue