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