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