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