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