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 17 package android.provider; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.content.ContentResolver; 22 import android.content.ContentValues; 23 import android.content.ContentUris; 24 import android.database.Cursor; 25 import android.database.DatabaseUtils; 26 import android.database.sqlite.SQLiteException; 27 import android.graphics.Bitmap; 28 import android.graphics.BitmapFactory; 29 import android.graphics.Matrix; 30 import android.media.MiniThumbFile; 31 import android.media.ThumbnailUtil; 32 import android.net.Uri; 33 import android.os.Environment; 34 import android.os.ParcelFileDescriptor; 35 import android.util.Log; 36 37 import java.io.FileInputStream; 38 import java.io.FileNotFoundException; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.OutputStream; 42 import java.io.UnsupportedEncodingException; 43 import 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 */ 49 public 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 */ cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri)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 */ getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options, Uri baseUri, boolean isVideo)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 { query(ContentResolver cr, Uri uri, String[] projection)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 query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy)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 query(ContentResolver cr, Uri uri, String[] projection, String selection, String [] selectionArgs, String orderBy)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 */ getBitmap(ContentResolver cr, Uri url)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 */ insertImage(ContentResolver cr, String imagePath, String name, String description)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 StoreThumbnail( ContentResolver cr, Bitmap source, long id, float width, float height, int kind)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 */ insertImage(ContentResolver cr, Bitmap source, String title, String description)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 */ getContentUri(String volumeName)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 { query(ContentResolver cr, Uri uri, String[] projection)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 queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)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 queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)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 */ cancelThumbnailRequest(ContentResolver cr, long origId)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 */ getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)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 */ getContentUri(String volumeName)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 */ keyFor(String name)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 */ getContentUri(String volumeName)965 public static Uri getContentUri(String volumeName) { 966 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 967 "/audio/media"); 968 } 969 getContentUriForPath(String path)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 */ getContentUri(String volumeName)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 getContentUri(String volumeName, long genreId)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 */ getContentUri(String volumeName)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 { getContentUri(String volumeName, long playlistId)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 */ getContentUri(String volumeName)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 { getContentUri(String volumeName, long artistId)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 */ getContentUri(String volumeName)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 query(ContentResolver cr, Uri uri, String[] projection)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 */ getContentUri(String volumeName)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 */ cancelThumbnailRequest(ContentResolver cr, long origId)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 */ getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)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 */ getContentUri(String volumeName)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 */ getMediaScannerUri()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