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