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