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