am 25b99efb: am ac0cb359: am 8e656df0: Merge "Add extras to AFD, send orientation metadata." into klp-dev
* commit '25b99efb3259c9f6d7bcdacd3cb2bafd6b1a0e63':
Add extras to AFD, send orientation metadata.
diff --git a/api/current.txt b/api/current.txt
index bc3a3f2..5669203 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7565,11 +7565,13 @@
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();
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index e4cc77f..28edde0 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -42,17 +43,35 @@
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.
+ * 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.
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
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");
}
@@ -63,8 +82,9 @@
mFd = fd;
mStartOffset = startOffset;
mLength = length;
+ mExtras = extras;
}
-
+
/**
* The AssetFileDescriptor contains its own ParcelFileDescriptor, which
* in addition to the normal FileDescriptor object also allows you to close
@@ -88,7 +108,15 @@
public long getStartOffset() {
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.
@@ -307,25 +335,37 @@
super.write(oneByte);
}
}
-
-
+
/* 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
= new Parcelable.Creator<AssetFileDescriptor>() {
public AssetFileDescriptor createFromParcel(Parcel in) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index c5e4f21..9d35847 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -28,7 +28,9 @@
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;
@@ -42,8 +44,10 @@
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;
@@ -76,6 +80,15 @@
/** {@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} */
@@ -657,6 +670,7 @@
openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
AssetFileDescriptor afd = null;
+ Bitmap bitmap = null;
try {
afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
@@ -688,21 +702,36 @@
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;
}
/**
@@ -770,4 +799,44 @@
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);
+ }
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 189e985..11ff2d8 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -27,6 +27,7 @@
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;
@@ -313,19 +314,7 @@
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) {