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