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