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