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