Skip to content

Commit

Permalink
Merge pull request #337 from cyb3rko/picasso-to-coil
Browse files Browse the repository at this point in the history
Replace Picasso with Coil
  • Loading branch information
jmattheis authored Apr 21, 2024
2 parents 8a1802e + fdc9261 commit d5903b2
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 168 deletions.
13 changes: 7 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ if (project.hasProperty('sign')) {
}

dependencies {
def markwon_version = "4.6.2"
implementation project(':client')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.core:core-splashscreen:1.0.1'
Expand All @@ -76,12 +77,12 @@ dependencies {
implementation 'androidx.preference:preference-ktx:1.2.1'

implementation 'com.github.cyb3rko:QuickPermissions-Kotlin:1.1.3'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'io.noties.markwon:core:4.6.2'
implementation 'io.noties.markwon:image-picasso:4.6.2'
implementation 'io.noties.markwon:image:4.6.2'
implementation 'io.noties.markwon:ext-tables:4.6.2'
implementation 'io.noties.markwon:ext-strikethrough:4.6.2'
implementation 'io.coil-kt:coil:2.6.0'
implementation "io.noties.markwon:core:$markwon_version"
implementation "io.noties.markwon:image-coil:$markwon_version"
implementation "io.noties.markwon:image:$markwon_version"
implementation "io.noties.markwon:ext-tables:$markwon_version"
implementation "io.noties.markwon:ext-strikethrough:$markwon_version"

implementation 'org.tinylog:tinylog-api-kotlin:2.6.2'
implementation 'org.tinylog:tinylog-impl:2.6.2'
Expand Down
67 changes: 67 additions & 0 deletions app/src/main/kotlin/com/github/gotify/CoilHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.github.gotify

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import coil.ImageLoader
import coil.annotation.ExperimentalCoilApi
import coil.disk.DiskCache
import coil.executeBlocking
import coil.request.ImageRequest
import com.github.gotify.api.CertUtils
import com.github.gotify.client.model.Application
import java.io.IOException
import okhttp3.OkHttpClient
import org.tinylog.kotlin.Logger

internal class CoilHandler(private val context: Context, private val settings: Settings) {
private val imageLoader = makeImageLoader()

private fun makeImageLoader(): ImageLoader {
val builder = OkHttpClient.Builder()
CertUtils.applySslSettings(builder, settings.sslSettings())
return ImageLoader.Builder(context)
.okHttpClient(builder.build())
.diskCache {
DiskCache.Builder()
.directory(context.cacheDir.resolve("coil-cache"))
.build()
}
.build()
}

@Throws(IOException::class)
fun getImageFromUrl(url: String?): Bitmap {
val request = ImageRequest.Builder(context)
.data(url)
.build()
return (imageLoader.executeBlocking(request).drawable as BitmapDrawable).bitmap
}

fun getIcon(app: Application?): Bitmap {
if (app == null) {
return BitmapFactory.decodeResource(context.resources, R.drawable.gotify)
}
try {
return getImageFromUrl(
Utils.resolveAbsoluteUrl("${settings.url}/", app.image)
)
} catch (e: IOException) {
Logger.error(e, "Could not load image for notification")
}
return BitmapFactory.decodeResource(context.resources, R.drawable.gotify)
}

fun get() = imageLoader

@OptIn(ExperimentalCoilApi::class)
fun evict() {
try {
imageLoader.diskCache?.clear()
imageLoader.memoryCache?.clear()
} catch (e: IOException) {
Logger.error(e, "Problem evicting Coil cache")
}
}
}
12 changes: 6 additions & 6 deletions app/src/main/kotlin/com/github/gotify/MarkwonFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import android.text.style.RelativeSizeSpan
import android.text.style.StyleSpan
import android.text.style.TypefaceSpan
import androidx.core.content.ContextCompat
import com.squareup.picasso.Picasso
import coil.ImageLoader
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonSpansFactory
Expand All @@ -22,7 +22,7 @@ import io.noties.markwon.core.MarkwonTheme
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
import io.noties.markwon.ext.tables.TableAwareMovementMethod
import io.noties.markwon.ext.tables.TablePlugin
import io.noties.markwon.image.picasso.PicassoImagesPlugin
import io.noties.markwon.image.coil.CoilImagesPlugin
import io.noties.markwon.movement.MovementMethodPlugin
import org.commonmark.ext.gfm.tables.TableCell
import org.commonmark.ext.gfm.tables.TablesExtension
Expand All @@ -36,11 +36,11 @@ import org.commonmark.node.StrongEmphasis
import org.commonmark.parser.Parser

internal object MarkwonFactory {
fun createForMessage(context: Context, picasso: Picasso): Markwon {
fun createForMessage(context: Context, imageLoader: ImageLoader): Markwon {
return Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(MovementMethodPlugin.create(TableAwareMovementMethod.create()))
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(CoilImagesPlugin.create(context, imageLoader))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.usePlugin(object : AbstractMarkwonPlugin() {
Expand All @@ -52,13 +52,13 @@ internal object MarkwonFactory {
.build()
}

fun createForNotification(context: Context, picasso: Picasso): Markwon {
fun createForNotification(context: Context, imageLoader: ImageLoader): Markwon {
val headingSizes = floatArrayOf(2f, 1.5f, 1.17f, 1f, .83f, .67f)
val bulletGapWidth = (8 * context.resources.displayMetrics.density + 0.5f).toInt()

return Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(CoilImagesPlugin.create(context, imageLoader))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
Expand Down
18 changes: 6 additions & 12 deletions app/src/main/kotlin/com/github/gotify/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@ package com.github.gotify
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.text.format.DateUtils
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import coil.target.Target
import com.github.gotify.client.JSON
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.squareup.picasso.Picasso.LoadedFrom
import com.squareup.picasso.Target
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
Expand Down Expand Up @@ -72,17 +68,15 @@ internal object Utils {
}
}

fun toDrawable(resources: Resources?, drawableReceiver: DrawableReceiver): Target {
fun toDrawable(drawableReceiver: DrawableReceiver): Target {
return object : Target {
override fun onBitmapLoaded(bitmap: Bitmap, from: LoadedFrom) {
drawableReceiver.loaded(BitmapDrawable(resources, bitmap))
override fun onSuccess(result: Drawable) {
drawableReceiver.loaded(result)
}

override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
Logger.error(e, "Bitmap failed")
override fun onError(error: Drawable?) {
Logger.error("Bitmap failed")
}

override fun onPrepareLoad(placeHolderDrawable: Drawable) {}
}
}

Expand Down
6 changes: 3 additions & 3 deletions app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import com.github.gotify.log.UncaughtExceptionHandler
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import java.security.cert.X509Certificate
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.tinylog.kotlin.Logger

internal class LoginActivity : AppCompatActivity() {
Expand Down Expand Up @@ -101,13 +101,13 @@ internal class LoginActivity : AppCompatActivity() {

private fun doCheckUrl() {
val url = binding.gotifyUrlEditext.text.toString().trim().trimEnd('/')
val parsedUrl = HttpUrl.parse(url)
val parsedUrl = url.toHttpUrlOrNull()
if (parsedUrl == null) {
Utils.showSnackBar(this, "Invalid URL (include http:// or https://)")
return
}

if ("http" == parsedUrl.scheme()) {
if ("http" == parsedUrl.scheme) {
showHttpWarning()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import coil.ImageLoader
import coil.load
import com.github.gotify.MarkwonFactory
import com.github.gotify.R
import com.github.gotify.Settings
Expand All @@ -26,7 +28,6 @@ import com.github.gotify.client.model.Message
import com.github.gotify.databinding.MessageItemBinding
import com.github.gotify.databinding.MessageItemCompactBinding
import com.github.gotify.messages.provider.MessageWithImage
import com.squareup.picasso.Picasso
import io.noties.markwon.Markwon
import java.text.DateFormat
import java.util.Date
Expand All @@ -35,11 +36,11 @@ import org.threeten.bp.OffsetDateTime
internal class ListMessageAdapter(
private val context: Context,
private val settings: Settings,
private val picasso: Picasso,
private val imageLoader: ImageLoader,
private val delete: Delete
) : ListAdapter<MessageWithImage, ListMessageAdapter.ViewHolder>(DiffCallback) {
private val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val markwon: Markwon = MarkwonFactory.createForMessage(context, picasso)
private val markwon: Markwon = MarkwonFactory.createForMessage(context, imageLoader)

private val timeFormatRelative =
context.resources.getString(R.string.time_format_value_relative)
Expand Down Expand Up @@ -81,10 +82,11 @@ internal class ListMessageAdapter(
}
holder.title.text = message.message.title
if (message.image != null) {
picasso.load(Utils.resolveAbsoluteUrl("${settings.url}/", message.image))
.error(R.drawable.ic_alarm)
.placeholder(R.drawable.ic_placeholder)
.into(holder.image)
val url = Utils.resolveAbsoluteUrl("${settings.url}/", message.image)
holder.image.load(url, imageLoader) {
error(R.drawable.ic_alarm)
placeholder(R.drawable.ic_placeholder)
}
}

val prefs = PreferenceManager.getDefaultSharedPreferences(context)
Expand Down
35 changes: 20 additions & 15 deletions app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.request.ImageRequest
import com.github.gotify.BuildConfig
import com.github.gotify.MissedMessageUtil
import com.github.gotify.R
Expand Down Expand Up @@ -57,7 +58,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.BaseTransientBottomBar.BaseCallback
import com.google.android.material.snackbar.Snackbar
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger
Expand Down Expand Up @@ -102,7 +102,7 @@ internal class MessagesActivity :
listMessageAdapter = ListMessageAdapter(
this,
viewModel.settings,
viewModel.picassoHandler.get()
viewModel.coilHandler.get()
) { message ->
scheduleDeletion(message)
}
Expand Down Expand Up @@ -169,19 +169,20 @@ internal class MessagesActivity :
}

private fun refreshAll() {
try {
viewModel.picassoHandler.evict()
} catch (e: IOException) {
Logger.error(e, "Problem evicting Picasso cache")
}
viewModel.coilHandler.evict()
startActivity(Intent(this, InitializationActivity::class.java))
finish()
}

private fun onRefresh() {
viewModel.coilHandler.evict()
viewModel.messages.clear()
launchCoroutine {
loadMore(viewModel.appId)
loadMore(viewModel.appId).forEachIndexed { index, message ->
if (message.image != null) {
listMessageAdapter.notifyItemChanged(index)
}
}
}
}

Expand All @@ -201,15 +202,18 @@ internal class MessagesActivity :
val item = menu.add(R.id.apps, index, APPLICATION_ORDER, app.name)
item.isCheckable = true
if (app.id == viewModel.appId) selectedItem = item
val t = Utils.toDrawable(resources) { icon -> item.icon = icon }
val t = Utils.toDrawable { icon -> item.icon = icon }
viewModel.targetReferences.add(t)
viewModel.picassoHandler
.get()
.load(Utils.resolveAbsoluteUrl(viewModel.settings.url + "/", app.image))
val request = ImageRequest.Builder(this)
.data(Utils.resolveAbsoluteUrl(viewModel.settings.url + "/", app.image))
.error(R.drawable.ic_alarm)
.placeholder(R.drawable.ic_placeholder)
.resize(100, 100)
.into(t)
.size(100, 100)
.target(t)
.build()
viewModel.coilHandler
.get()
.enqueue(request)
}
selectAppInMenu(selectedItem)
}
Expand Down Expand Up @@ -552,11 +556,12 @@ internal class MessagesActivity :
)
}

private suspend fun loadMore(appId: Long) {
private suspend fun loadMore(appId: Long): List<MessageWithImage> {
val messagesWithImages = viewModel.messages.loadMore(appId)
withContext(Dispatchers.Main) {
updateMessagesAndStopLoading(messagesWithImages)
}
return messagesWithImages
}

private suspend fun updateMessagesForApplication(withLoadingSpinner: Boolean, appId: Long) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package com.github.gotify.messages

import android.app.Activity
import androidx.lifecycle.ViewModel
import coil.target.Target
import com.github.gotify.CoilHandler
import com.github.gotify.Settings
import com.github.gotify.api.ClientFactory
import com.github.gotify.client.api.MessageApi
import com.github.gotify.messages.provider.ApplicationHolder
import com.github.gotify.messages.provider.MessageFacade
import com.github.gotify.messages.provider.MessageState
import com.github.gotify.picasso.PicassoHandler
import com.squareup.picasso.Target

internal class MessagesModel(parentView: Activity) : ViewModel() {
val settings = Settings(parentView)
val picassoHandler = PicassoHandler(parentView, settings)
val coilHandler = CoilHandler(parentView, settings)
val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token)
val appsHolder = ApplicationHolder(parentView, client)
val messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder)
Expand Down
Loading

0 comments on commit d5903b2

Please sign in to comment.