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