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