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