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