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