Merge "Add extras to AFD, send orientation metadata." into klp-dev
This commit is contained in:
@ -7497,11 +7497,13 @@ package android.content.res {
|
|||||||
|
|
||||||
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
|
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);
|
||||||
|
ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long, android.os.Bundle);
|
||||||
method public void close() throws java.io.IOException;
|
method public void close() throws java.io.IOException;
|
||||||
method public java.io.FileInputStream createInputStream() 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 java.io.FileOutputStream createOutputStream() throws java.io.IOException;
|
||||||
method public int describeContents();
|
method public int describeContents();
|
||||||
method public long getDeclaredLength();
|
method public long getDeclaredLength();
|
||||||
|
method public android.os.Bundle getExtras();
|
||||||
method public java.io.FileDescriptor getFileDescriptor();
|
method public java.io.FileDescriptor getFileDescriptor();
|
||||||
method public long getLength();
|
method public long getLength();
|
||||||
method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
|
method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.content.res;
|
package android.content.res;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@ -42,17 +43,35 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
|
|||||||
private final ParcelFileDescriptor mFd;
|
private final ParcelFileDescriptor mFd;
|
||||||
private final long mStartOffset;
|
private final long mStartOffset;
|
||||||
private final long mLength;
|
private final long mLength;
|
||||||
|
private final Bundle mExtras;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new AssetFileDescriptor from the given values.
|
* Create a new AssetFileDescriptor from the given values.
|
||||||
|
*
|
||||||
* @param fd The underlying file descriptor.
|
* @param fd The underlying file descriptor.
|
||||||
* @param startOffset The location within the file that the asset starts.
|
* @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
|
* @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,
|
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
|
||||||
long length) {
|
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) {
|
if (fd == null) {
|
||||||
throw new IllegalArgumentException("fd must not be null");
|
throw new IllegalArgumentException("fd must not be null");
|
||||||
}
|
}
|
||||||
@ -63,8 +82,9 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
|
|||||||
mFd = fd;
|
mFd = fd;
|
||||||
mStartOffset = startOffset;
|
mStartOffset = startOffset;
|
||||||
mLength = length;
|
mLength = length;
|
||||||
|
mExtras = extras;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The AssetFileDescriptor contains its own ParcelFileDescriptor, which
|
* The AssetFileDescriptor contains its own ParcelFileDescriptor, which
|
||||||
* in addition to the normal FileDescriptor object also allows you to close
|
* in addition to the normal FileDescriptor object also allows you to close
|
||||||
@ -88,7 +108,15 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
|
|||||||
public long getStartOffset() {
|
public long getStartOffset() {
|
||||||
return mStartOffset;
|
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
|
* 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.
|
* {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
|
||||||
@ -307,25 +335,37 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
|
|||||||
super.write(oneByte);
|
super.write(oneByte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Parcelable interface */
|
/* Parcelable interface */
|
||||||
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return mFd.describeContents();
|
return mFd.describeContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
mFd.writeToParcel(out, flags);
|
mFd.writeToParcel(out, flags);
|
||||||
out.writeLong(mStartOffset);
|
out.writeLong(mStartOffset);
|
||||||
out.writeLong(mLength);
|
out.writeLong(mLength);
|
||||||
|
if (mExtras != null) {
|
||||||
|
out.writeInt(1);
|
||||||
|
out.writeBundle(mExtras);
|
||||||
|
} else {
|
||||||
|
out.writeInt(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetFileDescriptor(Parcel src) {
|
AssetFileDescriptor(Parcel src) {
|
||||||
mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
|
mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
|
||||||
mStartOffset = src.readLong();
|
mStartOffset = src.readLong();
|
||||||
mLength = src.readLong();
|
mLength = src.readLong();
|
||||||
|
if (src.readInt() != 0) {
|
||||||
|
mExtras = src.readBundle();
|
||||||
|
} else {
|
||||||
|
mExtras = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
|
public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
|
||||||
= new Parcelable.Creator<AssetFileDescriptor>() {
|
= new Parcelable.Creator<AssetFileDescriptor>() {
|
||||||
public AssetFileDescriptor createFromParcel(Parcel in) {
|
public AssetFileDescriptor createFromParcel(Parcel in) {
|
||||||
|
@ -28,7 +28,9 @@ import android.content.res.AssetFileDescriptor;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
import android.media.ExifInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
@ -42,8 +44,10 @@ import libcore.io.IoUtils;
|
|||||||
import libcore.io.Libcore;
|
import libcore.io.Libcore;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -76,6 +80,15 @@ public final class DocumentsContract {
|
|||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
|
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} */
|
/** {@hide} */
|
||||||
public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
|
public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
@ -657,6 +670,7 @@ public final class DocumentsContract {
|
|||||||
openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
|
openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
|
||||||
|
|
||||||
AssetFileDescriptor afd = null;
|
AssetFileDescriptor afd = null;
|
||||||
|
Bitmap bitmap = null;
|
||||||
try {
|
try {
|
||||||
afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
|
afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
|
||||||
|
|
||||||
@ -688,21 +702,36 @@ public final class DocumentsContract {
|
|||||||
|
|
||||||
opts.inJustDecodeBounds = false;
|
opts.inJustDecodeBounds = false;
|
||||||
opts.inSampleSize = Math.min(widthSample, heightSample);
|
opts.inSampleSize = Math.min(widthSample, heightSample);
|
||||||
Log.d(TAG, "Decoding with sample size " + opts.inSampleSize);
|
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
is.reset();
|
is.reset();
|
||||||
return BitmapFactory.decodeStream(is, null, opts);
|
bitmap = BitmapFactory.decodeStream(is, null, opts);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Libcore.os.lseek(fd, offset, SEEK_SET);
|
Libcore.os.lseek(fd, offset, SEEK_SET);
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
e.rethrowAsIOException();
|
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 {
|
} finally {
|
||||||
IoUtils.closeQuietly(afd);
|
IoUtils.closeQuietly(afd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -770,4 +799,44 @@ public final class DocumentsContract {
|
|||||||
|
|
||||||
client.call(METHOD_DELETE_DOCUMENT, null, in);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import android.os.Environment;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.provider.DocumentsContract.Document;
|
import android.provider.DocumentsContract.Document;
|
||||||
import android.provider.DocumentsContract.Root;
|
import android.provider.DocumentsContract.Root;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.DocumentsProvider;
|
import android.provider.DocumentsProvider;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
@ -313,19 +314,7 @@ public class ExternalStorageProvider extends DocumentsProvider {
|
|||||||
String documentId, Point sizeHint, CancellationSignal signal)
|
String documentId, Point sizeHint, CancellationSignal signal)
|
||||||
throws FileNotFoundException {
|
throws FileNotFoundException {
|
||||||
final File file = getFileForDocId(documentId);
|
final File file = getFileForDocId(documentId);
|
||||||
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
|
return DocumentsContract.openImageThumbnail(file);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getTypeForFile(File file) {
|
private static String getTypeForFile(File file) {
|
||||||
|
Reference in New Issue
Block a user