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