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