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