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