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