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