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