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