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