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