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