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