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