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