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