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