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