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