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