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