Home | History | Annotate | Download | only in provider
      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 
     17 package android.provider;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.content.ContentResolver;
     22 import android.content.ContentUris;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.database.Cursor;
     27 import android.database.DatabaseUtils;
     28 import android.database.sqlite.SQLiteException;
     29 import android.graphics.Bitmap;
     30 import android.graphics.BitmapFactory;
     31 import android.graphics.Matrix;
     32 import android.media.MiniThumbFile;
     33 import android.media.ThumbnailUtils;
     34 import android.net.Uri;
     35 import android.os.Environment;
     36 import android.os.ParcelFileDescriptor;
     37 import android.service.media.CameraPrewarmService;
     38 import android.util.Log;
     39 
     40 import java.io.FileInputStream;
     41 import java.io.FileNotFoundException;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 import java.io.OutputStream;
     45 import java.util.Arrays;
     46 
     47 /**
     48  * The Media provider contains meta data for all available media on both internal
     49  * and external storage devices.
     50  */
     51 public final class MediaStore {
     52     private final static String TAG = "MediaStore";
     53 
     54     public static final String AUTHORITY = "media";
     55 
     56     private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
     57 
     58    /**
     59      * Broadcast Action:  A broadcast to indicate the end of an MTP session with the host.
     60      * This broadcast is only sent if MTP activity has modified the media database during the
     61      * most recent MTP session.
     62      *
     63      * @hide
     64      */
     65     public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
     66 
     67     /**
     68      * The method name used by the media scanner and mtp to tell the media provider to
     69      * rescan and reclassify that have become unhidden because of renaming folders or
     70      * removing nomedia files
     71      * @hide
     72      */
     73     public static final String UNHIDE_CALL = "unhide";
     74 
     75     /**
     76      * This is for internal use by the media scanner only.
     77      * Name of the (optional) Uri parameter that determines whether to skip deleting
     78      * the file pointed to by the _data column, when deleting the database entry.
     79      * The only appropriate value for this parameter is "false", in which case the
     80      * delete will be skipped. Note especially that setting this to true, or omitting
     81      * the parameter altogether, will perform the default action, which is different
     82      * for different types of media.
     83      * @hide
     84      */
     85     public static final String PARAM_DELETE_DATA = "deletedata";
     86 
     87     /**
     88      * Activity Action: Launch a music player.
     89      * The activity should be able to play, browse, or manipulate music files stored on the device.
     90      *
     91      * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead.
     92      */
     93     @Deprecated
     94     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     95     public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
     96 
     97     /**
     98      * Activity Action: Perform a search for media.
     99      * Contains at least the {@link android.app.SearchManager#QUERY} extra.
    100      * May also contain any combination of the following extras:
    101      * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
    102      *
    103      * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
    104      * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
    105      * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
    106      * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
    107      */
    108     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    109     public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
    110 
    111     /**
    112      * An intent to perform a search for music media and automatically play content from the
    113      * result when possible. This can be fired, for example, by the result of a voice recognition
    114      * command to listen to music.
    115      * <p>This intent always includes the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS}
    116      * and {@link android.app.SearchManager#QUERY} extras. The
    117      * {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra determines the search mode, and
    118      * the value of the {@link android.app.SearchManager#QUERY} extra depends on the search mode.
    119      * For more information about the search modes for this intent, see
    120      * <a href="{@docRoot}guide/components/intents-common.html#PlaySearch">Play music based
    121      * on a search query</a> in <a href="{@docRoot}guide/components/intents-common.html">Common
    122      * Intents</a>.</p>
    123      *
    124      * <p>This intent makes the most sense for apps that can support large-scale search of music,
    125      * such as services connected to an online database of music which can be streamed and played
    126      * on the device.</p>
    127      */
    128     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    129     public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
    130             "android.media.action.MEDIA_PLAY_FROM_SEARCH";
    131 
    132     /**
    133      * An intent to perform a search for readable media and automatically play content from the
    134      * result when possible. This can be fired, for example, by the result of a voice recognition
    135      * command to read a book or magazine.
    136      * <p>
    137      * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
    138      * contain any type of unstructured text search, like the name of a book or magazine, an author
    139      * a genre, a publisher, or any combination of these.
    140      * <p>
    141      * Because this intent includes an open-ended unstructured search string, it makes the most
    142      * sense for apps that can support large-scale search of text media, such as services connected
    143      * to an online database of books and/or magazines which can be read on the device.
    144      */
    145     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    146     public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH =
    147             "android.media.action.TEXT_OPEN_FROM_SEARCH";
    148 
    149     /**
    150      * An intent to perform a search for video media and automatically play content from the
    151      * result when possible. This can be fired, for example, by the result of a voice recognition
    152      * command to play movies.
    153      * <p>
    154      * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
    155      * contain any type of unstructured video search, like the name of a movie, one or more actors,
    156      * a genre, or any combination of these.
    157      * <p>
    158      * Because this intent includes an open-ended unstructured search string, it makes the most
    159      * sense for apps that can support large-scale search of video, such as services connected to an
    160      * online database of videos which can be streamed and played on the device.
    161      */
    162     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    163     public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH =
    164             "android.media.action.VIDEO_PLAY_FROM_SEARCH";
    165 
    166     /**
    167      * The name of the Intent-extra used to define the artist
    168      */
    169     public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
    170     /**
    171      * The name of the Intent-extra used to define the album
    172      */
    173     public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
    174     /**
    175      * The name of the Intent-extra used to define the song title
    176      */
    177     public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
    178     /**
    179      * The name of the Intent-extra used to define the genre.
    180      */
    181     public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
    182     /**
    183      * The name of the Intent-extra used to define the playlist.
    184      */
    185     public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
    186     /**
    187      * The name of the Intent-extra used to define the radio channel.
    188      */
    189     public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel";
    190     /**
    191      * The name of the Intent-extra used to define the search focus. The search focus
    192      * indicates whether the search should be for things related to the artist, album
    193      * or song that is identified by the other extras.
    194      */
    195     public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
    196 
    197     /**
    198      * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
    199      * This is an int property that overrides the activity's requestedOrientation.
    200      * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
    201      */
    202     public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
    203 
    204     /**
    205      * The name of an Intent-extra used to control the UI of a ViewImage.
    206      * This is a boolean property that overrides the activity's default fullscreen state.
    207      */
    208     public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
    209 
    210     /**
    211      * The name of an Intent-extra used to control the UI of a ViewImage.
    212      * This is a boolean property that specifies whether or not to show action icons.
    213      */
    214     public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
    215 
    216     /**
    217      * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
    218      * This is a boolean property that specifies whether or not to finish the MovieView activity
    219      * when the movie completes playing. The default value is true, which means to automatically
    220      * exit the movie player activity when the movie completes playing.
    221      */
    222     public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
    223 
    224     /**
    225      * The name of the Intent action used to launch a camera in still image mode.
    226      */
    227     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    228     public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
    229 
    230     /**
    231      * Name under which an activity handling {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or
    232      * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE} publishes the service name for its prewarm
    233      * service.
    234      * <p>
    235      * This meta-data should reference the fully qualified class name of the prewarm service
    236      * extending {@link CameraPrewarmService}.
    237      * <p>
    238      * The prewarm service will get bound and receive a prewarm signal
    239      * {@link CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent.
    240      * An application implementing a prewarm service should do the absolute minimum amount of work
    241      * to initialize the camera in order to reduce startup time in likely case that shortly after a
    242      * camera launch intent would be sent.
    243      */
    244     public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE =
    245             "android.media.still_image_camera_preview_service";
    246 
    247     /**
    248      * The name of the Intent action used to launch a camera in still image mode
    249      * for use when the device is secured (e.g. with a pin, password, pattern,
    250      * or face unlock). Applications responding to this intent must not expose
    251      * any personal content like existing photos or videos on the device. The
    252      * applications should be careful not to share any photo or video with other
    253      * applications or internet. The activity should use {@link
    254      * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display
    255      * on top of the lock screen while secured. There is no activity stack when
    256      * this flag is used, so launching more than one activity is strongly
    257      * discouraged.
    258      */
    259     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    260     public static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
    261             "android.media.action.STILL_IMAGE_CAMERA_SECURE";
    262 
    263     /**
    264      * The name of the Intent action used to launch a camera in video mode.
    265      */
    266     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    267     public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
    268 
    269     /**
    270      * Standard Intent action that can be sent to have the camera application
    271      * capture an image and return it.
    272      * <p>
    273      * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
    274      * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
    275      * object in the extra field. This is useful for applications that only need a small image.
    276      * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
    277      * value of EXTRA_OUTPUT.
    278      * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
    279      * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
    280      * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
    281      * If you don't set a ClipData, it will be copied there for you when calling
    282      * {@link Context#startActivity(Intent)}.
    283      *
    284      * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
    285      * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
    286      * is not granted, then attempting to use this action will result in a {@link
    287      * java.lang.SecurityException}.
    288      *
    289      *  @see #EXTRA_OUTPUT
    290      */
    291     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    292     public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
    293 
    294     /**
    295      * Intent action that can be sent to have the camera application capture an image and return
    296      * it when the device is secured (e.g. with a pin, password, pattern, or face unlock).
    297      * Applications responding to this intent must not expose any personal content like existing
    298      * photos or videos on the device. The applications should be careful not to share any photo
    299      * or video with other applications or internet. The activity should use {@link
    300      * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display on top of the
    301      * lock screen while secured. There is no activity stack when this flag is used, so
    302      * launching more than one activity is strongly discouraged.
    303      * <p>
    304      * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
    305      * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
    306      * object in the extra field. This is useful for applications that only need a small image.
    307      * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
    308      * value of EXTRA_OUTPUT.
    309      * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
    310      * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
    311      * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
    312      * If you don't set a ClipData, it will be copied there for you when calling
    313      * {@link Context#startActivity(Intent)}.
    314      *
    315      * @see #ACTION_IMAGE_CAPTURE
    316      * @see #EXTRA_OUTPUT
    317      */
    318     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    319     public static final String ACTION_IMAGE_CAPTURE_SECURE =
    320             "android.media.action.IMAGE_CAPTURE_SECURE";
    321 
    322     /**
    323      * Standard Intent action that can be sent to have the camera application
    324      * capture a video and return it.
    325      * <p>
    326      * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
    327      * <p>
    328      * The caller may pass in an extra EXTRA_OUTPUT to control
    329      * where the video is written. If EXTRA_OUTPUT is not present the video will be
    330      * written to the standard location for videos, and the Uri of that location will be
    331      * returned in the data field of the Uri.
    332      * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
    333      * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
    334      * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
    335      * If you don't set a ClipData, it will be copied there for you when calling
    336      * {@link Context#startActivity(Intent)}.
    337      *
    338      * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
    339      * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
    340      * is not granted, then atempting to use this action will result in a {@link
    341      * java.lang.SecurityException}.
    342      *
    343      * @see #EXTRA_OUTPUT
    344      * @see #EXTRA_VIDEO_QUALITY
    345      * @see #EXTRA_SIZE_LIMIT
    346      * @see #EXTRA_DURATION_LIMIT
    347      */
    348     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    349     public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
    350 
    351     /**
    352      * The name of the Intent-extra used to control the quality of a recorded video. This is an
    353      * integer property. Currently value 0 means low quality, suitable for MMS messages, and
    354      * value 1 means high quality. In the future other quality levels may be added.
    355      */
    356     public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
    357 
    358     /**
    359      * Specify the maximum allowed size.
    360      */
    361     public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
    362 
    363     /**
    364      * Specify the maximum allowed recording duration in seconds.
    365      */
    366     public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
    367 
    368     /**
    369      * The name of the Intent-extra used to indicate a content resolver Uri to be used to
    370      * store the requested image or video.
    371      */
    372     public final static String EXTRA_OUTPUT = "output";
    373 
    374     /**
    375       * The string that is used when a media attribute is not known. For example,
    376       * if an audio file does not have any meta data, the artist and album columns
    377       * will be set to this value.
    378       */
    379     public static final String UNKNOWN_STRING = "<unknown>";
    380 
    381     /**
    382      * Common fields for most MediaProvider tables
    383      */
    384 
    385     public interface MediaColumns extends BaseColumns {
    386         /**
    387          * Path to the file on disk.
    388          * <p>
    389          * Note that apps may not have filesystem permissions to directly access
    390          * this path. Instead of trying to open this path directly, apps should
    391          * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
    392          * access.
    393          * <p>
    394          * Type: TEXT
    395          */
    396         public static final String DATA = "_data";
    397 
    398         /**
    399          * The size of the file in bytes
    400          * <P>Type: INTEGER (long)</P>
    401          */
    402         public static final String SIZE = "_size";
    403 
    404         /**
    405          * The display name of the file
    406          * <P>Type: TEXT</P>
    407          */
    408         public static final String DISPLAY_NAME = "_display_name";
    409 
    410         /**
    411          * The title of the content
    412          * <P>Type: TEXT</P>
    413          */
    414         public static final String TITLE = "title";
    415 
    416         /**
    417          * The time the file was added to the media provider
    418          * Units are seconds since 1970.
    419          * <P>Type: INTEGER (long)</P>
    420          */
    421         public static final String DATE_ADDED = "date_added";
    422 
    423         /**
    424          * The time the file was last modified
    425          * Units are seconds since 1970.
    426          * NOTE: This is for internal use by the media scanner.  Do not modify this field.
    427          * <P>Type: INTEGER (long)</P>
    428          */
    429         public static final String DATE_MODIFIED = "date_modified";
    430 
    431         /**
    432          * The MIME type of the file
    433          * <P>Type: TEXT</P>
    434          */
    435         public static final String MIME_TYPE = "mime_type";
    436 
    437         /**
    438          * The MTP object handle of a newly transfered file.
    439          * Used to pass the new file's object handle through the media scanner
    440          * from MTP to the media provider
    441          * For internal use only by MTP, media scanner and media provider.
    442          * <P>Type: INTEGER</P>
    443          * @hide
    444          */
    445         public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
    446 
    447         /**
    448          * Non-zero if the media file is drm-protected
    449          * <P>Type: INTEGER (boolean)</P>
    450          * @hide
    451          */
    452         public static final String IS_DRM = "is_drm";
    453 
    454         /**
    455          * The width of the image/video in pixels.
    456          */
    457         public static final String WIDTH = "width";
    458 
    459         /**
    460          * The height of the image/video in pixels.
    461          */
    462         public static final String HEIGHT = "height";
    463      }
    464 
    465     /**
    466      * Media provider table containing an index of all files in the media storage,
    467      * including non-media files.  This should be used by applications that work with
    468      * non-media file types (text, HTML, PDF, etc) as well as applications that need to
    469      * work with multiple media file types in a single query.
    470      */
    471     public static final class Files {
    472 
    473         /**
    474          * Get the content:// style URI for the files table on the
    475          * given volume.
    476          *
    477          * @param volumeName the name of the volume to get the URI for
    478          * @return the URI to the files table on the given volume
    479          */
    480         public static Uri getContentUri(String volumeName) {
    481             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
    482                     "/file");
    483         }
    484 
    485         /**
    486          * Get the content:// style URI for a single row in the files table on the
    487          * given volume.
    488          *
    489          * @param volumeName the name of the volume to get the URI for
    490          * @param rowId the file to get the URI for
    491          * @return the URI to the files table on the given volume
    492          */
    493         public static final Uri getContentUri(String volumeName,
    494                 long rowId) {
    495             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
    496                     + "/file/" + rowId);
    497         }
    498 
    499         /**
    500          * For use only by the MTP implementation.
    501          * @hide
    502          */
    503         public static Uri getMtpObjectsUri(String volumeName) {
    504             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
    505                     "/object");
    506         }
    507 
    508         /**
    509          * For use only by the MTP implementation.
    510          * @hide
    511          */
    512         public static final Uri getMtpObjectsUri(String volumeName,
    513                 long fileId) {
    514             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
    515                     + "/object/" + fileId);
    516         }
    517 
    518         /**
    519          * Used to implement the MTP GetObjectReferences and SetObjectReferences commands.
    520          * @hide
    521          */
    522         public static final Uri getMtpReferencesUri(String volumeName,
    523                 long fileId) {
    524             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
    525                     + "/object/" + fileId + "/references");
    526         }
    527 
    528         /**
    529          * Fields for master table for all media files.
    530          * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
    531          */
    532         public interface FileColumns extends MediaColumns {
    533             /**
    534              * The MTP storage ID of the file
    535              * <P>Type: INTEGER</P>
    536              * @hide
    537              */
    538             public static final String STORAGE_ID = "storage_id";
    539 
    540             /**
    541              * The MTP format code of the file
    542              * <P>Type: INTEGER</P>
    543              * @hide
    544              */
    545             public static final String FORMAT = "format";
    546 
    547             /**
    548              * The index of the parent directory of the file
    549              * <P>Type: INTEGER</P>
    550              */
    551             public static final String PARENT = "parent";
    552 
    553             /**
    554              * The MIME type of the file
    555              * <P>Type: TEXT</P>
    556              */
    557             public static final String MIME_TYPE = "mime_type";
    558 
    559             /**
    560              * The title of the content
    561              * <P>Type: TEXT</P>
    562              */
    563             public static final String TITLE = "title";
    564 
    565             /**
    566              * The media type (audio, video, image or playlist)
    567              * of the file, or 0 for not a media file
    568              * <P>Type: TEXT</P>
    569              */
    570             public static final String MEDIA_TYPE = "media_type";
    571 
    572             /**
    573              * Constant for the {@link #MEDIA_TYPE} column indicating that file
    574              * is not an audio, image, video or playlist file.
    575              */
    576             public static final int MEDIA_TYPE_NONE = 0;
    577 
    578             /**
    579              * Constant for the {@link #MEDIA_TYPE} column indicating that file is an image file.
    580              */
    581             public static final int MEDIA_TYPE_IMAGE = 1;
    582 
    583             /**
    584              * Constant for the {@link #MEDIA_TYPE} column indicating that file is an audio file.
    585              */
    586             public static final int MEDIA_TYPE_AUDIO = 2;
    587 
    588             /**
    589              * Constant for the {@link #MEDIA_TYPE} column indicating that file is a video file.
    590              */
    591             public static final int MEDIA_TYPE_VIDEO = 3;
    592 
    593             /**
    594              * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
    595              */
    596             public static final int MEDIA_TYPE_PLAYLIST = 4;
    597         }
    598     }
    599 
    600     /**
    601      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
    602      * to be accessed elsewhere.
    603      */
    604     private static class InternalThumbnails implements BaseColumns {
    605         private static final int MINI_KIND = 1;
    606         private static final int FULL_SCREEN_KIND = 2;
    607         private static final int MICRO_KIND = 3;
    608         private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
    609         static final int DEFAULT_GROUP_ID = 0;
    610         private static final Object sThumbBufLock = new Object();
    611         private static byte[] sThumbBuf;
    612 
    613         private static Bitmap getMiniThumbFromFile(
    614                 Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
    615             Bitmap bitmap = null;
    616             Uri thumbUri = null;
    617             try {
    618                 long thumbId = c.getLong(0);
    619                 String filePath = c.getString(1);
    620                 thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
    621                 ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
    622                 bitmap = BitmapFactory.decodeFileDescriptor(
    623                         pfdInput.getFileDescriptor(), null, options);
    624                 pfdInput.close();
    625             } catch (FileNotFoundException ex) {
    626                 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
    627             } catch (IOException ex) {
    628                 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
    629             } catch (OutOfMemoryError ex) {
    630                 Log.e(TAG, "failed to allocate memory for thumbnail "
    631                         + thumbUri + "; " + ex);
    632             }
    633             return bitmap;
    634         }
    635 
    636         /**
    637          * This method cancels the thumbnail request so clients waiting for getThumbnail will be
    638          * interrupted and return immediately. Only the original process which made the getThumbnail
    639          * requests can cancel their own requests.
    640          *
    641          * @param cr ContentResolver
    642          * @param origId original image or video id. use -1 to cancel all requests.
    643          * @param groupId the same groupId used in getThumbnail
    644          * @param baseUri the base URI of requested thumbnails
    645          */
    646         static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
    647                 long groupId) {
    648             Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
    649                     .appendQueryParameter("orig_id", String.valueOf(origId))
    650                     .appendQueryParameter("group_id", String.valueOf(groupId)).build();
    651             Cursor c = null;
    652             try {
    653                 c = cr.query(cancelUri, PROJECTION, null, null, null);
    654             }
    655             finally {
    656                 if (c != null) c.close();
    657             }
    658         }
    659 
    660         /**
    661          * This method ensure thumbnails associated with origId are generated and decode the byte
    662          * stream from database (MICRO_KIND) or file (MINI_KIND).
    663          *
    664          * Special optimization has been done to avoid further IPC communication for MICRO_KIND
    665          * thumbnails.
    666          *
    667          * @param cr ContentResolver
    668          * @param origId original image or video id
    669          * @param kind could be MINI_KIND or MICRO_KIND
    670          * @param options this is only used for MINI_KIND when decoding the Bitmap
    671          * @param baseUri the base URI of requested thumbnails
    672          * @param groupId the id of group to which this request belongs
    673          * @return Bitmap bitmap of specified thumbnail kind
    674          */
    675         static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
    676                 BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
    677             Bitmap bitmap = null;
    678             // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
    679             // If the magic is non-zero, we simply return thumbnail if it does exist.
    680             // querying MediaProvider and simply return thumbnail.
    681             MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI
    682                     : Images.Media.EXTERNAL_CONTENT_URI);
    683             Cursor c = null;
    684             try {
    685                 long magic = thumbFile.getMagic(origId);
    686                 if (magic != 0) {
    687                     if (kind == MICRO_KIND) {
    688                         synchronized (sThumbBufLock) {
    689                             if (sThumbBuf == null) {
    690                                 sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
    691                             }
    692                             if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
    693                                 bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
    694                                 if (bitmap == null) {
    695                                     Log.w(TAG, "couldn't decode byte array.");
    696                                 }
    697                             }
    698                         }
    699                         return bitmap;
    700                     } else if (kind == MINI_KIND) {
    701                         String column = isVideo ? "video_id=" : "image_id=";
    702                         c = cr.query(baseUri, PROJECTION, column + origId, null, null);
    703                         if (c != null && c.moveToFirst()) {
    704                             bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
    705                             if (bitmap != null) {
    706                                 return bitmap;
    707                             }
    708                         }
    709                     }
    710                 }
    711 
    712                 Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
    713                         .appendQueryParameter("orig_id", String.valueOf(origId))
    714                         .appendQueryParameter("group_id", String.valueOf(groupId)).build();
    715                 if (c != null) c.close();
    716                 c = cr.query(blockingUri, PROJECTION, null, null, null);
    717                 // This happens when original image/video doesn't exist.
    718                 if (c == null) return null;
    719 
    720                 // Assuming thumbnail has been generated, at least original image exists.
    721                 if (kind == MICRO_KIND) {
    722                     synchronized (sThumbBufLock) {
    723                         if (sThumbBuf == null) {
    724                             sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
    725                         }
    726                         Arrays.fill(sThumbBuf, (byte)0);
    727                         if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
    728                             bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
    729                             if (bitmap == null) {
    730                                 Log.w(TAG, "couldn't decode byte array.");
    731                             }
    732                         }
    733                     }
    734                 } else if (kind == MINI_KIND) {
    735                     if (c.moveToFirst()) {
    736                         bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
    737                     }
    738                 } else {
    739                     throw new IllegalArgumentException("Unsupported kind: " + kind);
    740                 }
    741 
    742                 // We probably run out of space, so create the thumbnail in memory.
    743                 if (bitmap == null) {
    744                     Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
    745                             + ", kind=" + kind + ", isVideo="+isVideo);
    746                     Uri uri = Uri.parse(
    747                             baseUri.buildUpon().appendPath(String.valueOf(origId))
    748                                     .toString().replaceFirst("thumbnails", "media"));
    749                     if (c != null) c.close();
    750                     c = cr.query(uri, PROJECTION, null, null, null);
    751                     if (c == null || !c.moveToFirst()) {
    752                         return null;
    753                     }
    754                     String filePath = c.getString(1);
    755                     if (filePath != null) {
    756                         if (isVideo) {
    757                             bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
    758                         } else {
    759                             bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
    760                         }
    761                     }
    762                 }
    763             } catch (SQLiteException ex) {
    764                 Log.w(TAG, ex);
    765             } finally {
    766                 if (c != null) c.close();
    767                 // To avoid file descriptor leak in application process.
    768                 thumbFile.deactivate();
    769                 thumbFile = null;
    770             }
    771             return bitmap;
    772         }
    773     }
    774 
    775     /**
    776      * Contains meta data for all available images.
    777      */
    778     public static final class Images {
    779         public interface ImageColumns extends MediaColumns {
    780             /**
    781              * The description of the image
    782              * <P>Type: TEXT</P>
    783              */
    784             public static final String DESCRIPTION = "description";
    785 
    786             /**
    787              * The picasa id of the image
    788              * <P>Type: TEXT</P>
    789              */
    790             public static final String PICASA_ID = "picasa_id";
    791 
    792             /**
    793              * Whether the video should be published as public or private
    794              * <P>Type: INTEGER</P>
    795              */
    796             public static final String IS_PRIVATE = "isprivate";
    797 
    798             /**
    799              * The latitude where the image was captured.
    800              * <P>Type: DOUBLE</P>
    801              */
    802             public static final String LATITUDE = "latitude";
    803 
    804             /**
    805              * The longitude where the image was captured.
    806              * <P>Type: DOUBLE</P>
    807              */
    808             public static final String LONGITUDE = "longitude";
    809 
    810             /**
    811              * The date & time that the image was taken in units
    812              * of milliseconds since jan 1, 1970.
    813              * <P>Type: INTEGER</P>
    814              */
    815             public static final String DATE_TAKEN = "datetaken";
    816 
    817             /**
    818              * The orientation for the image expressed as degrees.
    819              * Only degrees 0, 90, 180, 270 will work.
    820              * <P>Type: INTEGER</P>
    821              */
    822             public static final String ORIENTATION = "orientation";
    823 
    824             /**
    825              * The mini thumb id.
    826              * <P>Type: INTEGER</P>
    827              */
    828             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
    829 
    830             /**
    831              * The bucket id of the image. This is a read-only property that
    832              * is automatically computed from the DATA column.
    833              * <P>Type: TEXT</P>
    834              */
    835             public static final String BUCKET_ID = "bucket_id";
    836 
    837             /**
    838              * The bucket display name of the image. This is a read-only property that
    839              * is automatically computed from the DATA column.
    840              * <P>Type: TEXT</P>
    841              */
    842             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
    843         }
    844 
    845         public static final class Media implements ImageColumns {
    846             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
    847                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
    848             }
    849 
    850             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
    851                     String where, String orderBy) {
    852                 return cr.query(uri, projection, where,
    853                                              null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
    854             }
    855 
    856             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
    857                     String selection, String [] selectionArgs, String orderBy) {
    858                 return cr.query(uri, projection, selection,
    859                         selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
    860             }
    861 
    862             /**
    863              * Retrieves an image for the given url as a {@link Bitmap}.
    864              *
    865              * @param cr The content resolver to use
    866              * @param url The url of the image
    867              * @throws FileNotFoundException
    868              * @throws IOException
    869              */
    870             public static final Bitmap getBitmap(ContentResolver cr, Uri url)
    871                     throws FileNotFoundException, IOException {
    872                 InputStream input = cr.openInputStream(url);
    873                 Bitmap bitmap = BitmapFactory.decodeStream(input);
    874                 input.close();
    875                 return bitmap;
    876             }
    877 
    878             /**
    879              * Insert an image and create a thumbnail for it.
    880              *
    881              * @param cr The content resolver to use
    882              * @param imagePath The path to the image to insert
    883              * @param name The name of the image
    884              * @param description The description of the image
    885              * @return The URL to the newly created image
    886              * @throws FileNotFoundException
    887              */
    888             public static final String insertImage(ContentResolver cr, String imagePath,
    889                     String name, String description) throws FileNotFoundException {
    890                 // Check if file exists with a FileInputStream
    891                 FileInputStream stream = new FileInputStream(imagePath);
    892                 try {
    893                     Bitmap bm = BitmapFactory.decodeFile(imagePath);
    894                     String ret = insertImage(cr, bm, name, description);
    895                     bm.recycle();
    896                     return ret;
    897                 } finally {
    898                     try {
    899                         stream.close();
    900                     } catch (IOException e) {
    901                     }
    902                 }
    903             }
    904 
    905             private static final Bitmap StoreThumbnail(
    906                     ContentResolver cr,
    907                     Bitmap source,
    908                     long id,
    909                     float width, float height,
    910                     int kind) {
    911                 // create the matrix to scale it
    912                 Matrix matrix = new Matrix();
    913 
    914                 float scaleX = width / source.getWidth();
    915                 float scaleY = height / source.getHeight();
    916 
    917                 matrix.setScale(scaleX, scaleY);
    918 
    919                 Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
    920                                                    source.getWidth(),
    921                                                    source.getHeight(), matrix,
    922                                                    true);
    923 
    924                 ContentValues values = new ContentValues(4);
    925                 values.put(Images.Thumbnails.KIND,     kind);
    926                 values.put(Images.Thumbnails.IMAGE_ID, (int)id);
    927                 values.put(Images.Thumbnails.HEIGHT,   thumb.getHeight());
    928                 values.put(Images.Thumbnails.WIDTH,    thumb.getWidth());
    929 
    930                 Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
    931 
    932                 try {
    933                     OutputStream thumbOut = cr.openOutputStream(url);
    934 
    935                     thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
    936                     thumbOut.close();
    937                     return thumb;
    938                 }
    939                 catch (FileNotFoundException ex) {
    940                     return null;
    941                 }
    942                 catch (IOException ex) {
    943                     return null;
    944                 }
    945             }
    946 
    947             /**
    948              * Insert an image and create a thumbnail for it.
    949              *
    950              * @param cr The content resolver to use
    951              * @param source The stream to use for the image
    952              * @param title The name of the image
    953              * @param description The description of the image
    954              * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
    955              *              for any reason.
    956              */
    957             public static final String insertImage(ContentResolver cr, Bitmap source,
    958                                                    String title, String description) {
    959                 ContentValues values = new ContentValues();
    960                 values.put(Images.Media.TITLE, title);
    961                 values.put(Images.Media.DESCRIPTION, description);
    962                 values.put(Images.Media.MIME_TYPE, "image/jpeg");
    963 
    964                 Uri url = null;
    965                 String stringUrl = null;    /* value to be returned */
    966 
    967                 try {
    968                     url = cr.insert(EXTERNAL_CONTENT_URI, values);
    969 
    970                     if (source != null) {
    971                         OutputStream imageOut = cr.openOutputStream(url);
    972                         try {
    973                             source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
    974                         } finally {
    975                             imageOut.close();
    976                         }
    977 
    978                         long id = ContentUris.parseId(url);
    979                         // Wait until MINI_KIND thumbnail is generated.
    980                         Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
    981                                 Images.Thumbnails.MINI_KIND, null);
    982                         // This is for backward compatibility.
    983                         Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
    984                                 Images.Thumbnails.MICRO_KIND);
    985                     } else {
    986                         Log.e(TAG, "Failed to create thumbnail, removing original");
    987                         cr.delete(url, null, null);
    988                         url = null;
    989                     }
    990                 } catch (Exception e) {
    991                     Log.e(TAG, "Failed to insert image", e);
    992                     if (url != null) {
    993                         cr.delete(url, null, null);
    994                         url = null;
    995                     }
    996                 }
    997 
    998                 if (url != null) {
    999                     stringUrl = url.toString();
   1000                 }
   1001 
   1002                 return stringUrl;
   1003             }
   1004 
   1005             /**
   1006              * Get the content:// style URI for the image media table on the
   1007              * given volume.
   1008              *
   1009              * @param volumeName the name of the volume to get the URI for
   1010              * @return the URI to the image media table on the given volume
   1011              */
   1012             public static Uri getContentUri(String volumeName) {
   1013                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1014                         "/images/media");
   1015             }
   1016 
   1017             /**
   1018              * The content:// style URI for the internal storage.
   1019              */
   1020             public static final Uri INTERNAL_CONTENT_URI =
   1021                     getContentUri("internal");
   1022 
   1023             /**
   1024              * The content:// style URI for the "primary" external storage
   1025              * volume.
   1026              */
   1027             public static final Uri EXTERNAL_CONTENT_URI =
   1028                     getContentUri("external");
   1029 
   1030             /**
   1031              * The MIME type of of this directory of
   1032              * images.  Note that each entry in this directory will have a standard
   1033              * image MIME type as appropriate -- for example, image/jpeg.
   1034              */
   1035             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
   1036 
   1037             /**
   1038              * The default sort order for this table
   1039              */
   1040             public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
   1041         }
   1042 
   1043         /**
   1044          * This class allows developers to query and get two kinds of thumbnails:
   1045          * MINI_KIND: 512 x 384 thumbnail
   1046          * MICRO_KIND: 96 x 96 thumbnail
   1047          */
   1048         public static class Thumbnails implements BaseColumns {
   1049             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
   1050                 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
   1051             }
   1052 
   1053             public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
   1054                     String[] projection) {
   1055                 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
   1056             }
   1057 
   1058             public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
   1059                     String[] projection) {
   1060                 return cr.query(EXTERNAL_CONTENT_URI, projection,
   1061                         IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
   1062                         kind, null, null);
   1063             }
   1064 
   1065             /**
   1066              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
   1067              * interrupted and return immediately. Only the original process which made the getThumbnail
   1068              * requests can cancel their own requests.
   1069              *
   1070              * @param cr ContentResolver
   1071              * @param origId original image id
   1072              */
   1073             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
   1074                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
   1075                         InternalThumbnails.DEFAULT_GROUP_ID);
   1076             }
   1077 
   1078             /**
   1079              * This method checks if the thumbnails of the specified image (origId) has been created.
   1080              * It will be blocked until the thumbnails are generated.
   1081              *
   1082              * @param cr ContentResolver used to dispatch queries to MediaProvider.
   1083              * @param origId Original image id associated with thumbnail of interest.
   1084              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
   1085              * @param options this is only used for MINI_KIND when decoding the Bitmap
   1086              * @return A Bitmap instance. It could be null if the original image
   1087              *         associated with origId doesn't exist or memory is not enough.
   1088              */
   1089             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
   1090                     BitmapFactory.Options options) {
   1091                 return InternalThumbnails.getThumbnail(cr, origId,
   1092                         InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
   1093                         EXTERNAL_CONTENT_URI, false);
   1094             }
   1095 
   1096             /**
   1097              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
   1098              * interrupted and return immediately. Only the original process which made the getThumbnail
   1099              * requests can cancel their own requests.
   1100              *
   1101              * @param cr ContentResolver
   1102              * @param origId original image id
   1103              * @param groupId the same groupId used in getThumbnail.
   1104              */
   1105             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
   1106                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
   1107             }
   1108 
   1109             /**
   1110              * This method checks if the thumbnails of the specified image (origId) has been created.
   1111              * It will be blocked until the thumbnails are generated.
   1112              *
   1113              * @param cr ContentResolver used to dispatch queries to MediaProvider.
   1114              * @param origId Original image id associated with thumbnail of interest.
   1115              * @param groupId the id of group to which this request belongs
   1116              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
   1117              * @param options this is only used for MINI_KIND when decoding the Bitmap
   1118              * @return A Bitmap instance. It could be null if the original image
   1119              *         associated with origId doesn't exist or memory is not enough.
   1120              */
   1121             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
   1122                     int kind, BitmapFactory.Options options) {
   1123                 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
   1124                         EXTERNAL_CONTENT_URI, false);
   1125             }
   1126 
   1127             /**
   1128              * Get the content:// style URI for the image media table on the
   1129              * given volume.
   1130              *
   1131              * @param volumeName the name of the volume to get the URI for
   1132              * @return the URI to the image media table on the given volume
   1133              */
   1134             public static Uri getContentUri(String volumeName) {
   1135                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1136                         "/images/thumbnails");
   1137             }
   1138 
   1139             /**
   1140              * The content:// style URI for the internal storage.
   1141              */
   1142             public static final Uri INTERNAL_CONTENT_URI =
   1143                     getContentUri("internal");
   1144 
   1145             /**
   1146              * The content:// style URI for the "primary" external storage
   1147              * volume.
   1148              */
   1149             public static final Uri EXTERNAL_CONTENT_URI =
   1150                     getContentUri("external");
   1151 
   1152             /**
   1153              * The default sort order for this table
   1154              */
   1155             public static final String DEFAULT_SORT_ORDER = "image_id ASC";
   1156 
   1157             /**
   1158              * Path to the thumbnail file on disk.
   1159              * <p>
   1160              * Note that apps may not have filesystem permissions to directly
   1161              * access this path. Instead of trying to open this path directly,
   1162              * apps should use
   1163              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
   1164              * access.
   1165              * <p>
   1166              * Type: TEXT
   1167              */
   1168             public static final String DATA = "_data";
   1169 
   1170             /**
   1171              * The original image for the thumbnal
   1172              * <P>Type: INTEGER (ID from Images table)</P>
   1173              */
   1174             public static final String IMAGE_ID = "image_id";
   1175 
   1176             /**
   1177              * The kind of the thumbnail
   1178              * <P>Type: INTEGER (One of the values below)</P>
   1179              */
   1180             public static final String KIND = "kind";
   1181 
   1182             public static final int MINI_KIND = 1;
   1183             public static final int FULL_SCREEN_KIND = 2;
   1184             public static final int MICRO_KIND = 3;
   1185             /**
   1186              * The blob raw data of thumbnail
   1187              * <P>Type: DATA STREAM</P>
   1188              */
   1189             public static final String THUMB_DATA = "thumb_data";
   1190 
   1191             /**
   1192              * The width of the thumbnal
   1193              * <P>Type: INTEGER (long)</P>
   1194              */
   1195             public static final String WIDTH = "width";
   1196 
   1197             /**
   1198              * The height of the thumbnail
   1199              * <P>Type: INTEGER (long)</P>
   1200              */
   1201             public static final String HEIGHT = "height";
   1202         }
   1203     }
   1204 
   1205     /**
   1206      * Container for all audio content.
   1207      */
   1208     public static final class Audio {
   1209         /**
   1210          * Columns for audio file that show up in multiple tables.
   1211          */
   1212         public interface AudioColumns extends MediaColumns {
   1213 
   1214             /**
   1215              * A non human readable key calculated from the TITLE, used for
   1216              * searching, sorting and grouping
   1217              * <P>Type: TEXT</P>
   1218              */
   1219             public static final String TITLE_KEY = "title_key";
   1220 
   1221             /**
   1222              * The duration of the audio file, in ms
   1223              * <P>Type: INTEGER (long)</P>
   1224              */
   1225             public static final String DURATION = "duration";
   1226 
   1227             /**
   1228              * The position, in ms, playback was at when playback for this file
   1229              * was last stopped.
   1230              * <P>Type: INTEGER (long)</P>
   1231              */
   1232             public static final String BOOKMARK = "bookmark";
   1233 
   1234             /**
   1235              * The id of the artist who created the audio file, if any
   1236              * <P>Type: INTEGER (long)</P>
   1237              */
   1238             public static final String ARTIST_ID = "artist_id";
   1239 
   1240             /**
   1241              * The artist who created the audio file, if any
   1242              * <P>Type: TEXT</P>
   1243              */
   1244             public static final String ARTIST = "artist";
   1245 
   1246             /**
   1247              * The artist credited for the album that contains the audio file
   1248              * <P>Type: TEXT</P>
   1249              * @hide
   1250              */
   1251             public static final String ALBUM_ARTIST = "album_artist";
   1252 
   1253             /**
   1254              * Whether the song is part of a compilation
   1255              * <P>Type: TEXT</P>
   1256              * @hide
   1257              */
   1258             public static final String COMPILATION = "compilation";
   1259 
   1260             /**
   1261              * A non human readable key calculated from the ARTIST, used for
   1262              * searching, sorting and grouping
   1263              * <P>Type: TEXT</P>
   1264              */
   1265             public static final String ARTIST_KEY = "artist_key";
   1266 
   1267             /**
   1268              * The composer of the audio file, if any
   1269              * <P>Type: TEXT</P>
   1270              */
   1271             public static final String COMPOSER = "composer";
   1272 
   1273             /**
   1274              * The id of the album the audio file is from, if any
   1275              * <P>Type: INTEGER (long)</P>
   1276              */
   1277             public static final String ALBUM_ID = "album_id";
   1278 
   1279             /**
   1280              * The album the audio file is from, if any
   1281              * <P>Type: TEXT</P>
   1282              */
   1283             public static final String ALBUM = "album";
   1284 
   1285             /**
   1286              * A non human readable key calculated from the ALBUM, used for
   1287              * searching, sorting and grouping
   1288              * <P>Type: TEXT</P>
   1289              */
   1290             public static final String ALBUM_KEY = "album_key";
   1291 
   1292             /**
   1293              * The track number of this song on the album, if any.
   1294              * This number encodes both the track number and the
   1295              * disc number. For multi-disc sets, this number will
   1296              * be 1xxx for tracks on the first disc, 2xxx for tracks
   1297              * on the second disc, etc.
   1298              * <P>Type: INTEGER</P>
   1299              */
   1300             public static final String TRACK = "track";
   1301 
   1302             /**
   1303              * The year the audio file was recorded, if any
   1304              * <P>Type: INTEGER</P>
   1305              */
   1306             public static final String YEAR = "year";
   1307 
   1308             /**
   1309              * Non-zero if the audio file is music
   1310              * <P>Type: INTEGER (boolean)</P>
   1311              */
   1312             public static final String IS_MUSIC = "is_music";
   1313 
   1314             /**
   1315              * Non-zero if the audio file is a podcast
   1316              * <P>Type: INTEGER (boolean)</P>
   1317              */
   1318             public static final String IS_PODCAST = "is_podcast";
   1319 
   1320             /**
   1321              * Non-zero if the audio file may be a ringtone
   1322              * <P>Type: INTEGER (boolean)</P>
   1323              */
   1324             public static final String IS_RINGTONE = "is_ringtone";
   1325 
   1326             /**
   1327              * Non-zero if the audio file may be an alarm
   1328              * <P>Type: INTEGER (boolean)</P>
   1329              */
   1330             public static final String IS_ALARM = "is_alarm";
   1331 
   1332             /**
   1333              * Non-zero if the audio file may be a notification sound
   1334              * <P>Type: INTEGER (boolean)</P>
   1335              */
   1336             public static final String IS_NOTIFICATION = "is_notification";
   1337 
   1338             /**
   1339              * The genre of the audio file, if any
   1340              * <P>Type: TEXT</P>
   1341              * Does not exist in the database - only used by the media scanner for inserts.
   1342              * @hide
   1343              */
   1344             public static final String GENRE = "genre";
   1345         }
   1346 
   1347         /**
   1348          * Converts a name to a "key" that can be used for grouping, sorting
   1349          * and searching.
   1350          * The rules that govern this conversion are:
   1351          * - remove 'special' characters like ()[]'!?.,
   1352          * - remove leading/trailing spaces
   1353          * - convert everything to lowercase
   1354          * - remove leading "the ", "an " and "a "
   1355          * - remove trailing ", the|an|a"
   1356          * - remove accents. This step leaves us with CollationKey data,
   1357          *   which is not human readable
   1358          *
   1359          * @param name The artist or album name to convert
   1360          * @return The "key" for the given name.
   1361          */
   1362         public static String keyFor(String name) {
   1363             if (name != null)  {
   1364                 boolean sortfirst = false;
   1365                 if (name.equals(UNKNOWN_STRING)) {
   1366                     return "\001";
   1367                 }
   1368                 // Check if the first character is \001. We use this to
   1369                 // force sorting of certain special files, like the silent ringtone.
   1370                 if (name.startsWith("\001")) {
   1371                     sortfirst = true;
   1372                 }
   1373                 name = name.trim().toLowerCase();
   1374                 if (name.startsWith("the ")) {
   1375                     name = name.substring(4);
   1376                 }
   1377                 if (name.startsWith("an ")) {
   1378                     name = name.substring(3);
   1379                 }
   1380                 if (name.startsWith("a ")) {
   1381                     name = name.substring(2);
   1382                 }
   1383                 if (name.endsWith(", the") || name.endsWith(",the") ||
   1384                     name.endsWith(", an") || name.endsWith(",an") ||
   1385                     name.endsWith(", a") || name.endsWith(",a")) {
   1386                     name = name.substring(0, name.lastIndexOf(','));
   1387                 }
   1388                 name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
   1389                 if (name.length() > 0) {
   1390                     // Insert a separator between the characters to avoid
   1391                     // matches on a partial character. If we ever change
   1392                     // to start-of-word-only matches, this can be removed.
   1393                     StringBuilder b = new StringBuilder();
   1394                     b.append('.');
   1395                     int nl = name.length();
   1396                     for (int i = 0; i < nl; i++) {
   1397                         b.append(name.charAt(i));
   1398                         b.append('.');
   1399                     }
   1400                     name = b.toString();
   1401                     String key = DatabaseUtils.getCollationKey(name);
   1402                     if (sortfirst) {
   1403                         key = "\001" + key;
   1404                     }
   1405                     return key;
   1406                } else {
   1407                     return "";
   1408                 }
   1409             }
   1410             return null;
   1411         }
   1412 
   1413         public static final class Media implements AudioColumns {
   1414 
   1415             private static final String[] EXTERNAL_PATHS;
   1416 
   1417             static {
   1418                 String secondary_storage = System.getenv("SECONDARY_STORAGE");
   1419                 if (secondary_storage != null) {
   1420                     EXTERNAL_PATHS = secondary_storage.split(":");
   1421                 } else {
   1422                     EXTERNAL_PATHS = new String[0];
   1423                 }
   1424             }
   1425 
   1426             /**
   1427              * Get the content:// style URI for the audio media table on the
   1428              * given volume.
   1429              *
   1430              * @param volumeName the name of the volume to get the URI for
   1431              * @return the URI to the audio media table on the given volume
   1432              */
   1433             public static Uri getContentUri(String volumeName) {
   1434                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1435                         "/audio/media");
   1436             }
   1437 
   1438             public static Uri getContentUriForPath(String path) {
   1439                 for (String ep : EXTERNAL_PATHS) {
   1440                     if (path.startsWith(ep)) {
   1441                         return EXTERNAL_CONTENT_URI;
   1442                     }
   1443                 }
   1444 
   1445                 return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
   1446                         EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
   1447             }
   1448 
   1449             /**
   1450              * The content:// style URI for the internal storage.
   1451              */
   1452             public static final Uri INTERNAL_CONTENT_URI =
   1453                     getContentUri("internal");
   1454 
   1455             /**
   1456              * The content:// style URI for the "primary" external storage
   1457              * volume.
   1458              */
   1459             public static final Uri EXTERNAL_CONTENT_URI =
   1460                     getContentUri("external");
   1461 
   1462             /**
   1463              * The MIME type for this table.
   1464              */
   1465             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
   1466 
   1467             /**
   1468              * The MIME type for an audio track.
   1469              */
   1470             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/audio";
   1471 
   1472             /**
   1473              * The default sort order for this table
   1474              */
   1475             public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
   1476 
   1477             /**
   1478              * Activity Action: Start SoundRecorder application.
   1479              * <p>Input: nothing.
   1480              * <p>Output: An uri to the recorded sound stored in the Media Library
   1481              * if the recording was successful.
   1482              * May also contain the extra EXTRA_MAX_BYTES.
   1483              * @see #EXTRA_MAX_BYTES
   1484              */
   1485             public static final String RECORD_SOUND_ACTION =
   1486                     "android.provider.MediaStore.RECORD_SOUND";
   1487 
   1488             /**
   1489              * The name of the Intent-extra used to define a maximum file size for
   1490              * a recording made by the SoundRecorder application.
   1491              *
   1492              * @see #RECORD_SOUND_ACTION
   1493              */
   1494              public static final String EXTRA_MAX_BYTES =
   1495                     "android.provider.MediaStore.extra.MAX_BYTES";
   1496         }
   1497 
   1498         /**
   1499          * Columns representing an audio genre
   1500          */
   1501         public interface GenresColumns {
   1502             /**
   1503              * The name of the genre
   1504              * <P>Type: TEXT</P>
   1505              */
   1506             public static final String NAME = "name";
   1507         }
   1508 
   1509         /**
   1510          * Contains all genres for audio files
   1511          */
   1512         public static final class Genres implements BaseColumns, GenresColumns {
   1513             /**
   1514              * Get the content:// style URI for the audio genres table on the
   1515              * given volume.
   1516              *
   1517              * @param volumeName the name of the volume to get the URI for
   1518              * @return the URI to the audio genres table on the given volume
   1519              */
   1520             public static Uri getContentUri(String volumeName) {
   1521                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1522                         "/audio/genres");
   1523             }
   1524 
   1525             /**
   1526              * Get the content:// style URI for querying the genres of an audio file.
   1527              *
   1528              * @param volumeName the name of the volume to get the URI for
   1529              * @param audioId the ID of the audio file for which to retrieve the genres
   1530              * @return the URI to for querying the genres for the audio file
   1531              * with the given the volume and audioID
   1532              */
   1533             public static Uri getContentUriForAudioId(String volumeName, int audioId) {
   1534                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1535                         "/audio/media/" + audioId + "/genres");
   1536             }
   1537 
   1538             /**
   1539              * The content:// style URI for the internal storage.
   1540              */
   1541             public static final Uri INTERNAL_CONTENT_URI =
   1542                     getContentUri("internal");
   1543 
   1544             /**
   1545              * The content:// style URI for the "primary" external storage
   1546              * volume.
   1547              */
   1548             public static final Uri EXTERNAL_CONTENT_URI =
   1549                     getContentUri("external");
   1550 
   1551             /**
   1552              * The MIME type for this table.
   1553              */
   1554             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
   1555 
   1556             /**
   1557              * The MIME type for entries in this table.
   1558              */
   1559             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
   1560 
   1561             /**
   1562              * The default sort order for this table
   1563              */
   1564             public static final String DEFAULT_SORT_ORDER = NAME;
   1565 
   1566             /**
   1567              * Sub-directory of each genre containing all members.
   1568              */
   1569             public static final class Members implements AudioColumns {
   1570 
   1571                 public static final Uri getContentUri(String volumeName,
   1572                         long genreId) {
   1573                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
   1574                             + "/audio/genres/" + genreId + "/members");
   1575                 }
   1576 
   1577                 /**
   1578                  * A subdirectory of each genre containing all member audio files.
   1579                  */
   1580                 public static final String CONTENT_DIRECTORY = "members";
   1581 
   1582                 /**
   1583                  * The default sort order for this table
   1584                  */
   1585                 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
   1586 
   1587                 /**
   1588                  * The ID of the audio file
   1589                  * <P>Type: INTEGER (long)</P>
   1590                  */
   1591                 public static final String AUDIO_ID = "audio_id";
   1592 
   1593                 /**
   1594                  * The ID of the genre
   1595                  * <P>Type: INTEGER (long)</P>
   1596                  */
   1597                 public static final String GENRE_ID = "genre_id";
   1598             }
   1599         }
   1600 
   1601         /**
   1602          * Columns representing a playlist
   1603          */
   1604         public interface PlaylistsColumns {
   1605             /**
   1606              * The name of the playlist
   1607              * <P>Type: TEXT</P>
   1608              */
   1609             public static final String NAME = "name";
   1610 
   1611             /**
   1612              * Path to the playlist file on disk.
   1613              * <p>
   1614              * Note that apps may not have filesystem permissions to directly
   1615              * access this path. Instead of trying to open this path directly,
   1616              * apps should use
   1617              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
   1618              * access.
   1619              * <p>
   1620              * Type: TEXT
   1621              */
   1622             public static final String DATA = "_data";
   1623 
   1624             /**
   1625              * The time the file was added to the media provider
   1626              * Units are seconds since 1970.
   1627              * <P>Type: INTEGER (long)</P>
   1628              */
   1629             public static final String DATE_ADDED = "date_added";
   1630 
   1631             /**
   1632              * The time the file was last modified
   1633              * Units are seconds since 1970.
   1634              * NOTE: This is for internal use by the media scanner.  Do not modify this field.
   1635              * <P>Type: INTEGER (long)</P>
   1636              */
   1637             public static final String DATE_MODIFIED = "date_modified";
   1638         }
   1639 
   1640         /**
   1641          * Contains playlists for audio files
   1642          */
   1643         public static final class Playlists implements BaseColumns,
   1644                 PlaylistsColumns {
   1645             /**
   1646              * Get the content:// style URI for the audio playlists table on the
   1647              * given volume.
   1648              *
   1649              * @param volumeName the name of the volume to get the URI for
   1650              * @return the URI to the audio playlists table on the given volume
   1651              */
   1652             public static Uri getContentUri(String volumeName) {
   1653                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1654                         "/audio/playlists");
   1655             }
   1656 
   1657             /**
   1658              * The content:// style URI for the internal storage.
   1659              */
   1660             public static final Uri INTERNAL_CONTENT_URI =
   1661                     getContentUri("internal");
   1662 
   1663             /**
   1664              * The content:// style URI for the "primary" external storage
   1665              * volume.
   1666              */
   1667             public static final Uri EXTERNAL_CONTENT_URI =
   1668                     getContentUri("external");
   1669 
   1670             /**
   1671              * The MIME type for this table.
   1672              */
   1673             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
   1674 
   1675             /**
   1676              * The MIME type for entries in this table.
   1677              */
   1678             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
   1679 
   1680             /**
   1681              * The default sort order for this table
   1682              */
   1683             public static final String DEFAULT_SORT_ORDER = NAME;
   1684 
   1685             /**
   1686              * Sub-directory of each playlist containing all members.
   1687              */
   1688             public static final class Members implements AudioColumns {
   1689                 public static final Uri getContentUri(String volumeName,
   1690                         long playlistId) {
   1691                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
   1692                             + "/audio/playlists/" + playlistId + "/members");
   1693                 }
   1694 
   1695                 /**
   1696                  * Convenience method to move a playlist item to a new location
   1697                  * @param res The content resolver to use
   1698                  * @param playlistId The numeric id of the playlist
   1699                  * @param from The position of the item to move
   1700                  * @param to The position to move the item to
   1701                  * @return true on success
   1702                  */
   1703                 public static final boolean moveItem(ContentResolver res,
   1704                         long playlistId, int from, int to) {
   1705                     Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
   1706                             playlistId)
   1707                             .buildUpon()
   1708                             .appendEncodedPath(String.valueOf(from))
   1709                             .appendQueryParameter("move", "true")
   1710                             .build();
   1711                     ContentValues values = new ContentValues();
   1712                     values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
   1713                     return res.update(uri, values, null, null) != 0;
   1714                 }
   1715 
   1716                 /**
   1717                  * The ID within the playlist.
   1718                  */
   1719                 public static final String _ID = "_id";
   1720 
   1721                 /**
   1722                  * A subdirectory of each playlist containing all member audio
   1723                  * files.
   1724                  */
   1725                 public static final String CONTENT_DIRECTORY = "members";
   1726 
   1727                 /**
   1728                  * The ID of the audio file
   1729                  * <P>Type: INTEGER (long)</P>
   1730                  */
   1731                 public static final String AUDIO_ID = "audio_id";
   1732 
   1733                 /**
   1734                  * The ID of the playlist
   1735                  * <P>Type: INTEGER (long)</P>
   1736                  */
   1737                 public static final String PLAYLIST_ID = "playlist_id";
   1738 
   1739                 /**
   1740                  * The order of the songs in the playlist
   1741                  * <P>Type: INTEGER (long)></P>
   1742                  */
   1743                 public static final String PLAY_ORDER = "play_order";
   1744 
   1745                 /**
   1746                  * The default sort order for this table
   1747                  */
   1748                 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
   1749             }
   1750         }
   1751 
   1752         /**
   1753          * Columns representing an artist
   1754          */
   1755         public interface ArtistColumns {
   1756             /**
   1757              * The artist who created the audio file, if any
   1758              * <P>Type: TEXT</P>
   1759              */
   1760             public static final String ARTIST = "artist";
   1761 
   1762             /**
   1763              * A non human readable key calculated from the ARTIST, used for
   1764              * searching, sorting and grouping
   1765              * <P>Type: TEXT</P>
   1766              */
   1767             public static final String ARTIST_KEY = "artist_key";
   1768 
   1769             /**
   1770              * The number of albums in the database for this artist
   1771              */
   1772             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
   1773 
   1774             /**
   1775              * The number of albums in the database for this artist
   1776              */
   1777             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
   1778         }
   1779 
   1780         /**
   1781          * Contains artists for audio files
   1782          */
   1783         public static final class Artists implements BaseColumns, ArtistColumns {
   1784             /**
   1785              * Get the content:// style URI for the artists table on the
   1786              * given volume.
   1787              *
   1788              * @param volumeName the name of the volume to get the URI for
   1789              * @return the URI to the audio artists table on the given volume
   1790              */
   1791             public static Uri getContentUri(String volumeName) {
   1792                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1793                         "/audio/artists");
   1794             }
   1795 
   1796             /**
   1797              * The content:// style URI for the internal storage.
   1798              */
   1799             public static final Uri INTERNAL_CONTENT_URI =
   1800                     getContentUri("internal");
   1801 
   1802             /**
   1803              * The content:// style URI for the "primary" external storage
   1804              * volume.
   1805              */
   1806             public static final Uri EXTERNAL_CONTENT_URI =
   1807                     getContentUri("external");
   1808 
   1809             /**
   1810              * The MIME type for this table.
   1811              */
   1812             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
   1813 
   1814             /**
   1815              * The MIME type for entries in this table.
   1816              */
   1817             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
   1818 
   1819             /**
   1820              * The default sort order for this table
   1821              */
   1822             public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
   1823 
   1824             /**
   1825              * Sub-directory of each artist containing all albums on which
   1826              * a song by the artist appears.
   1827              */
   1828             public static final class Albums implements AlbumColumns {
   1829                 public static final Uri getContentUri(String volumeName,
   1830                         long artistId) {
   1831                     return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
   1832                             + "/audio/artists/" + artistId + "/albums");
   1833                 }
   1834             }
   1835         }
   1836 
   1837         /**
   1838          * Columns representing an album
   1839          */
   1840         public interface AlbumColumns {
   1841 
   1842             /**
   1843              * The id for the album
   1844              * <P>Type: INTEGER</P>
   1845              */
   1846             public static final String ALBUM_ID = "album_id";
   1847 
   1848             /**
   1849              * The album on which the audio file appears, if any
   1850              * <P>Type: TEXT</P>
   1851              */
   1852             public static final String ALBUM = "album";
   1853 
   1854             /**
   1855              * The artist whose songs appear on this album
   1856              * <P>Type: TEXT</P>
   1857              */
   1858             public static final String ARTIST = "artist";
   1859 
   1860             /**
   1861              * The number of songs on this album
   1862              * <P>Type: INTEGER</P>
   1863              */
   1864             public static final String NUMBER_OF_SONGS = "numsongs";
   1865 
   1866             /**
   1867              * This column is available when getting album info via artist,
   1868              * and indicates the number of songs on the album by the given
   1869              * artist.
   1870              * <P>Type: INTEGER</P>
   1871              */
   1872             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
   1873 
   1874             /**
   1875              * The year in which the earliest songs
   1876              * on this album were released. This will often
   1877              * be the same as {@link #LAST_YEAR}, but for compilation albums
   1878              * they might differ.
   1879              * <P>Type: INTEGER</P>
   1880              */
   1881             public static final String FIRST_YEAR = "minyear";
   1882 
   1883             /**
   1884              * The year in which the latest songs
   1885              * on this album were released. This will often
   1886              * be the same as {@link #FIRST_YEAR}, but for compilation albums
   1887              * they might differ.
   1888              * <P>Type: INTEGER</P>
   1889              */
   1890             public static final String LAST_YEAR = "maxyear";
   1891 
   1892             /**
   1893              * A non human readable key calculated from the ALBUM, used for
   1894              * searching, sorting and grouping
   1895              * <P>Type: TEXT</P>
   1896              */
   1897             public static final String ALBUM_KEY = "album_key";
   1898 
   1899             /**
   1900              * Cached album art.
   1901              * <P>Type: TEXT</P>
   1902              */
   1903             public static final String ALBUM_ART = "album_art";
   1904         }
   1905 
   1906         /**
   1907          * Contains artists for audio files
   1908          */
   1909         public static final class Albums implements BaseColumns, AlbumColumns {
   1910             /**
   1911              * Get the content:// style URI for the albums table on the
   1912              * given volume.
   1913              *
   1914              * @param volumeName the name of the volume to get the URI for
   1915              * @return the URI to the audio albums table on the given volume
   1916              */
   1917             public static Uri getContentUri(String volumeName) {
   1918                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   1919                         "/audio/albums");
   1920             }
   1921 
   1922             /**
   1923              * The content:// style URI for the internal storage.
   1924              */
   1925             public static final Uri INTERNAL_CONTENT_URI =
   1926                     getContentUri("internal");
   1927 
   1928             /**
   1929              * The content:// style URI for the "primary" external storage
   1930              * volume.
   1931              */
   1932             public static final Uri EXTERNAL_CONTENT_URI =
   1933                     getContentUri("external");
   1934 
   1935             /**
   1936              * The MIME type for this table.
   1937              */
   1938             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
   1939 
   1940             /**
   1941              * The MIME type for entries in this table.
   1942              */
   1943             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
   1944 
   1945             /**
   1946              * The default sort order for this table
   1947              */
   1948             public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
   1949         }
   1950 
   1951         public static final class Radio {
   1952             /**
   1953              * The MIME type for entries in this table.
   1954              */
   1955             public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
   1956 
   1957             // Not instantiable.
   1958             private Radio() { }
   1959         }
   1960     }
   1961 
   1962     public static final class Video {
   1963 
   1964         /**
   1965          * The default sort order for this table.
   1966          */
   1967         public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
   1968 
   1969         public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
   1970             return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
   1971         }
   1972 
   1973         public interface VideoColumns extends MediaColumns {
   1974 
   1975             /**
   1976              * The duration of the video file, in ms
   1977              * <P>Type: INTEGER (long)</P>
   1978              */
   1979             public static final String DURATION = "duration";
   1980 
   1981             /**
   1982              * The artist who created the video file, if any
   1983              * <P>Type: TEXT</P>
   1984              */
   1985             public static final String ARTIST = "artist";
   1986 
   1987             /**
   1988              * The album the video file is from, if any
   1989              * <P>Type: TEXT</P>
   1990              */
   1991             public static final String ALBUM = "album";
   1992 
   1993             /**
   1994              * The resolution of the video file, formatted as "XxY"
   1995              * <P>Type: TEXT</P>
   1996              */
   1997             public static final String RESOLUTION = "resolution";
   1998 
   1999             /**
   2000              * The description of the video recording
   2001              * <P>Type: TEXT</P>
   2002              */
   2003             public static final String DESCRIPTION = "description";
   2004 
   2005             /**
   2006              * Whether the video should be published as public or private
   2007              * <P>Type: INTEGER</P>
   2008              */
   2009             public static final String IS_PRIVATE = "isprivate";
   2010 
   2011             /**
   2012              * The user-added tags associated with a video
   2013              * <P>Type: TEXT</P>
   2014              */
   2015             public static final String TAGS = "tags";
   2016 
   2017             /**
   2018              * The YouTube category of the video
   2019              * <P>Type: TEXT</P>
   2020              */
   2021             public static final String CATEGORY = "category";
   2022 
   2023             /**
   2024              * The language of the video
   2025              * <P>Type: TEXT</P>
   2026              */
   2027             public static final String LANGUAGE = "language";
   2028 
   2029             /**
   2030              * The latitude where the video was captured.
   2031              * <P>Type: DOUBLE</P>
   2032              */
   2033             public static final String LATITUDE = "latitude";
   2034 
   2035             /**
   2036              * The longitude where the video was captured.
   2037              * <P>Type: DOUBLE</P>
   2038              */
   2039             public static final String LONGITUDE = "longitude";
   2040 
   2041             /**
   2042              * The date & time that the video was taken in units
   2043              * of milliseconds since jan 1, 1970.
   2044              * <P>Type: INTEGER</P>
   2045              */
   2046             public static final String DATE_TAKEN = "datetaken";
   2047 
   2048             /**
   2049              * The mini thumb id.
   2050              * <P>Type: INTEGER</P>
   2051              */
   2052             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
   2053 
   2054             /**
   2055              * The bucket id of the video. This is a read-only property that
   2056              * is automatically computed from the DATA column.
   2057              * <P>Type: TEXT</P>
   2058              */
   2059             public static final String BUCKET_ID = "bucket_id";
   2060 
   2061             /**
   2062              * The bucket display name of the video. This is a read-only property that
   2063              * is automatically computed from the DATA column.
   2064              * <P>Type: TEXT</P>
   2065              */
   2066             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
   2067 
   2068             /**
   2069              * The bookmark for the video. Time in ms. Represents the location in the video that the
   2070              * video should start playing at the next time it is opened. If the value is null or
   2071              * out of the range 0..DURATION-1 then the video should start playing from the
   2072              * beginning.
   2073              * <P>Type: INTEGER</P>
   2074              */
   2075             public static final String BOOKMARK = "bookmark";
   2076         }
   2077 
   2078         public static final class Media implements VideoColumns {
   2079             /**
   2080              * Get the content:// style URI for the video media table on the
   2081              * given volume.
   2082              *
   2083              * @param volumeName the name of the volume to get the URI for
   2084              * @return the URI to the video media table on the given volume
   2085              */
   2086             public static Uri getContentUri(String volumeName) {
   2087                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   2088                         "/video/media");
   2089             }
   2090 
   2091             /**
   2092              * The content:// style URI for the internal storage.
   2093              */
   2094             public static final Uri INTERNAL_CONTENT_URI =
   2095                     getContentUri("internal");
   2096 
   2097             /**
   2098              * The content:// style URI for the "primary" external storage
   2099              * volume.
   2100              */
   2101             public static final Uri EXTERNAL_CONTENT_URI =
   2102                     getContentUri("external");
   2103 
   2104             /**
   2105              * The MIME type for this table.
   2106              */
   2107             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
   2108 
   2109             /**
   2110              * The default sort order for this table
   2111              */
   2112             public static final String DEFAULT_SORT_ORDER = TITLE;
   2113         }
   2114 
   2115         /**
   2116          * This class allows developers to query and get two kinds of thumbnails:
   2117          * MINI_KIND: 512 x 384 thumbnail
   2118          * MICRO_KIND: 96 x 96 thumbnail
   2119          *
   2120          */
   2121         public static class Thumbnails implements BaseColumns {
   2122             /**
   2123              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
   2124              * interrupted and return immediately. Only the original process which made the getThumbnail
   2125              * requests can cancel their own requests.
   2126              *
   2127              * @param cr ContentResolver
   2128              * @param origId original video id
   2129              */
   2130             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
   2131                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
   2132                         InternalThumbnails.DEFAULT_GROUP_ID);
   2133             }
   2134 
   2135             /**
   2136              * This method checks if the thumbnails of the specified image (origId) has been created.
   2137              * It will be blocked until the thumbnails are generated.
   2138              *
   2139              * @param cr ContentResolver used to dispatch queries to MediaProvider.
   2140              * @param origId Original image id associated with thumbnail of interest.
   2141              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
   2142              * @param options this is only used for MINI_KIND when decoding the Bitmap
   2143              * @return A Bitmap instance. It could be null if the original image
   2144              *         associated with origId doesn't exist or memory is not enough.
   2145              */
   2146             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
   2147                     BitmapFactory.Options options) {
   2148                 return InternalThumbnails.getThumbnail(cr, origId,
   2149                         InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
   2150                         EXTERNAL_CONTENT_URI, true);
   2151             }
   2152 
   2153             /**
   2154              * This method checks if the thumbnails of the specified image (origId) has been created.
   2155              * It will be blocked until the thumbnails are generated.
   2156              *
   2157              * @param cr ContentResolver used to dispatch queries to MediaProvider.
   2158              * @param origId Original image id associated with thumbnail of interest.
   2159              * @param groupId the id of group to which this request belongs
   2160              * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
   2161              * @param options this is only used for MINI_KIND when decoding the Bitmap
   2162              * @return A Bitmap instance. It could be null if the original image associated with
   2163              *         origId doesn't exist or memory is not enough.
   2164              */
   2165             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
   2166                     int kind, BitmapFactory.Options options) {
   2167                 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
   2168                         EXTERNAL_CONTENT_URI, true);
   2169             }
   2170 
   2171             /**
   2172              * This method cancels the thumbnail request so clients waiting for getThumbnail will be
   2173              * interrupted and return immediately. Only the original process which made the getThumbnail
   2174              * requests can cancel their own requests.
   2175              *
   2176              * @param cr ContentResolver
   2177              * @param origId original video id
   2178              * @param groupId the same groupId used in getThumbnail.
   2179              */
   2180             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
   2181                 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
   2182             }
   2183 
   2184             /**
   2185              * Get the content:// style URI for the image media table on the
   2186              * given volume.
   2187              *
   2188              * @param volumeName the name of the volume to get the URI for
   2189              * @return the URI to the image media table on the given volume
   2190              */
   2191             public static Uri getContentUri(String volumeName) {
   2192                 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
   2193                         "/video/thumbnails");
   2194             }
   2195 
   2196             /**
   2197              * The content:// style URI for the internal storage.
   2198              */
   2199             public static final Uri INTERNAL_CONTENT_URI =
   2200                     getContentUri("internal");
   2201 
   2202             /**
   2203              * The content:// style URI for the "primary" external storage
   2204              * volume.
   2205              */
   2206             public static final Uri EXTERNAL_CONTENT_URI =
   2207                     getContentUri("external");
   2208 
   2209             /**
   2210              * The default sort order for this table
   2211              */
   2212             public static final String DEFAULT_SORT_ORDER = "video_id ASC";
   2213 
   2214             /**
   2215              * Path to the thumbnail file on disk.
   2216              * <p>
   2217              * Note that apps may not have filesystem permissions to directly
   2218              * access this path. Instead of trying to open this path directly,
   2219              * apps should use
   2220              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
   2221              * access.
   2222              * <p>
   2223              * Type: TEXT
   2224              */
   2225             public static final String DATA = "_data";
   2226 
   2227             /**
   2228              * The original image for the thumbnal
   2229              * <P>Type: INTEGER (ID from Video table)</P>
   2230              */
   2231             public static final String VIDEO_ID = "video_id";
   2232 
   2233             /**
   2234              * The kind of the thumbnail
   2235              * <P>Type: INTEGER (One of the values below)</P>
   2236              */
   2237             public static final String KIND = "kind";
   2238 
   2239             public static final int MINI_KIND = 1;
   2240             public static final int FULL_SCREEN_KIND = 2;
   2241             public static final int MICRO_KIND = 3;
   2242 
   2243             /**
   2244              * The width of the thumbnal
   2245              * <P>Type: INTEGER (long)</P>
   2246              */
   2247             public static final String WIDTH = "width";
   2248 
   2249             /**
   2250              * The height of the thumbnail
   2251              * <P>Type: INTEGER (long)</P>
   2252              */
   2253             public static final String HEIGHT = "height";
   2254         }
   2255     }
   2256 
   2257     /**
   2258      * Uri for querying the state of the media scanner.
   2259      */
   2260     public static Uri getMediaScannerUri() {
   2261         return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
   2262     }
   2263 
   2264     /**
   2265      * Name of current volume being scanned by the media scanner.
   2266      */
   2267     public static final String MEDIA_SCANNER_VOLUME = "volume";
   2268 
   2269     /**
   2270      * Name of the file signaling the media scanner to ignore media in the containing directory
   2271      * and its subdirectories. Developers should use this to avoid application graphics showing
   2272      * up in the Gallery and likewise prevent application sounds and music from showing up in
   2273      * the Music app.
   2274      */
   2275     public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
   2276 
   2277     /**
   2278      * Get the media provider's version.
   2279      * Applications that import data from the media provider into their own caches
   2280      * can use this to detect that the media provider changed, and reimport data
   2281      * as needed. No other assumptions should be made about the meaning of the version.
   2282      * @param context Context to use for performing the query.
   2283      * @return A version string, or null if the version could not be determined.
   2284      */
   2285     public static String getVersion(Context context) {
   2286         Cursor c = context.getContentResolver().query(
   2287                 Uri.parse(CONTENT_AUTHORITY_SLASH + "none/version"),
   2288                 null, null, null, null);
   2289         if (c != null) {
   2290             try {
   2291                 if (c.moveToFirst()) {
   2292                     return c.getString(0);
   2293                 }
   2294             } finally {
   2295                 c.close();
   2296             }
   2297         }
   2298         return null;
   2299     }
   2300 }
   2301