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