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