diff --git a/build.gradle b/build.gradle index 84a256de4..8bedf3761 100644 --- a/build.gradle +++ b/build.gradle @@ -177,7 +177,7 @@ dependencies { implementation "me.leolin:ShortcutBadger:1.1.16" // display messagecount on the home screen icon. implementation 'com.jpardogo.materialtabstrip:library:1.0.9' // used in the emoji selector for the tab selection. implementation 'com.github.chrisbanes:PhotoView:2.1.3' // does the zooming on photos / media - implementation 'com.github.penfeizhou.android.animation:glide-plugin:3.0.2' // APNG & animated webp support. + implementation 'com.github.penfeizhou.android.animation:awebp:3.0.2' // animated webp support. implementation 'com.caverock:androidsvg-aar:1.4' // SVG support. implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' diff --git a/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpDrawableTranscoder.java b/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpDrawableTranscoder.java new file mode 100644 index 000000000..164fe0b43 --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpDrawableTranscoder.java @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.glide.webp; + +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.drawable.DrawableResource; +import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; +import com.github.penfeizhou.animation.webp.WebPDrawable; +import com.github.penfeizhou.animation.webp.decode.WebPDecoder; + +public class WebpDrawableTranscoder implements ResourceTranscoder { + @Nullable + @Override + public Resource transcode(@NonNull Resource toTranscode, @NonNull Options options) { + final WebPDrawable webPDrawable = new WebPDrawable(toTranscode.get()); + webPDrawable.setAutoPlay(true); + return new DrawableResource(webPDrawable) { + @NonNull + @Override + public Class getResourceClass() { + return Drawable.class; + } + + @Override + public int getSize() { + return webPDrawable.getMemorySize(); + } + + @Override + public void recycle() { + webPDrawable.stop(); + } + + @Override + public void initialize() { + super.initialize(); + } + }; + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpLoader.java b/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpLoader.java new file mode 100644 index 000000000..c28cb48c6 --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/glide/webp/WebpLoader.java @@ -0,0 +1,61 @@ +package org.thoughtcrime.securesms.glide.webp; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.SimpleResource; +import com.github.penfeizhou.animation.loader.ByteBufferLoader; +import com.github.penfeizhou.animation.io.StreamReader; +import com.github.penfeizhou.animation.loader.Loader; +import com.github.penfeizhou.animation.webp.decode.WebPDecoder; +import com.github.penfeizhou.animation.webp.decode.WebPParser; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +public class WebpLoader implements ResourceDecoder { + + @Override + public boolean handles(@NonNull InputStream source, @NonNull Options options) { + return WebPParser.isAWebP(new StreamReader(source)); + } + + @Nullable + @Override + public Resource decode(@NonNull final InputStream source, int width, int height, @NonNull Options options) throws IOException { + byte[] data = inputStreamToBytes(source); + if (data == null) { + return null; + } + ByteBuffer byteBuffer = ByteBuffer.wrap(data); + Loader loader = new ByteBufferLoader() { + @Override + public ByteBuffer getByteBuffer() { + byteBuffer.position(0); + return byteBuffer; + } + }; + return new SimpleResource<>(new WebPDecoder(loader, null)); + } + + private static byte[] inputStreamToBytes(InputStream is) { + final int bufferSize = 16384; + ByteArrayOutputStream buffer = new ByteArrayOutputStream(bufferSize); + try { + int nRead; + byte[] data = new byte[bufferSize]; + while ((nRead = is.read(data)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + } catch (IOException e) { + return null; + } + return buffer.toByteArray(); + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java b/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java index f7316573e..ab2eb7e7e 100644 --- a/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java +++ b/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java @@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; import androidx.annotation.NonNull; + +import android.graphics.drawable.Drawable; import android.util.Log; import com.bumptech.glide.Glide; @@ -11,9 +13,12 @@ import com.bumptech.glide.Registry; import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.load.model.UnitModelLoader; import com.bumptech.glide.module.AppGlideModule; +import com.github.penfeizhou.animation.webp.decode.WebPDecoder; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.glide.ContactPhotoLoader; +import org.thoughtcrime.securesms.glide.webp.WebpDrawableTranscoder; +import org.thoughtcrime.securesms.glide.webp.WebpLoader; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import java.io.File; @@ -49,5 +54,9 @@ public class SignalGlideModule extends AppGlideModule { registry.append(ContactPhoto.class, InputStream.class, new ContactPhotoLoader.Factory(context)); registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context)); //registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); + + registry + .prepend(InputStream.class, WebPDecoder.class, new WebpLoader()) + .register(WebPDecoder.class, Drawable.class, new WebpDrawableTranscoder()); } }