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