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