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