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