Update default favicon drawable

This commit is contained in:
Koitharu
2023-07-01 17:44:16 +03:00
parent a726b4f499
commit 55fdd6b7b1
7 changed files with 87 additions and 21 deletions

View File

@@ -5,45 +5,67 @@ import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.ColorFilter import android.graphics.ColorFilter
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Path
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.graphics.Rect import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.StyleRes
import androidx.core.content.withStyledAttributes
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.graphics.withClip
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import org.koitharu.kotatsu.R
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
class FaviconFallbackDrawable( class FaviconDrawable(
context: Context, context: Context,
@StyleRes styleResId: Int,
name: String, name: String,
) : Drawable() { ) : Drawable() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var colorBackground = Color.WHITE
private var colorStroke = Color.LTGRAY
private val letter = name.take(1).uppercase() private val letter = name.take(1).uppercase()
private val color = MaterialColors.harmonizeWithPrimary(context, colorOfString(name)) private var cornerSize = 0f
private var colorForeground = Color.DKGRAY
private val textBounds = Rect() private val textBounds = Rect()
private val tempRect = Rect() private val tempRect = Rect()
private val boundsF = RectF()
private val clipPath = Path()
init { init {
paint.style = Paint.Style.FILL context.withStyledAttributes(styleResId, R.styleable.FaviconFallbackDrawable) {
colorBackground = getColor(R.styleable.FaviconFallbackDrawable_backgroundColor, colorBackground)
colorStroke = getColor(R.styleable.FaviconFallbackDrawable_strokeColor, colorStroke)
cornerSize = getDimension(R.styleable.FaviconFallbackDrawable_cornerSize, cornerSize)
paint.strokeWidth = getDimension(R.styleable.FaviconFallbackDrawable_strokeWidth, 0f) * 2f
}
paint.textAlign = Paint.Align.CENTER paint.textAlign = Paint.Align.CENTER
paint.isFakeBoldText = true paint.isFakeBoldText = true
colorForeground = MaterialColors.harmonize(colorOfString(name), colorBackground)
} }
override fun draw(canvas: Canvas) { override fun draw(canvas: Canvas) {
val cx = bounds.exactCenterX() if (cornerSize > 0f) {
paint.color = color canvas.withClip(clipPath) {
canvas.drawPaint(paint) doDraw(canvas)
paint.color = Color.WHITE }
val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom } else {
canvas.drawText(letter, cx, ty, paint) doDraw(canvas)
}
} }
override fun onBoundsChange(bounds: Rect) { override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds) super.onBoundsChange(bounds)
boundsF.set(bounds)
val innerWidth = bounds.width() - (paint.strokeWidth * 2f) val innerWidth = bounds.width() - (paint.strokeWidth * 2f)
paint.textSize = getTextSizeForWidth(innerWidth, letter) * 0.5f paint.textSize = getTextSizeForWidth(innerWidth, letter) * 0.5f
paint.getTextBounds(letter, 0, letter.length, textBounds) paint.getTextBounds(letter, 0, letter.length, textBounds)
invalidateSelf() clipPath.reset()
clipPath.addRoundRect(boundsF, cornerSize, cornerSize, Path.Direction.CW)
clipPath.close()
} }
override fun setAlpha(alpha: Int) { override fun setAlpha(alpha: Int) {
@@ -58,6 +80,24 @@ class FaviconFallbackDrawable(
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun getOpacity() = PixelFormat.TRANSPARENT override fun getOpacity() = PixelFormat.TRANSPARENT
private fun doDraw(canvas: Canvas) {
// background
paint.color = colorBackground
paint.style = Paint.Style.FILL
canvas.drawPaint(paint)
// letter
paint.color = colorForeground
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.style = Paint.Style.STROKE
canvas.drawPath(clipPath, paint)
}
}
private fun getTextSizeForWidth(width: Float, text: String): Float { private fun getTextSizeForWidth(width: Float, text: String): Float {
val testTextSize = 48f val testTextSize = 48f
paint.textSize = testTextSize paint.textSize = testTextSize

View File

@@ -8,7 +8,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.ui.image.FaviconFallbackDrawable import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.disposeImageRequest import org.koitharu.kotatsu.core.util.ext.disposeImageRequest
@@ -76,7 +76,7 @@ fun exploreSourceListItemAD(
bind { bind {
binding.textViewTitle.text = item.source.title binding.textViewTitle.text = item.source.title
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
fallback(fallbackIcon) fallback(fallbackIcon)
placeholder(fallbackIcon) placeholder(fallbackIcon)
@@ -107,7 +107,7 @@ fun exploreSourceGridItemAD(
bind { bind {
binding.textViewTitle.text = item.source.title binding.textViewTitle.text = item.source.title
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Large, item.source.name)
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
fallback(fallbackIcon) fallback(fallbackIcon)
placeholder(fallbackIcon) placeholder(fallbackIcon)

View File

@@ -3,8 +3,9 @@ package org.koitharu.kotatsu.search.ui.suggestion.adapter
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.ui.image.FaviconFallbackDrawable import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
import org.koitharu.kotatsu.core.util.ext.disposeImageRequest import org.koitharu.kotatsu.core.util.ext.disposeImageRequest
import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.enqueueWith
import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.newImageRequest
@@ -31,7 +32,7 @@ fun searchSuggestionSourceAD(
bind { bind {
binding.textViewTitle.text = item.source.title binding.textViewTitle.text = item.source.title
binding.switchLocal.isChecked = item.isEnabled binding.switchLocal.isChecked = item.isEnabled
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
binding.imageViewCover.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { binding.imageViewCover.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
fallback(fallbackIcon) fallback(fallbackIcon)
placeholder(fallbackIcon) placeholder(fallbackIcon)

View File

@@ -9,7 +9,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.ui.image.FaviconFallbackDrawable import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
import org.koitharu.kotatsu.core.ui.list.OnTipCloseListener import org.koitharu.kotatsu.core.ui.list.OnTipCloseListener
import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.crossfade
import org.koitharu.kotatsu.core.util.ext.disposeImageRequest import org.koitharu.kotatsu.core.util.ext.disposeImageRequest
@@ -66,7 +66,7 @@ fun sourceConfigItemCheckableDelegate(
binding.textViewTitle.text = item.source.title binding.textViewTitle.text = item.source.title
binding.switchToggle.isChecked = item.isEnabled binding.switchToggle.isChecked = item.isEnabled
binding.textViewDescription.textAndVisible = item.summary binding.textViewDescription.textAndVisible = item.summary
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
crossfade(context) crossfade(context)
error(fallbackIcon) error(fallbackIcon)
@@ -107,7 +107,7 @@ fun sourceConfigItemDelegate2(
binding.imageViewRemove.isVisible = item.isEnabled binding.imageViewRemove.isVisible = item.isEnabled
binding.imageViewConfig.isVisible = item.isEnabled binding.imageViewConfig.isVisible = item.isEnabled
binding.textViewDescription.textAndVisible = item.summary binding.textViewDescription.textAndVisible = item.summary
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
crossfade(context) crossfade(context)
error(fallbackIcon) error(fallbackIcon)

View File

@@ -18,8 +18,11 @@
android:layout_height="72dp" android:layout_height="72dp"
android:background="?colorControlHighlight" android:background="?colorControlHighlight"
android:labelFor="@id/textView_title" android:labelFor="@id/textView_title"
android:padding="1dp"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:shapeAppearance="?shapeAppearanceCornerMedium" app:shapeAppearance="?shapeAppearanceCornerMedium"
app:strokeColor="?colorOutline"
app:strokeWidth="1dp"
tools:src="@tools:sample/avatars" /> tools:src="@tools:sample/avatars" />
<TextView <TextView

View File

@@ -116,9 +116,16 @@
<attr name="shapeAppearance" /> <attr name="shapeAppearance" />
<attr name="shapeAppearanceOverlay" /> <attr name="shapeAppearanceOverlay" />
<attr name="cardBackgroundColor" /> <attr name="cardBackgroundColor" />
<attr name="strokeWidth"/> <attr name="strokeWidth" />
<attr name="strokeColor" /> <attr name="strokeColor" />
<attr name="elevation" /> <attr name="elevation" />
</declare-styleable> </declare-styleable>
<declare-styleable name="FaviconFallbackDrawable">
<attr name="backgroundColor" />
<attr name="strokeColor" />
<attr name="strokeWidth" />
<attr name="cornerSize" />
</declare-styleable>
</resources> </resources>

View File

@@ -287,7 +287,7 @@
<item name="android:selectable">false</item> <item name="android:selectable">false</item>
</style> </style>
<!-- Progress drawable --> <!-- Drawable -->
<style name="ProgressDrawable"> <style name="ProgressDrawable">
<item name="android:fillAlpha">1</item> <item name="android:fillAlpha">1</item>
@@ -299,4 +299,19 @@
<item name="autoFitTextSize">true</item> <item name="autoFitTextSize">true</item>
</style> </style>
<style name="FaviconDrawable">
<item name="backgroundColor">?colorBackgroundFloating</item>
<item name="strokeColor">?colorOutline</item>
</style>
<style name="FaviconDrawable.Small">
<item name="strokeWidth">1dp</item>
<item name="cornerSize">8dp</item>
</style>
<style name="FaviconDrawable.Large">
<item name="strokeWidth">1dp</item>
<item name="cornerSize">12dp</item>
</style>
</resources> </resources>