Improve drawable and views state management
This commit is contained in:
@@ -14,7 +14,6 @@ import com.google.android.material.animation.ArgbEvaluatorCompat
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.util.KotatsuColors
|
||||
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
||||
import org.koitharu.kotatsu.core.util.ext.mangaSourceKey
|
||||
import kotlin.math.abs
|
||||
@@ -29,8 +28,8 @@ class AnimatedFaviconDrawable(
|
||||
private val period = context.getAnimationDuration(R.integer.config_longAnimTime) * 2
|
||||
private val timeAnimator = TimeAnimator()
|
||||
|
||||
private val colorHigh = MaterialColors.harmonize(KotatsuColors.random(name), colorBackground)
|
||||
private val colorLow = ArgbEvaluatorCompat.getInstance().evaluate(0.3f, colorHigh, colorBackground)
|
||||
private var colorHigh = MaterialColors.harmonize(colorForeground, currentBackgroundColor)
|
||||
private var colorLow = ArgbEvaluatorCompat.getInstance().evaluate(0.3f, colorHigh, currentBackgroundColor)
|
||||
|
||||
init {
|
||||
timeAnimator.setTimeListener(this)
|
||||
@@ -49,6 +48,8 @@ class AnimatedFaviconDrawable(
|
||||
|
||||
override fun getAlpha(): Int = 255
|
||||
|
||||
override fun isOpaque(): Boolean = false
|
||||
|
||||
override fun onTimeUpdate(animation: TimeAnimator?, totalTime: Long, deltaTime: Long) {
|
||||
callback?.also {
|
||||
updateColor()
|
||||
@@ -66,13 +67,21 @@ class AnimatedFaviconDrawable(
|
||||
|
||||
override fun isRunning(): Boolean = timeAnimator.isStarted
|
||||
|
||||
override fun onStateChange(state: IntArray): Boolean {
|
||||
val res = super.onStateChange(state)
|
||||
colorHigh = MaterialColors.harmonize(currentForegroundColor, currentBackgroundColor)
|
||||
colorLow = ArgbEvaluatorCompat.getInstance().evaluate(0.3f, colorHigh, currentBackgroundColor)
|
||||
updateColor()
|
||||
return res
|
||||
}
|
||||
|
||||
private fun updateColor() {
|
||||
if (period <= 0f) {
|
||||
return
|
||||
}
|
||||
val ph = period / 2
|
||||
val fraction = abs((System.currentTimeMillis() % period) - ph) / ph.toFloat()
|
||||
colorForeground = ArgbEvaluatorCompat.getInstance()
|
||||
currentForegroundColor = ArgbEvaluatorCompat.getInstance()
|
||||
.evaluate(interpolator.getInterpolation(fraction), colorLow, colorHigh)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,14 @@ class AnimatedPlaceholderDrawable(context: Context) : Drawable(), Animatable, Ti
|
||||
// this.alpha = alpha FIXME coil's crossfade
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOpacity(): Int = PixelFormat.OPAQUE
|
||||
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
|
||||
|
||||
override fun getAlpha(): Int = 255
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
|
||||
|
||||
override fun onTimeUpdate(animation: TimeAnimator?, totalTime: Long, deltaTime: Long) {
|
||||
callback?.also {
|
||||
updateColor()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package org.koitharu.kotatsu.core.ui.image
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import androidx.core.graphics.withClip
|
||||
@@ -21,18 +21,24 @@ import com.google.android.material.color.MaterialColors
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.util.KotatsuColors
|
||||
import org.koitharu.kotatsu.core.util.ext.hasFocusStateSpecified
|
||||
import org.koitharu.kotatsu.core.util.ext.mangaSourceKey
|
||||
|
||||
open class FaviconDrawable(
|
||||
context: Context,
|
||||
@StyleRes styleResId: Int,
|
||||
name: String,
|
||||
) : Drawable() {
|
||||
) : PaintDrawable() {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
protected var colorBackground = Color.WHITE
|
||||
override val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_FLAG)
|
||||
protected var currentBackgroundColor = Color.WHITE
|
||||
private set
|
||||
private var colorBackground: ColorStateList = ColorStateList.valueOf(currentBackgroundColor)
|
||||
protected var colorForeground = Color.DKGRAY
|
||||
private var colorStroke = Color.LTGRAY
|
||||
protected var currentForegroundColor = Color.DKGRAY
|
||||
protected var currentStrokeColor = Color.LTGRAY
|
||||
private set
|
||||
private var colorStroke: ColorStateList = ColorStateList.valueOf(currentStrokeColor)
|
||||
private val letter = name.take(1).uppercase()
|
||||
private var cornerSize = 0f
|
||||
private var intrinsicSize = -1
|
||||
@@ -43,15 +49,16 @@ open class FaviconDrawable(
|
||||
|
||||
init {
|
||||
context.withStyledAttributes(styleResId, R.styleable.FaviconFallbackDrawable) {
|
||||
colorBackground = getColor(R.styleable.FaviconFallbackDrawable_backgroundColor, colorBackground)
|
||||
colorStroke = getColor(R.styleable.FaviconFallbackDrawable_strokeColor, colorStroke)
|
||||
colorBackground = getColorStateList(R.styleable.FaviconFallbackDrawable_backgroundColor) ?: colorBackground
|
||||
colorStroke = getColorStateList(R.styleable.FaviconFallbackDrawable_strokeColor) ?: colorStroke
|
||||
cornerSize = getDimension(R.styleable.FaviconFallbackDrawable_cornerSize, cornerSize)
|
||||
paint.strokeWidth = getDimension(R.styleable.FaviconFallbackDrawable_strokeWidth, 0f) * 2f
|
||||
intrinsicSize = getDimensionPixelSize(R.styleable.FaviconFallbackDrawable_drawableSize, intrinsicSize)
|
||||
}
|
||||
paint.textAlign = Paint.Align.CENTER
|
||||
paint.isFakeBoldText = true
|
||||
colorForeground = MaterialColors.harmonize(KotatsuColors.random(name), colorBackground)
|
||||
colorForeground = KotatsuColors.random(name)
|
||||
currentForegroundColor = MaterialColors.harmonize(colorForeground, colorBackground.defaultColor)
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
@@ -75,35 +82,42 @@ open class FaviconDrawable(
|
||||
clipPath.close()
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
paint.alpha = alpha
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
paint.colorFilter = colorFilter
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int = intrinsicSize
|
||||
|
||||
override fun getIntrinsicHeight(): Int = intrinsicSize
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOpacity() = PixelFormat.TRANSPARENT
|
||||
override fun isOpaque(): Boolean = cornerSize == 0f && colorBackground.isOpaque
|
||||
|
||||
override fun isStateful(): Boolean = colorStroke.isStateful || colorBackground.isStateful
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
override fun hasFocusStateSpecified(): Boolean =
|
||||
colorBackground.hasFocusStateSpecified() || colorStroke.hasFocusStateSpecified()
|
||||
|
||||
override fun onStateChange(state: IntArray): Boolean {
|
||||
val prevStrokeColor = currentStrokeColor
|
||||
val prevBackgroundColor = currentBackgroundColor
|
||||
currentStrokeColor = colorStroke.getColorForState(state, colorStroke.defaultColor)
|
||||
currentBackgroundColor = colorBackground.getColorForState(state, colorBackground.defaultColor)
|
||||
if (currentBackgroundColor != prevBackgroundColor) {
|
||||
currentForegroundColor = MaterialColors.harmonize(colorForeground, currentBackgroundColor)
|
||||
}
|
||||
return prevBackgroundColor != currentBackgroundColor || prevStrokeColor != currentStrokeColor
|
||||
}
|
||||
|
||||
private fun doDraw(canvas: Canvas) {
|
||||
// background
|
||||
paint.color = colorBackground
|
||||
paint.color = currentBackgroundColor
|
||||
paint.style = Paint.Style.FILL
|
||||
canvas.drawPaint(paint)
|
||||
// letter
|
||||
paint.color = colorForeground
|
||||
paint.color = currentForegroundColor
|
||||
val cx = (boundsF.left + boundsF.right) * 0.6f
|
||||
val ty = boundsF.bottom * 0.7f + textBounds.height() * 0.5f - textBounds.bottom
|
||||
canvas.drawText(letter, cx, ty, paint)
|
||||
if (paint.strokeWidth > 0f) {
|
||||
// stroke
|
||||
paint.color = colorStroke
|
||||
paint.color = currentStrokeColor
|
||||
paint.style = Paint.Style.STROKE
|
||||
canvas.drawPath(clipPath, paint)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.koitharu.kotatsu.core.ui.image
|
||||
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
abstract class PaintDrawable : Drawable() {
|
||||
|
||||
protected abstract val paint: Paint
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
paint.alpha = alpha
|
||||
}
|
||||
|
||||
override fun getAlpha(): Int {
|
||||
return paint.alpha
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
paint.colorFilter = colorFilter
|
||||
}
|
||||
|
||||
override fun getColorFilter(): ColorFilter? {
|
||||
return paint.colorFilter
|
||||
}
|
||||
|
||||
override fun setDither(dither: Boolean) {
|
||||
paint.isDither = dither
|
||||
}
|
||||
|
||||
override fun setFilterBitmap(filter: Boolean) {
|
||||
paint.isFilterBitmap = filter
|
||||
}
|
||||
|
||||
override fun isFilterBitmap(): Boolean {
|
||||
return paint.isFilterBitmap
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
if (paint.colorFilter != null) {
|
||||
return PixelFormat.TRANSLUCENT
|
||||
}
|
||||
return when (paint.alpha) {
|
||||
0 -> PixelFormat.TRANSPARENT
|
||||
255 -> if (isOpaque()) PixelFormat.OPAQUE else PixelFormat.TRANSLUCENT
|
||||
else -> PixelFormat.TRANSLUCENT
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun isOpaque() = false
|
||||
}
|
||||
@@ -3,20 +3,20 @@ package org.koitharu.kotatsu.core.ui.image
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.graphics.PaintCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.hasFocusStateSpecified
|
||||
|
||||
class TextDrawable(
|
||||
val text: String,
|
||||
) : Drawable() {
|
||||
) : PaintDrawable() {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_FLAG)
|
||||
override val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_FLAG)
|
||||
private val textBounds = Rect()
|
||||
private val textPoint = PointF()
|
||||
|
||||
@@ -41,20 +41,6 @@ class TextDrawable(
|
||||
canvas.drawText(text, textPoint.x, textPoint.y, paint)
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
paint.alpha = alpha
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
paint.setColorFilter(colorFilter)
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int = when (paint.alpha) {
|
||||
0 -> PixelFormat.TRANSPARENT
|
||||
255 -> PixelFormat.OPAQUE
|
||||
else -> PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
textPoint.set(
|
||||
bounds.exactCenterX() - textBounds.exactCenterX(),
|
||||
@@ -66,16 +52,10 @@ class TextDrawable(
|
||||
|
||||
override fun getIntrinsicHeight(): Int = textBounds.height()
|
||||
|
||||
override fun setDither(dither: Boolean) {
|
||||
paint.isDither = dither
|
||||
}
|
||||
|
||||
override fun isStateful(): Boolean = textColor.isStateful
|
||||
|
||||
override fun hasFocusStateSpecified(): Boolean = textColor.getColorForState(
|
||||
intArrayOf(android.R.attr.state_focused),
|
||||
textColor.defaultColor,
|
||||
) != textColor.defaultColor
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
override fun hasFocusStateSpecified(): Boolean = textColor.hasFocusStateSpecified()
|
||||
|
||||
override fun onStateChange(state: IntArray): Boolean {
|
||||
val prevColor = paint.color
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.core.ui.widgets
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
@@ -11,7 +12,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList
|
||||
import org.koitharu.kotatsu.core.util.ext.measureDimension
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.parsers.util.toIntUp
|
||||
@@ -30,6 +31,7 @@ class DotsIndicator @JvmOverloads constructor(
|
||||
private var smallDotAlpha = 0.6f
|
||||
private var positionOffset: Float = 0f
|
||||
private var position: Int = 0
|
||||
private var dotsColor: ColorStateList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
private val inset = context.resources.resolveDp(1f)
|
||||
|
||||
var max: Int = 6
|
||||
@@ -52,10 +54,10 @@ class DotsIndicator @JvmOverloads constructor(
|
||||
init {
|
||||
paint.style = Paint.Style.FILL
|
||||
context.withStyledAttributes(attrs, R.styleable.DotsIndicator, defStyleAttr) {
|
||||
paint.color = getColor(
|
||||
R.styleable.DotsIndicator_dotColor,
|
||||
context.getThemeColor(materialR.attr.colorOnBackground, Color.DKGRAY),
|
||||
)
|
||||
dotsColor = getColorStateList(R.styleable.DotsIndicator_dotColor)
|
||||
?: context.getThemeColorStateList(materialR.attr.colorOnBackground)
|
||||
?: dotsColor
|
||||
paint.color = dotsColor.defaultColor
|
||||
indicatorSize = getDimension(R.styleable.DotsIndicator_dotSize, indicatorSize)
|
||||
dotSpacing = getDimension(R.styleable.DotsIndicator_dotSpacing, dotSpacing)
|
||||
smallDotScale = getFloat(R.styleable.DotsIndicator_dotScale, smallDotScale).coerceIn(0f, 1f)
|
||||
@@ -89,6 +91,13 @@ class DotsIndicator @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun drawableStateChanged() {
|
||||
if (dotsColor.isStateful) {
|
||||
paint.color = dotsColor.getColorForState(drawableState, dotsColor.defaultColor)
|
||||
}
|
||||
super.drawableStateChanged()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val dotSize = getDotSize()
|
||||
val desiredHeight = (dotSize + paddingTop + paddingBottom).toIntUp()
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@Deprecated("")
|
||||
class ProgressButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
|
||||
@@ -2,13 +2,13 @@ package org.koitharu.kotatsu.core.util
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.widget.EditText
|
||||
import androidx.annotation.CallSuper
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
abstract class EditTextValidator : TextWatcher {
|
||||
abstract class EditTextValidator : DefaultTextWatcher {
|
||||
|
||||
private var editTextRef: WeakReference<EditText>? = null
|
||||
|
||||
@@ -17,10 +17,6 @@ abstract class EditTextValidator : TextWatcher {
|
||||
"EditTextValidator is not attached to EditText"
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
@CallSuper
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val editText = editTextRef?.get() ?: return
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.util.ext
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Rect
|
||||
import kotlin.math.roundToInt
|
||||
@@ -18,3 +19,7 @@ inline fun <R> Bitmap.use(block: (Bitmap) -> R) = try {
|
||||
} finally {
|
||||
recycle()
|
||||
}
|
||||
|
||||
fun ColorStateList.hasFocusStateSpecified(): Boolean {
|
||||
return getColorForState(intArrayOf(android.R.attr.state_focused), defaultColor) != defaultColor
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.filter.ui.tags
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@@ -17,14 +16,19 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback
|
||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.SheetTagsBinding
|
||||
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
|
||||
import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TagsCatalogSheet : BaseAdaptiveSheet<SheetTagsBinding>(), OnListItemClickListener<TagCatalogItem>, TextWatcher,
|
||||
AdaptiveSheetCallback, View.OnFocusChangeListener, TextView.OnEditorActionListener {
|
||||
class TagsCatalogSheet : BaseAdaptiveSheet<SheetTagsBinding>(),
|
||||
OnListItemClickListener<TagCatalogItem>,
|
||||
DefaultTextWatcher,
|
||||
AdaptiveSheetCallback,
|
||||
View.OnFocusChangeListener,
|
||||
TextView.OnEditorActionListener {
|
||||
|
||||
private val viewModel by viewModels<TagsCatalogViewModel>(
|
||||
extrasProducer = {
|
||||
@@ -75,10 +79,6 @@ class TagsCatalogSheet : BaseAdaptiveSheet<SheetTagsBinding>(), OnListItemClickL
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val q = s?.toString().orEmpty()
|
||||
viewModel.searchQuery.value = q
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package org.koitharu.kotatsu.history.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.image.PaintDrawable
|
||||
import org.koitharu.kotatsu.core.util.ext.hasFocusStateSpecified
|
||||
import org.koitharu.kotatsu.core.util.ext.scale
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress.Companion.PROGRESS_NONE
|
||||
@@ -19,23 +20,29 @@ import org.koitharu.kotatsu.list.domain.ReadingProgress.Companion.PROGRESS_NONE
|
||||
class ReadingProgressDrawable(
|
||||
context: Context,
|
||||
@StyleRes styleResId: Int,
|
||||
) : Drawable() {
|
||||
) : PaintDrawable() {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
override val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_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 lineColor: ColorStateList
|
||||
private val outlineColor: ColorStateList
|
||||
private val backgroundColor: ColorStateList
|
||||
private val textColor: ColorStateList
|
||||
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
|
||||
|
||||
private var currentLineColor: Int = Color.TRANSPARENT
|
||||
private var currentOutlineColor: Int = Color.TRANSPARENT
|
||||
private var currentBackgroundColor: Int = Color.TRANSPARENT
|
||||
private var currentTextColor: Int = Color.TRANSPARENT
|
||||
private var hasBackground: Boolean = false
|
||||
private var hasOutline: Boolean = false
|
||||
private var hasText: Boolean = false
|
||||
|
||||
|
||||
var percent: Float = PROGRESS_NONE
|
||||
set(value) {
|
||||
field = value
|
||||
@@ -54,22 +61,21 @@ class ReadingProgressDrawable(
|
||||
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(),
|
||||
lineColor = ta.getColorStateList(R.styleable.ProgressDrawable_android_strokeColor) ?: ColorStateList.valueOf(
|
||||
Color.BLACK,
|
||||
)
|
||||
textColor = ta.getColor(R.styleable.ProgressDrawable_android_textColor, lineColor)
|
||||
outlineColor =
|
||||
ta.getColorStateList(R.styleable.ProgressDrawable_outlineColor) ?: ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
backgroundColor = ta.getColorStateList(R.styleable.ProgressDrawable_android_fillColor)?.withAlpha(
|
||||
(255 * ta.getFloat(R.styleable.ProgressDrawable_android_fillAlpha, 0f)).toInt(),
|
||||
) ?: ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
textColor = ta.getColorStateList(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)
|
||||
checkDrawable?.setTintList(textColor)
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
@@ -91,16 +97,16 @@ class ReadingProgressDrawable(
|
||||
val radius = minOf(bounds.width(), bounds.height()) / 2f
|
||||
if (hasBackground) {
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = backgroundColor
|
||||
paint.color = currentBackgroundColor
|
||||
canvas.drawCircle(cx, cy, radius, paint)
|
||||
}
|
||||
val innerRadius = radius - paint.strokeWidth / 2f
|
||||
paint.style = Paint.Style.STROKE
|
||||
if (hasOutline) {
|
||||
paint.color = outlineColor
|
||||
paint.color = currentOutlineColor
|
||||
canvas.drawCircle(cx, cy, innerRadius, paint)
|
||||
}
|
||||
paint.color = lineColor
|
||||
paint.color = currentLineColor
|
||||
canvas.drawArc(
|
||||
cx - innerRadius,
|
||||
cy - innerRadius,
|
||||
@@ -119,29 +125,49 @@ class ReadingProgressDrawable(
|
||||
checkDrawable.draw(canvas)
|
||||
} else {
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = textColor
|
||||
paint.color = currentTextColor
|
||||
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
|
||||
|
||||
override fun isStateful(): Boolean = lineColor.isStateful ||
|
||||
outlineColor.isStateful ||
|
||||
backgroundColor.isStateful ||
|
||||
textColor.isStateful ||
|
||||
checkDrawable?.isStateful == true
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
override fun hasFocusStateSpecified(): Boolean = lineColor.hasFocusStateSpecified() ||
|
||||
outlineColor.hasFocusStateSpecified() ||
|
||||
backgroundColor.hasFocusStateSpecified() ||
|
||||
textColor.hasFocusStateSpecified() ||
|
||||
checkDrawable?.hasFocusStateSpecified() == true
|
||||
|
||||
override fun onStateChange(state: IntArray): Boolean {
|
||||
val prevLineColor = currentLineColor
|
||||
currentLineColor = lineColor.getColorForState(state, lineColor.defaultColor)
|
||||
val prevOutlineColor = currentOutlineColor
|
||||
currentOutlineColor = outlineColor.getColorForState(state, outlineColor.defaultColor)
|
||||
val prevBackgroundColor = currentBackgroundColor
|
||||
currentBackgroundColor = backgroundColor.getColorForState(state, backgroundColor.defaultColor)
|
||||
val prevTextColor = currentTextColor
|
||||
currentTextColor = textColor.getColorForState(state, textColor.defaultColor)
|
||||
hasBackground = Color.alpha(currentBackgroundColor) != 0
|
||||
hasOutline = Color.alpha(currentOutlineColor) != 0
|
||||
hasText = Color.alpha(currentTextColor) != 0 && paint.textSize > 0
|
||||
return checkDrawable?.setState(state) == true ||
|
||||
prevLineColor != currentLineColor ||
|
||||
prevOutlineColor != currentOutlineColor ||
|
||||
prevBackgroundColor != currentBackgroundColor ||
|
||||
prevTextColor != currentTextColor
|
||||
}
|
||||
|
||||
private fun getTextSizeForWidth(width: Float, text: String): Float {
|
||||
val testTextSize = 48f
|
||||
paint.textSize = testTextSize
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
@@ -21,6 +20,7 @@ import com.google.android.material.textfield.TextInputLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
@@ -32,7 +32,7 @@ import com.google.android.material.R as materialR
|
||||
class ProtectActivity :
|
||||
BaseActivity<ActivityProtectBinding>(),
|
||||
TextView.OnEditorActionListener,
|
||||
TextWatcher,
|
||||
DefaultTextWatcher,
|
||||
View.OnClickListener {
|
||||
|
||||
private val viewModel by viewModels<ProtectViewModel>()
|
||||
@@ -98,10 +98,6 @@ class ProtectActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewBinding.layoutPassword.error = null
|
||||
viewBinding.buttonNext.isEnabled = !s.isNullOrEmpty()
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
@@ -19,12 +20,11 @@ import android.view.WindowInsets
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.withScale
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList
|
||||
import org.koitharu.kotatsu.core.util.ext.measureDimension
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
@@ -41,7 +41,7 @@ class ReaderInfoBarView @JvmOverloads constructor(
|
||||
@AttrRes defStyleAttr: Int = 0,
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_FLAG)
|
||||
private val textBounds = Rect()
|
||||
private val timeFormat = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||
private val systemStateReceiver = SystemStateReceiver()
|
||||
@@ -52,16 +52,16 @@ class ReaderInfoBarView @JvmOverloads constructor(
|
||||
private val insetRightFallback: Int
|
||||
private val insetTopFallback: Int
|
||||
private val insetCornerFallback = getSystemUiDimensionOffset("rounded_corner_content_padding")
|
||||
private val colorText = ColorUtils.setAlphaComponent(
|
||||
context.getThemeColor(materialR.attr.colorOnSurface, Color.BLACK),
|
||||
200,
|
||||
)
|
||||
private val colorOutline = ColorUtils.setAlphaComponent(
|
||||
context.getThemeColor(materialR.attr.colorSurface, Color.WHITE),
|
||||
200,
|
||||
)
|
||||
private val colorText =
|
||||
(context.getThemeColorStateList(materialR.attr.colorOnSurface)
|
||||
?: ColorStateList.valueOf(Color.BLACK)).withAlpha(200)
|
||||
private val colorOutline =
|
||||
(context.getThemeColorStateList(materialR.attr.colorSurface)
|
||||
?: ColorStateList.valueOf(Color.WHITE)).withAlpha(200)
|
||||
private val batteryIcon = ContextCompat.getDrawable(context, R.drawable.ic_battery_outline)
|
||||
|
||||
private var currentTextColor: Int = Color.TRANSPARENT
|
||||
private var currentOutlineColor: Int = Color.TRANSPARENT
|
||||
private var timeText = timeFormat.format(LocalTime.now())
|
||||
private var batteryText = ""
|
||||
private var text: String = ""
|
||||
@@ -169,6 +169,29 @@ class ReaderInfoBarView @JvmOverloads constructor(
|
||||
context.unregisterReceiver(systemStateReceiver)
|
||||
}
|
||||
|
||||
override fun verifyDrawable(who: Drawable): Boolean {
|
||||
return who == batteryIcon || super.verifyDrawable(who)
|
||||
}
|
||||
|
||||
override fun jumpDrawablesToCurrentState() {
|
||||
super.jumpDrawablesToCurrentState()
|
||||
batteryIcon?.jumpToCurrentState()
|
||||
}
|
||||
|
||||
override fun onCreateDrawableState(extraSpace: Int): IntArray? {
|
||||
val iconState = batteryIcon?.state ?: return super.onCreateDrawableState(extraSpace)
|
||||
return mergeDrawableStates(super.onCreateDrawableState(extraSpace + iconState.size), iconState)
|
||||
}
|
||||
|
||||
override fun drawableStateChanged() {
|
||||
currentTextColor = colorText.getColorForState(drawableState, colorText.defaultColor)
|
||||
currentOutlineColor = colorOutline.getColorForState(drawableState, colorOutline.defaultColor)
|
||||
super.drawableStateChanged()
|
||||
if (batteryIcon != null && batteryIcon.isStateful && batteryIcon.setState(drawableState)) {
|
||||
invalidateDrawable(batteryIcon)
|
||||
}
|
||||
}
|
||||
|
||||
fun update(state: ReaderUiState?) {
|
||||
text = if (state != null) {
|
||||
context.getString(
|
||||
@@ -200,17 +223,17 @@ class ReaderInfoBarView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun Canvas.drawTextOutline(text: String, x: Float, y: Float) {
|
||||
paint.color = colorOutline
|
||||
paint.color = currentOutlineColor
|
||||
paint.style = Paint.Style.STROKE
|
||||
drawText(text, x, y, paint)
|
||||
paint.color = colorText
|
||||
paint.color = currentTextColor
|
||||
paint.style = Paint.Style.FILL
|
||||
drawText(text, x, y, paint)
|
||||
}
|
||||
|
||||
private fun Drawable.drawWithOutline(canvas: Canvas) {
|
||||
var requiredScale = (bounds.width() + paint.strokeWidth * 2f) / bounds.width().toFloat()
|
||||
setTint(colorOutline)
|
||||
setTint(currentOutlineColor)
|
||||
canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) {
|
||||
draw(canvas)
|
||||
}
|
||||
@@ -218,7 +241,7 @@ class ReaderInfoBarView @JvmOverloads constructor(
|
||||
canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) {
|
||||
draw(canvas)
|
||||
}
|
||||
setTint(colorText)
|
||||
setTint(currentTextColor)
|
||||
draw(canvas)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@ import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.net.toUri
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.databinding.ActivityKitsuAuthBinding
|
||||
import org.koitharu.kotatsu.parsers.util.urlEncoded
|
||||
|
||||
class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClickListener, TextWatcher {
|
||||
class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClickListener, DefaultTextWatcher {
|
||||
|
||||
private val regexEmail = Regex("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", RegexOption.IGNORE_CASE)
|
||||
|
||||
@@ -43,10 +43,6 @@ class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClick
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val email = viewBinding.editEmail.text?.toString()?.trim()
|
||||
val password = viewBinding.editPassword.text?.toString()?.trim()
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
@@ -18,6 +17,7 @@ import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding
|
||||
@@ -27,7 +27,7 @@ private const val MIN_PASSWORD_LENGTH = 4
|
||||
@AndroidEntryPoint
|
||||
class ProtectSetupActivity :
|
||||
BaseActivity<ActivitySetupProtectBinding>(),
|
||||
TextWatcher,
|
||||
DefaultTextWatcher,
|
||||
View.OnClickListener,
|
||||
TextView.OnEditorActionListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
@@ -90,10 +90,6 @@ class ProtectSetupActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewBinding.editPassword.error = null
|
||||
val isEnoughLength = (s?.length ?: 0) >= MIN_PASSWORD_LENGTH
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
@@ -187,11 +188,7 @@ class SyncAuthActivity : BaseActivity<ActivitySyncAuthBinding>(), View.OnClickLi
|
||||
|
||||
private class PasswordTextWatcher(
|
||||
private val button: Button,
|
||||
) : TextWatcher {
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
) : DefaultTextWatcher {
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val text = s?.toString()
|
||||
|
||||
Reference in New Issue
Block a user