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