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