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