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