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