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