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