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