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