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