• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.provider;
18 
19 import android.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.content.ContentResolver;
22 import android.content.ContentValues;
23 import android.content.ContentUris;
24 import android.database.Cursor;
25 import android.database.DatabaseUtils;
26 import android.database.sqlite.SQLiteException;
27 import android.graphics.Bitmap;
28 import android.graphics.BitmapFactory;
29 import android.graphics.Matrix;
30 import android.media.MiniThumbFile;
31 import android.media.ThumbnailUtils;
32 import android.net.Uri;
33 import android.os.Environment;
34 import android.os.ParcelFileDescriptor;
35 import android.util.Log;
36 
37 import java.io.FileInputStream;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.io.UnsupportedEncodingException;
43 import java.text.Collator;
44 
45 /**
46  * The Media provider contains meta data for all available media on both internal
47  * and external storage devices.
48  */
49 public final class MediaStore {
50     private final static String TAG = "MediaStore";
51 
52     public static final String AUTHORITY = "media";
53 
54     private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
55 
56     /**
57      * Activity Action: Launch a music player.
58      * The activity should be able to play, browse, or manipulate music files stored on the device.
59      */
60     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
61     public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
62 
63     /**
64      * Activity Action: Perform a search for media.
65      * Contains at least the {@link android.app.SearchManager#QUERY} extra.
66      * May also contain any combination of the following extras:
67      * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
68      *
69      * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
70      * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
71      * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
72      * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
73      */
74     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
75     public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
76 
77     /**
78      * An intent to perform a search for music media and automatically play content from the
79      * result when possible. This can be fired, for example, by the result of a voice recognition
80      * command to listen to music.
81      * <p>
82      * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string
83      * that can contain any type of unstructured music search, like the name of an artist,
84      * an album, a song, a genre, or any combination of these.
85      * <p>
86      * Because this intent includes an open-ended unstructured search string, it makes the most
87      * sense for apps that can support large-scale search of music, such as services connected
88      * to an online database of music which can be streamed and played on the device.
89      */
90     public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
91             "android.media.action.MEDIA_PLAY_FROM_SEARCH";
92 
93     /**
94      * The name of the Intent-extra used to define the artist
95      */
96     public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
97     /**
98      * The name of the Intent-extra used to define the album
99      */
100     public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
101     /**
102      * The name of the Intent-extra used to define the song title
103      */
104     public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
105     /**
106      * The name of the Intent-extra used to define the search focus. The search focus
107      * indicates whether the search should be for things related to the artist, album
108      * or song that is identified by the other extras.
109      */
110     public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
111 
112     /**
113      * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
114      * This is an int property that overrides the activity's requestedOrientation.
115      * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
116      */
117     public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
118 
119     /**
120      * The name of an Intent-extra used to control the UI of a ViewImage.
121      * This is a boolean property that overrides the activity's default fullscreen state.
122      */
123     public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
124 
125     /**
126      * The name of an Intent-extra used to control the UI of a ViewImage.
127      * This is a boolean property that specifies whether or not to show action icons.
128      */
129     public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
130 
131     /**
132      * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
133      * This is a boolean property that specifies whether or not to finish the MovieView activity
134      * when the movie completes playing. The default value is true, which means to automatically
135      * exit the movie player activity when the movie completes playing.
136      */
137     public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
138 
139     /**
140      * The name of the Intent action used to launch a camera in still image mode.
141      */
142     public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
143 
144     /**
145      * The name of the Intent action used to launch a camera in video mode.
146      */
147     public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
148 
149     /**
150      * Standard Intent action that can be sent to have the camera application
151      * capture an image and return it.
152      * <p>
153      * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
154      * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
155      * object in the extra field. This is useful for applications that only need a small image.
156      * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
157      * value of EXTRA_OUTPUT.
158      * @see #EXTRA_OUTPUT
159      * @see #EXTRA_VIDEO_QUALITY
160      */
161     public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
162 
163     /**
164      * Standard Intent action that can be sent to have the camera application
165      * capture an video and return it.
166      * <p>
167      * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
168      * <p>
169      * The caller may pass in an extra EXTRA_OUTPUT to control
170      * where the video is written. If EXTRA_OUTPUT is not present the video will be
171      * written to the standard location for videos, and the Uri of that location will be
172      * returned in the data field of the Uri.
173      * @see #EXTRA_OUTPUT
174      */
175     public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
176 
177     /**
178      * The name of the Intent-extra used to control the quality of a recorded video. This is an
179      * integer property. Currently value 0 means low quality, suitable for MMS messages, and
180      * value 1 means high quality. In the future other quality levels may be added.
181      */
182     public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
183 
184     /**
185      * Specify the maximum allowed size.
186      */
187     public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
188 
189     /**
190      * Specify the maximum allowed recording duration in seconds.
191      */
192     public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
193 
194     /**
195      * The name of the Intent-extra used to indicate a content resolver Uri to be used to
196      * store the requested image or video.
197      */
198     public final static String EXTRA_OUTPUT = "output";
199 
200     /**
201       * The string that is used when a media attribute is not known. For example,
202       * if an audio file does not have any meta data, the artist and album columns
203       * will be set to this value.
204       */
205     public static final String UNKNOWN_STRING = "<unknown>";
206 
207     /**
208      * Common fields for most MediaProvider tables
209      */
210 
211     public interface MediaColumns extends BaseColumns {
212         /**
213          * The data stream for the file
214          * <P>Type: DATA STREAM</P>
215          */
216         public static final String DATA = "_data";
217 
218         /**
219          * The size of the file in bytes
220          * <P>Type: INTEGER (long)</P>
221          */
222         public static final String SIZE = "_size";
223 
224         /**
225          * The display name of the file
226          * <P>Type: TEXT</P>
227          */
228         public static final String DISPLAY_NAME = "_display_name";
229 
230         /**
231          * The title of the content
232          * <P>Type: TEXT</P>
233          */
234         public static final String TITLE = "title";
235 
236         /**
237          * The time the file was added to the media provider
238          * Units are seconds since 1970.
239          * <P>Type: INTEGER (long)</P>
240          */
241         public static final String DATE_ADDED = "date_added";
242 
243         /**
244          * The time the file was last modified
245          * Units are seconds since 1970.
246          * NOTE: This is for internal use by the media scanner.  Do not modify this field.
247          * <P>Type: INTEGER (long)</P>
248          */
249         public static final String DATE_MODIFIED = "date_modified";
250 
251         /**
252          * The MIME type of the file
253          * <P>Type: TEXT</P>
254          */
255         public static final String MIME_TYPE = "mime_type";
256      }
257 
258     /**
259      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
260      * to be accessed elsewhere.
261      */
262     private static class InternalThumbnails implements BaseColumns {
263         private static final int MINI_KIND = 1;
264         private static final int FULL_SCREEN_KIND = 2;
265         private static final int MICRO_KIND = 3;
266         private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
267         static final int DEFAULT_GROUP_ID = 0;
268         private static final Object sThumbBufLock = new Object();
269         private static byte[] sThumbBuf;
270 
getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options)271         private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
272             Bitmap bitmap = null;
273             Uri thumbUri = null;
274             try {
275                 long thumbId = c.getLong(0);
276                 String filePath = c.getString(1);
277                 thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
278                 ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
279                 bitmap = BitmapFactory.decodeFileDescriptor(
280                         pfdInput.getFileDescriptor(), null, options);
281                 pfdInput.close();
282             } catch (FileNotFoundException ex) {
283                 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
284             } catch (IOException ex) {
285                 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
286             } catch (OutOfMemoryError ex) {
287                 Log.e(TAG, "failed to allocate memory for thumbnail "
288                         + thumbUri + "; " + ex);
289             }
290             return bitmap;
291         }
292 
293         /**
294          * This method cancels the thumbnail request so clients waiting for getThumbnail will be
295          * interrupted and return immediately. Only the original process which made the getThumbnail
296          * requests can cancel their own requests.
297          *
298          * @param cr ContentResolver
299          * @param origId original image or video id. use -1 to cancel all requests.
300          * @param groupId the same groupId used in getThumbnail
301          * @param baseUri the base URI of requested thumbnails
302          */
cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri, long groupId)303         static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
304                 long groupId) {
305             Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
306                     .appendQueryParameter("orig_id", String.valueOf(origId))
307                     .appendQueryParameter("group_id", String.valueOf(groupId)).build();
308             Cursor c = null;
309             try {
310                 c = cr.query(cancelUri, PROJECTION, null, null, null);
311             }
312             finally {
313                 if (c != null) c.close();
314             }
315         }
316         /**
317          * This method ensure thumbnails associated with origId are generated and decode the byte
318          * stream from database (MICRO_KIND) or file (MINI_KIND).
319          *
320          * Special optimization has been done to avoid further IPC communication for MICRO_KIND
321          * thumbnails.
322          *
323          * @param cr ContentResolver
324          * @param origId original image or video id
325          * @param kind could be MINI_KIND or MICRO_KIND
326          * @param options this is only used for MINI_KIND when decoding the Bitmap
327          * @param baseUri the base URI of requested thumbnails
328          * @param groupId the id of group to which this request belongs
329          * @return Bitmap bitmap of specified thumbnail kind
330          */
getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options, Uri baseUri, boolean isVideo)331         static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
332                 BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
333             Bitmap bitmap = null;
334             String filePath = null;
335             // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
336             // If the magic is non-zero, we simply return thumbnail if it does exist.
337             // querying MediaProvider and simply return thumbnail.
338             MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
339             long magic = thumbFile.getMagic(origId);
340             if (magic != 0) {
341                 if (kind == MICRO_KIND) {
342                     synchronized (sThumbBufLock) {
343                         if (sThumbBuf == null) {
344                             sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
345                         }
346                         if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
347                             bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
348                             if (bitmap == null) {
349                                 Log.w(TAG, "couldn't decode byte array.");
350                             }
351                         }
352                     }
353                     return bitmap;
354                 } else if (kind == MINI_KIND) {
355                     String column = isVideo ? "video_id=" : "image_id=";
356                     Cursor c = null;
357                     try {
358                         c = cr.query(baseUri, PROJECTION, column + origId, null, null);
359                         if (c != null && c.moveToFirst()) {
360                             bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
361                             if (bitmap != null) {
362                                 return bitmap;
363                             }
364                         }
365                     } finally {
366                         if (c != null) c.close();
367                     }
368                 }
369             }
370 
371             Cursor c = null;
372             try {
373                 Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
374                         .appendQueryParameter("orig_id", String.valueOf(origId))
375                         .appendQueryParameter("group_id", String.valueOf(groupId)).build();
376                 c = cr.query(blockingUri, PROJECTION, null, null, null);
377                 // This happens when original image/video doesn't exist.
378                 if (c == null) return null;
379 
380                 // Assuming thumbnail has been generated, at least original image exists.
381                 if (kind == MICRO_KIND) {
382                     synchronized (sThumbBufLock) {
383                         if (sThumbBuf == null) {
384                             sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
385                         }
386                         if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
387                             bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
388                             if (bitmap == null) {
389                                 Log.w(TAG, "couldn't decode byte array.");
390                             }
391                         }
392                     }
393                 } else if (kind == MINI_KIND) {
394                     if (c.moveToFirst()) {
395                         bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
396                     }
397                 } else {
398                     throw new IllegalArgumentException("Unsupported kind: " + kind);
399                 }
400 
401                 // We probably run out of space, so create the thumbnail in memory.
402                 if (bitmap == null) {
403                     Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
404                             + ", kind=" + kind + ", isVideo="+isVideo);
405                     Uri uri = Uri.parse(
406                             baseUri.buildUpon().appendPath(String.valueOf(origId))
407                                     .toString().replaceFirst("thumbnails", "media"));
408                     if (filePath == null) {
409                         if (c != null) c.close();
410                         c = cr.query(uri, PROJECTION, null, null, null);
411                         if (c == null || !c.moveToFirst()) {
412                             return null;
413                         }
414                         filePath = c.getString(1);
415                     }
416                     if (isVideo) {
417                         bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
418                     } else {
419                         bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
420                     }
421                 }
422             } catch (SQLiteException ex) {
423                 Log.w(TAG, ex);
424             } finally {
425                 if (c != null) c.close();
426             }
427             return bitmap;
428         }
429     }
430 
431     /**
432      * Contains meta data for all available images.
433      */
434     public static final class Images {
435         public interface ImageColumns extends MediaColumns {
436             /**
437              * The description of the image
438              * <P>Type: TEXT</P>
439              */
440             public static final String DESCRIPTION = "description";
441 
442             /**
443              * The picasa id of the image
444              * <P>Type: TEXT</P>
445              */
446             public static final String PICASA_ID = "picasa_id";
447 
448             /**
449              * Whether the video should be published as public or private
450              * <P>Type: INTEGER</P>
451              */
452             public static final String IS_PRIVATE = "isprivate";
453 
454             /**
455              * The latitude where the image was captured.
456              * <P>Type: DOUBLE</P>
457              */
458             public static final String LATITUDE = "latitude";
459 
460             /**
461              * The longitude where the image was captured.
462              * <P>Type: DOUBLE</P>
463              */
464             public static final String LONGITUDE = "longitude";
465 
466             /**
467              * The date & time that the image was taken in units
468              * of milliseconds since jan 1, 1970.
469              * <P>Type: INTEGER</P>
470              */
471             public static final String DATE_TAKEN = "datetaken";
472 
473             /**
474              * The orientation for the image expressed as degrees.
475              * Only degrees 0, 90, 180, 270 will work.
476              * <P>Type: INTEGER</P>
477              */
478             public static final String ORIENTATION = "orientation";
479 
480             /**
481              * The mini thumb id.
482              * <P>Type: INTEGER</P>
483              */
484             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
485 
486             /**
487              * The bucket id of the image. This is a read-only property that
488              * is automatically computed from the DATA column.
489              * <P>Type: TEXT</P>
490              */
491             public static final String BUCKET_ID = "bucket_id";
492 
493             /**
494              * The bucket display name of the image. This is a read-only property that
495              * is automatically computed from the DATA column.
496              * <P>Type: TEXT</P>
497              */
498             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
499         }
500 
501         public static final class Media implements ImageColumns {
query(ContentResolver cr, Uri uri, String[] projection)502             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
503                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
504             }
505 
query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy)506             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
507                     String where, String orderBy) {
508                 return cr.query(uri, projection, where,
509                                              null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
510             }
511 
query(ContentResolver cr, Uri uri, String[] projection, String selection, String [] selectionArgs, String orderBy)512             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
513                     String selection, String [] selectionArgs, String orderBy) {
514                 return cr.query(uri, projection, selection,
515                         selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
516             }
517 
518             /**
519              * Retrieves an image for the given url as a {@link Bitmap}.
520              *
521              * @param cr The content resolver to use
522              * @param url The url of the image
523              * @throws FileNotFoundException
524              * @throws IOException
525              */
getBitmap(ContentResolver cr, Uri url)526             public static final Bitmap getBitmap(ContentResolver cr, Uri url)
527                     throws FileNotFoundException, IOException {
528                 InputStream input = cr.openInputStream(url);
529                 Bitmap bitmap = BitmapFactory.decodeStream(input);
530                 input.close();
531                 return bitmap;
532             }
533 
534             /**
535              * Insert an image and create a thumbnail for it.
536              *
537              * @param cr The content resolver to use
538              * @param imagePath The path to the image to insert
539              * @param name The name of the image
540              * @param description The description of the image
541              * @return The URL to the newly created image
542              * @throws FileNotFoundException
543              */
insertImage(ContentResolver cr, String imagePath, String name, String description)544             public static final String insertImage(ContentResolver cr, String imagePath,
545                     String name, String description) throws FileNotFoundException {
546                 // Check if file exists with a FileInputStream
547                 FileInputStream stream = new FileInputStream(imagePath);
548                 try {
549                     Bitmap bm = BitmapFactory.decodeFile(imagePath);
550                     String ret = insertImage(cr, bm, name, description);
551                     bm.recycle();
552                     return ret;
553                 } finally {
554                     try {
555                         stream.close();
556                     } catch (IOException e) {
557                     }
558                 }
559             }
560 
StoreThumbnail( ContentResolver cr, Bitmap source, long id, float width, float height, int kind)561             private static final Bitmap StoreThumbnail(
562                     ContentResolver cr,
563                     Bitmap source,
564                     long id,
565                     float width, float height,
566                     int kind) {
567                 // create the matrix to scale it
568                 Matrix matrix = new Matrix();
569 
570                 float scaleX = width / source.getWidth();
571                 float scaleY = height / source.getHeight();
572 
573                 matrix.setScale(scaleX, scaleY);
574 
575                 Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
576                                                    source.getWidth(),
577                                                    source.getHeight(), matrix,
578                                                    true);
579 
580                 ContentValues values = new ContentValues(4);
581                 values.put(Images.Thumbnails.KIND,     kind);
582                 values.put(Images.Thumbnails.IMAGE_ID, (int)id);
583                 values.put(Images.Thumbnails.HEIGHT,   thumb.getHeight());
584                 values.put(Images.Thumbnails.WIDTH,    thumb.getWidth());
585 
586                 Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
587 
588                 try {
589                     OutputStream thumbOut = cr.openOutputStream(url);
590 
591                     thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
592                     thumbOut.close();
593                     return thumb;
594                 }
595                 catch (FileNotFoundException ex) {
596                     return null;
597                 }
598                 catch (IOException ex) {
599                     return null;
600                 }
601             }
602 
603             /**
604              * Insert an image and create a thumbnail for it.
605              *
606              * @param cr The content resolver to use
607              * @param source The stream to use for the image
608              * @param title The name of the image
609              * @param description The description of the image
610              * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
611              *              for any reason.
612              */
insertImage(ContentResolver cr, Bitmap source, String title, String description)613             public static final String insertImage(ContentResolver cr, Bitmap source,
614                                                    String title, String description) {
615                 ContentValues values = new ContentValues();
616                 values.put(Images.Media.TITLE, title);
617                 values.put(Images.Media.DESCRIPTION, description);
618                 values.put(Images.Media.MIME_TYPE, "image/jpeg");
619 
620                 Uri url = null;
621                 String stringUrl = null;    /* value to be returned */
622 
623                 try {
624                     url = cr.insert(EXTERNAL_CONTENT_URI, values);
625 
626                     if (source != null) {
627                         OutputStream imageOut = cr.openOutputStream(url);
628                         try {
629                             source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
630                         } finally {
631                             imageOut.close();
632                         }
633 
634                         long id = ContentUris.parseId(url);
635                         // Wait until MINI_KIND thumbnail is generated.
636                         Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
637                                 Images.Thumbnails.MINI_KIND, null);
638                         // This is for backward compatibility.
639                         Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
640                                 Images.Thumbnails.MICRO_KIND);
641                     } else {
642                         Log.e(TAG, "Failed to create thumbnail, removing original");
643                         cr.delete(url, null, null);
644                         url = null;
645                     }
646                 } catch (Exception e) {
647                     Log.e(TAG, "Failed to insert image", e);
648                     if (url != null) {
649                         cr.delete(url, null, null);
650                         url = null;
651                     }
652                 }
653 
654                 if (url != null) {
655                     stringUrl = url.toString();
656                 }
657 
658                 return stringUrl;
659             }
660 
661             /**
662              * Get the content:// style URI for the image media table on the
663              * given volume.
664              *
665              * @param volumeName the name of the volume to get the URI for
666              * @return the URI to the image media table on the given volume
667              */
getContentUri(String volumeName)668             public static Uri getContentUri(String volumeName) {
669                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
670                         "/images/media");
671             }
672 
673             /**
674              * The content:// style URI for the internal storage.
675              */
676             public static final Uri INTERNAL_CONTENT_URI =
677                     getContentUri("internal");
678 
679             /**
680              * The content:// style URI for the "primary" external storage
681              * volume.
682              */
683             public static final Uri EXTERNAL_CONTENT_URI =
684                     getContentUri("external");
685 
686             /**
687              * The MIME type of of this directory of
688              * images.  Note that each entry in this directory will have a standard
689              * image MIME type as appropriate -- for example, image/jpeg.
690              */
691             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
692 
693             /**
694              * The default sort order for this table
695              */
696             public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
697         }
698 
699         /**
700          * This class allows developers to query and get two kinds of thumbnails:
701          * MINI_KIND: 512 x 384 thumbnail
702          * MICRO_KIND: 96 x 96 thumbnail
703          */
704         public static class Thumbnails implements BaseColumns {
query(ContentResolver cr, Uri uri, String[] projection)705             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
706                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
707             }
708 
queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)709             public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
710                     String[] projection) {
711                 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
712             }
713 
queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)714             public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
715                     String[] projection) {
716                 return cr.query(EXTERNAL_CONTENT_URI, projection,
717                         IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
718                         kind, null, null);
719             }
720 
721             /**
722              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
723              * interrupted and return immediately. Only the original process which made the getThumbnail
724              * requests can cancel their own requests.
725              *
726              * @param cr ContentResolver
727              * @param origId original image id
728              */
cancelThumbnailRequest(ContentResolver cr, long origId)729             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
730                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
731                         InternalThumbnails.DEFAULT_GROUP_ID);
732             }
733 
734             /**
735              * This method checks if the thumbnails of the specified image (origId) has been created.
736              * It will be blocked until the thumbnails are generated.
737              *
738              * @param cr ContentResolver used to dispatch queries to MediaProvider.
739              * @param origId Original image id associated with thumbnail of interest.
740              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
741              * @param options this is only used for MINI_KIND when decoding the Bitmap
742              * @return A Bitmap instance. It could be null if the original image
743              *         associated with origId doesn't exist or memory is not enough.
744              */
getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)745             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
746                     BitmapFactory.Options options) {
747                 return InternalThumbnails.getThumbnail(cr, origId,
748                         InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
749                         EXTERNAL_CONTENT_URI, false);
750             }
751 
752             /**
753              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
754              * interrupted and return immediately. Only the original process which made the getThumbnail
755              * requests can cancel their own requests.
756              *
757              * @param cr ContentResolver
758              * @param origId original image id
759              * @param groupId the same groupId used in getThumbnail.
760              */
cancelThumbnailRequest(ContentResolver cr, long origId, long groupId)761             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
762                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
763             }
764 
765             /**
766              * This method checks if the thumbnails of the specified image (origId) has been created.
767              * It will be blocked until the thumbnails are generated.
768              *
769              * @param cr ContentResolver used to dispatch queries to MediaProvider.
770              * @param origId Original image id associated with thumbnail of interest.
771              * @param groupId the id of group to which this request belongs
772              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
773              * @param options this is only used for MINI_KIND when decoding the Bitmap
774              * @return A Bitmap instance. It could be null if the original image
775              *         associated with origId doesn't exist or memory is not enough.
776              */
getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options)777             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
778                     int kind, BitmapFactory.Options options) {
779                 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
780                         EXTERNAL_CONTENT_URI, false);
781             }
782 
783             /**
784              * Get the content:// style URI for the image media table on the
785              * given volume.
786              *
787              * @param volumeName the name of the volume to get the URI for
788              * @return the URI to the image media table on the given volume
789              */
getContentUri(String volumeName)790             public static Uri getContentUri(String volumeName) {
791                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
792                         "/images/thumbnails");
793             }
794 
795             /**
796              * The content:// style URI for the internal storage.
797              */
798             public static final Uri INTERNAL_CONTENT_URI =
799                     getContentUri("internal");
800 
801             /**
802              * The content:// style URI for the "primary" external storage
803              * volume.
804              */
805             public static final Uri EXTERNAL_CONTENT_URI =
806                     getContentUri("external");
807 
808             /**
809              * The default sort order for this table
810              */
811             public static final String DEFAULT_SORT_ORDER = "image_id ASC";
812 
813             /**
814              * The data stream for the thumbnail
815              * <P>Type: DATA STREAM</P>
816              */
817             public static final String DATA = "_data";
818 
819             /**
820              * The original image for the thumbnal
821              * <P>Type: INTEGER (ID from Images table)</P>
822              */
823             public static final String IMAGE_ID = "image_id";
824 
825             /**
826              * The kind of the thumbnail
827              * <P>Type: INTEGER (One of the values below)</P>
828              */
829             public static final String KIND = "kind";
830 
831             public static final int MINI_KIND = 1;
832             public static final int FULL_SCREEN_KIND = 2;
833             public static final int MICRO_KIND = 3;
834             /**
835              * The blob raw data of thumbnail
836              * <P>Type: DATA STREAM</P>
837              */
838             public static final String THUMB_DATA = "thumb_data";
839 
840             /**
841              * The width of the thumbnal
842              * <P>Type: INTEGER (long)</P>
843              */
844             public static final String WIDTH = "width";
845 
846             /**
847              * The height of the thumbnail
848              * <P>Type: INTEGER (long)</P>
849              */
850             public static final String HEIGHT = "height";
851         }
852     }
853 
854     /**
855      * Container for all audio content.
856      */
857     public static final class Audio {
858         /**
859          * Columns for audio file that show up in multiple tables.
860          */
861         public interface AudioColumns extends MediaColumns {
862 
863             /**
864              * A non human readable key calculated from the TITLE, used for
865              * searching, sorting and grouping
866              * <P>Type: TEXT</P>
867              */
868             public static final String TITLE_KEY = "title_key";
869 
870             /**
871              * The duration of the audio file, in ms
872              * <P>Type: INTEGER (long)</P>
873              */
874             public static final String DURATION = "duration";
875 
876             /**
877              * The position, in ms, playback was at when playback for this file
878              * was last stopped.
879              * <P>Type: INTEGER (long)</P>
880              */
881             public static final String BOOKMARK = "bookmark";
882 
883             /**
884              * The id of the artist who created the audio file, if any
885              * <P>Type: INTEGER (long)</P>
886              */
887             public static final String ARTIST_ID = "artist_id";
888 
889             /**
890              * The artist who created the audio file, if any
891              * <P>Type: TEXT</P>
892              */
893             public static final String ARTIST = "artist";
894 
895             /**
896              * The artist credited for the album that contains the audio file
897              * <P>Type: TEXT</P>
898              * @hide
899              */
900             public static final String ALBUM_ARTIST = "album_artist";
901 
902             /**
903              * Whether the song is part of a compilation
904              * <P>Type: TEXT</P>
905              * @hide
906              */
907             public static final String COMPILATION = "compilation";
908 
909             /**
910              * A non human readable key calculated from the ARTIST, used for
911              * searching, sorting and grouping
912              * <P>Type: TEXT</P>
913              */
914             public static final String ARTIST_KEY = "artist_key";
915 
916             /**
917              * The composer of the audio file, if any
918              * <P>Type: TEXT</P>
919              */
920             public static final String COMPOSER = "composer";
921 
922             /**
923              * The id of the album the audio file is from, if any
924              * <P>Type: INTEGER (long)</P>
925              */
926             public static final String ALBUM_ID = "album_id";
927 
928             /**
929              * The album the audio file is from, if any
930              * <P>Type: TEXT</P>
931              */
932             public static final String ALBUM = "album";
933 
934             /**
935              * A non human readable key calculated from the ALBUM, used for
936              * searching, sorting and grouping
937              * <P>Type: TEXT</P>
938              */
939             public static final String ALBUM_KEY = "album_key";
940 
941             /**
942              * A URI to the album art, if any
943              * <P>Type: TEXT</P>
944              */
945             public static final String ALBUM_ART = "album_art";
946 
947             /**
948              * The track number of this song on the album, if any.
949              * This number encodes both the track number and the
950              * disc number. For multi-disc sets, this number will
951              * be 1xxx for tracks on the first disc, 2xxx for tracks
952              * on the second disc, etc.
953              * <P>Type: INTEGER</P>
954              */
955             public static final String TRACK = "track";
956 
957             /**
958              * The year the audio file was recorded, if any
959              * <P>Type: INTEGER</P>
960              */
961             public static final String YEAR = "year";
962 
963             /**
964              * Non-zero if the audio file is music
965              * <P>Type: INTEGER (boolean)</P>
966              */
967             public static final String IS_MUSIC = "is_music";
968 
969             /**
970              * Non-zero if the audio file is a podcast
971              * <P>Type: INTEGER (boolean)</P>
972              */
973             public static final String IS_PODCAST = "is_podcast";
974 
975             /**
976              * Non-zero id the audio file may be a ringtone
977              * <P>Type: INTEGER (boolean)</P>
978              */
979             public static final String IS_RINGTONE = "is_ringtone";
980 
981             /**
982              * Non-zero id the audio file may be an alarm
983              * <P>Type: INTEGER (boolean)</P>
984              */
985             public static final String IS_ALARM = "is_alarm";
986 
987             /**
988              * Non-zero id the audio file may be a notification sound
989              * <P>Type: INTEGER (boolean)</P>
990              */
991             public static final String IS_NOTIFICATION = "is_notification";
992         }
993 
994         /**
995          * Converts a name to a "key" that can be used for grouping, sorting
996          * and searching.
997          * The rules that govern this conversion are:
998          * - remove 'special' characters like ()[]'!?.,
999          * - remove leading/trailing spaces
1000          * - convert everything to lowercase
1001          * - remove leading "the ", "an " and "a "
1002          * - remove trailing ", the|an|a"
1003          * - remove accents. This step leaves us with CollationKey data,
1004          *   which is not human readable
1005          *
1006          * @param name The artist or album name to convert
1007          * @return The "key" for the given name.
1008          */
keyFor(String name)1009         public static String keyFor(String name) {
1010             if (name != null)  {
1011                 boolean sortfirst = false;
1012                 if (name.equals(UNKNOWN_STRING)) {
1013                     return "\001";
1014                 }
1015                 // Check if the first character is \001. We use this to
1016                 // force sorting of certain special files, like the silent ringtone.
1017                 if (name.startsWith("\001")) {
1018                     sortfirst = true;
1019                 }
1020                 name = name.trim().toLowerCase();
1021                 if (name.startsWith("the ")) {
1022                     name = name.substring(4);
1023                 }
1024                 if (name.startsWith("an ")) {
1025                     name = name.substring(3);
1026                 }
1027                 if (name.startsWith("a ")) {
1028                     name = name.substring(2);
1029                 }
1030                 if (name.endsWith(", the") || name.endsWith(",the") ||
1031                     name.endsWith(", an") || name.endsWith(",an") ||
1032                     name.endsWith(", a") || name.endsWith(",a")) {
1033                     name = name.substring(0, name.lastIndexOf(','));
1034                 }
1035                 name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
1036                 if (name.length() > 0) {
1037                     // Insert a separator between the characters to avoid
1038                     // matches on a partial character. If we ever change
1039                     // to start-of-word-only matches, this can be removed.
1040                     StringBuilder b = new StringBuilder();
1041                     b.append('.');
1042                     int nl = name.length();
1043                     for (int i = 0; i < nl; i++) {
1044                         b.append(name.charAt(i));
1045                         b.append('.');
1046                     }
1047                     name = b.toString();
1048                     String key = DatabaseUtils.getCollationKey(name);
1049                     if (sortfirst) {
1050                         key = "\001" + key;
1051                     }
1052                     return key;
1053                } else {
1054                     return "";
1055                 }
1056             }
1057             return null;
1058         }
1059 
1060         public static final class Media implements AudioColumns {
1061             /**
1062              * Get the content:// style URI for the audio media table on the
1063              * given volume.
1064              *
1065              * @param volumeName the name of the volume to get the URI for
1066              * @return the URI to the audio media table on the given volume
1067              */
getContentUri(String volumeName)1068             public static Uri getContentUri(String volumeName) {
1069                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1070                         "/audio/media");
1071             }
1072 
getContentUriForPath(String path)1073             public static Uri getContentUriForPath(String path) {
1074                 return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
1075                         EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
1076             }
1077 
1078             /**
1079              * The content:// style URI for the internal storage.
1080              */
1081             public static final Uri INTERNAL_CONTENT_URI =
1082                     getContentUri("internal");
1083 
1084             /**
1085              * The content:// style URI for the "primary" external storage
1086              * volume.
1087              */
1088             public static final Uri EXTERNAL_CONTENT_URI =
1089                     getContentUri("external");
1090 
1091             /**
1092              * The MIME type for this table.
1093              */
1094             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
1095 
1096             /**
1097              * The default sort order for this table
1098              */
1099             public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
1100 
1101             /**
1102              * Activity Action: Start SoundRecorder application.
1103              * <p>Input: nothing.
1104              * <p>Output: An uri to the recorded sound stored in the Media Library
1105              * if the recording was successful.
1106              * May also contain the extra EXTRA_MAX_BYTES.
1107              * @see #EXTRA_MAX_BYTES
1108              */
1109             public static final String RECORD_SOUND_ACTION =
1110                     "android.provider.MediaStore.RECORD_SOUND";
1111 
1112             /**
1113              * The name of the Intent-extra used to define a maximum file size for
1114              * a recording made by the SoundRecorder application.
1115              *
1116              * @see #RECORD_SOUND_ACTION
1117              */
1118              public static final String EXTRA_MAX_BYTES =
1119                     "android.provider.MediaStore.extra.MAX_BYTES";
1120         }
1121 
1122         /**
1123          * Columns representing an audio genre
1124          */
1125         public interface GenresColumns {
1126             /**
1127              * The name of the genre
1128              * <P>Type: TEXT</P>
1129              */
1130             public static final String NAME = "name";
1131         }
1132 
1133         /**
1134          * Contains all genres for audio files
1135          */
1136         public static final class Genres implements BaseColumns, GenresColumns {
1137             /**
1138              * Get the content:// style URI for the audio genres table on the
1139              * given volume.
1140              *
1141              * @param volumeName the name of the volume to get the URI for
1142              * @return the URI to the audio genres table on the given volume
1143              */
getContentUri(String volumeName)1144             public static Uri getContentUri(String volumeName) {
1145                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1146                         "/audio/genres");
1147             }
1148 
1149             /**
1150              * The content:// style URI for the internal storage.
1151              */
1152             public static final Uri INTERNAL_CONTENT_URI =
1153                     getContentUri("internal");
1154 
1155             /**
1156              * The content:// style URI for the "primary" external storage
1157              * volume.
1158              */
1159             public static final Uri EXTERNAL_CONTENT_URI =
1160                     getContentUri("external");
1161 
1162             /**
1163              * The MIME type for this table.
1164              */
1165             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
1166 
1167             /**
1168              * The MIME type for entries in this table.
1169              */
1170             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
1171 
1172             /**
1173              * The default sort order for this table
1174              */
1175             public static final String DEFAULT_SORT_ORDER = NAME;
1176 
1177             /**
1178              * Sub-directory of each genre containing all members.
1179              */
1180             public static final class Members implements AudioColumns {
1181 
getContentUri(String volumeName, long genreId)1182                 public static final Uri getContentUri(String volumeName,
1183                         long genreId) {
1184                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1185                             + "/audio/genres/" + genreId + "/members");
1186                 }
1187 
1188                 /**
1189                  * A subdirectory of each genre containing all member audio files.
1190                  */
1191                 public static final String CONTENT_DIRECTORY = "members";
1192 
1193                 /**
1194                  * The default sort order for this table
1195                  */
1196                 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
1197 
1198                 /**
1199                  * The ID of the audio file
1200                  * <P>Type: INTEGER (long)</P>
1201                  */
1202                 public static final String AUDIO_ID = "audio_id";
1203 
1204                 /**
1205                  * The ID of the genre
1206                  * <P>Type: INTEGER (long)</P>
1207                  */
1208                 public static final String GENRE_ID = "genre_id";
1209             }
1210         }
1211 
1212         /**
1213          * Columns representing a playlist
1214          */
1215         public interface PlaylistsColumns {
1216             /**
1217              * The name of the playlist
1218              * <P>Type: TEXT</P>
1219              */
1220             public static final String NAME = "name";
1221 
1222             /**
1223              * The data stream for the playlist file
1224              * <P>Type: DATA STREAM</P>
1225              */
1226             public static final String DATA = "_data";
1227 
1228             /**
1229              * The time the file was added to the media provider
1230              * Units are seconds since 1970.
1231              * <P>Type: INTEGER (long)</P>
1232              */
1233             public static final String DATE_ADDED = "date_added";
1234 
1235             /**
1236              * The time the file was last modified
1237              * Units are seconds since 1970.
1238              * NOTE: This is for internal use by the media scanner.  Do not modify this field.
1239              * <P>Type: INTEGER (long)</P>
1240              */
1241             public static final String DATE_MODIFIED = "date_modified";
1242         }
1243 
1244         /**
1245          * Contains playlists for audio files
1246          */
1247         public static final class Playlists implements BaseColumns,
1248                 PlaylistsColumns {
1249             /**
1250              * Get the content:// style URI for the audio playlists table on the
1251              * given volume.
1252              *
1253              * @param volumeName the name of the volume to get the URI for
1254              * @return the URI to the audio playlists table on the given volume
1255              */
getContentUri(String volumeName)1256             public static Uri getContentUri(String volumeName) {
1257                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1258                         "/audio/playlists");
1259             }
1260 
1261             /**
1262              * The content:// style URI for the internal storage.
1263              */
1264             public static final Uri INTERNAL_CONTENT_URI =
1265                     getContentUri("internal");
1266 
1267             /**
1268              * The content:// style URI for the "primary" external storage
1269              * volume.
1270              */
1271             public static final Uri EXTERNAL_CONTENT_URI =
1272                     getContentUri("external");
1273 
1274             /**
1275              * The MIME type for this table.
1276              */
1277             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
1278 
1279             /**
1280              * The MIME type for entries in this table.
1281              */
1282             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
1283 
1284             /**
1285              * The default sort order for this table
1286              */
1287             public static final String DEFAULT_SORT_ORDER = NAME;
1288 
1289             /**
1290              * Sub-directory of each playlist containing all members.
1291              */
1292             public static final class Members implements AudioColumns {
getContentUri(String volumeName, long playlistId)1293                 public static final Uri getContentUri(String volumeName,
1294                         long playlistId) {
1295                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1296                             + "/audio/playlists/" + playlistId + "/members");
1297                 }
1298 
1299                 /**
1300                  * Convenience method to move a playlist item to a new location
1301                  * @param res The content resolver to use
1302                  * @param playlistId The numeric id of the playlist
1303                  * @param from The position of the item to move
1304                  * @param to The position to move the item to
1305                  * @return true on success
1306                  */
moveItem(ContentResolver res, long playlistId, int from, int to)1307                 public static final boolean moveItem(ContentResolver res,
1308                         long playlistId, int from, int to) {
1309                     Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
1310                             playlistId)
1311                             .buildUpon()
1312                             .appendEncodedPath(String.valueOf(from))
1313                             .appendQueryParameter("move", "true")
1314                             .build();
1315                     ContentValues values = new ContentValues();
1316                     values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
1317                     return res.update(uri, values, null, null) != 0;
1318                 }
1319 
1320                 /**
1321                  * The ID within the playlist.
1322                  */
1323                 public static final String _ID = "_id";
1324 
1325                 /**
1326                  * A subdirectory of each playlist containing all member audio
1327                  * files.
1328                  */
1329                 public static final String CONTENT_DIRECTORY = "members";
1330 
1331                 /**
1332                  * The ID of the audio file
1333                  * <P>Type: INTEGER (long)</P>
1334                  */
1335                 public static final String AUDIO_ID = "audio_id";
1336 
1337                 /**
1338                  * The ID of the playlist
1339                  * <P>Type: INTEGER (long)</P>
1340                  */
1341                 public static final String PLAYLIST_ID = "playlist_id";
1342 
1343                 /**
1344                  * The order of the songs in the playlist
1345                  * <P>Type: INTEGER (long)></P>
1346                  */
1347                 public static final String PLAY_ORDER = "play_order";
1348 
1349                 /**
1350                  * The default sort order for this table
1351                  */
1352                 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
1353             }
1354         }
1355 
1356         /**
1357          * Columns representing an artist
1358          */
1359         public interface ArtistColumns {
1360             /**
1361              * The artist who created the audio file, if any
1362              * <P>Type: TEXT</P>
1363              */
1364             public static final String ARTIST = "artist";
1365 
1366             /**
1367              * A non human readable key calculated from the ARTIST, used for
1368              * searching, sorting and grouping
1369              * <P>Type: TEXT</P>
1370              */
1371             public static final String ARTIST_KEY = "artist_key";
1372 
1373             /**
1374              * The number of albums in the database for this artist
1375              */
1376             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
1377 
1378             /**
1379              * The number of albums in the database for this artist
1380              */
1381             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
1382         }
1383 
1384         /**
1385          * Contains artists for audio files
1386          */
1387         public static final class Artists implements BaseColumns, ArtistColumns {
1388             /**
1389              * Get the content:// style URI for the artists table on the
1390              * given volume.
1391              *
1392              * @param volumeName the name of the volume to get the URI for
1393              * @return the URI to the audio artists table on the given volume
1394              */
getContentUri(String volumeName)1395             public static Uri getContentUri(String volumeName) {
1396                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1397                         "/audio/artists");
1398             }
1399 
1400             /**
1401              * The content:// style URI for the internal storage.
1402              */
1403             public static final Uri INTERNAL_CONTENT_URI =
1404                     getContentUri("internal");
1405 
1406             /**
1407              * The content:// style URI for the "primary" external storage
1408              * volume.
1409              */
1410             public static final Uri EXTERNAL_CONTENT_URI =
1411                     getContentUri("external");
1412 
1413             /**
1414              * The MIME type for this table.
1415              */
1416             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
1417 
1418             /**
1419              * The MIME type for entries in this table.
1420              */
1421             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
1422 
1423             /**
1424              * The default sort order for this table
1425              */
1426             public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
1427 
1428             /**
1429              * Sub-directory of each artist containing all albums on which
1430              * a song by the artist appears.
1431              */
1432             public static final class Albums implements AlbumColumns {
getContentUri(String volumeName, long artistId)1433                 public static final Uri getContentUri(String volumeName,
1434                         long artistId) {
1435                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1436                             + "/audio/artists/" + artistId + "/albums");
1437                 }
1438             }
1439         }
1440 
1441         /**
1442          * Columns representing an album
1443          */
1444         public interface AlbumColumns {
1445 
1446             /**
1447              * The id for the album
1448              * <P>Type: INTEGER</P>
1449              */
1450             public static final String ALBUM_ID = "album_id";
1451 
1452             /**
1453              * The album on which the audio file appears, if any
1454              * <P>Type: TEXT</P>
1455              */
1456             public static final String ALBUM = "album";
1457 
1458             /**
1459              * The artist whose songs appear on this album
1460              * <P>Type: TEXT</P>
1461              */
1462             public static final String ARTIST = "artist";
1463 
1464             /**
1465              * The number of songs on this album
1466              * <P>Type: INTEGER</P>
1467              */
1468             public static final String NUMBER_OF_SONGS = "numsongs";
1469 
1470             /**
1471              * This column is available when getting album info via artist,
1472              * and indicates the number of songs on the album by the given
1473              * artist.
1474              * <P>Type: INTEGER</P>
1475              */
1476             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
1477 
1478             /**
1479              * The year in which the earliest songs
1480              * on this album were released. This will often
1481              * be the same as {@link #LAST_YEAR}, but for compilation albums
1482              * they might differ.
1483              * <P>Type: INTEGER</P>
1484              */
1485             public static final String FIRST_YEAR = "minyear";
1486 
1487             /**
1488              * The year in which the latest songs
1489              * on this album were released. This will often
1490              * be the same as {@link #FIRST_YEAR}, but for compilation albums
1491              * they might differ.
1492              * <P>Type: INTEGER</P>
1493              */
1494             public static final String LAST_YEAR = "maxyear";
1495 
1496             /**
1497              * A non human readable key calculated from the ALBUM, used for
1498              * searching, sorting and grouping
1499              * <P>Type: TEXT</P>
1500              */
1501             public static final String ALBUM_KEY = "album_key";
1502 
1503             /**
1504              * Cached album art.
1505              * <P>Type: TEXT</P>
1506              */
1507             public static final String ALBUM_ART = "album_art";
1508         }
1509 
1510         /**
1511          * Contains artists for audio files
1512          */
1513         public static final class Albums implements BaseColumns, AlbumColumns {
1514             /**
1515              * Get the content:// style URI for the albums table on the
1516              * given volume.
1517              *
1518              * @param volumeName the name of the volume to get the URI for
1519              * @return the URI to the audio albums table on the given volume
1520              */
getContentUri(String volumeName)1521             public static Uri getContentUri(String volumeName) {
1522                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1523                         "/audio/albums");
1524             }
1525 
1526             /**
1527              * The content:// style URI for the internal storage.
1528              */
1529             public static final Uri INTERNAL_CONTENT_URI =
1530                     getContentUri("internal");
1531 
1532             /**
1533              * The content:// style URI for the "primary" external storage
1534              * volume.
1535              */
1536             public static final Uri EXTERNAL_CONTENT_URI =
1537                     getContentUri("external");
1538 
1539             /**
1540              * The MIME type for this table.
1541              */
1542             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
1543 
1544             /**
1545              * The MIME type for entries in this table.
1546              */
1547             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
1548 
1549             /**
1550              * The default sort order for this table
1551              */
1552             public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
1553         }
1554     }
1555 
1556     public static final class Video {
1557 
1558         /**
1559          * The default sort order for this table.
1560          */
1561         public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
1562 
query(ContentResolver cr, Uri uri, String[] projection)1563         public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
1564             return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1565         }
1566 
1567         public interface VideoColumns extends MediaColumns {
1568 
1569             /**
1570              * The duration of the video file, in ms
1571              * <P>Type: INTEGER (long)</P>
1572              */
1573             public static final String DURATION = "duration";
1574 
1575             /**
1576              * The artist who created the video file, if any
1577              * <P>Type: TEXT</P>
1578              */
1579             public static final String ARTIST = "artist";
1580 
1581             /**
1582              * The album the video file is from, if any
1583              * <P>Type: TEXT</P>
1584              */
1585             public static final String ALBUM = "album";
1586 
1587             /**
1588              * The resolution of the video file, formatted as "XxY"
1589              * <P>Type: TEXT</P>
1590              */
1591             public static final String RESOLUTION = "resolution";
1592 
1593             /**
1594              * The description of the video recording
1595              * <P>Type: TEXT</P>
1596              */
1597             public static final String DESCRIPTION = "description";
1598 
1599             /**
1600              * Whether the video should be published as public or private
1601              * <P>Type: INTEGER</P>
1602              */
1603             public static final String IS_PRIVATE = "isprivate";
1604 
1605             /**
1606              * The user-added tags associated with a video
1607              * <P>Type: TEXT</P>
1608              */
1609             public static final String TAGS = "tags";
1610 
1611             /**
1612              * The YouTube category of the video
1613              * <P>Type: TEXT</P>
1614              */
1615             public static final String CATEGORY = "category";
1616 
1617             /**
1618              * The language of the video
1619              * <P>Type: TEXT</P>
1620              */
1621             public static final String LANGUAGE = "language";
1622 
1623             /**
1624              * The latitude where the image was captured.
1625              * <P>Type: DOUBLE</P>
1626              */
1627             public static final String LATITUDE = "latitude";
1628 
1629             /**
1630              * The longitude where the image was captured.
1631              * <P>Type: DOUBLE</P>
1632              */
1633             public static final String LONGITUDE = "longitude";
1634 
1635             /**
1636              * The date & time that the image was taken in units
1637              * of milliseconds since jan 1, 1970.
1638              * <P>Type: INTEGER</P>
1639              */
1640             public static final String DATE_TAKEN = "datetaken";
1641 
1642             /**
1643              * The mini thumb id.
1644              * <P>Type: INTEGER</P>
1645              */
1646             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
1647 
1648             /**
1649              * The bucket id of the video. This is a read-only property that
1650              * is automatically computed from the DATA column.
1651              * <P>Type: TEXT</P>
1652              */
1653             public static final String BUCKET_ID = "bucket_id";
1654 
1655             /**
1656              * The bucket display name of the video. This is a read-only property that
1657              * is automatically computed from the DATA column.
1658              * <P>Type: TEXT</P>
1659              */
1660             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
1661 
1662             /**
1663              * The bookmark for the video. Time in ms. Represents the location in the video that the
1664              * video should start playing at the next time it is opened. If the value is null or
1665              * out of the range 0..DURATION-1 then the video should start playing from the
1666              * beginning.
1667              * <P>Type: INTEGER</P>
1668              */
1669             public static final String BOOKMARK = "bookmark";
1670         }
1671 
1672         public static final class Media implements VideoColumns {
1673             /**
1674              * Get the content:// style URI for the video media table on the
1675              * given volume.
1676              *
1677              * @param volumeName the name of the volume to get the URI for
1678              * @return the URI to the video media table on the given volume
1679              */
getContentUri(String volumeName)1680             public static Uri getContentUri(String volumeName) {
1681                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1682                         "/video/media");
1683             }
1684 
1685             /**
1686              * The content:// style URI for the internal storage.
1687              */
1688             public static final Uri INTERNAL_CONTENT_URI =
1689                     getContentUri("internal");
1690 
1691             /**
1692              * The content:// style URI for the "primary" external storage
1693              * volume.
1694              */
1695             public static final Uri EXTERNAL_CONTENT_URI =
1696                     getContentUri("external");
1697 
1698             /**
1699              * The MIME type for this table.
1700              */
1701             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
1702 
1703             /**
1704              * The default sort order for this table
1705              */
1706             public static final String DEFAULT_SORT_ORDER = TITLE;
1707         }
1708 
1709         /**
1710          * This class allows developers to query and get two kinds of thumbnails:
1711          * MINI_KIND: 512 x 384 thumbnail
1712          * MICRO_KIND: 96 x 96 thumbnail
1713          *
1714          */
1715         public static class Thumbnails implements BaseColumns {
1716             /**
1717              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1718              * interrupted and return immediately. Only the original process which made the getThumbnail
1719              * requests can cancel their own requests.
1720              *
1721              * @param cr ContentResolver
1722              * @param origId original video id
1723              */
cancelThumbnailRequest(ContentResolver cr, long origId)1724             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
1725                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
1726                         InternalThumbnails.DEFAULT_GROUP_ID);
1727             }
1728 
1729             /**
1730              * This method checks if the thumbnails of the specified image (origId) has been created.
1731              * It will be blocked until the thumbnails are generated.
1732              *
1733              * @param cr ContentResolver used to dispatch queries to MediaProvider.
1734              * @param origId Original image id associated with thumbnail of interest.
1735              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
1736              * @param options this is only used for MINI_KIND when decoding the Bitmap
1737              * @return A Bitmap instance. It could be null if the original image
1738              *         associated with origId doesn't exist or memory is not enough.
1739              */
getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)1740             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
1741                     BitmapFactory.Options options) {
1742                 return InternalThumbnails.getThumbnail(cr, origId,
1743                         InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
1744                         EXTERNAL_CONTENT_URI, true);
1745             }
1746 
1747             /**
1748              * This method checks if the thumbnails of the specified image (origId) has been created.
1749              * It will be blocked until the thumbnails are generated.
1750              *
1751              * @param cr ContentResolver used to dispatch queries to MediaProvider.
1752              * @param origId Original image id associated with thumbnail of interest.
1753              * @param groupId the id of group to which this request belongs
1754              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
1755              * @param options this is only used for MINI_KIND when decoding the Bitmap
1756              * @return A Bitmap instance. It could be null if the original image associated with
1757              *         origId doesn't exist or memory is not enough.
1758              */
getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options)1759             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
1760                     int kind, BitmapFactory.Options options) {
1761                 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
1762                         EXTERNAL_CONTENT_URI, true);
1763             }
1764 
1765             /**
1766              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1767              * interrupted and return immediately. Only the original process which made the getThumbnail
1768              * requests can cancel their own requests.
1769              *
1770              * @param cr ContentResolver
1771              * @param origId original video id
1772              * @param groupId the same groupId used in getThumbnail.
1773              */
cancelThumbnailRequest(ContentResolver cr, long origId, long groupId)1774             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
1775                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
1776             }
1777 
1778             /**
1779              * Get the content:// style URI for the image media table on the
1780              * given volume.
1781              *
1782              * @param volumeName the name of the volume to get the URI for
1783              * @return the URI to the image media table on the given volume
1784              */
getContentUri(String volumeName)1785             public static Uri getContentUri(String volumeName) {
1786                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1787                         "/video/thumbnails");
1788             }
1789 
1790             /**
1791              * The content:// style URI for the internal storage.
1792              */
1793             public static final Uri INTERNAL_CONTENT_URI =
1794                     getContentUri("internal");
1795 
1796             /**
1797              * The content:// style URI for the "primary" external storage
1798              * volume.
1799              */
1800             public static final Uri EXTERNAL_CONTENT_URI =
1801                     getContentUri("external");
1802 
1803             /**
1804              * The default sort order for this table
1805              */
1806             public static final String DEFAULT_SORT_ORDER = "video_id ASC";
1807 
1808             /**
1809              * The data stream for the thumbnail
1810              * <P>Type: DATA STREAM</P>
1811              */
1812             public static final String DATA = "_data";
1813 
1814             /**
1815              * The original image for the thumbnal
1816              * <P>Type: INTEGER (ID from Video table)</P>
1817              */
1818             public static final String VIDEO_ID = "video_id";
1819 
1820             /**
1821              * The kind of the thumbnail
1822              * <P>Type: INTEGER (One of the values below)</P>
1823              */
1824             public static final String KIND = "kind";
1825 
1826             public static final int MINI_KIND = 1;
1827             public static final int FULL_SCREEN_KIND = 2;
1828             public static final int MICRO_KIND = 3;
1829 
1830             /**
1831              * The width of the thumbnal
1832              * <P>Type: INTEGER (long)</P>
1833              */
1834             public static final String WIDTH = "width";
1835 
1836             /**
1837              * The height of the thumbnail
1838              * <P>Type: INTEGER (long)</P>
1839              */
1840             public static final String HEIGHT = "height";
1841         }
1842     }
1843 
1844     /**
1845      * Uri for querying the state of the media scanner.
1846      */
getMediaScannerUri()1847     public static Uri getMediaScannerUri() {
1848         return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
1849     }
1850 
1851     /**
1852      * Name of current volume being scanned by the media scanner.
1853      */
1854     public static final String MEDIA_SCANNER_VOLUME = "volume";
1855 
1856     /**
1857      * Name of the file signaling the media scanner to ignore media in the containing directory
1858      * and its subdirectories. Developers should use this to avoid application graphics showing
1859      * up in the Gallery and likewise prevent application sounds and music from showing up in
1860      * the Music app.
1861      */
1862     public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
1863 }
1864