Call ImageDecoder directly in ResourcesImpl
Test: Existing tests
Add a new (hidden) ImageDecoder.Source that accepts an AssetInputStream.
This allows us to create an AnimatedImageDrawable without fear of the
client closing the stream.
Call it from ResourcesImpl instead of Drawable.createFromResourceStream.
Change-Id: I07e00ca60c97538335a6310e830b73211fd8e7bb
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 97cb78b..00e4841 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -27,9 +27,11 @@
import android.annotation.StyleableRes;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Configuration.NativeConfig;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -809,8 +811,13 @@
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
- is.close();
+ AssetInputStream ais = (AssetInputStream) is;
+ // ImageDecoder will close the input stream.
+ ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+ wrapper, value);
+ dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
}
} finally {
stack.pop();
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index bbf2145..c3e0280 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -25,7 +25,7 @@
import android.annotation.RawRes;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
+import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
@@ -257,6 +257,63 @@
}
}
+ /**
+ * Takes ownership of the AssetInputStream.
+ *
+ * @hide
+ */
+ public static class AssetInputStreamSource extends Source {
+ public AssetInputStreamSource(@NonNull AssetInputStream ais,
+ @NonNull Resources res, @NonNull TypedValue value) {
+ mAssetInputStream = ais;
+ mResources = res;
+
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ mDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ mDensity = value.density;
+ } else {
+ mDensity = Bitmap.DENSITY_NONE;
+ }
+ }
+
+ private AssetInputStream mAssetInputStream;
+ private final Resources mResources;
+ private final int mDensity;
+
+ @Override
+ public Resources getResources() { return mResources; }
+
+ @Override
+ public int getDensity() {
+ return mDensity;
+ }
+
+ @Override
+ public ImageDecoder createImageDecoder() throws IOException {
+ ImageDecoder decoder = null;
+ synchronized (this) {
+ if (mAssetInputStream == null) {
+ throw new IOException("Cannot reuse AssetInputStreamSource");
+ }
+ AssetInputStream ais = mAssetInputStream;
+ mAssetInputStream = null;
+ try {
+ long asset = ais.getNativeAsset();
+ decoder = nCreate(asset);
+ } finally {
+ if (decoder == null) {
+ IoUtils.closeQuietly(ais);
+ } else {
+ decoder.mInputStream = ais;
+ decoder.mOwnsInputStream = true;
+ }
+ }
+ return decoder;
+ }
+ }
+ }
+
private static class ResourceSource extends Source {
ResourceSource(@NonNull Resources res, int resId) {
mResources = res;
@@ -290,11 +347,7 @@
mResDensity = value.density;
}
- if (!(is instanceof AssetManager.AssetInputStream)) {
- // This should never happen.
- throw new RuntimeException("Resource is not an asset?");
- }
- long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
+ long asset = ((AssetInputStream) is).getNativeAsset();
decoder = nCreate(asset);
} finally {
if (decoder == null) {