diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconFallbackDrawable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconDrawable.kt similarity index 52% rename from app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconFallbackDrawable.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconDrawable.kt index ab065e004..492ee9f97 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconFallbackDrawable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/FaviconDrawable.kt @@ -5,45 +5,67 @@ 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 androidx.annotation.StyleRes +import androidx.core.content.withStyledAttributes import androidx.core.graphics.ColorUtils +import androidx.core.graphics.withClip import com.google.android.material.color.MaterialColors +import org.koitharu.kotatsu.R import kotlin.math.absoluteValue -class FaviconFallbackDrawable( +class FaviconDrawable( context: Context, + @StyleRes styleResId: Int, name: String, ) : Drawable() { 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 color = MaterialColors.harmonizeWithPrimary(context, colorOfString(name)) + private var cornerSize = 0f + private var colorForeground = Color.DKGRAY private val textBounds = Rect() private val tempRect = Rect() + private val boundsF = RectF() + private val clipPath = Path() 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.isFakeBoldText = true + colorForeground = MaterialColors.harmonize(colorOfString(name), colorBackground) } override fun draw(canvas: Canvas) { - val cx = bounds.exactCenterX() - paint.color = color - canvas.drawPaint(paint) - paint.color = Color.WHITE - val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom - canvas.drawText(letter, cx, ty, paint) + if (cornerSize > 0f) { + canvas.withClip(clipPath) { + doDraw(canvas) + } + } else { + doDraw(canvas) + } } override fun onBoundsChange(bounds: Rect) { super.onBoundsChange(bounds) + boundsF.set(bounds) val innerWidth = bounds.width() - (paint.strokeWidth * 2f) paint.textSize = getTextSizeForWidth(innerWidth, letter) * 0.5f 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) { @@ -58,6 +80,24 @@ class FaviconFallbackDrawable( @Deprecated("Deprecated in Java") 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 { val testTextSize = 48f paint.textSize = testTextSize diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt index 614b5ef78..c440e322a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt @@ -8,7 +8,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R 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.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.disposeImageRequest @@ -76,7 +76,7 @@ fun exploreSourceListItemAD( bind { 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 { fallback(fallbackIcon) placeholder(fallbackIcon) @@ -107,7 +107,7 @@ fun exploreSourceGridItemAD( bind { 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 { fallback(fallbackIcon) placeholder(fallbackIcon) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt index 6552643a1..138a4bff8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt @@ -3,8 +3,9 @@ package org.koitharu.kotatsu.search.ui.suggestion.adapter import androidx.lifecycle.LifecycleOwner import coil.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R 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.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest @@ -31,7 +32,7 @@ fun searchSuggestionSourceAD( bind { binding.textViewTitle.text = item.source.title 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 { fallback(fallbackIcon) placeholder(fallbackIcon) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt index bef802851..e407ab2c3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt @@ -9,7 +9,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R 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.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.disposeImageRequest @@ -66,7 +66,7 @@ fun sourceConfigItemCheckableDelegate( binding.textViewTitle.text = item.source.title binding.switchToggle.isChecked = item.isEnabled 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 { crossfade(context) error(fallbackIcon) @@ -107,7 +107,7 @@ fun sourceConfigItemDelegate2( binding.imageViewRemove.isVisible = item.isEnabled binding.imageViewConfig.isVisible = item.isEnabled 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 { crossfade(context) error(fallbackIcon) diff --git a/app/src/main/res/layout/item_explore_source_grid.xml b/app/src/main/res/layout/item_explore_source_grid.xml index 2f952ffee..e1cef0936 100644 --- a/app/src/main/res/layout/item_explore_source_grid.xml +++ b/app/src/main/res/layout/item_explore_source_grid.xml @@ -18,8 +18,11 @@ android:layout_height="72dp" android:background="?colorControlHighlight" android:labelFor="@id/textView_title" + android:padding="1dp" android:scaleType="fitCenter" app:shapeAppearance="?shapeAppearanceCornerMedium" + app:strokeColor="?colorOutline" + app:strokeWidth="1dp" tools:src="@tools:sample/avatars" /> - + - + + + + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d9a498bd5..5b6f7a48a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -287,7 +287,7 @@ false - + + + + + + +