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