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