MediaStore.java revision eb0098d8e89033b78017a9e0068e3ac7ca51c112
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.graphics.Bitmap;
27import android.graphics.BitmapFactory;
28import android.graphics.Matrix;
29import android.net.Uri;
30import android.os.Environment;
31import android.util.Log;
32
33import java.io.FileInputStream;
34import java.io.FileNotFoundException;
35import java.io.IOException;
36import java.io.InputStream;
37import java.io.OutputStream;
38import java.io.UnsupportedEncodingException;
39import java.text.Collator;
40
41/**
42 * The Media provider contains meta data for all available media on both internal
43 * and external storage devices.
44 */
45public final class MediaStore
46{
47    private final static String TAG = "MediaStore";
48
49    public static final String AUTHORITY = "media";
50
51    private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
52
53    /**
54     * Activity Action: Perform a search for media.
55     * Contains at least the {@link android.app.SearchManager#QUERY} extra.
56     * May also contain any combination of the following extras:
57     * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
58     *
59     * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
60     * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
61     * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
62     * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
63     */
64    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
65    public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
66
67    /**
68     * The name of the Intent-extra used to define the artist
69     */
70    public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
71    /**
72     * The name of the Intent-extra used to define the album
73     */
74    public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
75    /**
76     * The name of the Intent-extra used to define the song title
77     */
78    public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
79    /**
80     * The name of the Intent-extra used to define the search focus. The search focus
81     * indicates whether the search should be for things related to the artist, album
82     * or song that is identified by the other extras.
83     */
84    public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
85
86    /**
87     * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
88     * This is an int property that overrides the activity's requestedOrientation.
89     * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
90     */
91    public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
92
93    /**
94     * The name of an Intent-extra used to control the UI of a ViewImage.
95     * This is a boolean property that overrides the activity's default fullscreen state.
96     * @hide
97     */
98    public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
99
100    /**
101     * The name of an Intent-extra used to control the UI of a ViewImage.
102     * This is a boolean property that specifies whether or not to show action icons.
103     * @hide
104     */
105    public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
106
107    /**
108     * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
109     * This is a boolean property that specifies whether or not to finish the MovieView activity
110     * when the movie completes playing. The default value is true, which means to automatically
111     * exit the movie player activity when the movie completes playing.
112     */
113    public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
114
115    /**
116     * The name of the Intent action used to launch a camera in still image mode.
117     */
118    public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
119
120    /**
121     * The name of the Intent action used to launch a camera in video mode.
122     */
123    public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
124
125    /**
126     * Standard Intent action that can be sent to have the camera application
127     * capture an image and return it.
128     * <p>
129     * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
130     * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
131     * object in the extra field. This is useful for applications that only need a small image.
132     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
133     * value of EXTRA_OUTPUT.
134     * @see #EXTRA_OUTPUT
135     * @see #EXTRA_VIDEO_QUALITY
136     */
137    public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
138
139    /**
140     * Standard Intent action that can be sent to have the camera application
141     * capture an video and return it.
142     * <p>
143     * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
144     * <p>
145     * The caller may pass in an extra EXTRA_OUTPUT to control
146     * where the video is written. If EXTRA_OUTPUT is not present the video will be
147     * written to the standard location for videos, and the Uri of that location will be
148     * returned in the data field of the Uri.
149     * @see #EXTRA_OUTPUT
150     */
151    public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
152
153    /**
154     * The name of the Intent-extra used to control the quality of a recorded video. This is an
155     * integer property. Currently value 0 means low quality, suitable for MMS messages, and
156     * value 1 means high quality. In the future other quality levels may be added.
157     */
158    public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
159
160    /**
161     * Specify the maximum allowed size.
162     * @hide
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     * @hide
169     */
170    public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
171
172    /**
173     * The name of the Intent-extra used to indicate a content resolver Uri to be used to
174     * store the requested image or video.
175     */
176    public final static String EXTRA_OUTPUT = "output";
177
178    /**
179     * Common fields for most MediaProvider tables
180     */
181
182     public interface MediaColumns extends BaseColumns {
183        /**
184         * The data stream for the file
185         * <P>Type: DATA STREAM</P>
186         */
187        public static final String DATA = "_data";
188
189        /**
190         * The size of the file in bytes
191         * <P>Type: INTEGER (long)</P>
192         */
193        public static final String SIZE = "_size";
194
195        /**
196         * The display name of the file
197         * <P>Type: TEXT</P>
198         */
199        public static final String DISPLAY_NAME = "_display_name";
200
201        /**
202         * The title of the content
203         * <P>Type: TEXT</P>
204         */
205        public static final String TITLE = "title";
206
207        /**
208         * The time the file was added to the media provider
209         * Units are seconds since 1970.
210         * <P>Type: INTEGER (long)</P>
211         */
212        public static final String DATE_ADDED = "date_added";
213
214        /**
215         * The time the file was last modified
216         * Units are seconds since 1970.
217         * NOTE: This is for internal use by the media scanner.  Do not modify this field.
218         * <P>Type: INTEGER (long)</P>
219         */
220        public static final String DATE_MODIFIED = "date_modified";
221
222        /**
223         * The MIME type of the file
224         * <P>Type: TEXT</P>
225         */
226        public static final String MIME_TYPE = "mime_type";
227     }
228
229    /**
230     * Contains meta data for all available images.
231     */
232    public static final class Images
233    {
234        public interface ImageColumns extends MediaColumns {
235            /**
236             * The description of the image
237             * <P>Type: TEXT</P>
238             */
239            public static final String DESCRIPTION = "description";
240
241            /**
242             * The picasa id of the image
243             * <P>Type: TEXT</P>
244             */
245            public static final String PICASA_ID = "picasa_id";
246
247            /**
248             * Whether the video should be published as public or private
249             * <P>Type: INTEGER</P>
250             */
251            public static final String IS_PRIVATE = "isprivate";
252
253            /**
254             * The latitude where the image was captured.
255             * <P>Type: DOUBLE</P>
256             */
257            public static final String LATITUDE = "latitude";
258
259            /**
260             * The longitude where the image was captured.
261             * <P>Type: DOUBLE</P>
262             */
263            public static final String LONGITUDE = "longitude";
264
265            /**
266             * The date & time that the image was taken in units
267             * of milliseconds since jan 1, 1970.
268             * <P>Type: INTEGER</P>
269             */
270            public static final String DATE_TAKEN = "datetaken";
271
272            /**
273             * The orientation for the image expressed as degrees.
274             * Only degrees 0, 90, 180, 270 will work.
275             * <P>Type: INTEGER</P>
276             */
277            public static final String ORIENTATION = "orientation";
278
279            /**
280             * The mini thumb id.
281             * <P>Type: INTEGER</P>
282             */
283            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
284
285            /**
286             * The bucket id of the image. This is a read-only property that
287             * is automatically computed from the DATA column.
288             * <P>Type: TEXT</P>
289             */
290            public static final String BUCKET_ID = "bucket_id";
291
292            /**
293             * The bucket display name of the image. This is a read-only property that
294             * is automatically computed from the DATA column.
295             * <P>Type: TEXT</P>
296             */
297            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
298        }
299
300        public static final class Media implements ImageColumns {
301            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
302            {
303                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
304            }
305
306            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
307                                           String where, String orderBy)
308            {
309                return cr.query(uri, projection, where,
310                                             null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
311            }
312
313            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
314                    String selection, String [] selectionArgs, String orderBy)
315            {
316                return cr.query(uri, projection, selection,
317                        selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
318            }
319
320            /**
321             * Retrieves an image for the given url as a {@link Bitmap}.
322             *
323             * @param cr The content resolver to use
324             * @param url The url of the image
325             * @throws FileNotFoundException
326             * @throws IOException
327             */
328            public static final Bitmap getBitmap(ContentResolver cr, Uri url)
329                    throws FileNotFoundException, IOException
330            {
331                InputStream input = cr.openInputStream(url);
332                Bitmap bitmap = BitmapFactory.decodeStream(input);
333                input.close();
334                return bitmap;
335            }
336
337            /**
338             * Insert an image and create a thumbnail for it.
339             *
340             * @param cr The content resolver to use
341             * @param imagePath The path to the image to insert
342             * @param name The name of the image
343             * @param description The description of the image
344             * @return The URL to the newly created image
345             * @throws FileNotFoundException
346             */
347            public static final String insertImage(ContentResolver cr, String imagePath, String name,
348                                                   String description) throws FileNotFoundException
349            {
350                // Check if file exists with a FileInputStream
351                FileInputStream stream = new FileInputStream(imagePath);
352                try {
353                    Bitmap bm = BitmapFactory.decodeFile(imagePath);
354                    String ret = insertImage(cr, bm, name, description);
355                    bm.recycle();
356                    return ret;
357                } finally {
358                    try {
359                        stream.close();
360                    } catch (IOException e) {
361                    }
362                }
363            }
364
365            private static final Bitmap StoreThumbnail(
366                    ContentResolver cr,
367                    Bitmap source,
368                    long id,
369                    float width, float height,
370                    int kind) {
371                // create the matrix to scale it
372                Matrix matrix = new Matrix();
373
374                float scaleX = width / source.getWidth();
375                float scaleY = height / source.getHeight();
376
377                matrix.setScale(scaleX, scaleY);
378
379                Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
380                                                   source.getWidth(),
381                                                   source.getHeight(), matrix,
382                                                   true);
383
384                ContentValues values = new ContentValues(4);
385                values.put(Images.Thumbnails.KIND,     kind);
386                values.put(Images.Thumbnails.IMAGE_ID, (int)id);
387                values.put(Images.Thumbnails.HEIGHT,   thumb.getHeight());
388                values.put(Images.Thumbnails.WIDTH,    thumb.getWidth());
389
390                Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
391
392                try {
393                    OutputStream thumbOut = cr.openOutputStream(url);
394
395                    thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
396                    thumbOut.close();
397                    return thumb;
398                }
399                catch (FileNotFoundException ex) {
400                    return null;
401                }
402                catch (IOException ex) {
403                    return null;
404                }
405            }
406
407            /**
408             * Insert an image and create a thumbnail for it.
409             *
410             * @param cr The content resolver to use
411             * @param source The stream to use for the image
412             * @param title The name of the image
413             * @param description The description of the image
414             * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
415             *              for any reason.
416             */
417            public static final String insertImage(ContentResolver cr, Bitmap source,
418                                                   String title, String description)
419            {
420                ContentValues values = new ContentValues();
421                values.put(Images.Media.TITLE, title);
422                values.put(Images.Media.DESCRIPTION, description);
423                values.put(Images.Media.MIME_TYPE, "image/jpeg");
424
425                Uri url = null;
426                String stringUrl = null;    /* value to be returned */
427
428                try
429                {
430                    url = cr.insert(EXTERNAL_CONTENT_URI, values);
431
432                    if (source != null) {
433                        OutputStream imageOut = cr.openOutputStream(url);
434                        try {
435                            source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
436                        } finally {
437                            imageOut.close();
438                        }
439
440                        long id = ContentUris.parseId(url);
441                        Bitmap miniThumb  = StoreThumbnail(cr, source, id, 320F, 240F, Images.Thumbnails.MINI_KIND);
442                        Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND);
443                    } else {
444                        Log.e(TAG, "Failed to create thumbnail, removing original");
445                        cr.delete(url, null, null);
446                        url = null;
447                    }
448                } catch (Exception e) {
449                    Log.e(TAG, "Failed to insert image", e);
450                    if (url != null) {
451                        cr.delete(url, null, null);
452                        url = null;
453                    }
454                }
455
456                if (url != null) {
457                    stringUrl = url.toString();
458                }
459
460                return stringUrl;
461            }
462
463            /**
464             * Get the content:// style URI for the image media table on the
465             * given volume.
466             *
467             * @param volumeName the name of the volume to get the URI for
468             * @return the URI to the image media table on the given volume
469             */
470            public static Uri getContentUri(String volumeName) {
471                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
472                        "/images/media");
473            }
474
475            /**
476             * The content:// style URI for the internal storage.
477             */
478            public static final Uri INTERNAL_CONTENT_URI =
479                    getContentUri("internal");
480
481            /**
482             * The content:// style URI for the "primary" external storage
483             * volume.
484             */
485            public static final Uri EXTERNAL_CONTENT_URI =
486                    getContentUri("external");
487
488            /**
489             * The MIME type of of this directory of
490             * images.  Note that each entry in this directory will have a standard
491             * image MIME type as appropriate -- for example, image/jpeg.
492             */
493            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
494
495            /**
496             * The default sort order for this table
497             */
498            public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
499       }
500
501        public static class Thumbnails implements BaseColumns
502        {
503            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
504            {
505                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
506            }
507
508            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)
509            {
510                return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
511            }
512
513            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)
514            {
515                return cr.query(EXTERNAL_CONTENT_URI, projection,
516                        IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
517                        kind, null, null);
518            }
519
520            /**
521             * Get the content:// style URI for the image media table on the
522             * given volume.
523             *
524             * @param volumeName the name of the volume to get the URI for
525             * @return the URI to the image media table on the given volume
526             */
527            public static Uri getContentUri(String volumeName) {
528                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
529                        "/images/thumbnails");
530            }
531
532            /**
533             * The content:// style URI for the internal storage.
534             */
535            public static final Uri INTERNAL_CONTENT_URI =
536                    getContentUri("internal");
537
538            /**
539             * The content:// style URI for the "primary" external storage
540             * volume.
541             */
542            public static final Uri EXTERNAL_CONTENT_URI =
543                    getContentUri("external");
544
545            /**
546             * The default sort order for this table
547             */
548            public static final String DEFAULT_SORT_ORDER = "image_id ASC";
549
550            /**
551             * The data stream for the thumbnail
552             * <P>Type: DATA STREAM</P>
553             */
554            public static final String DATA = "_data";
555
556            /**
557             * The original image for the thumbnal
558             * <P>Type: INTEGER (ID from Images table)</P>
559             */
560            public static final String IMAGE_ID = "image_id";
561
562            /**
563             * The kind of the thumbnail
564             * <P>Type: INTEGER (One of the values below)</P>
565             */
566            public static final String KIND = "kind";
567
568            public static final int MINI_KIND = 1;
569            public static final int FULL_SCREEN_KIND = 2;
570            public static final int MICRO_KIND = 3;
571
572            /**
573             * The width of the thumbnal
574             * <P>Type: INTEGER (long)</P>
575             */
576            public static final String WIDTH = "width";
577
578            /**
579             * The height of the thumbnail
580             * <P>Type: INTEGER (long)</P>
581             */
582            public static final String HEIGHT = "height";
583        }
584    }
585
586    /**
587     * Container for all audio content.
588     */
589    public static final class Audio {
590        /**
591         * Columns for audio file that show up in multiple tables.
592         */
593        public interface AudioColumns extends MediaColumns {
594
595            /**
596             * A non human readable key calculated from the TITLE, used for
597             * searching, sorting and grouping
598             * <P>Type: TEXT</P>
599             */
600            public static final String TITLE_KEY = "title_key";
601
602            /**
603             * The duration of the audio file, in ms
604             * <P>Type: INTEGER (long)</P>
605             */
606            public static final String DURATION = "duration";
607
608            /**
609             * The position, in ms, playback was at when playback for this file
610             * was last stopped.
611             * <P>Type: INTEGER (long)</P>
612             * @hide
613             */
614            public static final String BOOKMARK = "bookmark";
615
616            /**
617             * The id of the artist who created the audio file, if any
618             * <P>Type: INTEGER (long)</P>
619             */
620            public static final String ARTIST_ID = "artist_id";
621
622            /**
623             * The artist who created the audio file, if any
624             * <P>Type: TEXT</P>
625             */
626            public static final String ARTIST = "artist";
627
628            /**
629             * A non human readable key calculated from the ARTIST, used for
630             * searching, sorting and grouping
631             * <P>Type: TEXT</P>
632             */
633            public static final String ARTIST_KEY = "artist_key";
634
635            /**
636             * The composer of the audio file, if any
637             * <P>Type: TEXT</P>
638             */
639            public static final String COMPOSER = "composer";
640
641            /**
642             * The id of the album the audio file is from, if any
643             * <P>Type: INTEGER (long)</P>
644             */
645            public static final String ALBUM_ID = "album_id";
646
647            /**
648             * The album the audio file is from, if any
649             * <P>Type: TEXT</P>
650             */
651            public static final String ALBUM = "album";
652
653            /**
654             * A non human readable key calculated from the ALBUM, used for
655             * searching, sorting and grouping
656             * <P>Type: TEXT</P>
657             */
658            public static final String ALBUM_KEY = "album_key";
659
660            /**
661             * A URI to the album art, if any
662             * <P>Type: TEXT</P>
663             */
664            public static final String ALBUM_ART = "album_art";
665
666            /**
667             * The track number of this song on the album, if any.
668             * This number encodes both the track number and the
669             * disc number. For multi-disc sets, this number will
670             * be 1xxx for tracks on the first disc, 2xxx for tracks
671             * on the second disc, etc.
672             * <P>Type: INTEGER</P>
673             */
674            public static final String TRACK = "track";
675
676            /**
677             * The year the audio file was recorded, if any
678             * <P>Type: INTEGER</P>
679             */
680            public static final String YEAR = "year";
681
682            /**
683             * Non-zero if the audio file is music
684             * <P>Type: INTEGER (boolean)</P>
685             */
686            public static final String IS_MUSIC = "is_music";
687
688            /**
689             * Non-zero if the audio file is a podcast
690             * <P>Type: INTEGER (boolean)</P>
691             * @hide
692             */
693            public static final String IS_PODCAST = "is_podcast";
694
695            /**
696             * Non-zero id the audio file may be a ringtone
697             * <P>Type: INTEGER (boolean)</P>
698             */
699            public static final String IS_RINGTONE = "is_ringtone";
700
701            /**
702             * Non-zero id the audio file may be an alarm
703             * <P>Type: INTEGER (boolean)</P>
704             */
705            public static final String IS_ALARM = "is_alarm";
706
707            /**
708             * Non-zero id the audio file may be a notification sound
709             * <P>Type: INTEGER (boolean)</P>
710             */
711            public static final String IS_NOTIFICATION = "is_notification";
712        }
713
714        /**
715         * Converts a name to a "key" that can be used for grouping, sorting
716         * and searching.
717         * The rules that govern this conversion are:
718         * - remove 'special' characters like ()[]'!?.,
719         * - remove leading/trailing spaces
720         * - convert everything to lowercase
721         * - remove leading "the ", "an " and "a "
722         * - remove trailing ", the|an|a"
723         * - remove accents. This step leaves us with CollationKey data,
724         *   which is not human readable
725         *
726         * @param name The artist or album name to convert
727         * @return The "key" for the given name.
728         */
729        public static String keyFor(String name) {
730            if (name != null)  {
731                boolean sortfirst = false;
732                if (name.equals(android.media.MediaFile.UNKNOWN_STRING)) {
733                    return "\001";
734                }
735                // Check if the first character is \001. We use this to
736                // force sorting of certain special files, like the silent ringtone.
737                if (name.startsWith("\001")) {
738                    sortfirst = true;
739                }
740                name = name.trim().toLowerCase();
741                if (name.startsWith("the ")) {
742                    name = name.substring(4);
743                }
744                if (name.startsWith("an ")) {
745                    name = name.substring(3);
746                }
747                if (name.startsWith("a ")) {
748                    name = name.substring(2);
749                }
750                if (name.endsWith(", the") || name.endsWith(",the") ||
751                    name.endsWith(", an") || name.endsWith(",an") ||
752                    name.endsWith(", a") || name.endsWith(",a")) {
753                    name = name.substring(0, name.lastIndexOf(','));
754                }
755                name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
756                if (name.length() > 0) {
757                    // Insert a separator between the characters to avoid
758                    // matches on a partial character. If we ever change
759                    // to start-of-word-only matches, this can be removed.
760                    StringBuilder b = new StringBuilder();
761                    b.append('.');
762                    int nl = name.length();
763                    for (int i = 0; i < nl; i++) {
764                        b.append(name.charAt(i));
765                        b.append('.');
766                    }
767                    name = b.toString();
768                    String key = DatabaseUtils.getCollationKey(name);
769                    if (sortfirst) {
770                        key = "\001" + key;
771                    }
772                    return key;
773               } else {
774                    return "";
775                }
776            }
777            return null;
778        }
779
780        public static final class Media implements AudioColumns {
781            /**
782             * Get the content:// style URI for the audio media table on the
783             * given volume.
784             *
785             * @param volumeName the name of the volume to get the URI for
786             * @return the URI to the audio media table on the given volume
787             */
788            public static Uri getContentUri(String volumeName) {
789                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
790                        "/audio/media");
791            }
792
793            public static Uri getContentUriForPath(String path) {
794                return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
795                        EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
796            }
797
798            /**
799             * The content:// style URI for the internal storage.
800             */
801            public static final Uri INTERNAL_CONTENT_URI =
802                    getContentUri("internal");
803
804            /**
805             * The content:// style URI for the "primary" external storage
806             * volume.
807             */
808            public static final Uri EXTERNAL_CONTENT_URI =
809                    getContentUri("external");
810
811            /**
812             * The MIME type for this table.
813             */
814            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
815
816            /**
817             * The default sort order for this table
818             */
819            public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
820
821            /**
822             * Activity Action: Start SoundRecorder application.
823             * <p>Input: nothing.
824             * <p>Output: An uri to the recorded sound stored in the Media Library
825             * if the recording was successful.
826             * May also contain the extra EXTRA_MAX_BYTES.
827             * @see #EXTRA_MAX_BYTES
828             */
829            public static final String RECORD_SOUND_ACTION =
830                    "android.provider.MediaStore.RECORD_SOUND";
831
832            /**
833             * The name of the Intent-extra used to define a maximum file size for
834             * a recording made by the SoundRecorder application.
835             *
836             * @see #RECORD_SOUND_ACTION
837             */
838             public static final String EXTRA_MAX_BYTES =
839                    "android.provider.MediaStore.extra.MAX_BYTES";
840        }
841
842        /**
843         * Columns representing an audio genre
844         */
845        public interface GenresColumns {
846            /**
847             * The name of the genre
848             * <P>Type: TEXT</P>
849             */
850            public static final String NAME = "name";
851        }
852
853        /**
854         * Contains all genres for audio files
855         */
856        public static final class Genres implements BaseColumns, GenresColumns {
857            /**
858             * Get the content:// style URI for the audio genres table on the
859             * given volume.
860             *
861             * @param volumeName the name of the volume to get the URI for
862             * @return the URI to the audio genres table on the given volume
863             */
864            public static Uri getContentUri(String volumeName) {
865                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
866                        "/audio/genres");
867            }
868
869            /**
870             * The content:// style URI for the internal storage.
871             */
872            public static final Uri INTERNAL_CONTENT_URI =
873                    getContentUri("internal");
874
875            /**
876             * The content:// style URI for the "primary" external storage
877             * volume.
878             */
879            public static final Uri EXTERNAL_CONTENT_URI =
880                    getContentUri("external");
881
882            /**
883             * The MIME type for this table.
884             */
885            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
886
887            /**
888             * The MIME type for entries in this table.
889             */
890            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
891
892            /**
893             * The default sort order for this table
894             */
895            public static final String DEFAULT_SORT_ORDER = NAME;
896
897            /**
898             * Sub-directory of each genre containing all members.
899             */
900            public static final class Members implements AudioColumns {
901
902                public static final Uri getContentUri(String volumeName,
903                        long genreId) {
904                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
905                            + "/audio/genres/" + genreId + "/members");
906                }
907
908                /**
909                 * A subdirectory of each genre containing all member audio files.
910                 */
911                public static final String CONTENT_DIRECTORY = "members";
912
913                /**
914                 * The default sort order for this table
915                 */
916                public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
917
918                /**
919                 * The ID of the audio file
920                 * <P>Type: INTEGER (long)</P>
921                 */
922                public static final String AUDIO_ID = "audio_id";
923
924                /**
925                 * The ID of the genre
926                 * <P>Type: INTEGER (long)</P>
927                 */
928                public static final String GENRE_ID = "genre_id";
929            }
930        }
931
932        /**
933         * Columns representing a playlist
934         */
935        public interface PlaylistsColumns {
936            /**
937             * The name of the playlist
938             * <P>Type: TEXT</P>
939             */
940            public static final String NAME = "name";
941
942            /**
943             * The data stream for the playlist file
944             * <P>Type: DATA STREAM</P>
945             */
946            public static final String DATA = "_data";
947
948            /**
949             * The time the file was added to the media provider
950             * Units are seconds since 1970.
951             * <P>Type: INTEGER (long)</P>
952             */
953            public static final String DATE_ADDED = "date_added";
954
955            /**
956             * The time the file was last modified
957             * Units are seconds since 1970.
958             * NOTE: This is for internal use by the media scanner.  Do not modify this field.
959             * <P>Type: INTEGER (long)</P>
960             */
961            public static final String DATE_MODIFIED = "date_modified";
962        }
963
964        /**
965         * Contains playlists for audio files
966         */
967        public static final class Playlists implements BaseColumns,
968                PlaylistsColumns {
969            /**
970             * Get the content:// style URI for the audio playlists table on the
971             * given volume.
972             *
973             * @param volumeName the name of the volume to get the URI for
974             * @return the URI to the audio playlists table on the given volume
975             */
976            public static Uri getContentUri(String volumeName) {
977                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
978                        "/audio/playlists");
979            }
980
981            /**
982             * The content:// style URI for the internal storage.
983             */
984            public static final Uri INTERNAL_CONTENT_URI =
985                    getContentUri("internal");
986
987            /**
988             * The content:// style URI for the "primary" external storage
989             * volume.
990             */
991            public static final Uri EXTERNAL_CONTENT_URI =
992                    getContentUri("external");
993
994            /**
995             * The MIME type for this table.
996             */
997            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
998
999            /**
1000             * The MIME type for entries in this table.
1001             */
1002            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
1003
1004            /**
1005             * The default sort order for this table
1006             */
1007            public static final String DEFAULT_SORT_ORDER = NAME;
1008
1009            /**
1010             * Sub-directory of each playlist containing all members.
1011             */
1012            public static final class Members implements AudioColumns {
1013                public static final Uri getContentUri(String volumeName,
1014                        long playlistId) {
1015                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1016                            + "/audio/playlists/" + playlistId + "/members");
1017                }
1018
1019                /**
1020                 * The ID within the playlist.
1021                 */
1022                public static final String _ID = "_id";
1023
1024                /**
1025                 * A subdirectory of each playlist containing all member audio
1026                 * files.
1027                 */
1028                public static final String CONTENT_DIRECTORY = "members";
1029
1030                /**
1031                 * The ID of the audio file
1032                 * <P>Type: INTEGER (long)</P>
1033                 */
1034                public static final String AUDIO_ID = "audio_id";
1035
1036                /**
1037                 * The ID of the playlist
1038                 * <P>Type: INTEGER (long)</P>
1039                 */
1040                public static final String PLAYLIST_ID = "playlist_id";
1041
1042                /**
1043                 * The order of the songs in the playlist
1044                 * <P>Type: INTEGER (long)></P>
1045                 */
1046                public static final String PLAY_ORDER = "play_order";
1047
1048                /**
1049                 * The default sort order for this table
1050                 */
1051                public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
1052            }
1053        }
1054
1055        /**
1056         * Columns representing an artist
1057         */
1058        public interface ArtistColumns {
1059            /**
1060             * The artist who created the audio file, if any
1061             * <P>Type: TEXT</P>
1062             */
1063            public static final String ARTIST = "artist";
1064
1065            /**
1066             * A non human readable key calculated from the ARTIST, used for
1067             * searching, sorting and grouping
1068             * <P>Type: TEXT</P>
1069             */
1070            public static final String ARTIST_KEY = "artist_key";
1071
1072            /**
1073             * The number of albums in the database for this artist
1074             */
1075            public static final String NUMBER_OF_ALBUMS = "number_of_albums";
1076
1077            /**
1078             * The number of albums in the database for this artist
1079             */
1080            public static final String NUMBER_OF_TRACKS = "number_of_tracks";
1081        }
1082
1083        /**
1084         * Contains artists for audio files
1085         */
1086        public static final class Artists implements BaseColumns, ArtistColumns {
1087            /**
1088             * Get the content:// style URI for the artists table on the
1089             * given volume.
1090             *
1091             * @param volumeName the name of the volume to get the URI for
1092             * @return the URI to the audio artists table on the given volume
1093             */
1094            public static Uri getContentUri(String volumeName) {
1095                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1096                        "/audio/artists");
1097            }
1098
1099            /**
1100             * The content:// style URI for the internal storage.
1101             */
1102            public static final Uri INTERNAL_CONTENT_URI =
1103                    getContentUri("internal");
1104
1105            /**
1106             * The content:// style URI for the "primary" external storage
1107             * volume.
1108             */
1109            public static final Uri EXTERNAL_CONTENT_URI =
1110                    getContentUri("external");
1111
1112            /**
1113             * The MIME type for this table.
1114             */
1115            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
1116
1117            /**
1118             * The MIME type for entries in this table.
1119             */
1120            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
1121
1122            /**
1123             * The default sort order for this table
1124             */
1125            public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
1126
1127            /**
1128             * Sub-directory of each artist containing all albums on which
1129             * a song by the artist appears.
1130             */
1131            public static final class Albums implements AlbumColumns {
1132                public static final Uri getContentUri(String volumeName,
1133                        long artistId) {
1134                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1135                            + "/audio/artists/" + artistId + "/albums");
1136                }
1137            }
1138        }
1139
1140        /**
1141         * Columns representing an album
1142         */
1143        public interface AlbumColumns {
1144
1145            /**
1146             * The id for the album
1147             * <P>Type: INTEGER</P>
1148             */
1149            public static final String ALBUM_ID = "album_id";
1150
1151            /**
1152             * The album on which the audio file appears, if any
1153             * <P>Type: TEXT</P>
1154             */
1155            public static final String ALBUM = "album";
1156
1157            /**
1158             * The artist whose songs appear on this album
1159             * <P>Type: TEXT</P>
1160             */
1161            public static final String ARTIST = "artist";
1162
1163            /**
1164             * The number of songs on this album
1165             * <P>Type: INTEGER</P>
1166             */
1167            public static final String NUMBER_OF_SONGS = "numsongs";
1168
1169            /**
1170             * This column is available when getting album info via artist,
1171             * and indicates the number of songs on the album by the given
1172             * artist.
1173             * <P>Type: INTEGER</P>
1174             */
1175            public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
1176
1177            /**
1178             * The year in which the earliest songs
1179             * on this album were released. This will often
1180             * be the same as {@link #LAST_YEAR}, but for compilation albums
1181             * they might differ.
1182             * <P>Type: INTEGER</P>
1183             */
1184            public static final String FIRST_YEAR = "minyear";
1185
1186            /**
1187             * The year in which the latest songs
1188             * on this album were released. This will often
1189             * be the same as {@link #FIRST_YEAR}, but for compilation albums
1190             * they might differ.
1191             * <P>Type: INTEGER</P>
1192             */
1193            public static final String LAST_YEAR = "maxyear";
1194
1195            /**
1196             * A non human readable key calculated from the ALBUM, used for
1197             * searching, sorting and grouping
1198             * <P>Type: TEXT</P>
1199             */
1200            public static final String ALBUM_KEY = "album_key";
1201
1202            /**
1203             * Cached album art.
1204             * <P>Type: TEXT</P>
1205             */
1206            public static final String ALBUM_ART = "album_art";
1207        }
1208
1209        /**
1210         * Contains artists for audio files
1211         */
1212        public static final class Albums implements BaseColumns, AlbumColumns {
1213            /**
1214             * Get the content:// style URI for the albums table on the
1215             * given volume.
1216             *
1217             * @param volumeName the name of the volume to get the URI for
1218             * @return the URI to the audio albums table on the given volume
1219             */
1220            public static Uri getContentUri(String volumeName) {
1221                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1222                        "/audio/albums");
1223            }
1224
1225            /**
1226             * The content:// style URI for the internal storage.
1227             */
1228            public static final Uri INTERNAL_CONTENT_URI =
1229                    getContentUri("internal");
1230
1231            /**
1232             * The content:// style URI for the "primary" external storage
1233             * volume.
1234             */
1235            public static final Uri EXTERNAL_CONTENT_URI =
1236                    getContentUri("external");
1237
1238            /**
1239             * The MIME type for this table.
1240             */
1241            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
1242
1243            /**
1244             * The MIME type for entries in this table.
1245             */
1246            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
1247
1248            /**
1249             * The default sort order for this table
1250             */
1251            public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
1252        }
1253    }
1254
1255    public static final class Video {
1256
1257        /**
1258         * The default sort order for this table.
1259         */
1260        public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
1261
1262        public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
1263        {
1264            return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1265        }
1266
1267        public interface VideoColumns extends MediaColumns {
1268
1269            /**
1270             * The duration of the video file, in ms
1271             * <P>Type: INTEGER (long)</P>
1272             */
1273            public static final String DURATION = "duration";
1274
1275            /**
1276             * The artist who created the video file, if any
1277             * <P>Type: TEXT</P>
1278             */
1279            public static final String ARTIST = "artist";
1280
1281            /**
1282             * The album the video file is from, if any
1283             * <P>Type: TEXT</P>
1284             */
1285            public static final String ALBUM = "album";
1286
1287            /**
1288             * The resolution of the video file, formatted as "XxY"
1289             * <P>Type: TEXT</P>
1290             */
1291            public static final String RESOLUTION = "resolution";
1292
1293            /**
1294             * The description of the video recording
1295             * <P>Type: TEXT</P>
1296             */
1297            public static final String DESCRIPTION = "description";
1298
1299            /**
1300             * Whether the video should be published as public or private
1301             * <P>Type: INTEGER</P>
1302             */
1303            public static final String IS_PRIVATE = "isprivate";
1304
1305            /**
1306             * The user-added tags associated with a video
1307             * <P>Type: TEXT</P>
1308             */
1309            public static final String TAGS = "tags";
1310
1311            /**
1312             * The YouTube category of the video
1313             * <P>Type: TEXT</P>
1314             */
1315            public static final String CATEGORY = "category";
1316
1317            /**
1318             * The language of the video
1319             * <P>Type: TEXT</P>
1320             */
1321            public static final String LANGUAGE = "language";
1322
1323            /**
1324             * The latitude where the image was captured.
1325             * <P>Type: DOUBLE</P>
1326             */
1327            public static final String LATITUDE = "latitude";
1328
1329            /**
1330             * The longitude where the image was captured.
1331             * <P>Type: DOUBLE</P>
1332             */
1333            public static final String LONGITUDE = "longitude";
1334
1335            /**
1336             * The date & time that the image was taken in units
1337             * of milliseconds since jan 1, 1970.
1338             * <P>Type: INTEGER</P>
1339             */
1340            public static final String DATE_TAKEN = "datetaken";
1341
1342            /**
1343             * The mini thumb id.
1344             * <P>Type: INTEGER</P>
1345             */
1346            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
1347
1348            /**
1349             * The bucket id of the video. This is a read-only property that
1350             * is automatically computed from the DATA column.
1351             * <P>Type: TEXT</P>
1352             */
1353            public static final String BUCKET_ID = "bucket_id";
1354
1355            /**
1356             * The bucket display name of the video. This is a read-only property that
1357             * is automatically computed from the DATA column.
1358             * <P>Type: TEXT</P>
1359             */
1360            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
1361
1362            /**
1363             * The bookmark for the video. Time in ms. Represents the location in the video that the
1364             * video should start playing at the next time it is opened. If the value is null or
1365             * out of the range 0..DURATION-1 then the video should start playing from the
1366             * beginning.
1367             * <P>Type: INTEGER</P>
1368             */
1369            public static final String BOOKMARK = "bookmark";
1370        }
1371
1372        public static final class Media implements VideoColumns {
1373            /**
1374             * Get the content:// style URI for the video media table on the
1375             * given volume.
1376             *
1377             * @param volumeName the name of the volume to get the URI for
1378             * @return the URI to the video media table on the given volume
1379             */
1380            public static Uri getContentUri(String volumeName) {
1381                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1382                        "/video/media");
1383            }
1384
1385            /**
1386             * The content:// style URI for the internal storage.
1387             */
1388            public static final Uri INTERNAL_CONTENT_URI =
1389                    getContentUri("internal");
1390
1391            /**
1392             * The content:// style URI for the "primary" external storage
1393             * volume.
1394             */
1395            public static final Uri EXTERNAL_CONTENT_URI =
1396                    getContentUri("external");
1397
1398            /**
1399             * The MIME type for this table.
1400             */
1401            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
1402
1403            /**
1404             * The default sort order for this table
1405             */
1406            public static final String DEFAULT_SORT_ORDER = TITLE;
1407        }
1408    }
1409
1410    /**
1411     * Uri for querying the state of the media scanner.
1412     */
1413    public static Uri getMediaScannerUri() {
1414        return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
1415    }
1416
1417    /**
1418     * Name of current volume being scanned by the media scanner.
1419     */
1420    public static final String MEDIA_SCANNER_VOLUME = "volume";
1421}
1422