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