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.ContentValues; 23 import android.content.ContentUris; 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 44 /** 45 * The Media provider contains meta data for all available media on both internal 46 * and external storage devices. 47 */ 48 public 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 private static final String[] EXTERNAL_PATHS; 1329 1330 static { 1331 String secondary_storage = System.getenv("SECONDARY_STORAGE"); 1332 if (secondary_storage != null) { 1333 EXTERNAL_PATHS = secondary_storage.split(":"); 1334 } else { 1335 EXTERNAL_PATHS = new String[0]; 1336 } 1337 } 1338 1339 /** 1340 * Get the content:// style URI for the audio media table on the 1341 * given volume. 1342 * 1343 * @param volumeName the name of the volume to get the URI for 1344 * @return the URI to the audio media table on the given volume 1345 */ 1346 public static Uri getContentUri(String volumeName) { 1347 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1348 "/audio/media"); 1349 } 1350 1351 public static Uri getContentUriForPath(String path) { 1352 for (String ep : EXTERNAL_PATHS) { 1353 if (path.startsWith(ep)) { 1354 return EXTERNAL_CONTENT_URI; 1355 } 1356 } 1357 1358 return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ? 1359 EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); 1360 } 1361 1362 /** 1363 * The content:// style URI for the internal storage. 1364 */ 1365 public static final Uri INTERNAL_CONTENT_URI = 1366 getContentUri("internal"); 1367 1368 /** 1369 * The content:// style URI for the "primary" external storage 1370 * volume. 1371 */ 1372 public static final Uri EXTERNAL_CONTENT_URI = 1373 getContentUri("external"); 1374 1375 /** 1376 * The MIME type for this table. 1377 */ 1378 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio"; 1379 1380 /** 1381 * The default sort order for this table 1382 */ 1383 public static final String DEFAULT_SORT_ORDER = TITLE_KEY; 1384 1385 /** 1386 * Activity Action: Start SoundRecorder application. 1387 * <p>Input: nothing. 1388 * <p>Output: An uri to the recorded sound stored in the Media Library 1389 * if the recording was successful. 1390 * May also contain the extra EXTRA_MAX_BYTES. 1391 * @see #EXTRA_MAX_BYTES 1392 */ 1393 public static final String RECORD_SOUND_ACTION = 1394 "android.provider.MediaStore.RECORD_SOUND"; 1395 1396 /** 1397 * The name of the Intent-extra used to define a maximum file size for 1398 * a recording made by the SoundRecorder application. 1399 * 1400 * @see #RECORD_SOUND_ACTION 1401 */ 1402 public static final String EXTRA_MAX_BYTES = 1403 "android.provider.MediaStore.extra.MAX_BYTES"; 1404 } 1405 1406 /** 1407 * Columns representing an audio genre 1408 */ 1409 public interface GenresColumns { 1410 /** 1411 * The name of the genre 1412 * <P>Type: TEXT</P> 1413 */ 1414 public static final String NAME = "name"; 1415 } 1416 1417 /** 1418 * Contains all genres for audio files 1419 */ 1420 public static final class Genres implements BaseColumns, GenresColumns { 1421 /** 1422 * Get the content:// style URI for the audio genres table on the 1423 * given volume. 1424 * 1425 * @param volumeName the name of the volume to get the URI for 1426 * @return the URI to the audio genres table on the given volume 1427 */ 1428 public static Uri getContentUri(String volumeName) { 1429 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1430 "/audio/genres"); 1431 } 1432 1433 /** 1434 * Get the content:// style URI for querying the genres of an audio file. 1435 * 1436 * @param volumeName the name of the volume to get the URI for 1437 * @param audioId the ID of the audio file for which to retrieve the genres 1438 * @return the URI to for querying the genres for the audio file 1439 * with the given the volume and audioID 1440 */ 1441 public static Uri getContentUriForAudioId(String volumeName, int audioId) { 1442 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1443 "/audio/media/" + audioId + "/genres"); 1444 } 1445 1446 /** 1447 * The content:// style URI for the internal storage. 1448 */ 1449 public static final Uri INTERNAL_CONTENT_URI = 1450 getContentUri("internal"); 1451 1452 /** 1453 * The content:// style URI for the "primary" external storage 1454 * volume. 1455 */ 1456 public static final Uri EXTERNAL_CONTENT_URI = 1457 getContentUri("external"); 1458 1459 /** 1460 * The MIME type for this table. 1461 */ 1462 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre"; 1463 1464 /** 1465 * The MIME type for entries in this table. 1466 */ 1467 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre"; 1468 1469 /** 1470 * The default sort order for this table 1471 */ 1472 public static final String DEFAULT_SORT_ORDER = NAME; 1473 1474 /** 1475 * Sub-directory of each genre containing all members. 1476 */ 1477 public static final class Members implements AudioColumns { 1478 1479 public static final Uri getContentUri(String volumeName, 1480 long genreId) { 1481 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 1482 + "/audio/genres/" + genreId + "/members"); 1483 } 1484 1485 /** 1486 * A subdirectory of each genre containing all member audio files. 1487 */ 1488 public static final String CONTENT_DIRECTORY = "members"; 1489 1490 /** 1491 * The default sort order for this table 1492 */ 1493 public static final String DEFAULT_SORT_ORDER = TITLE_KEY; 1494 1495 /** 1496 * The ID of the audio file 1497 * <P>Type: INTEGER (long)</P> 1498 */ 1499 public static final String AUDIO_ID = "audio_id"; 1500 1501 /** 1502 * The ID of the genre 1503 * <P>Type: INTEGER (long)</P> 1504 */ 1505 public static final String GENRE_ID = "genre_id"; 1506 } 1507 } 1508 1509 /** 1510 * Columns representing a playlist 1511 */ 1512 public interface PlaylistsColumns { 1513 /** 1514 * The name of the playlist 1515 * <P>Type: TEXT</P> 1516 */ 1517 public static final String NAME = "name"; 1518 1519 /** 1520 * The data stream for the playlist file 1521 * <P>Type: DATA STREAM</P> 1522 */ 1523 public static final String DATA = "_data"; 1524 1525 /** 1526 * The time the file was added to the media provider 1527 * Units are seconds since 1970. 1528 * <P>Type: INTEGER (long)</P> 1529 */ 1530 public static final String DATE_ADDED = "date_added"; 1531 1532 /** 1533 * The time the file was last modified 1534 * Units are seconds since 1970. 1535 * NOTE: This is for internal use by the media scanner. Do not modify this field. 1536 * <P>Type: INTEGER (long)</P> 1537 */ 1538 public static final String DATE_MODIFIED = "date_modified"; 1539 } 1540 1541 /** 1542 * Contains playlists for audio files 1543 */ 1544 public static final class Playlists implements BaseColumns, 1545 PlaylistsColumns { 1546 /** 1547 * Get the content:// style URI for the audio playlists table on the 1548 * given volume. 1549 * 1550 * @param volumeName the name of the volume to get the URI for 1551 * @return the URI to the audio playlists table on the given volume 1552 */ 1553 public static Uri getContentUri(String volumeName) { 1554 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1555 "/audio/playlists"); 1556 } 1557 1558 /** 1559 * The content:// style URI for the internal storage. 1560 */ 1561 public static final Uri INTERNAL_CONTENT_URI = 1562 getContentUri("internal"); 1563 1564 /** 1565 * The content:// style URI for the "primary" external storage 1566 * volume. 1567 */ 1568 public static final Uri EXTERNAL_CONTENT_URI = 1569 getContentUri("external"); 1570 1571 /** 1572 * The MIME type for this table. 1573 */ 1574 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist"; 1575 1576 /** 1577 * The MIME type for entries in this table. 1578 */ 1579 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist"; 1580 1581 /** 1582 * The default sort order for this table 1583 */ 1584 public static final String DEFAULT_SORT_ORDER = NAME; 1585 1586 /** 1587 * Sub-directory of each playlist containing all members. 1588 */ 1589 public static final class Members implements AudioColumns { 1590 public static final Uri getContentUri(String volumeName, 1591 long playlistId) { 1592 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 1593 + "/audio/playlists/" + playlistId + "/members"); 1594 } 1595 1596 /** 1597 * Convenience method to move a playlist item to a new location 1598 * @param res The content resolver to use 1599 * @param playlistId The numeric id of the playlist 1600 * @param from The position of the item to move 1601 * @param to The position to move the item to 1602 * @return true on success 1603 */ 1604 public static final boolean moveItem(ContentResolver res, 1605 long playlistId, int from, int to) { 1606 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", 1607 playlistId) 1608 .buildUpon() 1609 .appendEncodedPath(String.valueOf(from)) 1610 .appendQueryParameter("move", "true") 1611 .build(); 1612 ContentValues values = new ContentValues(); 1613 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to); 1614 return res.update(uri, values, null, null) != 0; 1615 } 1616 1617 /** 1618 * The ID within the playlist. 1619 */ 1620 public static final String _ID = "_id"; 1621 1622 /** 1623 * A subdirectory of each playlist containing all member audio 1624 * files. 1625 */ 1626 public static final String CONTENT_DIRECTORY = "members"; 1627 1628 /** 1629 * The ID of the audio file 1630 * <P>Type: INTEGER (long)</P> 1631 */ 1632 public static final String AUDIO_ID = "audio_id"; 1633 1634 /** 1635 * The ID of the playlist 1636 * <P>Type: INTEGER (long)</P> 1637 */ 1638 public static final String PLAYLIST_ID = "playlist_id"; 1639 1640 /** 1641 * The order of the songs in the playlist 1642 * <P>Type: INTEGER (long)></P> 1643 */ 1644 public static final String PLAY_ORDER = "play_order"; 1645 1646 /** 1647 * The default sort order for this table 1648 */ 1649 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER; 1650 } 1651 } 1652 1653 /** 1654 * Columns representing an artist 1655 */ 1656 public interface ArtistColumns { 1657 /** 1658 * The artist who created the audio file, if any 1659 * <P>Type: TEXT</P> 1660 */ 1661 public static final String ARTIST = "artist"; 1662 1663 /** 1664 * A non human readable key calculated from the ARTIST, used for 1665 * searching, sorting and grouping 1666 * <P>Type: TEXT</P> 1667 */ 1668 public static final String ARTIST_KEY = "artist_key"; 1669 1670 /** 1671 * The number of albums in the database for this artist 1672 */ 1673 public static final String NUMBER_OF_ALBUMS = "number_of_albums"; 1674 1675 /** 1676 * The number of albums in the database for this artist 1677 */ 1678 public static final String NUMBER_OF_TRACKS = "number_of_tracks"; 1679 } 1680 1681 /** 1682 * Contains artists for audio files 1683 */ 1684 public static final class Artists implements BaseColumns, ArtistColumns { 1685 /** 1686 * Get the content:// style URI for the artists table on the 1687 * given volume. 1688 * 1689 * @param volumeName the name of the volume to get the URI for 1690 * @return the URI to the audio artists table on the given volume 1691 */ 1692 public static Uri getContentUri(String volumeName) { 1693 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1694 "/audio/artists"); 1695 } 1696 1697 /** 1698 * The content:// style URI for the internal storage. 1699 */ 1700 public static final Uri INTERNAL_CONTENT_URI = 1701 getContentUri("internal"); 1702 1703 /** 1704 * The content:// style URI for the "primary" external storage 1705 * volume. 1706 */ 1707 public static final Uri EXTERNAL_CONTENT_URI = 1708 getContentUri("external"); 1709 1710 /** 1711 * The MIME type for this table. 1712 */ 1713 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists"; 1714 1715 /** 1716 * The MIME type for entries in this table. 1717 */ 1718 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist"; 1719 1720 /** 1721 * The default sort order for this table 1722 */ 1723 public static final String DEFAULT_SORT_ORDER = ARTIST_KEY; 1724 1725 /** 1726 * Sub-directory of each artist containing all albums on which 1727 * a song by the artist appears. 1728 */ 1729 public static final class Albums implements AlbumColumns { 1730 public static final Uri getContentUri(String volumeName, 1731 long artistId) { 1732 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 1733 + "/audio/artists/" + artistId + "/albums"); 1734 } 1735 } 1736 } 1737 1738 /** 1739 * Columns representing an album 1740 */ 1741 public interface AlbumColumns { 1742 1743 /** 1744 * The id for the album 1745 * <P>Type: INTEGER</P> 1746 */ 1747 public static final String ALBUM_ID = "album_id"; 1748 1749 /** 1750 * The album on which the audio file appears, if any 1751 * <P>Type: TEXT</P> 1752 */ 1753 public static final String ALBUM = "album"; 1754 1755 /** 1756 * The artist whose songs appear on this album 1757 * <P>Type: TEXT</P> 1758 */ 1759 public static final String ARTIST = "artist"; 1760 1761 /** 1762 * The number of songs on this album 1763 * <P>Type: INTEGER</P> 1764 */ 1765 public static final String NUMBER_OF_SONGS = "numsongs"; 1766 1767 /** 1768 * This column is available when getting album info via artist, 1769 * and indicates the number of songs on the album by the given 1770 * artist. 1771 * <P>Type: INTEGER</P> 1772 */ 1773 public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist"; 1774 1775 /** 1776 * The year in which the earliest songs 1777 * on this album were released. This will often 1778 * be the same as {@link #LAST_YEAR}, but for compilation albums 1779 * they might differ. 1780 * <P>Type: INTEGER</P> 1781 */ 1782 public static final String FIRST_YEAR = "minyear"; 1783 1784 /** 1785 * The year in which the latest songs 1786 * on this album were released. This will often 1787 * be the same as {@link #FIRST_YEAR}, but for compilation albums 1788 * they might differ. 1789 * <P>Type: INTEGER</P> 1790 */ 1791 public static final String LAST_YEAR = "maxyear"; 1792 1793 /** 1794 * A non human readable key calculated from the ALBUM, used for 1795 * searching, sorting and grouping 1796 * <P>Type: TEXT</P> 1797 */ 1798 public static final String ALBUM_KEY = "album_key"; 1799 1800 /** 1801 * Cached album art. 1802 * <P>Type: TEXT</P> 1803 */ 1804 public static final String ALBUM_ART = "album_art"; 1805 } 1806 1807 /** 1808 * Contains artists for audio files 1809 */ 1810 public static final class Albums implements BaseColumns, AlbumColumns { 1811 /** 1812 * Get the content:// style URI for the albums table on the 1813 * given volume. 1814 * 1815 * @param volumeName the name of the volume to get the URI for 1816 * @return the URI to the audio albums table on the given volume 1817 */ 1818 public static Uri getContentUri(String volumeName) { 1819 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1820 "/audio/albums"); 1821 } 1822 1823 /** 1824 * The content:// style URI for the internal storage. 1825 */ 1826 public static final Uri INTERNAL_CONTENT_URI = 1827 getContentUri("internal"); 1828 1829 /** 1830 * The content:// style URI for the "primary" external storage 1831 * volume. 1832 */ 1833 public static final Uri EXTERNAL_CONTENT_URI = 1834 getContentUri("external"); 1835 1836 /** 1837 * The MIME type for this table. 1838 */ 1839 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums"; 1840 1841 /** 1842 * The MIME type for entries in this table. 1843 */ 1844 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album"; 1845 1846 /** 1847 * The default sort order for this table 1848 */ 1849 public static final String DEFAULT_SORT_ORDER = ALBUM_KEY; 1850 } 1851 } 1852 1853 public static final class Video { 1854 1855 /** 1856 * The default sort order for this table. 1857 */ 1858 public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME; 1859 1860 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { 1861 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); 1862 } 1863 1864 public interface VideoColumns extends MediaColumns { 1865 1866 /** 1867 * The duration of the video file, in ms 1868 * <P>Type: INTEGER (long)</P> 1869 */ 1870 public static final String DURATION = "duration"; 1871 1872 /** 1873 * The artist who created the video file, if any 1874 * <P>Type: TEXT</P> 1875 */ 1876 public static final String ARTIST = "artist"; 1877 1878 /** 1879 * The album the video file is from, if any 1880 * <P>Type: TEXT</P> 1881 */ 1882 public static final String ALBUM = "album"; 1883 1884 /** 1885 * The resolution of the video file, formatted as "XxY" 1886 * <P>Type: TEXT</P> 1887 */ 1888 public static final String RESOLUTION = "resolution"; 1889 1890 /** 1891 * The description of the video recording 1892 * <P>Type: TEXT</P> 1893 */ 1894 public static final String DESCRIPTION = "description"; 1895 1896 /** 1897 * Whether the video should be published as public or private 1898 * <P>Type: INTEGER</P> 1899 */ 1900 public static final String IS_PRIVATE = "isprivate"; 1901 1902 /** 1903 * The user-added tags associated with a video 1904 * <P>Type: TEXT</P> 1905 */ 1906 public static final String TAGS = "tags"; 1907 1908 /** 1909 * The YouTube category of the video 1910 * <P>Type: TEXT</P> 1911 */ 1912 public static final String CATEGORY = "category"; 1913 1914 /** 1915 * The language of the video 1916 * <P>Type: TEXT</P> 1917 */ 1918 public static final String LANGUAGE = "language"; 1919 1920 /** 1921 * The latitude where the video was captured. 1922 * <P>Type: DOUBLE</P> 1923 */ 1924 public static final String LATITUDE = "latitude"; 1925 1926 /** 1927 * The longitude where the video was captured. 1928 * <P>Type: DOUBLE</P> 1929 */ 1930 public static final String LONGITUDE = "longitude"; 1931 1932 /** 1933 * The date & time that the video was taken in units 1934 * of milliseconds since jan 1, 1970. 1935 * <P>Type: INTEGER</P> 1936 */ 1937 public static final String DATE_TAKEN = "datetaken"; 1938 1939 /** 1940 * The mini thumb id. 1941 * <P>Type: INTEGER</P> 1942 */ 1943 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; 1944 1945 /** 1946 * The bucket id of the video. This is a read-only property that 1947 * is automatically computed from the DATA column. 1948 * <P>Type: TEXT</P> 1949 */ 1950 public static final String BUCKET_ID = "bucket_id"; 1951 1952 /** 1953 * The bucket display name of the video. This is a read-only property that 1954 * is automatically computed from the DATA column. 1955 * <P>Type: TEXT</P> 1956 */ 1957 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; 1958 1959 /** 1960 * The bookmark for the video. Time in ms. Represents the location in the video that the 1961 * video should start playing at the next time it is opened. If the value is null or 1962 * out of the range 0..DURATION-1 then the video should start playing from the 1963 * beginning. 1964 * <P>Type: INTEGER</P> 1965 */ 1966 public static final String BOOKMARK = "bookmark"; 1967 } 1968 1969 public static final class Media implements VideoColumns { 1970 /** 1971 * Get the content:// style URI for the video media table on the 1972 * given volume. 1973 * 1974 * @param volumeName the name of the volume to get the URI for 1975 * @return the URI to the video media table on the given volume 1976 */ 1977 public static Uri getContentUri(String volumeName) { 1978 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1979 "/video/media"); 1980 } 1981 1982 /** 1983 * The content:// style URI for the internal storage. 1984 */ 1985 public static final Uri INTERNAL_CONTENT_URI = 1986 getContentUri("internal"); 1987 1988 /** 1989 * The content:// style URI for the "primary" external storage 1990 * volume. 1991 */ 1992 public static final Uri EXTERNAL_CONTENT_URI = 1993 getContentUri("external"); 1994 1995 /** 1996 * The MIME type for this table. 1997 */ 1998 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video"; 1999 2000 /** 2001 * The default sort order for this table 2002 */ 2003 public static final String DEFAULT_SORT_ORDER = TITLE; 2004 } 2005 2006 /** 2007 * This class allows developers to query and get two kinds of thumbnails: 2008 * MINI_KIND: 512 x 384 thumbnail 2009 * MICRO_KIND: 96 x 96 thumbnail 2010 * 2011 */ 2012 public static class Thumbnails implements BaseColumns { 2013 /** 2014 * This method cancels the thumbnail request so clients waiting for getThumbnail will be 2015 * interrupted and return immediately. Only the original process which made the getThumbnail 2016 * requests can cancel their own requests. 2017 * 2018 * @param cr ContentResolver 2019 * @param origId original video id 2020 */ 2021 public static void cancelThumbnailRequest(ContentResolver cr, long origId) { 2022 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, 2023 InternalThumbnails.DEFAULT_GROUP_ID); 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 kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND. 2033 * @param options this is only used for MINI_KIND when decoding the Bitmap 2034 * @return A Bitmap instance. It could be null if the original image 2035 * associated with origId doesn't exist or memory is not enough. 2036 */ 2037 public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind, 2038 BitmapFactory.Options options) { 2039 return InternalThumbnails.getThumbnail(cr, origId, 2040 InternalThumbnails.DEFAULT_GROUP_ID, kind, options, 2041 EXTERNAL_CONTENT_URI, true); 2042 } 2043 2044 /** 2045 * This method checks if the thumbnails of the specified image (origId) has been created. 2046 * It will be blocked until the thumbnails are generated. 2047 * 2048 * @param cr ContentResolver used to dispatch queries to MediaProvider. 2049 * @param origId Original image id associated with thumbnail of interest. 2050 * @param groupId the id of group to which this request belongs 2051 * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND 2052 * @param options this is only used for MINI_KIND when decoding the Bitmap 2053 * @return A Bitmap instance. It could be null if the original image associated with 2054 * origId doesn't exist or memory is not enough. 2055 */ 2056 public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, 2057 int kind, BitmapFactory.Options options) { 2058 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options, 2059 EXTERNAL_CONTENT_URI, true); 2060 } 2061 2062 /** 2063 * This method cancels the thumbnail request so clients waiting for getThumbnail will be 2064 * interrupted and return immediately. Only the original process which made the getThumbnail 2065 * requests can cancel their own requests. 2066 * 2067 * @param cr ContentResolver 2068 * @param origId original video id 2069 * @param groupId the same groupId used in getThumbnail. 2070 */ 2071 public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) { 2072 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId); 2073 } 2074 2075 /** 2076 * Get the content:// style URI for the image media table on the 2077 * given volume. 2078 * 2079 * @param volumeName the name of the volume to get the URI for 2080 * @return the URI to the image media table on the given volume 2081 */ 2082 public static Uri getContentUri(String volumeName) { 2083 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 2084 "/video/thumbnails"); 2085 } 2086 2087 /** 2088 * The content:// style URI for the internal storage. 2089 */ 2090 public static final Uri INTERNAL_CONTENT_URI = 2091 getContentUri("internal"); 2092 2093 /** 2094 * The content:// style URI for the "primary" external storage 2095 * volume. 2096 */ 2097 public static final Uri EXTERNAL_CONTENT_URI = 2098 getContentUri("external"); 2099 2100 /** 2101 * The default sort order for this table 2102 */ 2103 public static final String DEFAULT_SORT_ORDER = "video_id ASC"; 2104 2105 /** 2106 * The data stream for the thumbnail 2107 * <P>Type: DATA STREAM</P> 2108 */ 2109 public static final String DATA = "_data"; 2110 2111 /** 2112 * The original image for the thumbnal 2113 * <P>Type: INTEGER (ID from Video table)</P> 2114 */ 2115 public static final String VIDEO_ID = "video_id"; 2116 2117 /** 2118 * The kind of the thumbnail 2119 * <P>Type: INTEGER (One of the values below)</P> 2120 */ 2121 public static final String KIND = "kind"; 2122 2123 public static final int MINI_KIND = 1; 2124 public static final int FULL_SCREEN_KIND = 2; 2125 public static final int MICRO_KIND = 3; 2126 2127 /** 2128 * The width of the thumbnal 2129 * <P>Type: INTEGER (long)</P> 2130 */ 2131 public static final String WIDTH = "width"; 2132 2133 /** 2134 * The height of the thumbnail 2135 * <P>Type: INTEGER (long)</P> 2136 */ 2137 public static final String HEIGHT = "height"; 2138 } 2139 } 2140 2141 /** 2142 * Uri for querying the state of the media scanner. 2143 */ 2144 public static Uri getMediaScannerUri() { 2145 return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner"); 2146 } 2147 2148 /** 2149 * Name of current volume being scanned by the media scanner. 2150 */ 2151 public static final String MEDIA_SCANNER_VOLUME = "volume"; 2152 2153 /** 2154 * Name of the file signaling the media scanner to ignore media in the containing directory 2155 * and its subdirectories. Developers should use this to avoid application graphics showing 2156 * up in the Gallery and likewise prevent application sounds and music from showing up in 2157 * the Music app. 2158 */ 2159 public static final String MEDIA_IGNORE_FILENAME = ".nomedia"; 2160 2161 /** 2162 * Get the media provider's version. 2163 * Applications that import data from the media provider into their own caches 2164 * can use this to detect that the media provider changed, and reimport data 2165 * as needed. No other assumptions should be made about the meaning of the version. 2166 * @param context Context to use for performing the query. 2167 * @return A version string, or null if the version could not be determined. 2168 */ 2169 public static String getVersion(Context context) { 2170 Cursor c = context.getContentResolver().query( 2171 Uri.parse(CONTENT_AUTHORITY_SLASH + "none/version"), 2172 null, null, null, null); 2173 if (c != null) { 2174 try { 2175 if (c.moveToFirst()) { 2176 return c.getString(0); 2177 } 2178 } finally { 2179 c.close(); 2180 } 2181 } 2182 return null; 2183 } 2184 2185 } 2186