MediaStore.java revision 1dbf1f81e29405674c5da5baa1ca59c18f54ad87
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.ContentUris;
23import android.content.ContentValues;
24import android.content.Context;
25import android.database.Cursor;
26import android.database.DatabaseUtils;
27import android.database.sqlite.SQLiteException;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.graphics.Matrix;
31import android.media.MiniThumbFile;
32import android.media.ThumbnailUtils;
33import android.net.Uri;
34import android.os.Environment;
35import android.os.ParcelFileDescriptor;
36import android.util.Log;
37
38import java.io.FileInputStream;
39import java.io.FileNotFoundException;
40import java.io.IOException;
41import java.io.InputStream;
42import java.io.OutputStream;
43
44/**
45 * The Media provider contains meta data for all available media on both internal
46 * and external storage devices.
47 */
48public final class MediaStore {
49    private final static String TAG = "MediaStore";
50
51    public static final String AUTHORITY = "media";
52
53    private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
54
55   /**
56     * Broadcast Action:  A broadcast to indicate the end of an MTP session with the host.
57     * This broadcast is only sent if MTP activity has modified the media database during the
58     * most recent MTP session.
59     *
60     * @hide
61     */
62    public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
63
64    /**
65     * The method name used by the media scanner and mtp to tell the media provider to
66     * rescan and reclassify that have become unhidden because of renaming folders or
67     * removing nomedia files
68     * @hide
69     */
70    public static final String UNHIDE_CALL = "unhide";
71
72    /**
73     * This is for internal use by the media scanner only.
74     * Name of the (optional) Uri parameter that determines whether to skip deleting
75     * the file pointed to by the _data column, when deleting the database entry.
76     * The only appropriate value for this parameter is "false", in which case the
77     * delete will be skipped. Note especially that setting this to true, or omitting
78     * the parameter altogether, will perform the default action, which is different
79     * for different types of media.
80     * @hide
81     */
82    public static final String PARAM_DELETE_DATA = "deletedata";
83
84    /**
85     * Activity Action: Launch a music player.
86     * The activity should be able to play, browse, or manipulate music files stored on the device.
87     *
88     * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead.
89     */
90    @Deprecated
91    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
92    public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
93
94    /**
95     * Activity Action: Perform a search for media.
96     * Contains at least the {@link android.app.SearchManager#QUERY} extra.
97     * May also contain any combination of the following extras:
98     * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
99     *
100     * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
101     * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
102     * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
103     * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
104     */
105    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
106    public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
107
108    /**
109     * An intent to perform a search for music media and automatically play content from the
110     * result when possible. This can be fired, for example, by the result of a voice recognition
111     * command to listen to music.
112     * <p>
113     * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string
114     * that can contain any type of unstructured music search, like the name of an artist,
115     * an album, a song, a genre, or any combination of these.
116     * <p>
117     * Because this intent includes an open-ended unstructured search string, it makes the most
118     * sense for apps that can support large-scale search of music, such as services connected
119     * to an online database of music which can be streamed and played on the device.
120     */
121    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
122    public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
123            "android.media.action.MEDIA_PLAY_FROM_SEARCH";
124
125    /**
126     * An intent to perform a search for readable media and automatically play content from the
127     * result when possible. This can be fired, for example, by the result of a voice recognition
128     * command to read a book or magazine.
129     * <p>
130     * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
131     * contain any type of unstructured text search, like the name of a book or magazine, an author
132     * a genre, a publisher, or any combination of these.
133     * <p>
134     * Because this intent includes an open-ended unstructured search string, it makes the most
135     * sense for apps that can support large-scale search of text media, such as services connected
136     * to an online database of books and/or magazines which can be read on the device.
137     */
138    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
139    public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH =
140            "android.media.action.TEXT_OPEN_FROM_SEARCH";
141
142    /**
143     * An intent to perform a search for video media and automatically play content from the
144     * result when possible. This can be fired, for example, by the result of a voice recognition
145     * command to play movies.
146     * <p>
147     * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
148     * contain any type of unstructured video search, like the name of a movie, one or more actors,
149     * a genre, or any combination of these.
150     * <p>
151     * Because this intent includes an open-ended unstructured search string, it makes the most
152     * sense for apps that can support large-scale search of video, such as services connected to an
153     * online database of videos which can be streamed and played on the device.
154     */
155    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
156    public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH =
157            "android.media.action.VIDEO_PLAY_FROM_SEARCH";
158
159    /**
160     * The name of the Intent-extra used to define the artist
161     */
162    public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
163    /**
164     * The name of the Intent-extra used to define the album
165     */
166    public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
167    /**
168     * The name of the Intent-extra used to define the song title
169     */
170    public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
171    /**
172     * The name of the Intent-extra used to define the genre.
173     */
174    public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
175    /**
176     * The name of the Intent-extra used to define the playlist.
177     */
178    public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
179    /**
180     * The name of the Intent-extra used to define the radio channel.
181     */
182    public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel";
183    /**
184     * The name of the Intent-extra used to define the search focus. The search focus
185     * indicates whether the search should be for things related to the artist, album
186     * or song that is identified by the other extras.
187     */
188    public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
189
190    /**
191     * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
192     * This is an int property that overrides the activity's requestedOrientation.
193     * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
194     */
195    public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
196
197    /**
198     * The name of an Intent-extra used to control the UI of a ViewImage.
199     * This is a boolean property that overrides the activity's default fullscreen state.
200     */
201    public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
202
203    /**
204     * The name of an Intent-extra used to control the UI of a ViewImage.
205     * This is a boolean property that specifies whether or not to show action icons.
206     */
207    public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
208
209    /**
210     * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
211     * This is a boolean property that specifies whether or not to finish the MovieView activity
212     * when the movie completes playing. The default value is true, which means to automatically
213     * exit the movie player activity when the movie completes playing.
214     */
215    public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
216
217    /**
218     * The name of the Intent action used to launch a camera in still image mode.
219     */
220    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
221    public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
222
223    /**
224     * The name of the Intent action used to launch a camera in still image mode
225     * for use when the device is secured (e.g. with a pin, password, pattern,
226     * or face unlock). Applications responding to this intent must not expose
227     * any personal content like existing photos or videos on the device. The
228     * applications should be careful not to share any photo or video with other
229     * applications or internet. The activity should use {@link
230     * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display
231     * on top of the lock screen while secured. There is no activity stack when
232     * this flag is used, so launching more than one activity is strongly
233     * discouraged.
234     */
235    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
236    public static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
237            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
238
239    /**
240     * The name of the Intent action used to launch a camera in video mode.
241     */
242    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
243    public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
244
245    /**
246     * Standard Intent action that can be sent to have the camera application
247     * capture an image and return it.
248     * <p>
249     * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
250     * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
251     * object in the extra field. This is useful for applications that only need a small image.
252     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
253     * value of EXTRA_OUTPUT.
254     * @see #EXTRA_OUTPUT
255     */
256    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
257    public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
258
259    /**
260     * Intent action that can be sent to have the camera application capture an image and return
261     * it when the device is secured (e.g. with a pin, password, pattern, or face unlock).
262     * Applications responding to this intent must not expose any personal content like existing
263     * photos or videos on the device. The applications should be careful not to share any photo
264     * or video with other applications or internet. The activity should use {@link
265     * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display on top of the
266     * lock screen while secured. There is no activity stack when this flag is used, so
267     * launching more than one activity is strongly discouraged.
268     * <p>
269     * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
270     * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
271     * object in the extra field. This is useful for applications that only need a small image.
272     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
273     * value of EXTRA_OUTPUT.
274     *
275     * @see #ACTION_IMAGE_CAPTURE
276     * @see #EXTRA_OUTPUT
277     */
278    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
279    public static final String ACTION_IMAGE_CAPTURE_SECURE =
280            "android.media.action.IMAGE_CAPTURE_SECURE";
281
282    /**
283     * Standard Intent action that can be sent to have the camera application
284     * capture a video and return it.
285     * <p>
286     * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
287     * <p>
288     * The caller may pass in an extra EXTRA_OUTPUT to control
289     * where the video is written. If EXTRA_OUTPUT is not present the video will be
290     * written to the standard location for videos, and the Uri of that location will be
291     * returned in the data field of the Uri.
292     * @see #EXTRA_OUTPUT
293     * @see #EXTRA_VIDEO_QUALITY
294     * @see #EXTRA_SIZE_LIMIT
295     * @see #EXTRA_DURATION_LIMIT
296     */
297    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
298    public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
299
300    /**
301     * The name of the Intent-extra used to control the quality of a recorded video. This is an
302     * integer property. Currently value 0 means low quality, suitable for MMS messages, and
303     * value 1 means high quality. In the future other quality levels may be added.
304     */
305    public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
306
307    /**
308     * Specify the maximum allowed size.
309     */
310    public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
311
312    /**
313     * Specify the maximum allowed recording duration in seconds.
314     */
315    public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
316
317    /**
318     * The name of the Intent-extra used to indicate a content resolver Uri to be used to
319     * store the requested image or video.
320     */
321    public final static String EXTRA_OUTPUT = "output";
322
323    /**
324      * The string that is used when a media attribute is not known. For example,
325      * if an audio file does not have any meta data, the artist and album columns
326      * will be set to this value.
327      */
328    public static final String UNKNOWN_STRING = "<unknown>";
329
330    /**
331     * Common fields for most MediaProvider tables
332     */
333
334    public interface MediaColumns extends BaseColumns {
335        /**
336         * The data stream for the file
337         * <P>Type: DATA STREAM</P>
338         */
339        public static final String DATA = "_data";
340
341        /**
342         * The size of the file in bytes
343         * <P>Type: INTEGER (long)</P>
344         */
345        public static final String SIZE = "_size";
346
347        /**
348         * The display name of the file
349         * <P>Type: TEXT</P>
350         */
351        public static final String DISPLAY_NAME = "_display_name";
352
353        /**
354         * The title of the content
355         * <P>Type: TEXT</P>
356         */
357        public static final String TITLE = "title";
358
359        /**
360         * The time the file was added to the media provider
361         * Units are seconds since 1970.
362         * <P>Type: INTEGER (long)</P>
363         */
364        public static final String DATE_ADDED = "date_added";
365
366        /**
367         * The time the file was last modified
368         * Units are seconds since 1970.
369         * NOTE: This is for internal use by the media scanner.  Do not modify this field.
370         * <P>Type: INTEGER (long)</P>
371         */
372        public static final String DATE_MODIFIED = "date_modified";
373
374        /**
375         * The MIME type of the file
376         * <P>Type: TEXT</P>
377         */
378        public static final String MIME_TYPE = "mime_type";
379
380        /**
381         * The MTP object handle of a newly transfered file.
382         * Used to pass the new file's object handle through the media scanner
383         * from MTP to the media provider
384         * For internal use only by MTP, media scanner and media provider.
385         * <P>Type: INTEGER</P>
386         * @hide
387         */
388        public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
389
390        /**
391         * Non-zero if the media file is drm-protected
392         * <P>Type: INTEGER (boolean)</P>
393         * @hide
394         */
395        public static final String IS_DRM = "is_drm";
396
397        /**
398         * The width of the image/video in pixels.
399         */
400        public static final String WIDTH = "width";
401
402        /**
403         * The height of the image/video in pixels.
404         */
405        public static final String HEIGHT = "height";
406     }
407
408    /**
409     * Media provider table containing an index of all files in the media storage,
410     * including non-media files.  This should be used by applications that work with
411     * non-media file types (text, HTML, PDF, etc) as well as applications that need to
412     * work with multiple media file types in a single query.
413     */
414    public static final class Files {
415
416        /**
417         * Get the content:// style URI for the files table on the
418         * given volume.
419         *
420         * @param volumeName the name of the volume to get the URI for
421         * @return the URI to the files table on the given volume
422         */
423        public static Uri getContentUri(String volumeName) {
424            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
425                    "/file");
426        }
427
428        /**
429         * Get the content:// style URI for a single row in the files table on the
430         * given volume.
431         *
432         * @param volumeName the name of the volume to get the URI for
433         * @param rowId the file to get the URI for
434         * @return the URI to the files table on the given volume
435         */
436        public static final Uri getContentUri(String volumeName,
437                long rowId) {
438            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
439                    + "/file/" + rowId);
440        }
441
442        /**
443         * For use only by the MTP implementation.
444         * @hide
445         */
446        public static Uri getMtpObjectsUri(String volumeName) {
447            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
448                    "/object");
449        }
450
451        /**
452         * For use only by the MTP implementation.
453         * @hide
454         */
455        public static final Uri getMtpObjectsUri(String volumeName,
456                long fileId) {
457            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
458                    + "/object/" + fileId);
459        }
460
461        /**
462         * Used to implement the MTP GetObjectReferences and SetObjectReferences commands.
463         * @hide
464         */
465        public static final Uri getMtpReferencesUri(String volumeName,
466                long fileId) {
467            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
468                    + "/object/" + fileId + "/references");
469        }
470
471        /**
472         * Fields for master table for all media files.
473         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
474         */
475        public interface FileColumns extends MediaColumns {
476            /**
477             * The MTP storage ID of the file
478             * <P>Type: INTEGER</P>
479             * @hide
480             */
481            public static final String STORAGE_ID = "storage_id";
482
483            /**
484             * The MTP format code of the file
485             * <P>Type: INTEGER</P>
486             * @hide
487             */
488            public static final String FORMAT = "format";
489
490            /**
491             * The index of the parent directory of the file
492             * <P>Type: INTEGER</P>
493             */
494            public static final String PARENT = "parent";
495
496            /**
497             * The MIME type of the file
498             * <P>Type: TEXT</P>
499             */
500            public static final String MIME_TYPE = "mime_type";
501
502            /**
503             * The title of the content
504             * <P>Type: TEXT</P>
505             */
506            public static final String TITLE = "title";
507
508            /**
509             * The media type (audio, video, image or playlist)
510             * of the file, or 0 for not a media file
511             * <P>Type: TEXT</P>
512             */
513            public static final String MEDIA_TYPE = "media_type";
514
515            /**
516             * Constant for the {@link #MEDIA_TYPE} column indicating that file
517             * is not an audio, image, video or playlist file.
518             */
519            public static final int MEDIA_TYPE_NONE = 0;
520
521            /**
522             * Constant for the {@link #MEDIA_TYPE} column indicating that file is an image file.
523             */
524            public static final int MEDIA_TYPE_IMAGE = 1;
525
526            /**
527             * Constant for the {@link #MEDIA_TYPE} column indicating that file is an audio file.
528             */
529            public static final int MEDIA_TYPE_AUDIO = 2;
530
531            /**
532             * Constant for the {@link #MEDIA_TYPE} column indicating that file is a video file.
533             */
534            public static final int MEDIA_TYPE_VIDEO = 3;
535
536            /**
537             * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
538             */
539            public static final int MEDIA_TYPE_PLAYLIST = 4;
540        }
541    }
542
543    /**
544     * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
545     * to be accessed elsewhere.
546     */
547    private static class InternalThumbnails implements BaseColumns {
548        private static final int MINI_KIND = 1;
549        private static final int FULL_SCREEN_KIND = 2;
550        private static final int MICRO_KIND = 3;
551        private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
552        static final int DEFAULT_GROUP_ID = 0;
553        private static final Object sThumbBufLock = new Object();
554        private static byte[] sThumbBuf;
555
556        private static Bitmap getMiniThumbFromFile(
557                Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
558            Bitmap bitmap = null;
559            Uri thumbUri = null;
560            try {
561                long thumbId = c.getLong(0);
562                String filePath = c.getString(1);
563                thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
564                ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
565                bitmap = BitmapFactory.decodeFileDescriptor(
566                        pfdInput.getFileDescriptor(), null, options);
567                pfdInput.close();
568            } catch (FileNotFoundException ex) {
569                Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
570            } catch (IOException ex) {
571                Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
572            } catch (OutOfMemoryError ex) {
573                Log.e(TAG, "failed to allocate memory for thumbnail "
574                        + thumbUri + "; " + ex);
575            }
576            return bitmap;
577        }
578
579        /**
580         * This method cancels the thumbnail request so clients waiting for getThumbnail will be
581         * interrupted and return immediately. Only the original process which made the getThumbnail
582         * requests can cancel their own requests.
583         *
584         * @param cr ContentResolver
585         * @param origId original image or video id. use -1 to cancel all requests.
586         * @param groupId the same groupId used in getThumbnail
587         * @param baseUri the base URI of requested thumbnails
588         */
589        static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
590                long groupId) {
591            Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
592                    .appendQueryParameter("orig_id", String.valueOf(origId))
593                    .appendQueryParameter("group_id", String.valueOf(groupId)).build();
594            Cursor c = null;
595            try {
596                c = cr.query(cancelUri, PROJECTION, null, null, null);
597            }
598            finally {
599                if (c != null) c.close();
600            }
601        }
602
603        /**
604         * This method ensure thumbnails associated with origId are generated and decode the byte
605         * stream from database (MICRO_KIND) or file (MINI_KIND).
606         *
607         * Special optimization has been done to avoid further IPC communication for MICRO_KIND
608         * thumbnails.
609         *
610         * @param cr ContentResolver
611         * @param origId original image or video id
612         * @param kind could be MINI_KIND or MICRO_KIND
613         * @param options this is only used for MINI_KIND when decoding the Bitmap
614         * @param baseUri the base URI of requested thumbnails
615         * @param groupId the id of group to which this request belongs
616         * @return Bitmap bitmap of specified thumbnail kind
617         */
618        static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
619                BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
620            Bitmap bitmap = null;
621            String filePath = null;
622            // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
623            // If the magic is non-zero, we simply return thumbnail if it does exist.
624            // querying MediaProvider and simply return thumbnail.
625            MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI
626                    : Images.Media.EXTERNAL_CONTENT_URI);
627            Cursor c = null;
628            try {
629                long magic = thumbFile.getMagic(origId);
630                if (magic != 0) {
631                    if (kind == MICRO_KIND) {
632                        synchronized (sThumbBufLock) {
633                            if (sThumbBuf == null) {
634                                sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
635                            }
636                            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
637                                bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
638                                if (bitmap == null) {
639                                    Log.w(TAG, "couldn't decode byte array.");
640                                }
641                            }
642                        }
643                        return bitmap;
644                    } else if (kind == MINI_KIND) {
645                        String column = isVideo ? "video_id=" : "image_id=";
646                        c = cr.query(baseUri, PROJECTION, column + origId, null, null);
647                        if (c != null && c.moveToFirst()) {
648                            bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
649                            if (bitmap != null) {
650                                return bitmap;
651                            }
652                        }
653                    }
654                }
655
656                Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
657                        .appendQueryParameter("orig_id", String.valueOf(origId))
658                        .appendQueryParameter("group_id", String.valueOf(groupId)).build();
659                if (c != null) c.close();
660                c = cr.query(blockingUri, PROJECTION, null, null, null);
661                // This happens when original image/video doesn't exist.
662                if (c == null) return null;
663
664                // Assuming thumbnail has been generated, at least original image exists.
665                if (kind == MICRO_KIND) {
666                    synchronized (sThumbBufLock) {
667                        if (sThumbBuf == null) {
668                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
669                        }
670                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
671                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
672                            if (bitmap == null) {
673                                Log.w(TAG, "couldn't decode byte array.");
674                            }
675                        }
676                    }
677                } else if (kind == MINI_KIND) {
678                    if (c.moveToFirst()) {
679                        bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
680                    }
681                } else {
682                    throw new IllegalArgumentException("Unsupported kind: " + kind);
683                }
684
685                // We probably run out of space, so create the thumbnail in memory.
686                if (bitmap == null) {
687                    Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
688                            + ", kind=" + kind + ", isVideo="+isVideo);
689                    Uri uri = Uri.parse(
690                            baseUri.buildUpon().appendPath(String.valueOf(origId))
691                                    .toString().replaceFirst("thumbnails", "media"));
692                    if (filePath == null) {
693                        if (c != null) c.close();
694                        c = cr.query(uri, PROJECTION, null, null, null);
695                        if (c == null || !c.moveToFirst()) {
696                            return null;
697                        }
698                        filePath = c.getString(1);
699                    }
700                    if (isVideo) {
701                        bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
702                    } else {
703                        bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
704                    }
705                }
706            } catch (SQLiteException ex) {
707                Log.w(TAG, ex);
708            } finally {
709                if (c != null) c.close();
710                // To avoid file descriptor leak in application process.
711                thumbFile.deactivate();
712                thumbFile = null;
713            }
714            return bitmap;
715        }
716    }
717
718    /**
719     * Contains meta data for all available images.
720     */
721    public static final class Images {
722        public interface ImageColumns extends MediaColumns {
723            /**
724             * The description of the image
725             * <P>Type: TEXT</P>
726             */
727            public static final String DESCRIPTION = "description";
728
729            /**
730             * The picasa id of the image
731             * <P>Type: TEXT</P>
732             */
733            public static final String PICASA_ID = "picasa_id";
734
735            /**
736             * Whether the video should be published as public or private
737             * <P>Type: INTEGER</P>
738             */
739            public static final String IS_PRIVATE = "isprivate";
740
741            /**
742             * The latitude where the image was captured.
743             * <P>Type: DOUBLE</P>
744             */
745            public static final String LATITUDE = "latitude";
746
747            /**
748             * The longitude where the image was captured.
749             * <P>Type: DOUBLE</P>
750             */
751            public static final String LONGITUDE = "longitude";
752
753            /**
754             * The date & time that the image was taken in units
755             * of milliseconds since jan 1, 1970.
756             * <P>Type: INTEGER</P>
757             */
758            public static final String DATE_TAKEN = "datetaken";
759
760            /**
761             * The orientation for the image expressed as degrees.
762             * Only degrees 0, 90, 180, 270 will work.
763             * <P>Type: INTEGER</P>
764             */
765            public static final String ORIENTATION = "orientation";
766
767            /**
768             * The mini thumb id.
769             * <P>Type: INTEGER</P>
770             */
771            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
772
773            /**
774             * The bucket id of the image. This is a read-only property that
775             * is automatically computed from the DATA column.
776             * <P>Type: TEXT</P>
777             */
778            public static final String BUCKET_ID = "bucket_id";
779
780            /**
781             * The bucket display name of the image. This is a read-only property that
782             * is automatically computed from the DATA column.
783             * <P>Type: TEXT</P>
784             */
785            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
786        }
787
788        public static final class Media implements ImageColumns {
789            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
790                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
791            }
792
793            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
794                    String where, String orderBy) {
795                return cr.query(uri, projection, where,
796                                             null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
797            }
798
799            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
800                    String selection, String [] selectionArgs, String orderBy) {
801                return cr.query(uri, projection, selection,
802                        selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
803            }
804
805            /**
806             * Retrieves an image for the given url as a {@link Bitmap}.
807             *
808             * @param cr The content resolver to use
809             * @param url The url of the image
810             * @throws FileNotFoundException
811             * @throws IOException
812             */
813            public static final Bitmap getBitmap(ContentResolver cr, Uri url)
814                    throws FileNotFoundException, IOException {
815                InputStream input = cr.openInputStream(url);
816                Bitmap bitmap = BitmapFactory.decodeStream(input);
817                input.close();
818                return bitmap;
819            }
820
821            /**
822             * Insert an image and create a thumbnail for it.
823             *
824             * @param cr The content resolver to use
825             * @param imagePath The path to the image to insert
826             * @param name The name of the image
827             * @param description The description of the image
828             * @return The URL to the newly created image
829             * @throws FileNotFoundException
830             */
831            public static final String insertImage(ContentResolver cr, String imagePath,
832                    String name, String description) throws FileNotFoundException {
833                // Check if file exists with a FileInputStream
834                FileInputStream stream = new FileInputStream(imagePath);
835                try {
836                    Bitmap bm = BitmapFactory.decodeFile(imagePath);
837                    String ret = insertImage(cr, bm, name, description);
838                    bm.recycle();
839                    return ret;
840                } finally {
841                    try {
842                        stream.close();
843                    } catch (IOException e) {
844                    }
845                }
846            }
847
848            private static final Bitmap StoreThumbnail(
849                    ContentResolver cr,
850                    Bitmap source,
851                    long id,
852                    float width, float height,
853                    int kind) {
854                // create the matrix to scale it
855                Matrix matrix = new Matrix();
856
857                float scaleX = width / source.getWidth();
858                float scaleY = height / source.getHeight();
859
860                matrix.setScale(scaleX, scaleY);
861
862                Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
863                                                   source.getWidth(),
864                                                   source.getHeight(), matrix,
865                                                   true);
866
867                ContentValues values = new ContentValues(4);
868                values.put(Images.Thumbnails.KIND,     kind);
869                values.put(Images.Thumbnails.IMAGE_ID, (int)id);
870                values.put(Images.Thumbnails.HEIGHT,   thumb.getHeight());
871                values.put(Images.Thumbnails.WIDTH,    thumb.getWidth());
872
873                Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
874
875                try {
876                    OutputStream thumbOut = cr.openOutputStream(url);
877
878                    thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
879                    thumbOut.close();
880                    return thumb;
881                }
882                catch (FileNotFoundException ex) {
883                    return null;
884                }
885                catch (IOException ex) {
886                    return null;
887                }
888            }
889
890            /**
891             * Insert an image and create a thumbnail for it.
892             *
893             * @param cr The content resolver to use
894             * @param source The stream to use for the image
895             * @param title The name of the image
896             * @param description The description of the image
897             * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
898             *              for any reason.
899             */
900            public static final String insertImage(ContentResolver cr, Bitmap source,
901                                                   String title, String description) {
902                ContentValues values = new ContentValues();
903                values.put(Images.Media.TITLE, title);
904                values.put(Images.Media.DESCRIPTION, description);
905                values.put(Images.Media.MIME_TYPE, "image/jpeg");
906
907                Uri url = null;
908                String stringUrl = null;    /* value to be returned */
909
910                try {
911                    url = cr.insert(EXTERNAL_CONTENT_URI, values);
912
913                    if (source != null) {
914                        OutputStream imageOut = cr.openOutputStream(url);
915                        try {
916                            source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
917                        } finally {
918                            imageOut.close();
919                        }
920
921                        long id = ContentUris.parseId(url);
922                        // Wait until MINI_KIND thumbnail is generated.
923                        Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
924                                Images.Thumbnails.MINI_KIND, null);
925                        // This is for backward compatibility.
926                        Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
927                                Images.Thumbnails.MICRO_KIND);
928                    } else {
929                        Log.e(TAG, "Failed to create thumbnail, removing original");
930                        cr.delete(url, null, null);
931                        url = null;
932                    }
933                } catch (Exception e) {
934                    Log.e(TAG, "Failed to insert image", e);
935                    if (url != null) {
936                        cr.delete(url, null, null);
937                        url = null;
938                    }
939                }
940
941                if (url != null) {
942                    stringUrl = url.toString();
943                }
944
945                return stringUrl;
946            }
947
948            /**
949             * Get the content:// style URI for the image media table on the
950             * given volume.
951             *
952             * @param volumeName the name of the volume to get the URI for
953             * @return the URI to the image media table on the given volume
954             */
955            public static Uri getContentUri(String volumeName) {
956                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
957                        "/images/media");
958            }
959
960            /**
961             * The content:// style URI for the internal storage.
962             */
963            public static final Uri INTERNAL_CONTENT_URI =
964                    getContentUri("internal");
965
966            /**
967             * The content:// style URI for the "primary" external storage
968             * volume.
969             */
970            public static final Uri EXTERNAL_CONTENT_URI =
971                    getContentUri("external");
972
973            /**
974             * The MIME type of of this directory of
975             * images.  Note that each entry in this directory will have a standard
976             * image MIME type as appropriate -- for example, image/jpeg.
977             */
978            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
979
980            /**
981             * The default sort order for this table
982             */
983            public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
984        }
985
986        /**
987         * This class allows developers to query and get two kinds of thumbnails:
988         * MINI_KIND: 512 x 384 thumbnail
989         * MICRO_KIND: 96 x 96 thumbnail
990         */
991        public static class Thumbnails implements BaseColumns {
992            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
993                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
994            }
995
996            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
997                    String[] projection) {
998                return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
999            }
1000
1001            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
1002                    String[] projection) {
1003                return cr.query(EXTERNAL_CONTENT_URI, projection,
1004                        IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
1005                        kind, null, null);
1006            }
1007
1008            /**
1009             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1010             * interrupted and return immediately. Only the original process which made the getThumbnail
1011             * requests can cancel their own requests.
1012             *
1013             * @param cr ContentResolver
1014             * @param origId original image id
1015             */
1016            public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
1017                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
1018                        InternalThumbnails.DEFAULT_GROUP_ID);
1019            }
1020
1021            /**
1022             * This method checks if the thumbnails of the specified image (origId) has been created.
1023             * It will be blocked until the thumbnails are generated.
1024             *
1025             * @param cr ContentResolver used to dispatch queries to MediaProvider.
1026             * @param origId Original image id associated with thumbnail of interest.
1027             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
1028             * @param options this is only used for MINI_KIND when decoding the Bitmap
1029             * @return A Bitmap instance. It could be null if the original image
1030             *         associated with origId doesn't exist or memory is not enough.
1031             */
1032            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
1033                    BitmapFactory.Options options) {
1034                return InternalThumbnails.getThumbnail(cr, origId,
1035                        InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
1036                        EXTERNAL_CONTENT_URI, false);
1037            }
1038
1039            /**
1040             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1041             * interrupted and return immediately. Only the original process which made the getThumbnail
1042             * requests can cancel their own requests.
1043             *
1044             * @param cr ContentResolver
1045             * @param origId original image id
1046             * @param groupId the same groupId used in getThumbnail.
1047             */
1048            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
1049                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
1050            }
1051
1052            /**
1053             * This method checks if the thumbnails of the specified image (origId) has been created.
1054             * It will be blocked until the thumbnails are generated.
1055             *
1056             * @param cr ContentResolver used to dispatch queries to MediaProvider.
1057             * @param origId Original image id associated with thumbnail of interest.
1058             * @param groupId the id of group to which this request belongs
1059             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
1060             * @param options this is only used for MINI_KIND when decoding the Bitmap
1061             * @return A Bitmap instance. It could be null if the original image
1062             *         associated with origId doesn't exist or memory is not enough.
1063             */
1064            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
1065                    int kind, BitmapFactory.Options options) {
1066                return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
1067                        EXTERNAL_CONTENT_URI, false);
1068            }
1069
1070            /**
1071             * Get the content:// style URI for the image media table on the
1072             * given volume.
1073             *
1074             * @param volumeName the name of the volume to get the URI for
1075             * @return the URI to the image media table on the given volume
1076             */
1077            public static Uri getContentUri(String volumeName) {
1078                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1079                        "/images/thumbnails");
1080            }
1081
1082            /**
1083             * The content:// style URI for the internal storage.
1084             */
1085            public static final Uri INTERNAL_CONTENT_URI =
1086                    getContentUri("internal");
1087
1088            /**
1089             * The content:// style URI for the "primary" external storage
1090             * volume.
1091             */
1092            public static final Uri EXTERNAL_CONTENT_URI =
1093                    getContentUri("external");
1094
1095            /**
1096             * The default sort order for this table
1097             */
1098            public static final String DEFAULT_SORT_ORDER = "image_id ASC";
1099
1100            /**
1101             * The data stream for the thumbnail
1102             * <P>Type: DATA STREAM</P>
1103             */
1104            public static final String DATA = "_data";
1105
1106            /**
1107             * The original image for the thumbnal
1108             * <P>Type: INTEGER (ID from Images table)</P>
1109             */
1110            public static final String IMAGE_ID = "image_id";
1111
1112            /**
1113             * The kind of the thumbnail
1114             * <P>Type: INTEGER (One of the values below)</P>
1115             */
1116            public static final String KIND = "kind";
1117
1118            public static final int MINI_KIND = 1;
1119            public static final int FULL_SCREEN_KIND = 2;
1120            public static final int MICRO_KIND = 3;
1121            /**
1122             * The blob raw data of thumbnail
1123             * <P>Type: DATA STREAM</P>
1124             */
1125            public static final String THUMB_DATA = "thumb_data";
1126
1127            /**
1128             * The width of the thumbnal
1129             * <P>Type: INTEGER (long)</P>
1130             */
1131            public static final String WIDTH = "width";
1132
1133            /**
1134             * The height of the thumbnail
1135             * <P>Type: INTEGER (long)</P>
1136             */
1137            public static final String HEIGHT = "height";
1138        }
1139    }
1140
1141    /**
1142     * Container for all audio content.
1143     */
1144    public static final class Audio {
1145        /**
1146         * Columns for audio file that show up in multiple tables.
1147         */
1148        public interface AudioColumns extends MediaColumns {
1149
1150            /**
1151             * A non human readable key calculated from the TITLE, used for
1152             * searching, sorting and grouping
1153             * <P>Type: TEXT</P>
1154             */
1155            public static final String TITLE_KEY = "title_key";
1156
1157            /**
1158             * The duration of the audio file, in ms
1159             * <P>Type: INTEGER (long)</P>
1160             */
1161            public static final String DURATION = "duration";
1162
1163            /**
1164             * The position, in ms, playback was at when playback for this file
1165             * was last stopped.
1166             * <P>Type: INTEGER (long)</P>
1167             */
1168            public static final String BOOKMARK = "bookmark";
1169
1170            /**
1171             * The id of the artist who created the audio file, if any
1172             * <P>Type: INTEGER (long)</P>
1173             */
1174            public static final String ARTIST_ID = "artist_id";
1175
1176            /**
1177             * The artist who created the audio file, if any
1178             * <P>Type: TEXT</P>
1179             */
1180            public static final String ARTIST = "artist";
1181
1182            /**
1183             * The artist credited for the album that contains the audio file
1184             * <P>Type: TEXT</P>
1185             * @hide
1186             */
1187            public static final String ALBUM_ARTIST = "album_artist";
1188
1189            /**
1190             * Whether the song is part of a compilation
1191             * <P>Type: TEXT</P>
1192             * @hide
1193             */
1194            public static final String COMPILATION = "compilation";
1195
1196            /**
1197             * A non human readable key calculated from the ARTIST, used for
1198             * searching, sorting and grouping
1199             * <P>Type: TEXT</P>
1200             */
1201            public static final String ARTIST_KEY = "artist_key";
1202
1203            /**
1204             * The composer of the audio file, if any
1205             * <P>Type: TEXT</P>
1206             */
1207            public static final String COMPOSER = "composer";
1208
1209            /**
1210             * The id of the album the audio file is from, if any
1211             * <P>Type: INTEGER (long)</P>
1212             */
1213            public static final String ALBUM_ID = "album_id";
1214
1215            /**
1216             * The album the audio file is from, if any
1217             * <P>Type: TEXT</P>
1218             */
1219            public static final String ALBUM = "album";
1220
1221            /**
1222             * A non human readable key calculated from the ALBUM, used for
1223             * searching, sorting and grouping
1224             * <P>Type: TEXT</P>
1225             */
1226            public static final String ALBUM_KEY = "album_key";
1227
1228            /**
1229             * The track number of this song on the album, if any.
1230             * This number encodes both the track number and the
1231             * disc number. For multi-disc sets, this number will
1232             * be 1xxx for tracks on the first disc, 2xxx for tracks
1233             * on the second disc, etc.
1234             * <P>Type: INTEGER</P>
1235             */
1236            public static final String TRACK = "track";
1237
1238            /**
1239             * The year the audio file was recorded, if any
1240             * <P>Type: INTEGER</P>
1241             */
1242            public static final String YEAR = "year";
1243
1244            /**
1245             * Non-zero if the audio file is music
1246             * <P>Type: INTEGER (boolean)</P>
1247             */
1248            public static final String IS_MUSIC = "is_music";
1249
1250            /**
1251             * Non-zero if the audio file is a podcast
1252             * <P>Type: INTEGER (boolean)</P>
1253             */
1254            public static final String IS_PODCAST = "is_podcast";
1255
1256            /**
1257             * Non-zero if the audio file may be a ringtone
1258             * <P>Type: INTEGER (boolean)</P>
1259             */
1260            public static final String IS_RINGTONE = "is_ringtone";
1261
1262            /**
1263             * Non-zero if the audio file may be an alarm
1264             * <P>Type: INTEGER (boolean)</P>
1265             */
1266            public static final String IS_ALARM = "is_alarm";
1267
1268            /**
1269             * Non-zero if the audio file may be a notification sound
1270             * <P>Type: INTEGER (boolean)</P>
1271             */
1272            public static final String IS_NOTIFICATION = "is_notification";
1273
1274            /**
1275             * The genre of the audio file, if any
1276             * <P>Type: TEXT</P>
1277             * Does not exist in the database - only used by the media scanner for inserts.
1278             * @hide
1279             */
1280            public static final String GENRE = "genre";
1281        }
1282
1283        /**
1284         * Converts a name to a "key" that can be used for grouping, sorting
1285         * and searching.
1286         * The rules that govern this conversion are:
1287         * - remove 'special' characters like ()[]'!?.,
1288         * - remove leading/trailing spaces
1289         * - convert everything to lowercase
1290         * - remove leading "the ", "an " and "a "
1291         * - remove trailing ", the|an|a"
1292         * - remove accents. This step leaves us with CollationKey data,
1293         *   which is not human readable
1294         *
1295         * @param name The artist or album name to convert
1296         * @return The "key" for the given name.
1297         */
1298        public static String keyFor(String name) {
1299            if (name != null)  {
1300                boolean sortfirst = false;
1301                if (name.equals(UNKNOWN_STRING)) {
1302                    return "\001";
1303                }
1304                // Check if the first character is \001. We use this to
1305                // force sorting of certain special files, like the silent ringtone.
1306                if (name.startsWith("\001")) {
1307                    sortfirst = true;
1308                }
1309                name = name.trim().toLowerCase();
1310                if (name.startsWith("the ")) {
1311                    name = name.substring(4);
1312                }
1313                if (name.startsWith("an ")) {
1314                    name = name.substring(3);
1315                }
1316                if (name.startsWith("a ")) {
1317                    name = name.substring(2);
1318                }
1319                if (name.endsWith(", the") || name.endsWith(",the") ||
1320                    name.endsWith(", an") || name.endsWith(",an") ||
1321                    name.endsWith(", a") || name.endsWith(",a")) {
1322                    name = name.substring(0, name.lastIndexOf(','));
1323                }
1324                name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
1325                if (name.length() > 0) {
1326                    // Insert a separator between the characters to avoid
1327                    // matches on a partial character. If we ever change
1328                    // to start-of-word-only matches, this can be removed.
1329                    StringBuilder b = new StringBuilder();
1330                    b.append('.');
1331                    int nl = name.length();
1332                    for (int i = 0; i < nl; i++) {
1333                        b.append(name.charAt(i));
1334                        b.append('.');
1335                    }
1336                    name = b.toString();
1337                    String key = DatabaseUtils.getCollationKey(name);
1338                    if (sortfirst) {
1339                        key = "\001" + key;
1340                    }
1341                    return key;
1342               } else {
1343                    return "";
1344                }
1345            }
1346            return null;
1347        }
1348
1349        public static final class Media implements AudioColumns {
1350
1351            private static final String[] EXTERNAL_PATHS;
1352
1353            static {
1354                String secondary_storage = System.getenv("SECONDARY_STORAGE");
1355                if (secondary_storage != null) {
1356                    EXTERNAL_PATHS = secondary_storage.split(":");
1357                } else {
1358                    EXTERNAL_PATHS = new String[0];
1359                }
1360            }
1361
1362            /**
1363             * Get the content:// style URI for the audio media table on the
1364             * given volume.
1365             *
1366             * @param volumeName the name of the volume to get the URI for
1367             * @return the URI to the audio media table on the given volume
1368             */
1369            public static Uri getContentUri(String volumeName) {
1370                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1371                        "/audio/media");
1372            }
1373
1374            public static Uri getContentUriForPath(String path) {
1375                for (String ep : EXTERNAL_PATHS) {
1376                    if (path.startsWith(ep)) {
1377                        return EXTERNAL_CONTENT_URI;
1378                    }
1379                }
1380
1381                return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
1382                        EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
1383            }
1384
1385            /**
1386             * The content:// style URI for the internal storage.
1387             */
1388            public static final Uri INTERNAL_CONTENT_URI =
1389                    getContentUri("internal");
1390
1391            /**
1392             * The content:// style URI for the "primary" external storage
1393             * volume.
1394             */
1395            public static final Uri EXTERNAL_CONTENT_URI =
1396                    getContentUri("external");
1397
1398            /**
1399             * The MIME type for this table.
1400             */
1401            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
1402
1403            /**
1404             * The MIME type for an audio track.
1405             */
1406            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/audio";
1407
1408            /**
1409             * The default sort order for this table
1410             */
1411            public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
1412
1413            /**
1414             * Activity Action: Start SoundRecorder application.
1415             * <p>Input: nothing.
1416             * <p>Output: An uri to the recorded sound stored in the Media Library
1417             * if the recording was successful.
1418             * May also contain the extra EXTRA_MAX_BYTES.
1419             * @see #EXTRA_MAX_BYTES
1420             */
1421            public static final String RECORD_SOUND_ACTION =
1422                    "android.provider.MediaStore.RECORD_SOUND";
1423
1424            /**
1425             * The name of the Intent-extra used to define a maximum file size for
1426             * a recording made by the SoundRecorder application.
1427             *
1428             * @see #RECORD_SOUND_ACTION
1429             */
1430             public static final String EXTRA_MAX_BYTES =
1431                    "android.provider.MediaStore.extra.MAX_BYTES";
1432        }
1433
1434        /**
1435         * Columns representing an audio genre
1436         */
1437        public interface GenresColumns {
1438            /**
1439             * The name of the genre
1440             * <P>Type: TEXT</P>
1441             */
1442            public static final String NAME = "name";
1443        }
1444
1445        /**
1446         * Contains all genres for audio files
1447         */
1448        public static final class Genres implements BaseColumns, GenresColumns {
1449            /**
1450             * Get the content:// style URI for the audio genres table on the
1451             * given volume.
1452             *
1453             * @param volumeName the name of the volume to get the URI for
1454             * @return the URI to the audio genres table on the given volume
1455             */
1456            public static Uri getContentUri(String volumeName) {
1457                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1458                        "/audio/genres");
1459            }
1460
1461            /**
1462             * Get the content:// style URI for querying the genres of an audio file.
1463             *
1464             * @param volumeName the name of the volume to get the URI for
1465             * @param audioId the ID of the audio file for which to retrieve the genres
1466             * @return the URI to for querying the genres for the audio file
1467             * with the given the volume and audioID
1468             */
1469            public static Uri getContentUriForAudioId(String volumeName, int audioId) {
1470                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1471                        "/audio/media/" + audioId + "/genres");
1472            }
1473
1474            /**
1475             * The content:// style URI for the internal storage.
1476             */
1477            public static final Uri INTERNAL_CONTENT_URI =
1478                    getContentUri("internal");
1479
1480            /**
1481             * The content:// style URI for the "primary" external storage
1482             * volume.
1483             */
1484            public static final Uri EXTERNAL_CONTENT_URI =
1485                    getContentUri("external");
1486
1487            /**
1488             * The MIME type for this table.
1489             */
1490            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
1491
1492            /**
1493             * The MIME type for entries in this table.
1494             */
1495            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
1496
1497            /**
1498             * The default sort order for this table
1499             */
1500            public static final String DEFAULT_SORT_ORDER = NAME;
1501
1502            /**
1503             * Sub-directory of each genre containing all members.
1504             */
1505            public static final class Members implements AudioColumns {
1506
1507                public static final Uri getContentUri(String volumeName,
1508                        long genreId) {
1509                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1510                            + "/audio/genres/" + genreId + "/members");
1511                }
1512
1513                /**
1514                 * A subdirectory of each genre containing all member audio files.
1515                 */
1516                public static final String CONTENT_DIRECTORY = "members";
1517
1518                /**
1519                 * The default sort order for this table
1520                 */
1521                public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
1522
1523                /**
1524                 * The ID of the audio file
1525                 * <P>Type: INTEGER (long)</P>
1526                 */
1527                public static final String AUDIO_ID = "audio_id";
1528
1529                /**
1530                 * The ID of the genre
1531                 * <P>Type: INTEGER (long)</P>
1532                 */
1533                public static final String GENRE_ID = "genre_id";
1534            }
1535        }
1536
1537        /**
1538         * Columns representing a playlist
1539         */
1540        public interface PlaylistsColumns {
1541            /**
1542             * The name of the playlist
1543             * <P>Type: TEXT</P>
1544             */
1545            public static final String NAME = "name";
1546
1547            /**
1548             * The data stream for the playlist file
1549             * <P>Type: DATA STREAM</P>
1550             */
1551            public static final String DATA = "_data";
1552
1553            /**
1554             * The time the file was added to the media provider
1555             * Units are seconds since 1970.
1556             * <P>Type: INTEGER (long)</P>
1557             */
1558            public static final String DATE_ADDED = "date_added";
1559
1560            /**
1561             * The time the file was last modified
1562             * Units are seconds since 1970.
1563             * NOTE: This is for internal use by the media scanner.  Do not modify this field.
1564             * <P>Type: INTEGER (long)</P>
1565             */
1566            public static final String DATE_MODIFIED = "date_modified";
1567        }
1568
1569        /**
1570         * Contains playlists for audio files
1571         */
1572        public static final class Playlists implements BaseColumns,
1573                PlaylistsColumns {
1574            /**
1575             * Get the content:// style URI for the audio playlists table on the
1576             * given volume.
1577             *
1578             * @param volumeName the name of the volume to get the URI for
1579             * @return the URI to the audio playlists table on the given volume
1580             */
1581            public static Uri getContentUri(String volumeName) {
1582                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1583                        "/audio/playlists");
1584            }
1585
1586            /**
1587             * The content:// style URI for the internal storage.
1588             */
1589            public static final Uri INTERNAL_CONTENT_URI =
1590                    getContentUri("internal");
1591
1592            /**
1593             * The content:// style URI for the "primary" external storage
1594             * volume.
1595             */
1596            public static final Uri EXTERNAL_CONTENT_URI =
1597                    getContentUri("external");
1598
1599            /**
1600             * The MIME type for this table.
1601             */
1602            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
1603
1604            /**
1605             * The MIME type for entries in this table.
1606             */
1607            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
1608
1609            /**
1610             * The default sort order for this table
1611             */
1612            public static final String DEFAULT_SORT_ORDER = NAME;
1613
1614            /**
1615             * Sub-directory of each playlist containing all members.
1616             */
1617            public static final class Members implements AudioColumns {
1618                public static final Uri getContentUri(String volumeName,
1619                        long playlistId) {
1620                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1621                            + "/audio/playlists/" + playlistId + "/members");
1622                }
1623
1624                /**
1625                 * Convenience method to move a playlist item to a new location
1626                 * @param res The content resolver to use
1627                 * @param playlistId The numeric id of the playlist
1628                 * @param from The position of the item to move
1629                 * @param to The position to move the item to
1630                 * @return true on success
1631                 */
1632                public static final boolean moveItem(ContentResolver res,
1633                        long playlistId, int from, int to) {
1634                    Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
1635                            playlistId)
1636                            .buildUpon()
1637                            .appendEncodedPath(String.valueOf(from))
1638                            .appendQueryParameter("move", "true")
1639                            .build();
1640                    ContentValues values = new ContentValues();
1641                    values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
1642                    return res.update(uri, values, null, null) != 0;
1643                }
1644
1645                /**
1646                 * The ID within the playlist.
1647                 */
1648                public static final String _ID = "_id";
1649
1650                /**
1651                 * A subdirectory of each playlist containing all member audio
1652                 * files.
1653                 */
1654                public static final String CONTENT_DIRECTORY = "members";
1655
1656                /**
1657                 * The ID of the audio file
1658                 * <P>Type: INTEGER (long)</P>
1659                 */
1660                public static final String AUDIO_ID = "audio_id";
1661
1662                /**
1663                 * The ID of the playlist
1664                 * <P>Type: INTEGER (long)</P>
1665                 */
1666                public static final String PLAYLIST_ID = "playlist_id";
1667
1668                /**
1669                 * The order of the songs in the playlist
1670                 * <P>Type: INTEGER (long)></P>
1671                 */
1672                public static final String PLAY_ORDER = "play_order";
1673
1674                /**
1675                 * The default sort order for this table
1676                 */
1677                public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
1678            }
1679        }
1680
1681        /**
1682         * Columns representing an artist
1683         */
1684        public interface ArtistColumns {
1685            /**
1686             * The artist who created the audio file, if any
1687             * <P>Type: TEXT</P>
1688             */
1689            public static final String ARTIST = "artist";
1690
1691            /**
1692             * A non human readable key calculated from the ARTIST, used for
1693             * searching, sorting and grouping
1694             * <P>Type: TEXT</P>
1695             */
1696            public static final String ARTIST_KEY = "artist_key";
1697
1698            /**
1699             * The number of albums in the database for this artist
1700             */
1701            public static final String NUMBER_OF_ALBUMS = "number_of_albums";
1702
1703            /**
1704             * The number of albums in the database for this artist
1705             */
1706            public static final String NUMBER_OF_TRACKS = "number_of_tracks";
1707        }
1708
1709        /**
1710         * Contains artists for audio files
1711         */
1712        public static final class Artists implements BaseColumns, ArtistColumns {
1713            /**
1714             * Get the content:// style URI for the artists table on the
1715             * given volume.
1716             *
1717             * @param volumeName the name of the volume to get the URI for
1718             * @return the URI to the audio artists table on the given volume
1719             */
1720            public static Uri getContentUri(String volumeName) {
1721                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1722                        "/audio/artists");
1723            }
1724
1725            /**
1726             * The content:// style URI for the internal storage.
1727             */
1728            public static final Uri INTERNAL_CONTENT_URI =
1729                    getContentUri("internal");
1730
1731            /**
1732             * The content:// style URI for the "primary" external storage
1733             * volume.
1734             */
1735            public static final Uri EXTERNAL_CONTENT_URI =
1736                    getContentUri("external");
1737
1738            /**
1739             * The MIME type for this table.
1740             */
1741            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
1742
1743            /**
1744             * The MIME type for entries in this table.
1745             */
1746            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
1747
1748            /**
1749             * The default sort order for this table
1750             */
1751            public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
1752
1753            /**
1754             * Sub-directory of each artist containing all albums on which
1755             * a song by the artist appears.
1756             */
1757            public static final class Albums implements AlbumColumns {
1758                public static final Uri getContentUri(String volumeName,
1759                        long artistId) {
1760                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1761                            + "/audio/artists/" + artistId + "/albums");
1762                }
1763            }
1764        }
1765
1766        /**
1767         * Columns representing an album
1768         */
1769        public interface AlbumColumns {
1770
1771            /**
1772             * The id for the album
1773             * <P>Type: INTEGER</P>
1774             */
1775            public static final String ALBUM_ID = "album_id";
1776
1777            /**
1778             * The album on which the audio file appears, if any
1779             * <P>Type: TEXT</P>
1780             */
1781            public static final String ALBUM = "album";
1782
1783            /**
1784             * The artist whose songs appear on this album
1785             * <P>Type: TEXT</P>
1786             */
1787            public static final String ARTIST = "artist";
1788
1789            /**
1790             * The number of songs on this album
1791             * <P>Type: INTEGER</P>
1792             */
1793            public static final String NUMBER_OF_SONGS = "numsongs";
1794
1795            /**
1796             * This column is available when getting album info via artist,
1797             * and indicates the number of songs on the album by the given
1798             * artist.
1799             * <P>Type: INTEGER</P>
1800             */
1801            public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
1802
1803            /**
1804             * The year in which the earliest songs
1805             * on this album were released. This will often
1806             * be the same as {@link #LAST_YEAR}, but for compilation albums
1807             * they might differ.
1808             * <P>Type: INTEGER</P>
1809             */
1810            public static final String FIRST_YEAR = "minyear";
1811
1812            /**
1813             * The year in which the latest songs
1814             * on this album were released. This will often
1815             * be the same as {@link #FIRST_YEAR}, but for compilation albums
1816             * they might differ.
1817             * <P>Type: INTEGER</P>
1818             */
1819            public static final String LAST_YEAR = "maxyear";
1820
1821            /**
1822             * A non human readable key calculated from the ALBUM, used for
1823             * searching, sorting and grouping
1824             * <P>Type: TEXT</P>
1825             */
1826            public static final String ALBUM_KEY = "album_key";
1827
1828            /**
1829             * Cached album art.
1830             * <P>Type: TEXT</P>
1831             */
1832            public static final String ALBUM_ART = "album_art";
1833        }
1834
1835        /**
1836         * Contains artists for audio files
1837         */
1838        public static final class Albums implements BaseColumns, AlbumColumns {
1839            /**
1840             * Get the content:// style URI for the albums table on the
1841             * given volume.
1842             *
1843             * @param volumeName the name of the volume to get the URI for
1844             * @return the URI to the audio albums table on the given volume
1845             */
1846            public static Uri getContentUri(String volumeName) {
1847                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1848                        "/audio/albums");
1849            }
1850
1851            /**
1852             * The content:// style URI for the internal storage.
1853             */
1854            public static final Uri INTERNAL_CONTENT_URI =
1855                    getContentUri("internal");
1856
1857            /**
1858             * The content:// style URI for the "primary" external storage
1859             * volume.
1860             */
1861            public static final Uri EXTERNAL_CONTENT_URI =
1862                    getContentUri("external");
1863
1864            /**
1865             * The MIME type for this table.
1866             */
1867            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
1868
1869            /**
1870             * The MIME type for entries in this table.
1871             */
1872            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
1873
1874            /**
1875             * The default sort order for this table
1876             */
1877            public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
1878        }
1879
1880        public static final class Radio {
1881            /**
1882             * The MIME type for entries in this table.
1883             */
1884            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
1885        }
1886    }
1887
1888    public static final class Video {
1889
1890        /**
1891         * The default sort order for this table.
1892         */
1893        public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
1894
1895        public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
1896            return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1897        }
1898
1899        public interface VideoColumns extends MediaColumns {
1900
1901            /**
1902             * The duration of the video file, in ms
1903             * <P>Type: INTEGER (long)</P>
1904             */
1905            public static final String DURATION = "duration";
1906
1907            /**
1908             * The artist who created the video file, if any
1909             * <P>Type: TEXT</P>
1910             */
1911            public static final String ARTIST = "artist";
1912
1913            /**
1914             * The album the video file is from, if any
1915             * <P>Type: TEXT</P>
1916             */
1917            public static final String ALBUM = "album";
1918
1919            /**
1920             * The resolution of the video file, formatted as "XxY"
1921             * <P>Type: TEXT</P>
1922             */
1923            public static final String RESOLUTION = "resolution";
1924
1925            /**
1926             * The description of the video recording
1927             * <P>Type: TEXT</P>
1928             */
1929            public static final String DESCRIPTION = "description";
1930
1931            /**
1932             * Whether the video should be published as public or private
1933             * <P>Type: INTEGER</P>
1934             */
1935            public static final String IS_PRIVATE = "isprivate";
1936
1937            /**
1938             * The user-added tags associated with a video
1939             * <P>Type: TEXT</P>
1940             */
1941            public static final String TAGS = "tags";
1942
1943            /**
1944             * The YouTube category of the video
1945             * <P>Type: TEXT</P>
1946             */
1947            public static final String CATEGORY = "category";
1948
1949            /**
1950             * The language of the video
1951             * <P>Type: TEXT</P>
1952             */
1953            public static final String LANGUAGE = "language";
1954
1955            /**
1956             * The latitude where the video was captured.
1957             * <P>Type: DOUBLE</P>
1958             */
1959            public static final String LATITUDE = "latitude";
1960
1961            /**
1962             * The longitude where the video was captured.
1963             * <P>Type: DOUBLE</P>
1964             */
1965            public static final String LONGITUDE = "longitude";
1966
1967            /**
1968             * The date & time that the video was taken in units
1969             * of milliseconds since jan 1, 1970.
1970             * <P>Type: INTEGER</P>
1971             */
1972            public static final String DATE_TAKEN = "datetaken";
1973
1974            /**
1975             * The mini thumb id.
1976             * <P>Type: INTEGER</P>
1977             */
1978            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
1979
1980            /**
1981             * The bucket id of the video. This is a read-only property that
1982             * is automatically computed from the DATA column.
1983             * <P>Type: TEXT</P>
1984             */
1985            public static final String BUCKET_ID = "bucket_id";
1986
1987            /**
1988             * The bucket display name of the video. This is a read-only property that
1989             * is automatically computed from the DATA column.
1990             * <P>Type: TEXT</P>
1991             */
1992            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
1993
1994            /**
1995             * The bookmark for the video. Time in ms. Represents the location in the video that the
1996             * video should start playing at the next time it is opened. If the value is null or
1997             * out of the range 0..DURATION-1 then the video should start playing from the
1998             * beginning.
1999             * <P>Type: INTEGER</P>
2000             */
2001            public static final String BOOKMARK = "bookmark";
2002        }
2003
2004        public static final class Media implements VideoColumns {
2005            /**
2006             * Get the content:// style URI for the video media table on the
2007             * given volume.
2008             *
2009             * @param volumeName the name of the volume to get the URI for
2010             * @return the URI to the video media table on the given volume
2011             */
2012            public static Uri getContentUri(String volumeName) {
2013                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
2014                        "/video/media");
2015            }
2016
2017            /**
2018             * The content:// style URI for the internal storage.
2019             */
2020            public static final Uri INTERNAL_CONTENT_URI =
2021                    getContentUri("internal");
2022
2023            /**
2024             * The content:// style URI for the "primary" external storage
2025             * volume.
2026             */
2027            public static final Uri EXTERNAL_CONTENT_URI =
2028                    getContentUri("external");
2029
2030            /**
2031             * The MIME type for this table.
2032             */
2033            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
2034
2035            /**
2036             * The default sort order for this table
2037             */
2038            public static final String DEFAULT_SORT_ORDER = TITLE;
2039        }
2040
2041        /**
2042         * This class allows developers to query and get two kinds of thumbnails:
2043         * MINI_KIND: 512 x 384 thumbnail
2044         * MICRO_KIND: 96 x 96 thumbnail
2045         *
2046         */
2047        public static class Thumbnails implements BaseColumns {
2048            /**
2049             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
2050             * interrupted and return immediately. Only the original process which made the getThumbnail
2051             * requests can cancel their own requests.
2052             *
2053             * @param cr ContentResolver
2054             * @param origId original video id
2055             */
2056            public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
2057                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
2058                        InternalThumbnails.DEFAULT_GROUP_ID);
2059            }
2060
2061            /**
2062             * This method checks if the thumbnails of the specified image (origId) has been created.
2063             * It will be blocked until the thumbnails are generated.
2064             *
2065             * @param cr ContentResolver used to dispatch queries to MediaProvider.
2066             * @param origId Original image id associated with thumbnail of interest.
2067             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
2068             * @param options this is only used for MINI_KIND when decoding the Bitmap
2069             * @return A Bitmap instance. It could be null if the original image
2070             *         associated with origId doesn't exist or memory is not enough.
2071             */
2072            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
2073                    BitmapFactory.Options options) {
2074                return InternalThumbnails.getThumbnail(cr, origId,
2075                        InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
2076                        EXTERNAL_CONTENT_URI, true);
2077            }
2078
2079            /**
2080             * This method checks if the thumbnails of the specified image (origId) has been created.
2081             * It will be blocked until the thumbnails are generated.
2082             *
2083             * @param cr ContentResolver used to dispatch queries to MediaProvider.
2084             * @param origId Original image id associated with thumbnail of interest.
2085             * @param groupId the id of group to which this request belongs
2086             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
2087             * @param options this is only used for MINI_KIND when decoding the Bitmap
2088             * @return A Bitmap instance. It could be null if the original image associated with
2089             *         origId doesn't exist or memory is not enough.
2090             */
2091            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
2092                    int kind, BitmapFactory.Options options) {
2093                return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
2094                        EXTERNAL_CONTENT_URI, true);
2095            }
2096
2097            /**
2098             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
2099             * interrupted and return immediately. Only the original process which made the getThumbnail
2100             * requests can cancel their own requests.
2101             *
2102             * @param cr ContentResolver
2103             * @param origId original video id
2104             * @param groupId the same groupId used in getThumbnail.
2105             */
2106            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
2107                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
2108            }
2109
2110            /**
2111             * Get the content:// style URI for the image media table on the
2112             * given volume.
2113             *
2114             * @param volumeName the name of the volume to get the URI for
2115             * @return the URI to the image media table on the given volume
2116             */
2117            public static Uri getContentUri(String volumeName) {
2118                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
2119                        "/video/thumbnails");
2120            }
2121
2122            /**
2123             * The content:// style URI for the internal storage.
2124             */
2125            public static final Uri INTERNAL_CONTENT_URI =
2126                    getContentUri("internal");
2127
2128            /**
2129             * The content:// style URI for the "primary" external storage
2130             * volume.
2131             */
2132            public static final Uri EXTERNAL_CONTENT_URI =
2133                    getContentUri("external");
2134
2135            /**
2136             * The default sort order for this table
2137             */
2138            public static final String DEFAULT_SORT_ORDER = "video_id ASC";
2139
2140            /**
2141             * The data stream for the thumbnail
2142             * <P>Type: DATA STREAM</P>
2143             */
2144            public static final String DATA = "_data";
2145
2146            /**
2147             * The original image for the thumbnal
2148             * <P>Type: INTEGER (ID from Video table)</P>
2149             */
2150            public static final String VIDEO_ID = "video_id";
2151
2152            /**
2153             * The kind of the thumbnail
2154             * <P>Type: INTEGER (One of the values below)</P>
2155             */
2156            public static final String KIND = "kind";
2157
2158            public static final int MINI_KIND = 1;
2159            public static final int FULL_SCREEN_KIND = 2;
2160            public static final int MICRO_KIND = 3;
2161
2162            /**
2163             * The width of the thumbnal
2164             * <P>Type: INTEGER (long)</P>
2165             */
2166            public static final String WIDTH = "width";
2167
2168            /**
2169             * The height of the thumbnail
2170             * <P>Type: INTEGER (long)</P>
2171             */
2172            public static final String HEIGHT = "height";
2173        }
2174    }
2175
2176    /**
2177     * Uri for querying the state of the media scanner.
2178     */
2179    public static Uri getMediaScannerUri() {
2180        return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
2181    }
2182
2183    /**
2184     * Name of current volume being scanned by the media scanner.
2185     */
2186    public static final String MEDIA_SCANNER_VOLUME = "volume";
2187
2188    /**
2189     * Name of the file signaling the media scanner to ignore media in the containing directory
2190     * and its subdirectories. Developers should use this to avoid application graphics showing
2191     * up in the Gallery and likewise prevent application sounds and music from showing up in
2192     * the Music app.
2193     */
2194    public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
2195
2196    /**
2197     * Get the media provider's version.
2198     * Applications that import data from the media provider into their own caches
2199     * can use this to detect that the media provider changed, and reimport data
2200     * as needed. No other assumptions should be made about the meaning of the version.
2201     * @param context Context to use for performing the query.
2202     * @return A version string, or null if the version could not be determined.
2203     */
2204    public static String getVersion(Context context) {
2205        Cursor c = context.getContentResolver().query(
2206                Uri.parse(CONTENT_AUTHORITY_SLASH + "none/version"),
2207                null, null, null, null);
2208        if (c != null) {
2209            try {
2210                if (c.moveToFirst()) {
2211                    return c.getString(0);
2212                }
2213            } finally {
2214                c.close();
2215            }
2216        }
2217        return null;
2218    }
2219
2220}
2221