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