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