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