summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAndroid (Google) Code Review <android-gerrit@google.com>2009-09-23 14:31:04 -0400
committerAndroid (Google) Code Review <android-gerrit@google.com>2009-09-23 14:31:04 -0400
commit830207cab4047431d7a38443531a3a912bfecf3a (patch)
tree63ca54b12473187c82b660315b7860ae15f97075 /core
parent3136d4b0108deaca5ab525881de47d2979911131 (diff)
parent00c575a3fccb9d3065e913f1b8fcf93e18d44eaf (diff)
downloadframeworks_base-830207cab4047431d7a38443531a3a912bfecf3a.zip
frameworks_base-830207cab4047431d7a38443531a3a912bfecf3a.tar.gz
frameworks_base-830207cab4047431d7a38443531a3a912bfecf3a.tar.bz2
Merge change 25635 into eclair
* changes: Add new thumbnail API.
Diffstat (limited to 'core')
-rw-r--r--core/java/android/provider/MediaStore.java291
1 files changed, 259 insertions, 32 deletions
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 49b5bb1..106e833 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -23,11 +23,15 @@ import android.content.ContentValues;
import android.content.ContentUris;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
+import android.media.MiniThumbFile;
+import android.media.ThumbnailUtil;
import android.net.Uri;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileInputStream;
@@ -42,8 +46,7 @@ import java.text.Collator;
* The Media provider contains meta data for all available media on both internal
* and external storage devices.
*/
-public final class MediaStore
-{
+public final class MediaStore {
private final static String TAG = "MediaStore";
public static final String AUTHORITY = "media";
@@ -179,7 +182,7 @@ public final class MediaStore
* Common fields for most MediaProvider tables
*/
- public interface MediaColumns extends BaseColumns {
+ public interface MediaColumns extends BaseColumns {
/**
* The data stream for the file
* <P>Type: DATA STREAM</P>
@@ -227,10 +230,128 @@ public final class MediaStore
}
/**
+ * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
+ * to be accessed elsewhere.
+ */
+ private static class InternalThumbnails implements BaseColumns {
+ private static final int MINI_KIND = 1;
+ private static final int FULL_SCREEN_KIND = 2;
+ private static final int MICRO_KIND = 3;
+ private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
+
+ /**
+ * This method ensure thumbnails associated with origId are generated and decode the byte
+ * stream from database (MICRO_KIND) or file (MINI_KIND).
+ *
+ * Special optimization has been done to avoid further IPC communication for MICRO_KIND
+ * thumbnails.
+ *
+ * @param cr ContentResolver
+ * @param origId original image or video id
+ * @param kind could be MINI_KIND or MICRO_KIND
+ * @param options this is only used for MINI_KIND when decoding the Bitmap
+ * @param baseUri the base URI of requested thumbnails
+ * @return Bitmap bitmap of specified thumbnail kind
+ */
+ static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
+ Bitmap bitmap = null;
+ String filePath = null;
+ // some optimization for MICRO_KIND: if the magic is non-zero, we don't bother
+ // querying MediaProvider and simply return thumbnail.
+ if (kind == MICRO_KIND) {
+ MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
+ if (thumbFile.getMagic(origId) != 0) {
+ byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+ if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ if (bitmap == null) {
+ Log.w(TAG, "couldn't decode byte array.");
+ }
+ }
+ return bitmap;
+ }
+ }
+
+ Cursor c = null;
+ try {
+ Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
+ .appendQueryParameter("orig_id", String.valueOf(origId)).build();
+ c = cr.query(blockingUri, PROJECTION, null, null, null);
+ // This happens when original image/video doesn't exist.
+ if (c == null) return null;
+
+ // Assuming thumbnail has been generated, at least original image exists.
+ if (kind == MICRO_KIND) {
+ MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
+ byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+ if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ if (bitmap == null) {
+ Log.w(TAG, "couldn't decode byte array.");
+ }
+ }
+ } else if (kind == MINI_KIND) {
+ if (c.moveToFirst()) {
+ ParcelFileDescriptor pfdInput;
+ Uri thumbUri = null;
+ try {
+ long thumbId = c.getLong(0);
+ filePath = c.getString(1);
+ thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
+ pfdInput = cr.openFileDescriptor(thumbUri, "r");
+ bitmap = BitmapFactory.decodeFileDescriptor(
+ pfdInput.getFileDescriptor(), null, options);
+ pfdInput.close();
+ } catch (FileNotFoundException ex) {
+ Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
+ } catch (IOException ex) {
+ Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
+ } catch (OutOfMemoryError ex) {
+ Log.e(TAG, "failed to allocate memory for thumbnail "
+ + thumbUri + "; " + ex);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+ }
+
+ // We probably run out of space, so create the thumbnail in memory.
+ if (bitmap == null) {
+ Log.v(TAG, "We probably run out of space, so create the thumbnail in memory.");
+ int targetSize = kind == MINI_KIND ? ThumbnailUtil.THUMBNAIL_TARGET_SIZE :
+ ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
+ int maxPixelNum = kind == MINI_KIND ? ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS :
+ ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
+ Uri uri = Uri.parse(
+ baseUri.buildUpon().appendPath(String.valueOf(origId))
+ .toString().replaceFirst("thumbnails", "media"));
+ if (isVideo) {
+ c = cr.query(uri, PROJECTION, null, null, null);
+ if (c != null && c.moveToFirst()) {
+ bitmap = ThumbnailUtil.createVideoThumbnail(c.getString(1));
+ if (kind == MICRO_KIND) {
+ bitmap = ThumbnailUtil.extractMiniThumb(bitmap,
+ targetSize, targetSize, ThumbnailUtil.RECYCLE_INPUT);
+ }
+ }
+ } else {
+ bitmap = ThumbnailUtil.makeBitmap(targetSize, maxPixelNum, uri, cr);
+ }
+ }
+ } catch (SQLiteException ex) {
+ Log.w(TAG, ex);
+ } finally {
+ if (c != null) c.close();
+ }
+ return bitmap;
+ }
+ }
+
+ /**
* Contains meta data for all available images.
*/
- public static final class Images
- {
+ public static final class Images {
public interface ImageColumns extends MediaColumns {
/**
* The description of the image
@@ -298,21 +419,18 @@ public final class MediaStore
}
public static final class Media implements ImageColumns {
- public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
- {
+ public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
}
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
- String where, String orderBy)
- {
+ String where, String orderBy) {
return cr.query(uri, projection, where,
null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
- String selection, String [] selectionArgs, String orderBy)
- {
+ String selection, String [] selectionArgs, String orderBy) {
return cr.query(uri, projection, selection,
selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
@@ -326,8 +444,7 @@ public final class MediaStore
* @throws IOException
*/
public static final Bitmap getBitmap(ContentResolver cr, Uri url)
- throws FileNotFoundException, IOException
- {
+ throws FileNotFoundException, IOException {
InputStream input = cr.openInputStream(url);
Bitmap bitmap = BitmapFactory.decodeStream(input);
input.close();
@@ -344,9 +461,8 @@ public final class MediaStore
* @return The URL to the newly created image
* @throws FileNotFoundException
*/
- public static final String insertImage(ContentResolver cr, String imagePath, String name,
- String description) throws FileNotFoundException
- {
+ public static final String insertImage(ContentResolver cr, String imagePath,
+ String name, String description) throws FileNotFoundException {
// Check if file exists with a FileInputStream
FileInputStream stream = new FileInputStream(imagePath);
try {
@@ -415,8 +531,7 @@ public final class MediaStore
* for any reason.
*/
public static final String insertImage(ContentResolver cr, Bitmap source,
- String title, String description)
- {
+ String title, String description) {
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, title);
values.put(Images.Media.DESCRIPTION, description);
@@ -425,8 +540,7 @@ public final class MediaStore
Uri url = null;
String stringUrl = null; /* value to be returned */
- try
- {
+ try {
url = cr.insert(EXTERNAL_CONTENT_URI, values);
if (source != null) {
@@ -496,28 +610,48 @@ public final class MediaStore
* The default sort order for this table
*/
public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
- }
+ }
- public static class Thumbnails implements BaseColumns
- {
- public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
- {
+ /**
+ * This class allows developers to query and get two kinds of thumbnails:
+ * MINI_KIND: 512 x 384 thumbnail
+ * MICRO_KIND: 96 x 96 thumbnail
+ */
+ public static class Thumbnails implements BaseColumns {
+ public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
}
- public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)
- {
+ public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
+ String[] projection) {
return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
}
- public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)
- {
+ public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
+ String[] projection) {
return cr.query(EXTERNAL_CONTENT_URI, projection,
IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
kind, null, null);
}
/**
+ * This method checks if the thumbnails of the specified image (origId) has been created.
+ * It will be blocked until the thumbnails are generated.
+ *
+ * @param cr ContentResolver used to dispatch queries to MediaProvider.
+ * @param origId Original image id associated with thumbnail of interest.
+ * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
+ * @param options this is only used for MINI_KIND when decoding the Bitmap
+ * @return A Bitmap instance. It could be null if the original image
+ * associated with origId doesn't exist or memory is not enough.
+ */
+ public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ BitmapFactory.Options options) {
+ return InternalThumbnails.getThumbnail(cr, origId, kind, options,
+ EXTERNAL_CONTENT_URI, false);
+ }
+
+ /**
* Get the content:// style URI for the image media table on the
* given volume.
*
@@ -568,6 +702,11 @@ public final class MediaStore
public static final int MINI_KIND = 1;
public static final int FULL_SCREEN_KIND = 2;
public static final int MICRO_KIND = 3;
+ /**
+ * The blob raw data of thumbnail
+ * <P>Type: DATA STREAM</P>
+ */
+ public static final String THUMB_DATA = "thumb_data";
/**
* The width of the thumbnal
@@ -1182,7 +1321,7 @@ public final class MediaStore
* <P>Type: INTEGER</P>
*/
public static final String FIRST_YEAR = "minyear";
-
+
/**
* The year in which the latest songs
* on this album were released. This will often
@@ -1259,8 +1398,7 @@ public final class MediaStore
*/
public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
- public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
- {
+ public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
}
@@ -1405,6 +1543,95 @@ public final class MediaStore
*/
public static final String DEFAULT_SORT_ORDER = TITLE;
}
+
+ /**
+ * This class allows developers to query and get two kinds of thumbnails:
+ * MINI_KIND: 512 x 384 thumbnail
+ * MICRO_KIND: 96 x 96 thumbnail
+ *
+ */
+ public static class Thumbnails implements BaseColumns {
+ /**
+ * This method checks if the thumbnails of the specified image (origId) has been created.
+ * It will be blocked until the thumbnails are generated.
+ *
+ * @param cr ContentResolver used to dispatch queries to MediaProvider.
+ * @param origId Original image id associated with thumbnail of interest.
+ * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
+ * @param options this is only used for MINI_KIND when decoding the Bitmap
+ * @return A Bitmap instance. It could be null if the original image associated with
+ * origId doesn't exist or memory is not enough.
+ */
+ public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ BitmapFactory.Options options) {
+ return InternalThumbnails.getThumbnail(cr, origId, kind, options,
+ EXTERNAL_CONTENT_URI, true);
+ }
+
+ /**
+ * Get the content:// style URI for the image media table on the
+ * given volume.
+ *
+ * @param volumeName the name of the volume to get the URI for
+ * @return the URI to the image media table on the given volume
+ */
+ public static Uri getContentUri(String volumeName) {
+ return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+ "/video/thumbnails");
+ }
+
+ /**
+ * The content:// style URI for the internal storage.
+ */
+ public static final Uri INTERNAL_CONTENT_URI =
+ getContentUri("internal");
+
+ /**
+ * The content:// style URI for the "primary" external storage
+ * volume.
+ */
+ public static final Uri EXTERNAL_CONTENT_URI =
+ getContentUri("external");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "video_id ASC";
+
+ /**
+ * The data stream for the thumbnail
+ * <P>Type: DATA STREAM</P>
+ */
+ public static final String DATA = "_data";
+
+ /**
+ * The original image for the thumbnal
+ * <P>Type: INTEGER (ID from Video table)</P>
+ */
+ public static final String VIDEO_ID = "video_id";
+
+ /**
+ * The kind of the thumbnail
+ * <P>Type: INTEGER (One of the values below)</P>
+ */
+ public static final String KIND = "kind";
+
+ public static final int MINI_KIND = 1;
+ public static final int FULL_SCREEN_KIND = 2;
+ public static final int MICRO_KIND = 3;
+
+ /**
+ * The width of the thumbnal
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String WIDTH = "width";
+
+ /**
+ * The height of the thumbnail
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String HEIGHT = "height";
+ }
}
/**