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