diff --git a/instrumentation/src/androidTest/java/com/bumptech/glide/LoadAnimatedImageResourceTest.java b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadAnimatedImageResourceTest.java new file mode 100644 index 0000000000..691b0b9777 --- /dev/null +++ b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadAnimatedImageResourceTest.java @@ -0,0 +1,99 @@ +package com.bumptech.glide; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; + +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.drawable.AnimatedImageDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.bumptech.glide.test.GlideApp; +import com.bumptech.glide.test.ResourceIds; +import com.bumptech.glide.testutil.ConcurrencyHelper; +import com.bumptech.glide.testutil.TearDownGlide; +import java.io.IOException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +/** + * Tests that Glide is able to load animated images (WebP and AVIF) stored in resources and loaded + * as {@link android.graphics.drawable.AnimatedImageDrawable}s when the underlying Android platform + * supports it. + */ +@RunWith(AndroidJUnit4.class) +public class LoadAnimatedImageResourceTest { + @Rule public final TearDownGlide tearDownGlide = new TearDownGlide(); + private final ConcurrencyHelper concurrency = new ConcurrencyHelper(); + + private Context context; + + private static final boolean IS_ANIMATED_WEBP_SUPPORTED = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + private static final boolean IS_ANIMATED_AVIF_SUPPORTED = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + context = ApplicationProvider.getApplicationContext(); + } + + @Test + public void loadAnimatedImageResourceId_fromInt_decodesAnimatedImageDrawable_Webp() { + assumeTrue(IS_ANIMATED_WEBP_SUPPORTED); + Drawable frame = + concurrency.get(Glide.with(context).load(ResourceIds.raw.animated_webp).submit()); + + assertThat(frame).isNotNull(); + assertThat(frame).isInstanceOf(AnimatedImageDrawable.class); + } + + @Test + public void loadAnimatedImageResourceId_fromInt_decodesAnimatedImageDrawable_Avif() { + assumeTrue(IS_ANIMATED_AVIF_SUPPORTED); + Drawable frame = + concurrency.get(Glide.with(context).load(ResourceIds.raw.animated_avif).submit()); + + assertThat(frame).isNotNull(); + assertThat(frame).isInstanceOf(AnimatedImageDrawable.class); + } + + @Test + public void loadAnimatedImageUri_fromId_decodesAnimatedImageDrawable_Webp() { + assumeTrue(IS_ANIMATED_WEBP_SUPPORTED); + Uri uri = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(context.getPackageName()) + .path(String.valueOf(ResourceIds.raw.animated_webp)) + .build(); + + Drawable frame = concurrency.get(GlideApp.with(context).load(uri).submit()); + + assertThat(frame).isNotNull(); + assertThat(frame).isInstanceOf(AnimatedImageDrawable.class); + } + + @Test + public void loadAnimatedImageUri_fromId_decodesAnimatedImageDrawable_Avif() { + assumeTrue(IS_ANIMATED_AVIF_SUPPORTED); + Uri uri = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(context.getPackageName()) + .path(String.valueOf(ResourceIds.raw.animated_avif)) + .build(); + + Drawable frame = concurrency.get(GlideApp.with(context).load(uri).submit()); + + assertThat(frame).isNotNull(); + assertThat(frame).isInstanceOf(AnimatedImageDrawable.class); + } +} diff --git a/instrumentation/src/androidTest/java/com/bumptech/glide/test/ResourceIds.java b/instrumentation/src/androidTest/java/com/bumptech/glide/test/ResourceIds.java index b4025ab0fa..86b266da86 100644 --- a/instrumentation/src/androidTest/java/com/bumptech/glide/test/ResourceIds.java +++ b/instrumentation/src/androidTest/java/com/bumptech/glide/test/ResourceIds.java @@ -25,6 +25,8 @@ public interface raw { int opaque_interlaced_gif = getResourceId("raw", "opaque_interlaced_gif"); int webkit_logo_p3 = getResourceId("raw", "webkit_logo_p3"); int video = getResourceId("raw", "video"); + int animated_webp = getResourceId("raw", "dl_world_anim_webp"); + int animated_avif = getResourceId("raw", "dl_world_anim_avif"); } public interface drawable { diff --git a/instrumentation/src/main/res/raw/dl_world_anim_avif.avif b/instrumentation/src/main/res/raw/dl_world_anim_avif.avif new file mode 100644 index 0000000000..22c428a383 Binary files /dev/null and b/instrumentation/src/main/res/raw/dl_world_anim_avif.avif differ diff --git a/instrumentation/src/main/res/raw/dl_world_anim_webp.webp b/instrumentation/src/main/res/raw/dl_world_anim_webp.webp new file mode 100644 index 0000000000..a9152404dd Binary files /dev/null and b/instrumentation/src/main/res/raw/dl_world_anim_webp.webp differ diff --git a/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java index e09e4d9f84..c78b1d4f76 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java @@ -27,7 +27,7 @@ /** * Allows decoding animated images using {@link ImageDecoder}. * - *
Supported formats: WebP on Android P+. + *
Supported formats: WebP on Android P+. AVIF on Android 12/S+. */ @RequiresApi(Build.VERSION_CODES.P) public final class AnimatedImageDecoder { @@ -61,7 +61,8 @@ boolean handles(InputStream is) throws IOException { } private boolean isHandled(ImageType imageType) { - return imageType == ImageType.ANIMATED_WEBP; + return imageType == ImageType.ANIMATED_WEBP + || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && imageType == ImageType.ANIMATED_AVIF); } @Synthetic