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