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