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