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