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