diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TextDrawable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TextDrawable.kt index a31c80f3d..544782880 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TextDrawable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TextDrawable.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.core.ui.image +import android.annotation.SuppressLint +import android.content.Context import android.content.res.ColorStateList import android.graphics.Canvas import android.graphics.Color @@ -8,8 +10,11 @@ import android.graphics.PointF import android.graphics.Rect import android.os.Build import android.widget.TextView +import androidx.annotation.AttrRes import androidx.annotation.RequiresApi import androidx.core.graphics.PaintCompat +import com.google.android.material.resources.TextAppearance +import org.koitharu.kotatsu.core.util.ext.getThemeResId import org.koitharu.kotatsu.core.util.ext.hasFocusStateSpecified class TextDrawable( @@ -71,6 +76,17 @@ class TextDrawable( companion object { + @SuppressLint("RestrictedApi") + fun create(context: Context, text: String, @AttrRes textAppearanceAttr: Int): TextDrawable { + val drawable = TextDrawable(text) + val textAppearance = TextAppearance(context, context.getThemeResId(textAppearanceAttr, androidx.appcompat.R.style.TextAppearance_AppCompat)) + drawable.textSize = textAppearance.textSize + drawable.textColor = textAppearance.textColor ?: drawable.textColor + drawable.paint.typeface = textAppearance.getFont(context) + drawable.paint.letterSpacing = textAppearance.letterSpacing + return drawable + } + fun compound(textView: TextView, text: String): TextDrawable? { val drawable = TextDrawable(text) drawable.textSize = textView.textSize diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/CoverImageView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/CoverImageView.kt index efafc18f0..4bcebba0c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/CoverImageView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/CoverImageView.kt @@ -1,23 +1,32 @@ package org.koitharu.kotatsu.image.ui import android.content.Context +import android.os.Build import android.util.AttributeSet import android.view.ViewGroup import android.view.ViewTreeObserver import android.view.ViewTreeObserver.OnPreDrawListener import androidx.annotation.AttrRes +import androidx.annotation.RequiresApi import androidx.core.content.withStyledAttributes import androidx.core.graphics.ColorUtils import androidx.core.graphics.drawable.toDrawable +import coil3.network.HttpException +import coil3.request.ErrorResult +import coil3.request.ImageRequest +import coil3.request.SuccessResult import coil3.request.transformations import coil3.size.Dimension import coil3.size.Size import coil3.size.ViewSizeResolver import kotlinx.coroutines.suspendCancellableCoroutine +import okio.FileNotFoundException +import org.jsoup.HttpStatusException import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.image.CoilImageView import org.koitharu.kotatsu.core.ui.image.AnimatedPlaceholderDrawable +import org.koitharu.kotatsu.core.ui.image.TextDrawable import org.koitharu.kotatsu.core.ui.image.TrimTransformation import org.koitharu.kotatsu.core.util.ext.bookmarkExtra import org.koitharu.kotatsu.core.util.ext.decodeRegion @@ -66,6 +75,9 @@ class CoverImageView @JvmOverloads constructor( if (fallbackDrawable == null) { fallbackDrawable = context.getThemeColor(materialR.attr.colorSurfaceContainer).toDrawable() } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + addImageRequestListener(ErrorForegroundListener()) + } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { @@ -149,6 +161,39 @@ class CoverImageView @JvmOverloads constructor( } } + @RequiresApi(Build.VERSION_CODES.M) + private inner class ErrorForegroundListener : ImageRequest.Listener { + + override fun onSuccess(request: ImageRequest, result: SuccessResult) { + super.onSuccess(request, result) + foreground = null + } + + override fun onCancel(request: ImageRequest) { + super.onCancel(request) + foreground = null + } + + override fun onStart(request: ImageRequest) { + super.onStart(request) + foreground = null + } + + override fun onError(request: ImageRequest, result: ErrorResult) { + super.onError(request, result) + foreground = result.throwable.getShortMessage()?.let { text -> + TextDrawable.create(context, text, materialR.attr.textAppearanceTitleSmall) + } + } + + private fun Throwable.getShortMessage(): String? = when (this) { + is HttpException -> response.code.toString() + is HttpStatusException -> statusCode.toString() + is FileNotFoundException -> "404" + else -> cause?.getShortMessage() + } + } + private class CoverSizeResolver( override val view: CoverImageView, ) : ViewSizeResolver {