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