From 4ce4997bd329feadd4ec2e9fda30c7a18d108c93 Mon Sep 17 00:00:00 2001 From: Isabel Martin Date: Wed, 15 May 2024 09:58:17 -0700 Subject: [PATCH] MBL-1420: Webp format now supported for animated gifs (#2039) --- app/build.gradle | 2 ++ .../libs/utils/extensions/StringExt.kt | 10 +++++- .../kickstarter/ui/extensions/ImageViewExt.kt | 36 ++++++++++++++----- .../ui/views/ImageWithCaptionView.kt | 20 +++++++---- .../libs/htmlparser/HTMLParserTest.kt | 26 ++++++++++++++ 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 11f1695e87..4a6716b61a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -257,6 +257,8 @@ dependencies { // GLIDE final glide_version = "4.16.0" + // webpdecoder + implementation "com.github.zjupure:webpdecoder:2.6.$glide_version" implementation "com.github.bumptech.glide:glide:$glide_version" annotationProcessor "com.github.bumptech.glide:compiler:$glide_version" kapt "com.github.bumptech.glide:compiler:$glide_version" diff --git a/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt b/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt index 651071d9be..e6e8ba078b 100644 --- a/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt +++ b/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt @@ -135,7 +135,15 @@ fun String?.parseToDouble(): Double { } /** - * Returns a boolean that reflects if the string is an email address + * Returns a boolean that reflects if the string contains the .webp extension + */ +fun String.isWebp(): Boolean { + val gifPattern = "(?:\\/\\/.*\\.(?:webp))" + return gifPattern.toRegex().find(this) != null +} + +/** + * Returns a boolean that reflects if the string contains the .gif extension */ fun String.isGif(): Boolean { val gifPattern = "(?:\\/\\/.*\\.(?:gif))" diff --git a/app/src/main/java/com/kickstarter/ui/extensions/ImageViewExt.kt b/app/src/main/java/com/kickstarter/ui/extensions/ImageViewExt.kt index f230090273..d41fc9b9c0 100644 --- a/app/src/main/java/com/kickstarter/ui/extensions/ImageViewExt.kt +++ b/app/src/main/java/com/kickstarter/ui/extensions/ImageViewExt.kt @@ -7,15 +7,17 @@ import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.appcompat.widget.AppCompatImageView import com.bumptech.glide.Glide +import com.bumptech.glide.integration.webp.decoder.WebpDrawable +import com.bumptech.glide.integration.webp.decoder.WebpDrawableTransformation import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target import com.google.firebase.crashlytics.FirebaseCrashlytics import com.kickstarter.R import com.kickstarter.libs.utils.extensions.isKSApplication -import javax.sql.DataSource fun ImageView.loadCircleImage(url: String?) { url?.let { @@ -80,7 +82,7 @@ fun ImageView.loadImageWithResize( } } } -fun ImageView.loadImage(url: String?, context: Context, imageViewPlaceholder: AppCompatImageView? = null) { +fun ImageView.loadImage(url: String?, context: Context, imageZoomablePlaceholder: AppCompatImageView? = null) { url?.let { val targetView = this if (context.applicationContext.isKSApplication()) { @@ -95,7 +97,8 @@ fun ImageView.loadImage(url: String?, context: Context, imageViewPlaceholder: Ap dataSource: com.bumptech.glide.load.DataSource, isFirstResource: Boolean ): Boolean { - imageViewPlaceholder?.setImageDrawable(resource) + targetView.setImageDrawable(resource) + imageZoomablePlaceholder?.setImageDrawable(resource) return isFirstResource } @@ -106,12 +109,11 @@ fun ImageView.loadImage(url: String?, context: Context, imageViewPlaceholder: Ap isFirstResource: Boolean ): Boolean { targetView.setImageDrawable(null) - imageViewPlaceholder?.setImageDrawable(null) + imageZoomablePlaceholder?.setImageDrawable(null) return isFirstResource } }) - .placeholder(ColorDrawable(Color.TRANSPARENT)) - .diskCacheStrategy(DiskCacheStrategy.ALL) + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .load(url) .into(this) } catch (e: Exception) { @@ -125,15 +127,31 @@ fun ImageView.loadImage(url: String?, context: Context, imageViewPlaceholder: Ap } } +fun ImageView.loadWebp(url: String?, context: Context) { + url?.let { + try { + val roundedCorners = RoundedCorners(1) + Glide.with(this) + .load(it) + .optionalTransform(roundedCorners) + .optionalTransform(WebpDrawable::class.java, WebpDrawableTransformation(roundedCorners)) + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + .into(this) + } catch (e: Exception) { + this.setImageResource(R.drawable.image_placeholder) + FirebaseCrashlytics.getInstance().setCustomKey("ImageView.loadImageWithResize", " with url: $url ${e.message ?: ""}") + FirebaseCrashlytics.getInstance().recordException(e) + } + } +} fun ImageView.loadGifImage(url: String?, context: Context) { url?.let { if (context.applicationContext.isKSApplication()) { try { Glide.with(context) .asGif() - .placeholder(ColorDrawable(Color.TRANSPARENT)) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .load(url) + .load(it) + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .into(this) } catch (e: Exception) { this.setImageResource(R.drawable.image_placeholder) diff --git a/app/src/main/java/com/kickstarter/ui/views/ImageWithCaptionView.kt b/app/src/main/java/com/kickstarter/ui/views/ImageWithCaptionView.kt index bbf31aaa9b..21ed7741da 100644 --- a/app/src/main/java/com/kickstarter/ui/views/ImageWithCaptionView.kt +++ b/app/src/main/java/com/kickstarter/ui/views/ImageWithCaptionView.kt @@ -12,8 +12,10 @@ import com.aghajari.zoomhelper.ZoomHelper import com.kickstarter.R import com.kickstarter.databinding.ViewImageWithCaptionBinding import com.kickstarter.libs.utils.extensions.isGif +import com.kickstarter.libs.utils.extensions.isWebp import com.kickstarter.ui.extensions.loadGifImage import com.kickstarter.ui.extensions.loadImage +import com.kickstarter.ui.extensions.loadWebp import com.kickstarter.ui.extensions.makeLinks @SuppressLint("ClickableViewAccessibility") @@ -35,12 +37,18 @@ class ImageWithCaptionView @JvmOverloads constructor( binding.imageView.setImageDrawable(null) binding.imageViewPlaceholder.setImageDrawable(null) } else { - if (src.isGif()) { - binding.imageView.loadGifImage(src, context) - } else { - binding.imageView.loadImage(src, context, binding.imageViewPlaceholder) - ZoomHelper.addZoomableView(binding.imageView) - ZoomHelper.removeZoomableView(binding.imageViewPlaceholder) + when { + src.isWebp() -> { + binding.imageView.loadWebp(src, context) + } + src.isGif() -> { + binding.imageView.loadGifImage(src, context) + } + else -> { + binding.imageView.loadImage(src, context, binding.imageViewPlaceholder) + ZoomHelper.addZoomableView(binding.imageView) + ZoomHelper.removeZoomableView(binding.imageViewPlaceholder) + } } } } diff --git a/app/src/test/java/com/kickstarter/libs/htmlparser/HTMLParserTest.kt b/app/src/test/java/com/kickstarter/libs/htmlparser/HTMLParserTest.kt index e0f2cc3239..4304555744 100644 --- a/app/src/test/java/com/kickstarter/libs/htmlparser/HTMLParserTest.kt +++ b/app/src/test/java/com/kickstarter/libs/htmlparser/HTMLParserTest.kt @@ -1,7 +1,10 @@ package com.kickstarter.libs.htmlparser +import com.kickstarter.libs.utils.extensions.isGif +import com.kickstarter.libs.utils.extensions.isWebp import junit.framework.TestCase import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue import org.junit.Test import java.net.URI @@ -344,4 +347,27 @@ class HTMLParserTest { assert(h6.components[0].styles[0] == TextComponent.TextStyleType.HEADER6) assert(h6.components[0].text == "This is heading 6") } + + @Test + fun given_HTML_withGifAndWebpExtensions_HtmlParser_returns_ImageViewElement_with_URL() { + val htmlGif = "
\n" + + "
\n" + + " \"\" \n" + + "
\n" + + "
\n" + val htmlWebMP = + "
\n" + + "
\n" + + " \"\"\n" + + "
\n" + + "
" + + val listOfElements = HTMLParser().parse(htmlGif + htmlWebMP) + assert(listOfElements.size == 2) + val imageViewGif: ImageViewElement = listOfElements.first() as ImageViewElement + val imageViewWebp: ImageViewElement = listOfElements.last() as ImageViewElement + + assertTrue(imageViewGif.src.isGif()) + assertTrue(imageViewWebp.src.isWebp()) + } }