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