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