Loading api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -7497,11 +7497,13 @@ package android.content.res { public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable { ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long); ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long, android.os.Bundle); method public void close() throws java.io.IOException; method public java.io.FileInputStream createInputStream() throws java.io.IOException; method public java.io.FileOutputStream createOutputStream() throws java.io.IOException; method public int describeContents(); method public long getDeclaredLength(); method public android.os.Bundle getExtras(); method public java.io.FileDescriptor getFileDescriptor(); method public long getLength(); method public android.os.ParcelFileDescriptor getParcelFileDescriptor(); core/java/android/content/res/AssetFileDescriptor.java +48 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content.res; import android.os.Bundle; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; Loading @@ -42,9 +43,11 @@ public class AssetFileDescriptor implements Parcelable, Closeable { private final ParcelFileDescriptor mFd; private final long mStartOffset; private final long mLength; private final Bundle mExtras; /** * Create a new AssetFileDescriptor from the given values. * * @param fd The underlying file descriptor. * @param startOffset The location within the file that the asset starts. * This must be 0 if length is UNKNOWN_LENGTH. Loading @@ -53,6 +56,22 @@ public class AssetFileDescriptor implements Parcelable, Closeable { */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { this(fd, startOffset, length, null); } /** * Create a new AssetFileDescriptor from the given values. * * @param fd The underlying file descriptor. * @param startOffset The location within the file that the asset starts. * This must be 0 if length is UNKNOWN_LENGTH. * @param length The number of bytes of the asset, or * {@link #UNKNOWN_LENGTH} if it extends to the end of the file. * @param extras additional details that can be used to interpret the * underlying file descriptor. May be null. */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length, Bundle extras) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } Loading @@ -63,6 +82,7 @@ public class AssetFileDescriptor implements Parcelable, Closeable { mFd = fd; mStartOffset = startOffset; mLength = length; mExtras = extras; } /** Loading @@ -89,6 +109,14 @@ public class AssetFileDescriptor implements Parcelable, Closeable { return mStartOffset; } /** * Returns any additional details that can be used to interpret the * underlying file descriptor. May be null. */ public Bundle getExtras() { return mExtras; } /** * Returns the total number of bytes of this asset entry's data. May be * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. Loading Loading @@ -308,22 +336,34 @@ public class AssetFileDescriptor implements Parcelable, Closeable { } } /* Parcelable interface */ @Override public int describeContents() { return mFd.describeContents(); } @Override public void writeToParcel(Parcel out, int flags) { mFd.writeToParcel(out, flags); out.writeLong(mStartOffset); out.writeLong(mLength); if (mExtras != null) { out.writeInt(1); out.writeBundle(mExtras); } else { out.writeInt(0); } } AssetFileDescriptor(Parcel src) { mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); mStartOffset = src.readLong(); mLength = src.readLong(); if (src.readInt() != 0) { mExtras = src.readBundle(); } else { mExtras = null; } } public static final Parcelable.Creator<AssetFileDescriptor> CREATOR Loading core/java/android/provider/DocumentsContract.java +72 −3 Original line number Diff line number Diff line Loading @@ -28,7 +28,9 @@ import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.Point; import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; Loading @@ -42,8 +44,10 @@ import libcore.io.IoUtils; import libcore.io.Libcore; import java.io.BufferedInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; Loading Loading @@ -76,6 +80,15 @@ public final class DocumentsContract { /** {@hide} */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; /** * Included in {@link AssetFileDescriptor#getExtras()} when returned * thumbnail should be rotated. * * @see MediaStore.Images.ImageColumns#ORIENTATION * @hide */ public static final String EXTRA_ORIENTATION = "android.content.extra.ORIENTATION"; /** {@hide} */ public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT"; /** {@hide} */ Loading Loading @@ -657,6 +670,7 @@ public final class DocumentsContract { openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size); AssetFileDescriptor afd = null; Bitmap bitmap = null; try { afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal); Loading Loading @@ -688,21 +702,36 @@ public final class DocumentsContract { opts.inJustDecodeBounds = false; opts.inSampleSize = Math.min(widthSample, heightSample); Log.d(TAG, "Decoding with sample size " + opts.inSampleSize); if (is != null) { is.reset(); return BitmapFactory.decodeStream(is, null, opts); bitmap = BitmapFactory.decodeStream(is, null, opts); } else { try { Libcore.os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { e.rethrowAsIOException(); } return BitmapFactory.decodeFileDescriptor(fd, null, opts); bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts); } // Transform the bitmap if requested. We use a side-channel to // communicate the orientation, since EXIF thumbnails don't contain // the rotation flags of the original image. final Bundle extras = afd.getExtras(); final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; if (orientation != 0) { final int width = bitmap.getWidth(); final int height = bitmap.getHeight(); final Matrix m = new Matrix(); m.setRotate(orientation, width / 2, height / 2); bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); } } finally { IoUtils.closeQuietly(afd); } return bitmap; } /** Loading Loading @@ -770,4 +799,44 @@ public final class DocumentsContract { client.call(METHOD_DELETE_DOCUMENT, null, in); } /** * Open the given image for thumbnail purposes, using any embedded EXIF * thumbnail if available, and providing orientation hints from the parent * image. * * @hide */ public static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException { final ParcelFileDescriptor pfd = ParcelFileDescriptor.open( file, ParcelFileDescriptor.MODE_READ_ONLY); Bundle extras = null; try { final ExifInterface exif = new ExifInterface(file.getAbsolutePath()); switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) { case ExifInterface.ORIENTATION_ROTATE_90: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 90); break; case ExifInterface.ORIENTATION_ROTATE_180: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 180); break; case ExifInterface.ORIENTATION_ROTATE_270: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 270); break; } final long[] thumb = exif.getThumbnailRange(); if (thumb != null) { return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras); } } catch (IOException e) { } return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras); } } packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +2 −13 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsContract; import android.provider.DocumentsProvider; import android.webkit.MimeTypeMap; Loading Loading @@ -313,19 +314,7 @@ public class ExternalStorageProvider extends DocumentsProvider { String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { final File file = getFileForDocId(documentId); final ParcelFileDescriptor pfd = ParcelFileDescriptor.open( file, ParcelFileDescriptor.MODE_READ_ONLY); try { final ExifInterface exif = new ExifInterface(file.getAbsolutePath()); final long[] thumb = exif.getThumbnailRange(); if (thumb != null) { return new AssetFileDescriptor(pfd, thumb[0], thumb[1]); } } catch (IOException e) { } return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); return DocumentsContract.openImageThumbnail(file); } private static String getTypeForFile(File file) { Loading Loading
api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -7497,11 +7497,13 @@ package android.content.res { public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable { ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long); ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long, android.os.Bundle); method public void close() throws java.io.IOException; method public java.io.FileInputStream createInputStream() throws java.io.IOException; method public java.io.FileOutputStream createOutputStream() throws java.io.IOException; method public int describeContents(); method public long getDeclaredLength(); method public android.os.Bundle getExtras(); method public java.io.FileDescriptor getFileDescriptor(); method public long getLength(); method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
core/java/android/content/res/AssetFileDescriptor.java +48 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content.res; import android.os.Bundle; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; Loading @@ -42,9 +43,11 @@ public class AssetFileDescriptor implements Parcelable, Closeable { private final ParcelFileDescriptor mFd; private final long mStartOffset; private final long mLength; private final Bundle mExtras; /** * Create a new AssetFileDescriptor from the given values. * * @param fd The underlying file descriptor. * @param startOffset The location within the file that the asset starts. * This must be 0 if length is UNKNOWN_LENGTH. Loading @@ -53,6 +56,22 @@ public class AssetFileDescriptor implements Parcelable, Closeable { */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { this(fd, startOffset, length, null); } /** * Create a new AssetFileDescriptor from the given values. * * @param fd The underlying file descriptor. * @param startOffset The location within the file that the asset starts. * This must be 0 if length is UNKNOWN_LENGTH. * @param length The number of bytes of the asset, or * {@link #UNKNOWN_LENGTH} if it extends to the end of the file. * @param extras additional details that can be used to interpret the * underlying file descriptor. May be null. */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length, Bundle extras) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } Loading @@ -63,6 +82,7 @@ public class AssetFileDescriptor implements Parcelable, Closeable { mFd = fd; mStartOffset = startOffset; mLength = length; mExtras = extras; } /** Loading @@ -89,6 +109,14 @@ public class AssetFileDescriptor implements Parcelable, Closeable { return mStartOffset; } /** * Returns any additional details that can be used to interpret the * underlying file descriptor. May be null. */ public Bundle getExtras() { return mExtras; } /** * Returns the total number of bytes of this asset entry's data. May be * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. Loading Loading @@ -308,22 +336,34 @@ public class AssetFileDescriptor implements Parcelable, Closeable { } } /* Parcelable interface */ @Override public int describeContents() { return mFd.describeContents(); } @Override public void writeToParcel(Parcel out, int flags) { mFd.writeToParcel(out, flags); out.writeLong(mStartOffset); out.writeLong(mLength); if (mExtras != null) { out.writeInt(1); out.writeBundle(mExtras); } else { out.writeInt(0); } } AssetFileDescriptor(Parcel src) { mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); mStartOffset = src.readLong(); mLength = src.readLong(); if (src.readInt() != 0) { mExtras = src.readBundle(); } else { mExtras = null; } } public static final Parcelable.Creator<AssetFileDescriptor> CREATOR Loading
core/java/android/provider/DocumentsContract.java +72 −3 Original line number Diff line number Diff line Loading @@ -28,7 +28,9 @@ import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.Point; import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; Loading @@ -42,8 +44,10 @@ import libcore.io.IoUtils; import libcore.io.Libcore; import java.io.BufferedInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; Loading Loading @@ -76,6 +80,15 @@ public final class DocumentsContract { /** {@hide} */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; /** * Included in {@link AssetFileDescriptor#getExtras()} when returned * thumbnail should be rotated. * * @see MediaStore.Images.ImageColumns#ORIENTATION * @hide */ public static final String EXTRA_ORIENTATION = "android.content.extra.ORIENTATION"; /** {@hide} */ public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT"; /** {@hide} */ Loading Loading @@ -657,6 +670,7 @@ public final class DocumentsContract { openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size); AssetFileDescriptor afd = null; Bitmap bitmap = null; try { afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal); Loading Loading @@ -688,21 +702,36 @@ public final class DocumentsContract { opts.inJustDecodeBounds = false; opts.inSampleSize = Math.min(widthSample, heightSample); Log.d(TAG, "Decoding with sample size " + opts.inSampleSize); if (is != null) { is.reset(); return BitmapFactory.decodeStream(is, null, opts); bitmap = BitmapFactory.decodeStream(is, null, opts); } else { try { Libcore.os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { e.rethrowAsIOException(); } return BitmapFactory.decodeFileDescriptor(fd, null, opts); bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts); } // Transform the bitmap if requested. We use a side-channel to // communicate the orientation, since EXIF thumbnails don't contain // the rotation flags of the original image. final Bundle extras = afd.getExtras(); final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; if (orientation != 0) { final int width = bitmap.getWidth(); final int height = bitmap.getHeight(); final Matrix m = new Matrix(); m.setRotate(orientation, width / 2, height / 2); bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); } } finally { IoUtils.closeQuietly(afd); } return bitmap; } /** Loading Loading @@ -770,4 +799,44 @@ public final class DocumentsContract { client.call(METHOD_DELETE_DOCUMENT, null, in); } /** * Open the given image for thumbnail purposes, using any embedded EXIF * thumbnail if available, and providing orientation hints from the parent * image. * * @hide */ public static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException { final ParcelFileDescriptor pfd = ParcelFileDescriptor.open( file, ParcelFileDescriptor.MODE_READ_ONLY); Bundle extras = null; try { final ExifInterface exif = new ExifInterface(file.getAbsolutePath()); switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) { case ExifInterface.ORIENTATION_ROTATE_90: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 90); break; case ExifInterface.ORIENTATION_ROTATE_180: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 180); break; case ExifInterface.ORIENTATION_ROTATE_270: extras = new Bundle(1); extras.putInt(EXTRA_ORIENTATION, 270); break; } final long[] thumb = exif.getThumbnailRange(); if (thumb != null) { return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras); } } catch (IOException e) { } return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras); } }
packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +2 −13 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsContract; import android.provider.DocumentsProvider; import android.webkit.MimeTypeMap; Loading Loading @@ -313,19 +314,7 @@ public class ExternalStorageProvider extends DocumentsProvider { String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { final File file = getFileForDocId(documentId); final ParcelFileDescriptor pfd = ParcelFileDescriptor.open( file, ParcelFileDescriptor.MODE_READ_ONLY); try { final ExifInterface exif = new ExifInterface(file.getAbsolutePath()); final long[] thumb = exif.getThumbnailRange(); if (thumb != null) { return new AssetFileDescriptor(pfd, thumb[0], thumb[1]); } } catch (IOException e) { } return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); return DocumentsContract.openImageThumbnail(file); } private static String getTypeForFile(File file) { Loading