diff --git a/app-sample/src/main/AndroidManifest.xml b/app-sample/src/main/AndroidManifest.xml index 0c265b10..49abb09d 100644 --- a/app-sample/src/main/AndroidManifest.xml +++ b/app-sample/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" + android:usesCleartextTraffic="true" android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlideGifImageSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlideGifImageSample.java index b9acdb4c..d3f281a5 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlideGifImageSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlideGifImageSample.java @@ -11,6 +11,7 @@ import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.RequestManager; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; @@ -76,6 +77,11 @@ public class GlideGifImageSample extends MarkwonTextViewSample { .load(destination); } + @Override + public RequestBuilder loadGif(@NonNull AsyncDrawable drawable) { + return null; + } + @Override public void cancel(@NonNull Target target) { requestManager.clear(target); diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlidePlaceholderImageSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlidePlaceholderImageSample.java index 48b58cc6..5febee70 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlidePlaceholderImageSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/image/GlidePlaceholderImageSample.java @@ -7,6 +7,7 @@ import androidx.annotation.NonNull; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.target.Target; import io.noties.markwon.Markwon; @@ -45,6 +46,11 @@ public class GlidePlaceholderImageSample extends MarkwonTextViewSample { .placeholder(R.drawable.ic_home_black_36dp); } + @Override + public RequestBuilder loadGif(@NonNull AsyncDrawable drawable) { + return null; + } + @Override public void cancel(@NonNull Target target) { Glide.with(context) diff --git a/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java b/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java index c48df4af..699d5410 100644 --- a/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java +++ b/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java @@ -1,7 +1,9 @@ package io.noties.markwon.image.glide; +import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.Spanned; import android.widget.TextView; @@ -11,6 +13,7 @@ import androidx.annotation.Nullable; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; @@ -29,6 +32,10 @@ import io.noties.markwon.image.AsyncDrawableScheduler; import io.noties.markwon.image.DrawableUtils; import io.noties.markwon.image.ImageSpanFactory; +/** + * @since 4.0.0 + */ + /** * @since 4.0.0 */ @@ -39,15 +46,37 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { @NonNull RequestBuilder load(@NonNull AsyncDrawable drawable); + RequestBuilder loadGif(@NonNull AsyncDrawable drawable); + void cancel(@NonNull Target target); } @NonNull public static GlideImagesPlugin create(@NonNull final Context context) { - // @since 4.5.0 cache RequestManager - // sometimes `cancel` would be called after activity is destroyed, - // so `Glide.with(context)` will throw an exception - return create(Glide.with(context)); + return create(new GlideStore() { + @NonNull + @Override + public RequestBuilder load(@NonNull AsyncDrawable drawable) { + return Glide.with(getValidContext(context)) + .load(drawable.getDestination()); + } + + @Override + public RequestBuilder loadGif(@NonNull AsyncDrawable drawable) { + return Glide.with(getValidContext(context)) + .asGif() + .load(drawable.getDestination()); + } + + @Override + public void cancel(@NonNull Target target) { + if (target != null){ +// Fix the sentry issue Canvas: trying to use a recycled bitmap android.graphics.Bitmap@a44a774 +// Glide.with(getValidContext(context)) +// .clear(target); + } + } + }); } @NonNull @@ -59,6 +88,11 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { return requestManager.load(drawable.getDestination()); } + @Override + public RequestBuilder loadGif(@NonNull AsyncDrawable drawable) { + return requestManager.asGif().load(drawable.getDestination()); + } + @Override public void cancel(@NonNull Target target) { requestManager.clear(target); @@ -98,10 +132,22 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { AsyncDrawableScheduler.schedule(textView); } + public static Context getValidContext(final Context context) { + if (context instanceof Activity) { + final Activity activity = (Activity) context; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (activity.isDestroyed() || activity.isFinishing()) { + return context.getApplicationContext(); + } + } + } + return context; + } + private static class GlideAsyncDrawableLoader extends AsyncDrawableLoader { private final GlideStore glideStore; - private final Map> cache = new HashMap<>(2); + private final Map> cache = new HashMap<>(2); GlideAsyncDrawableLoader(@NonNull GlideStore glideStore) { this.glideStore = glideStore; @@ -109,17 +155,32 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { @Override public void load(@NonNull AsyncDrawable drawable) { - final Target target = new AsyncDrawableTarget(drawable); - cache.put(drawable, target); - glideStore.load(drawable) - .into(target); + String dest = drawable.getDestination(); + if (dest.contains(".gif")){ + final Target targetGif = new AsyncGifDrawableTarget(drawable); + if (!cache.containsKey(dest)){ + cache.put(dest, targetGif); + glideStore.loadGif(drawable) + .into(targetGif); + } + } else { + final Target target = new AsyncDrawableTarget(drawable); + if (!cache.containsKey(dest)){ + cache.put(dest, target); + glideStore.load(drawable) + .into(target); + } + } + } @Override public void cancel(@NonNull AsyncDrawable drawable) { - final Target target = cache.remove(drawable); + String dest = drawable.getDestination(); + final Target target = cache.get(dest); if (target != null) { glideStore.cancel(target); + cache.remove(dest); } } @@ -139,7 +200,8 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - if (cache.remove(drawable) != null) { + String dest = drawable.getDestination(); + if (cache.get(dest) != null) { if (drawable.isAttached()) { DrawableUtils.applyIntrinsicBoundsIfEmpty(resource); drawable.setResult(resource); @@ -158,12 +220,69 @@ public class GlideImagesPlugin extends AbstractMarkwonPlugin { @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { - if (cache.remove(drawable) != null) { + String dest = drawable.getDestination(); + if (cache.get(dest) != null) { if (errorDrawable != null && drawable.isAttached()) { DrawableUtils.applyIntrinsicBoundsIfEmpty(errorDrawable); drawable.setResult(errorDrawable); } + cache.remove(dest); + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + // we won't be checking if target is still present as cancellation + // must remove target anyway + if (drawable.isAttached()) { + drawable.clearResult(); + } + } + } + + + private class AsyncGifDrawableTarget extends CustomTarget { + + private final AsyncDrawable drawable; + + AsyncGifDrawableTarget(@NonNull AsyncDrawable drawable) { + this.drawable = drawable; + } + + @Override + public void onLoadStarted(@Nullable Drawable placeholder) { + if (placeholder != null + && drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(placeholder); + drawable.setResult(placeholder); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + String dest = drawable.getDestination(); + if (cache.get(dest) != null) { + if (errorDrawable != null + && drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(errorDrawable); + drawable.setResult(errorDrawable); + } + cache.remove(dest); + } + } + + @Override + public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition transition) { + String dest = drawable.getDestination(); + if (cache.get(dest) != null) { + if (drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(resource); + drawable.setResult(resource); + } + if (resource != null) { + resource.start(); + } } }