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