Skip to content

Commit 337af76

Browse files
committed
fix: log image load errors and show placeholder on error
1 parent 7d6399b commit 337af76

File tree

3 files changed

+64
-16
lines changed

3 files changed

+64
-16
lines changed

app/src/main/kotlin/com/github/gotify/CoilInstance.kt

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ package com.github.gotify
33
import android.content.Context
44
import android.graphics.Bitmap
55
import android.graphics.BitmapFactory
6-
import android.graphics.drawable.BitmapDrawable
6+
import androidx.annotation.DrawableRes
7+
import androidx.appcompat.content.res.AppCompatResources
8+
import androidx.core.graphics.drawable.toBitmap
79
import coil.ImageLoader
810
import coil.annotation.ExperimentalCoilApi
911
import coil.decode.SvgDecoder
1012
import coil.disk.DiskCache
1113
import coil.executeBlocking
14+
import coil.request.ErrorResult
1215
import coil.request.ImageRequest
16+
import coil.request.SuccessResult
1317
import com.github.gotify.api.CertUtils
1418
import com.github.gotify.client.model.Application
1519
import java.io.IOException
@@ -23,27 +27,34 @@ object CoilInstance {
2327
private var holder: Pair<SSLSettings, ImageLoader>? = null
2428

2529
@Throws(IOException::class)
26-
fun getImageFromUrl(context: Context, url: String?): Bitmap {
27-
val request = ImageRequest.Builder(context)
28-
.data(url)
29-
.build()
30-
return (get(context).executeBlocking(request).drawable as BitmapDrawable).bitmap
30+
fun getImageFromUrl(
31+
context: Context,
32+
url: String?,
33+
@DrawableRes placeholder: Int = R.drawable.ic_placeholder
34+
): Bitmap {
35+
val request = ImageRequest.Builder(context).data(url).build()
36+
37+
return when (val result = get(context).executeBlocking(request)) {
38+
is SuccessResult -> result.drawable.toBitmap()
39+
is ErrorResult -> {
40+
Logger.error(
41+
result.throwable
42+
) { "Could not load image ${Utils.redactPassword(url)}" }
43+
AppCompatResources.getDrawable(context, placeholder)!!.toBitmap()
44+
}
45+
}
3146
}
3247

3348
fun getIcon(context: Context, app: Application?): Bitmap {
3449
if (app == null) {
3550
return BitmapFactory.decodeResource(context.resources, R.drawable.gotify)
3651
}
3752
val baseUrl = Settings(context).url
38-
try {
39-
return getImageFromUrl(
40-
context,
41-
Utils.resolveAbsoluteUrl("$baseUrl/", app.image)
42-
)
43-
} catch (e: IOException) {
44-
Logger.error(e, "Could not load image for notification")
45-
}
46-
return BitmapFactory.decodeResource(context.resources, R.drawable.gotify)
53+
return getImageFromUrl(
54+
context,
55+
Utils.resolveAbsoluteUrl("$baseUrl/", app.image),
56+
R.drawable.gotify
57+
)
4758
}
4859

4960
@OptIn(ExperimentalCoilApi::class)

app/src/main/kotlin/com/github/gotify/MarkwonFactory.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import android.text.style.StyleSpan
1111
import android.text.style.TypefaceSpan
1212
import androidx.core.content.ContextCompat
1313
import coil.ImageLoader
14+
import coil.request.Disposable
15+
import coil.request.ImageRequest
1416
import io.noties.markwon.AbstractMarkwonPlugin
1517
import io.noties.markwon.Markwon
1618
import io.noties.markwon.MarkwonSpansFactory
@@ -22,6 +24,7 @@ import io.noties.markwon.core.MarkwonTheme
2224
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
2325
import io.noties.markwon.ext.tables.TableAwareMovementMethod
2426
import io.noties.markwon.ext.tables.TablePlugin
27+
import io.noties.markwon.image.AsyncDrawable
2528
import io.noties.markwon.image.coil.CoilImagesPlugin
2629
import io.noties.markwon.movement.MovementMethodPlugin
2730
import org.commonmark.ext.gfm.tables.TableCell
@@ -34,13 +37,37 @@ import org.commonmark.node.Link
3437
import org.commonmark.node.ListItem
3538
import org.commonmark.node.StrongEmphasis
3639
import org.commonmark.parser.Parser
40+
import org.tinylog.kotlin.Logger
3741

3842
internal object MarkwonFactory {
3943
fun createForMessage(context: Context, imageLoader: ImageLoader): Markwon {
4044
return Markwon.builder(context)
4145
.usePlugin(CorePlugin.create())
4246
.usePlugin(MovementMethodPlugin.create(TableAwareMovementMethod.create()))
43-
.usePlugin(CoilImagesPlugin.create(context, imageLoader))
47+
.usePlugin(
48+
CoilImagesPlugin.create(
49+
object : CoilImagesPlugin.CoilStore {
50+
override fun load(drawable: AsyncDrawable): ImageRequest {
51+
return ImageRequest.Builder(context)
52+
.data(drawable.destination)
53+
.placeholder(R.drawable.ic_placeholder)
54+
.listener(onError = { _, err ->
55+
Logger.error(err.throwable) {
56+
"Could not load markdown image: ${Utils.redactPassword(
57+
drawable.destination
58+
)}"
59+
}
60+
})
61+
.build()
62+
}
63+
64+
override fun cancel(disposable: Disposable) {
65+
disposable.dispose()
66+
}
67+
},
68+
imageLoader
69+
)
70+
)
4471
.usePlugin(StrikethroughPlugin.create())
4572
.usePlugin(TablePlugin.create(context))
4673
.usePlugin(object : AbstractMarkwonPlugin() {

app/src/main/kotlin/com/github/gotify/Utils.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import kotlinx.coroutines.CoroutineDispatcher
2020
import kotlinx.coroutines.CoroutineScope
2121
import kotlinx.coroutines.Dispatchers
2222
import kotlinx.coroutines.launch
23+
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
2324
import org.threeten.bp.OffsetDateTime
2425
import org.tinylog.kotlin.Logger
2526

@@ -92,4 +93,13 @@ internal object Utils {
9293
context.getSystemService(ActivityManager::class.java).appTasks?.getOrNull(0)
9394
?.setExcludeFromRecents(excludeFromRecent)
9495
}
96+
97+
fun redactPassword(stringUrl: String?): String {
98+
val url = stringUrl?.toHttpUrlOrNull()
99+
return when {
100+
url == null -> "unknown"
101+
url.password.isEmpty() -> url.toString()
102+
else -> url.newBuilder().password("REDACTED").toString()
103+
}
104+
}
95105
}

0 commit comments

Comments
 (0)