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