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