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