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