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