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