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