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