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