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