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