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 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 */ getContentUri(String volumeName)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 */ getContentUri(String volumeName, long rowId)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 */ getMtpObjectsUri(String volumeName)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 */ getMtpObjectsUri(String volumeName, long fileId)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 */ getMtpReferencesUri(String volumeName, long fileId)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 getMiniThumbFromFile( Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options)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 */ cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri, long groupId)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 */ getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options, Uri baseUri, boolean isVideo)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 { query(ContentResolver cr, Uri uri, String[] projection)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 query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy)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 query(ContentResolver cr, Uri uri, String[] projection, String selection, String [] selectionArgs, String orderBy)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 */ getBitmap(ContentResolver cr, Uri url)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 */ insertImage(ContentResolver cr, String imagePath, String name, String description)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 StoreThumbnail( ContentResolver cr, Bitmap source, long id, float width, float height, int kind)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 */ insertImage(ContentResolver cr, Bitmap source, String title, String description)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 */ getContentUri(String volumeName)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 { query(ContentResolver cr, Uri uri, String[] projection)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 queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)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 queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)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 */ cancelThumbnailRequest(ContentResolver cr, long origId)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 */ getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)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 */ cancelThumbnailRequest(ContentResolver cr, long origId, long groupId)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 */ getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options)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 */ getContentUri(String volumeName)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 */ keyFor(String name)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 */ getContentUri(String volumeName)1420 public static Uri getContentUri(String volumeName) { 1421 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1422 "/audio/media"); 1423 } 1424 getContentUriForPath(String path)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 */ getContentUri(String volumeName)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 */ getContentUriForAudioId(String volumeName, int audioId)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 getContentUri(String volumeName, long genreId)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 */ getContentUri(String volumeName)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 { getContentUri(String volumeName, long playlistId)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 */ moveItem(ContentResolver res, long playlistId, int from, int to)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 */ getContentUri(String volumeName)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 { getContentUri(String volumeName, long artistId)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 */ getContentUri(String volumeName)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. Radio()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 query(ContentResolver cr, Uri uri, String[] projection)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 */ getContentUri(String volumeName)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 */ cancelThumbnailRequest(ContentResolver cr, long origId)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 */ getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options)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 */ getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options)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 */ cancelThumbnailRequest(ContentResolver cr, long origId, long groupId)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 */ getContentUri(String volumeName)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 */ getMediaScannerUri()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 */ getVersion(Context context)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