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