MediaStore.java revision eb0098d8e89033b78017a9e0068e3ac7ca51c112
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.graphics.Bitmap; 27import android.graphics.BitmapFactory; 28import android.graphics.Matrix; 29import android.net.Uri; 30import android.os.Environment; 31import android.util.Log; 32 33import java.io.FileInputStream; 34import java.io.FileNotFoundException; 35import java.io.IOException; 36import java.io.InputStream; 37import java.io.OutputStream; 38import java.io.UnsupportedEncodingException; 39import java.text.Collator; 40 41/** 42 * The Media provider contains meta data for all available media on both internal 43 * and external storage devices. 44 */ 45public final class MediaStore 46{ 47 private final static String TAG = "MediaStore"; 48 49 public static final String AUTHORITY = "media"; 50 51 private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/"; 52 53 /** 54 * Activity Action: Perform a search for media. 55 * Contains at least the {@link android.app.SearchManager#QUERY} extra. 56 * May also contain any combination of the following extras: 57 * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS 58 * 59 * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST 60 * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM 61 * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE 62 * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS 63 */ 64 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 65 public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH"; 66 67 /** 68 * The name of the Intent-extra used to define the artist 69 */ 70 public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist"; 71 /** 72 * The name of the Intent-extra used to define the album 73 */ 74 public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album"; 75 /** 76 * The name of the Intent-extra used to define the song title 77 */ 78 public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title"; 79 /** 80 * The name of the Intent-extra used to define the search focus. The search focus 81 * indicates whether the search should be for things related to the artist, album 82 * or song that is identified by the other extras. 83 */ 84 public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus"; 85 86 /** 87 * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView. 88 * This is an int property that overrides the activity's requestedOrientation. 89 * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 90 */ 91 public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation"; 92 93 /** 94 * The name of an Intent-extra used to control the UI of a ViewImage. 95 * This is a boolean property that overrides the activity's default fullscreen state. 96 * @hide 97 */ 98 public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen"; 99 100 /** 101 * The name of an Intent-extra used to control the UI of a ViewImage. 102 * This is a boolean property that specifies whether or not to show action icons. 103 * @hide 104 */ 105 public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons"; 106 107 /** 108 * The name of the Intent-extra used to control the onCompletion behavior of a MovieView. 109 * This is a boolean property that specifies whether or not to finish the MovieView activity 110 * when the movie completes playing. The default value is true, which means to automatically 111 * exit the movie player activity when the movie completes playing. 112 */ 113 public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion"; 114 115 /** 116 * The name of the Intent action used to launch a camera in still image mode. 117 */ 118 public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; 119 120 /** 121 * The name of the Intent action used to launch a camera in video mode. 122 */ 123 public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA"; 124 125 /** 126 * Standard Intent action that can be sent to have the camera application 127 * capture an image and return it. 128 * <p> 129 * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written. 130 * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap 131 * object in the extra field. This is useful for applications that only need a small image. 132 * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri 133 * value of EXTRA_OUTPUT. 134 * @see #EXTRA_OUTPUT 135 * @see #EXTRA_VIDEO_QUALITY 136 */ 137 public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; 138 139 /** 140 * Standard Intent action that can be sent to have the camera application 141 * capture an video and return it. 142 * <p> 143 * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality. 144 * <p> 145 * The caller may pass in an extra EXTRA_OUTPUT to control 146 * where the video is written. If EXTRA_OUTPUT is not present the video will be 147 * written to the standard location for videos, and the Uri of that location will be 148 * returned in the data field of the Uri. 149 * @see #EXTRA_OUTPUT 150 */ 151 public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; 152 153 /** 154 * The name of the Intent-extra used to control the quality of a recorded video. This is an 155 * integer property. Currently value 0 means low quality, suitable for MMS messages, and 156 * value 1 means high quality. In the future other quality levels may be added. 157 */ 158 public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; 159 160 /** 161 * Specify the maximum allowed size. 162 * @hide 163 */ 164 public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit"; 165 166 /** 167 * Specify the maximum allowed recording duration in seconds. 168 * @hide 169 */ 170 public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; 171 172 /** 173 * The name of the Intent-extra used to indicate a content resolver Uri to be used to 174 * store the requested image or video. 175 */ 176 public final static String EXTRA_OUTPUT = "output"; 177 178 /** 179 * Common fields for most MediaProvider tables 180 */ 181 182 public interface MediaColumns extends BaseColumns { 183 /** 184 * The data stream for the file 185 * <P>Type: DATA STREAM</P> 186 */ 187 public static final String DATA = "_data"; 188 189 /** 190 * The size of the file in bytes 191 * <P>Type: INTEGER (long)</P> 192 */ 193 public static final String SIZE = "_size"; 194 195 /** 196 * The display name of the file 197 * <P>Type: TEXT</P> 198 */ 199 public static final String DISPLAY_NAME = "_display_name"; 200 201 /** 202 * The title of the content 203 * <P>Type: TEXT</P> 204 */ 205 public static final String TITLE = "title"; 206 207 /** 208 * The time the file was added to the media provider 209 * Units are seconds since 1970. 210 * <P>Type: INTEGER (long)</P> 211 */ 212 public static final String DATE_ADDED = "date_added"; 213 214 /** 215 * The time the file was last modified 216 * Units are seconds since 1970. 217 * NOTE: This is for internal use by the media scanner. Do not modify this field. 218 * <P>Type: INTEGER (long)</P> 219 */ 220 public static final String DATE_MODIFIED = "date_modified"; 221 222 /** 223 * The MIME type of the file 224 * <P>Type: TEXT</P> 225 */ 226 public static final String MIME_TYPE = "mime_type"; 227 } 228 229 /** 230 * Contains meta data for all available images. 231 */ 232 public static final class Images 233 { 234 public interface ImageColumns extends MediaColumns { 235 /** 236 * The description of the image 237 * <P>Type: TEXT</P> 238 */ 239 public static final String DESCRIPTION = "description"; 240 241 /** 242 * The picasa id of the image 243 * <P>Type: TEXT</P> 244 */ 245 public static final String PICASA_ID = "picasa_id"; 246 247 /** 248 * Whether the video should be published as public or private 249 * <P>Type: INTEGER</P> 250 */ 251 public static final String IS_PRIVATE = "isprivate"; 252 253 /** 254 * The latitude where the image was captured. 255 * <P>Type: DOUBLE</P> 256 */ 257 public static final String LATITUDE = "latitude"; 258 259 /** 260 * The longitude where the image was captured. 261 * <P>Type: DOUBLE</P> 262 */ 263 public static final String LONGITUDE = "longitude"; 264 265 /** 266 * The date & time that the image was taken in units 267 * of milliseconds since jan 1, 1970. 268 * <P>Type: INTEGER</P> 269 */ 270 public static final String DATE_TAKEN = "datetaken"; 271 272 /** 273 * The orientation for the image expressed as degrees. 274 * Only degrees 0, 90, 180, 270 will work. 275 * <P>Type: INTEGER</P> 276 */ 277 public static final String ORIENTATION = "orientation"; 278 279 /** 280 * The mini thumb id. 281 * <P>Type: INTEGER</P> 282 */ 283 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; 284 285 /** 286 * The bucket id of the image. This is a read-only property that 287 * is automatically computed from the DATA column. 288 * <P>Type: TEXT</P> 289 */ 290 public static final String BUCKET_ID = "bucket_id"; 291 292 /** 293 * The bucket display name of the image. This is a read-only property that 294 * is automatically computed from the DATA column. 295 * <P>Type: TEXT</P> 296 */ 297 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; 298 } 299 300 public static final class Media implements ImageColumns { 301 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) 302 { 303 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); 304 } 305 306 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection, 307 String where, String orderBy) 308 { 309 return cr.query(uri, projection, where, 310 null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); 311 } 312 313 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection, 314 String selection, String [] selectionArgs, String orderBy) 315 { 316 return cr.query(uri, projection, selection, 317 selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); 318 } 319 320 /** 321 * Retrieves an image for the given url as a {@link Bitmap}. 322 * 323 * @param cr The content resolver to use 324 * @param url The url of the image 325 * @throws FileNotFoundException 326 * @throws IOException 327 */ 328 public static final Bitmap getBitmap(ContentResolver cr, Uri url) 329 throws FileNotFoundException, IOException 330 { 331 InputStream input = cr.openInputStream(url); 332 Bitmap bitmap = BitmapFactory.decodeStream(input); 333 input.close(); 334 return bitmap; 335 } 336 337 /** 338 * Insert an image and create a thumbnail for it. 339 * 340 * @param cr The content resolver to use 341 * @param imagePath The path to the image to insert 342 * @param name The name of the image 343 * @param description The description of the image 344 * @return The URL to the newly created image 345 * @throws FileNotFoundException 346 */ 347 public static final String insertImage(ContentResolver cr, String imagePath, String name, 348 String description) throws FileNotFoundException 349 { 350 // Check if file exists with a FileInputStream 351 FileInputStream stream = new FileInputStream(imagePath); 352 try { 353 Bitmap bm = BitmapFactory.decodeFile(imagePath); 354 String ret = insertImage(cr, bm, name, description); 355 bm.recycle(); 356 return ret; 357 } finally { 358 try { 359 stream.close(); 360 } catch (IOException e) { 361 } 362 } 363 } 364 365 private static final Bitmap StoreThumbnail( 366 ContentResolver cr, 367 Bitmap source, 368 long id, 369 float width, float height, 370 int kind) { 371 // create the matrix to scale it 372 Matrix matrix = new Matrix(); 373 374 float scaleX = width / source.getWidth(); 375 float scaleY = height / source.getHeight(); 376 377 matrix.setScale(scaleX, scaleY); 378 379 Bitmap thumb = Bitmap.createBitmap(source, 0, 0, 380 source.getWidth(), 381 source.getHeight(), matrix, 382 true); 383 384 ContentValues values = new ContentValues(4); 385 values.put(Images.Thumbnails.KIND, kind); 386 values.put(Images.Thumbnails.IMAGE_ID, (int)id); 387 values.put(Images.Thumbnails.HEIGHT, thumb.getHeight()); 388 values.put(Images.Thumbnails.WIDTH, thumb.getWidth()); 389 390 Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values); 391 392 try { 393 OutputStream thumbOut = cr.openOutputStream(url); 394 395 thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut); 396 thumbOut.close(); 397 return thumb; 398 } 399 catch (FileNotFoundException ex) { 400 return null; 401 } 402 catch (IOException ex) { 403 return null; 404 } 405 } 406 407 /** 408 * Insert an image and create a thumbnail for it. 409 * 410 * @param cr The content resolver to use 411 * @param source The stream to use for the image 412 * @param title The name of the image 413 * @param description The description of the image 414 * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored 415 * for any reason. 416 */ 417 public static final String insertImage(ContentResolver cr, Bitmap source, 418 String title, String description) 419 { 420 ContentValues values = new ContentValues(); 421 values.put(Images.Media.TITLE, title); 422 values.put(Images.Media.DESCRIPTION, description); 423 values.put(Images.Media.MIME_TYPE, "image/jpeg"); 424 425 Uri url = null; 426 String stringUrl = null; /* value to be returned */ 427 428 try 429 { 430 url = cr.insert(EXTERNAL_CONTENT_URI, values); 431 432 if (source != null) { 433 OutputStream imageOut = cr.openOutputStream(url); 434 try { 435 source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut); 436 } finally { 437 imageOut.close(); 438 } 439 440 long id = ContentUris.parseId(url); 441 Bitmap miniThumb = StoreThumbnail(cr, source, id, 320F, 240F, Images.Thumbnails.MINI_KIND); 442 Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND); 443 } else { 444 Log.e(TAG, "Failed to create thumbnail, removing original"); 445 cr.delete(url, null, null); 446 url = null; 447 } 448 } catch (Exception e) { 449 Log.e(TAG, "Failed to insert image", e); 450 if (url != null) { 451 cr.delete(url, null, null); 452 url = null; 453 } 454 } 455 456 if (url != null) { 457 stringUrl = url.toString(); 458 } 459 460 return stringUrl; 461 } 462 463 /** 464 * Get the content:// style URI for the image media table on the 465 * given volume. 466 * 467 * @param volumeName the name of the volume to get the URI for 468 * @return the URI to the image media table on the given volume 469 */ 470 public static Uri getContentUri(String volumeName) { 471 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 472 "/images/media"); 473 } 474 475 /** 476 * The content:// style URI for the internal storage. 477 */ 478 public static final Uri INTERNAL_CONTENT_URI = 479 getContentUri("internal"); 480 481 /** 482 * The content:// style URI for the "primary" external storage 483 * volume. 484 */ 485 public static final Uri EXTERNAL_CONTENT_URI = 486 getContentUri("external"); 487 488 /** 489 * The MIME type of of this directory of 490 * images. Note that each entry in this directory will have a standard 491 * image MIME type as appropriate -- for example, image/jpeg. 492 */ 493 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image"; 494 495 /** 496 * The default sort order for this table 497 */ 498 public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME; 499 } 500 501 public static class Thumbnails implements BaseColumns 502 { 503 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) 504 { 505 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); 506 } 507 508 public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection) 509 { 510 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER); 511 } 512 513 public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection) 514 { 515 return cr.query(EXTERNAL_CONTENT_URI, projection, 516 IMAGE_ID + " = " + origId + " AND " + KIND + " = " + 517 kind, null, null); 518 } 519 520 /** 521 * Get the content:// style URI for the image media table on the 522 * given volume. 523 * 524 * @param volumeName the name of the volume to get the URI for 525 * @return the URI to the image media table on the given volume 526 */ 527 public static Uri getContentUri(String volumeName) { 528 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 529 "/images/thumbnails"); 530 } 531 532 /** 533 * The content:// style URI for the internal storage. 534 */ 535 public static final Uri INTERNAL_CONTENT_URI = 536 getContentUri("internal"); 537 538 /** 539 * The content:// style URI for the "primary" external storage 540 * volume. 541 */ 542 public static final Uri EXTERNAL_CONTENT_URI = 543 getContentUri("external"); 544 545 /** 546 * The default sort order for this table 547 */ 548 public static final String DEFAULT_SORT_ORDER = "image_id ASC"; 549 550 /** 551 * The data stream for the thumbnail 552 * <P>Type: DATA STREAM</P> 553 */ 554 public static final String DATA = "_data"; 555 556 /** 557 * The original image for the thumbnal 558 * <P>Type: INTEGER (ID from Images table)</P> 559 */ 560 public static final String IMAGE_ID = "image_id"; 561 562 /** 563 * The kind of the thumbnail 564 * <P>Type: INTEGER (One of the values below)</P> 565 */ 566 public static final String KIND = "kind"; 567 568 public static final int MINI_KIND = 1; 569 public static final int FULL_SCREEN_KIND = 2; 570 public static final int MICRO_KIND = 3; 571 572 /** 573 * The width of the thumbnal 574 * <P>Type: INTEGER (long)</P> 575 */ 576 public static final String WIDTH = "width"; 577 578 /** 579 * The height of the thumbnail 580 * <P>Type: INTEGER (long)</P> 581 */ 582 public static final String HEIGHT = "height"; 583 } 584 } 585 586 /** 587 * Container for all audio content. 588 */ 589 public static final class Audio { 590 /** 591 * Columns for audio file that show up in multiple tables. 592 */ 593 public interface AudioColumns extends MediaColumns { 594 595 /** 596 * A non human readable key calculated from the TITLE, used for 597 * searching, sorting and grouping 598 * <P>Type: TEXT</P> 599 */ 600 public static final String TITLE_KEY = "title_key"; 601 602 /** 603 * The duration of the audio file, in ms 604 * <P>Type: INTEGER (long)</P> 605 */ 606 public static final String DURATION = "duration"; 607 608 /** 609 * The position, in ms, playback was at when playback for this file 610 * was last stopped. 611 * <P>Type: INTEGER (long)</P> 612 * @hide 613 */ 614 public static final String BOOKMARK = "bookmark"; 615 616 /** 617 * The id of the artist who created the audio file, if any 618 * <P>Type: INTEGER (long)</P> 619 */ 620 public static final String ARTIST_ID = "artist_id"; 621 622 /** 623 * The artist who created the audio file, if any 624 * <P>Type: TEXT</P> 625 */ 626 public static final String ARTIST = "artist"; 627 628 /** 629 * A non human readable key calculated from the ARTIST, used for 630 * searching, sorting and grouping 631 * <P>Type: TEXT</P> 632 */ 633 public static final String ARTIST_KEY = "artist_key"; 634 635 /** 636 * The composer of the audio file, if any 637 * <P>Type: TEXT</P> 638 */ 639 public static final String COMPOSER = "composer"; 640 641 /** 642 * The id of the album the audio file is from, if any 643 * <P>Type: INTEGER (long)</P> 644 */ 645 public static final String ALBUM_ID = "album_id"; 646 647 /** 648 * The album the audio file is from, if any 649 * <P>Type: TEXT</P> 650 */ 651 public static final String ALBUM = "album"; 652 653 /** 654 * A non human readable key calculated from the ALBUM, used for 655 * searching, sorting and grouping 656 * <P>Type: TEXT</P> 657 */ 658 public static final String ALBUM_KEY = "album_key"; 659 660 /** 661 * A URI to the album art, if any 662 * <P>Type: TEXT</P> 663 */ 664 public static final String ALBUM_ART = "album_art"; 665 666 /** 667 * The track number of this song on the album, if any. 668 * This number encodes both the track number and the 669 * disc number. For multi-disc sets, this number will 670 * be 1xxx for tracks on the first disc, 2xxx for tracks 671 * on the second disc, etc. 672 * <P>Type: INTEGER</P> 673 */ 674 public static final String TRACK = "track"; 675 676 /** 677 * The year the audio file was recorded, if any 678 * <P>Type: INTEGER</P> 679 */ 680 public static final String YEAR = "year"; 681 682 /** 683 * Non-zero if the audio file is music 684 * <P>Type: INTEGER (boolean)</P> 685 */ 686 public static final String IS_MUSIC = "is_music"; 687 688 /** 689 * Non-zero if the audio file is a podcast 690 * <P>Type: INTEGER (boolean)</P> 691 * @hide 692 */ 693 public static final String IS_PODCAST = "is_podcast"; 694 695 /** 696 * Non-zero id the audio file may be a ringtone 697 * <P>Type: INTEGER (boolean)</P> 698 */ 699 public static final String IS_RINGTONE = "is_ringtone"; 700 701 /** 702 * Non-zero id the audio file may be an alarm 703 * <P>Type: INTEGER (boolean)</P> 704 */ 705 public static final String IS_ALARM = "is_alarm"; 706 707 /** 708 * Non-zero id the audio file may be a notification sound 709 * <P>Type: INTEGER (boolean)</P> 710 */ 711 public static final String IS_NOTIFICATION = "is_notification"; 712 } 713 714 /** 715 * Converts a name to a "key" that can be used for grouping, sorting 716 * and searching. 717 * The rules that govern this conversion are: 718 * - remove 'special' characters like ()[]'!?., 719 * - remove leading/trailing spaces 720 * - convert everything to lowercase 721 * - remove leading "the ", "an " and "a " 722 * - remove trailing ", the|an|a" 723 * - remove accents. This step leaves us with CollationKey data, 724 * which is not human readable 725 * 726 * @param name The artist or album name to convert 727 * @return The "key" for the given name. 728 */ 729 public static String keyFor(String name) { 730 if (name != null) { 731 boolean sortfirst = false; 732 if (name.equals(android.media.MediaFile.UNKNOWN_STRING)) { 733 return "\001"; 734 } 735 // Check if the first character is \001. We use this to 736 // force sorting of certain special files, like the silent ringtone. 737 if (name.startsWith("\001")) { 738 sortfirst = true; 739 } 740 name = name.trim().toLowerCase(); 741 if (name.startsWith("the ")) { 742 name = name.substring(4); 743 } 744 if (name.startsWith("an ")) { 745 name = name.substring(3); 746 } 747 if (name.startsWith("a ")) { 748 name = name.substring(2); 749 } 750 if (name.endsWith(", the") || name.endsWith(",the") || 751 name.endsWith(", an") || name.endsWith(",an") || 752 name.endsWith(", a") || name.endsWith(",a")) { 753 name = name.substring(0, name.lastIndexOf(',')); 754 } 755 name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim(); 756 if (name.length() > 0) { 757 // Insert a separator between the characters to avoid 758 // matches on a partial character. If we ever change 759 // to start-of-word-only matches, this can be removed. 760 StringBuilder b = new StringBuilder(); 761 b.append('.'); 762 int nl = name.length(); 763 for (int i = 0; i < nl; i++) { 764 b.append(name.charAt(i)); 765 b.append('.'); 766 } 767 name = b.toString(); 768 String key = DatabaseUtils.getCollationKey(name); 769 if (sortfirst) { 770 key = "\001" + key; 771 } 772 return key; 773 } else { 774 return ""; 775 } 776 } 777 return null; 778 } 779 780 public static final class Media implements AudioColumns { 781 /** 782 * Get the content:// style URI for the audio media table on the 783 * given volume. 784 * 785 * @param volumeName the name of the volume to get the URI for 786 * @return the URI to the audio media table on the given volume 787 */ 788 public static Uri getContentUri(String volumeName) { 789 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 790 "/audio/media"); 791 } 792 793 public static Uri getContentUriForPath(String path) { 794 return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ? 795 EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); 796 } 797 798 /** 799 * The content:// style URI for the internal storage. 800 */ 801 public static final Uri INTERNAL_CONTENT_URI = 802 getContentUri("internal"); 803 804 /** 805 * The content:// style URI for the "primary" external storage 806 * volume. 807 */ 808 public static final Uri EXTERNAL_CONTENT_URI = 809 getContentUri("external"); 810 811 /** 812 * The MIME type for this table. 813 */ 814 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio"; 815 816 /** 817 * The default sort order for this table 818 */ 819 public static final String DEFAULT_SORT_ORDER = TITLE_KEY; 820 821 /** 822 * Activity Action: Start SoundRecorder application. 823 * <p>Input: nothing. 824 * <p>Output: An uri to the recorded sound stored in the Media Library 825 * if the recording was successful. 826 * May also contain the extra EXTRA_MAX_BYTES. 827 * @see #EXTRA_MAX_BYTES 828 */ 829 public static final String RECORD_SOUND_ACTION = 830 "android.provider.MediaStore.RECORD_SOUND"; 831 832 /** 833 * The name of the Intent-extra used to define a maximum file size for 834 * a recording made by the SoundRecorder application. 835 * 836 * @see #RECORD_SOUND_ACTION 837 */ 838 public static final String EXTRA_MAX_BYTES = 839 "android.provider.MediaStore.extra.MAX_BYTES"; 840 } 841 842 /** 843 * Columns representing an audio genre 844 */ 845 public interface GenresColumns { 846 /** 847 * The name of the genre 848 * <P>Type: TEXT</P> 849 */ 850 public static final String NAME = "name"; 851 } 852 853 /** 854 * Contains all genres for audio files 855 */ 856 public static final class Genres implements BaseColumns, GenresColumns { 857 /** 858 * Get the content:// style URI for the audio genres table on the 859 * given volume. 860 * 861 * @param volumeName the name of the volume to get the URI for 862 * @return the URI to the audio genres table on the given volume 863 */ 864 public static Uri getContentUri(String volumeName) { 865 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 866 "/audio/genres"); 867 } 868 869 /** 870 * The content:// style URI for the internal storage. 871 */ 872 public static final Uri INTERNAL_CONTENT_URI = 873 getContentUri("internal"); 874 875 /** 876 * The content:// style URI for the "primary" external storage 877 * volume. 878 */ 879 public static final Uri EXTERNAL_CONTENT_URI = 880 getContentUri("external"); 881 882 /** 883 * The MIME type for this table. 884 */ 885 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre"; 886 887 /** 888 * The MIME type for entries in this table. 889 */ 890 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre"; 891 892 /** 893 * The default sort order for this table 894 */ 895 public static final String DEFAULT_SORT_ORDER = NAME; 896 897 /** 898 * Sub-directory of each genre containing all members. 899 */ 900 public static final class Members implements AudioColumns { 901 902 public static final Uri getContentUri(String volumeName, 903 long genreId) { 904 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 905 + "/audio/genres/" + genreId + "/members"); 906 } 907 908 /** 909 * A subdirectory of each genre containing all member audio files. 910 */ 911 public static final String CONTENT_DIRECTORY = "members"; 912 913 /** 914 * The default sort order for this table 915 */ 916 public static final String DEFAULT_SORT_ORDER = TITLE_KEY; 917 918 /** 919 * The ID of the audio file 920 * <P>Type: INTEGER (long)</P> 921 */ 922 public static final String AUDIO_ID = "audio_id"; 923 924 /** 925 * The ID of the genre 926 * <P>Type: INTEGER (long)</P> 927 */ 928 public static final String GENRE_ID = "genre_id"; 929 } 930 } 931 932 /** 933 * Columns representing a playlist 934 */ 935 public interface PlaylistsColumns { 936 /** 937 * The name of the playlist 938 * <P>Type: TEXT</P> 939 */ 940 public static final String NAME = "name"; 941 942 /** 943 * The data stream for the playlist file 944 * <P>Type: DATA STREAM</P> 945 */ 946 public static final String DATA = "_data"; 947 948 /** 949 * The time the file was added to the media provider 950 * Units are seconds since 1970. 951 * <P>Type: INTEGER (long)</P> 952 */ 953 public static final String DATE_ADDED = "date_added"; 954 955 /** 956 * The time the file was last modified 957 * Units are seconds since 1970. 958 * NOTE: This is for internal use by the media scanner. Do not modify this field. 959 * <P>Type: INTEGER (long)</P> 960 */ 961 public static final String DATE_MODIFIED = "date_modified"; 962 } 963 964 /** 965 * Contains playlists for audio files 966 */ 967 public static final class Playlists implements BaseColumns, 968 PlaylistsColumns { 969 /** 970 * Get the content:// style URI for the audio playlists table on the 971 * given volume. 972 * 973 * @param volumeName the name of the volume to get the URI for 974 * @return the URI to the audio playlists table on the given volume 975 */ 976 public static Uri getContentUri(String volumeName) { 977 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 978 "/audio/playlists"); 979 } 980 981 /** 982 * The content:// style URI for the internal storage. 983 */ 984 public static final Uri INTERNAL_CONTENT_URI = 985 getContentUri("internal"); 986 987 /** 988 * The content:// style URI for the "primary" external storage 989 * volume. 990 */ 991 public static final Uri EXTERNAL_CONTENT_URI = 992 getContentUri("external"); 993 994 /** 995 * The MIME type for this table. 996 */ 997 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist"; 998 999 /** 1000 * The MIME type for entries in this table. 1001 */ 1002 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist"; 1003 1004 /** 1005 * The default sort order for this table 1006 */ 1007 public static final String DEFAULT_SORT_ORDER = NAME; 1008 1009 /** 1010 * Sub-directory of each playlist containing all members. 1011 */ 1012 public static final class Members implements AudioColumns { 1013 public static final Uri getContentUri(String volumeName, 1014 long playlistId) { 1015 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 1016 + "/audio/playlists/" + playlistId + "/members"); 1017 } 1018 1019 /** 1020 * The ID within the playlist. 1021 */ 1022 public static final String _ID = "_id"; 1023 1024 /** 1025 * A subdirectory of each playlist containing all member audio 1026 * files. 1027 */ 1028 public static final String CONTENT_DIRECTORY = "members"; 1029 1030 /** 1031 * The ID of the audio file 1032 * <P>Type: INTEGER (long)</P> 1033 */ 1034 public static final String AUDIO_ID = "audio_id"; 1035 1036 /** 1037 * The ID of the playlist 1038 * <P>Type: INTEGER (long)</P> 1039 */ 1040 public static final String PLAYLIST_ID = "playlist_id"; 1041 1042 /** 1043 * The order of the songs in the playlist 1044 * <P>Type: INTEGER (long)></P> 1045 */ 1046 public static final String PLAY_ORDER = "play_order"; 1047 1048 /** 1049 * The default sort order for this table 1050 */ 1051 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER; 1052 } 1053 } 1054 1055 /** 1056 * Columns representing an artist 1057 */ 1058 public interface ArtistColumns { 1059 /** 1060 * The artist who created the audio file, if any 1061 * <P>Type: TEXT</P> 1062 */ 1063 public static final String ARTIST = "artist"; 1064 1065 /** 1066 * A non human readable key calculated from the ARTIST, used for 1067 * searching, sorting and grouping 1068 * <P>Type: TEXT</P> 1069 */ 1070 public static final String ARTIST_KEY = "artist_key"; 1071 1072 /** 1073 * The number of albums in the database for this artist 1074 */ 1075 public static final String NUMBER_OF_ALBUMS = "number_of_albums"; 1076 1077 /** 1078 * The number of albums in the database for this artist 1079 */ 1080 public static final String NUMBER_OF_TRACKS = "number_of_tracks"; 1081 } 1082 1083 /** 1084 * Contains artists for audio files 1085 */ 1086 public static final class Artists implements BaseColumns, ArtistColumns { 1087 /** 1088 * Get the content:// style URI for the artists table on the 1089 * given volume. 1090 * 1091 * @param volumeName the name of the volume to get the URI for 1092 * @return the URI to the audio artists table on the given volume 1093 */ 1094 public static Uri getContentUri(String volumeName) { 1095 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1096 "/audio/artists"); 1097 } 1098 1099 /** 1100 * The content:// style URI for the internal storage. 1101 */ 1102 public static final Uri INTERNAL_CONTENT_URI = 1103 getContentUri("internal"); 1104 1105 /** 1106 * The content:// style URI for the "primary" external storage 1107 * volume. 1108 */ 1109 public static final Uri EXTERNAL_CONTENT_URI = 1110 getContentUri("external"); 1111 1112 /** 1113 * The MIME type for this table. 1114 */ 1115 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists"; 1116 1117 /** 1118 * The MIME type for entries in this table. 1119 */ 1120 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist"; 1121 1122 /** 1123 * The default sort order for this table 1124 */ 1125 public static final String DEFAULT_SORT_ORDER = ARTIST_KEY; 1126 1127 /** 1128 * Sub-directory of each artist containing all albums on which 1129 * a song by the artist appears. 1130 */ 1131 public static final class Albums implements AlbumColumns { 1132 public static final Uri getContentUri(String volumeName, 1133 long artistId) { 1134 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName 1135 + "/audio/artists/" + artistId + "/albums"); 1136 } 1137 } 1138 } 1139 1140 /** 1141 * Columns representing an album 1142 */ 1143 public interface AlbumColumns { 1144 1145 /** 1146 * The id for the album 1147 * <P>Type: INTEGER</P> 1148 */ 1149 public static final String ALBUM_ID = "album_id"; 1150 1151 /** 1152 * The album on which the audio file appears, if any 1153 * <P>Type: TEXT</P> 1154 */ 1155 public static final String ALBUM = "album"; 1156 1157 /** 1158 * The artist whose songs appear on this album 1159 * <P>Type: TEXT</P> 1160 */ 1161 public static final String ARTIST = "artist"; 1162 1163 /** 1164 * The number of songs on this album 1165 * <P>Type: INTEGER</P> 1166 */ 1167 public static final String NUMBER_OF_SONGS = "numsongs"; 1168 1169 /** 1170 * This column is available when getting album info via artist, 1171 * and indicates the number of songs on the album by the given 1172 * artist. 1173 * <P>Type: INTEGER</P> 1174 */ 1175 public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist"; 1176 1177 /** 1178 * The year in which the earliest songs 1179 * on this album were released. This will often 1180 * be the same as {@link #LAST_YEAR}, but for compilation albums 1181 * they might differ. 1182 * <P>Type: INTEGER</P> 1183 */ 1184 public static final String FIRST_YEAR = "minyear"; 1185 1186 /** 1187 * The year in which the latest songs 1188 * on this album were released. This will often 1189 * be the same as {@link #FIRST_YEAR}, but for compilation albums 1190 * they might differ. 1191 * <P>Type: INTEGER</P> 1192 */ 1193 public static final String LAST_YEAR = "maxyear"; 1194 1195 /** 1196 * A non human readable key calculated from the ALBUM, used for 1197 * searching, sorting and grouping 1198 * <P>Type: TEXT</P> 1199 */ 1200 public static final String ALBUM_KEY = "album_key"; 1201 1202 /** 1203 * Cached album art. 1204 * <P>Type: TEXT</P> 1205 */ 1206 public static final String ALBUM_ART = "album_art"; 1207 } 1208 1209 /** 1210 * Contains artists for audio files 1211 */ 1212 public static final class Albums implements BaseColumns, AlbumColumns { 1213 /** 1214 * Get the content:// style URI for the albums table on the 1215 * given volume. 1216 * 1217 * @param volumeName the name of the volume to get the URI for 1218 * @return the URI to the audio albums table on the given volume 1219 */ 1220 public static Uri getContentUri(String volumeName) { 1221 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1222 "/audio/albums"); 1223 } 1224 1225 /** 1226 * The content:// style URI for the internal storage. 1227 */ 1228 public static final Uri INTERNAL_CONTENT_URI = 1229 getContentUri("internal"); 1230 1231 /** 1232 * The content:// style URI for the "primary" external storage 1233 * volume. 1234 */ 1235 public static final Uri EXTERNAL_CONTENT_URI = 1236 getContentUri("external"); 1237 1238 /** 1239 * The MIME type for this table. 1240 */ 1241 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums"; 1242 1243 /** 1244 * The MIME type for entries in this table. 1245 */ 1246 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album"; 1247 1248 /** 1249 * The default sort order for this table 1250 */ 1251 public static final String DEFAULT_SORT_ORDER = ALBUM_KEY; 1252 } 1253 } 1254 1255 public static final class Video { 1256 1257 /** 1258 * The default sort order for this table. 1259 */ 1260 public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME; 1261 1262 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) 1263 { 1264 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); 1265 } 1266 1267 public interface VideoColumns extends MediaColumns { 1268 1269 /** 1270 * The duration of the video file, in ms 1271 * <P>Type: INTEGER (long)</P> 1272 */ 1273 public static final String DURATION = "duration"; 1274 1275 /** 1276 * The artist who created the video file, if any 1277 * <P>Type: TEXT</P> 1278 */ 1279 public static final String ARTIST = "artist"; 1280 1281 /** 1282 * The album the video file is from, if any 1283 * <P>Type: TEXT</P> 1284 */ 1285 public static final String ALBUM = "album"; 1286 1287 /** 1288 * The resolution of the video file, formatted as "XxY" 1289 * <P>Type: TEXT</P> 1290 */ 1291 public static final String RESOLUTION = "resolution"; 1292 1293 /** 1294 * The description of the video recording 1295 * <P>Type: TEXT</P> 1296 */ 1297 public static final String DESCRIPTION = "description"; 1298 1299 /** 1300 * Whether the video should be published as public or private 1301 * <P>Type: INTEGER</P> 1302 */ 1303 public static final String IS_PRIVATE = "isprivate"; 1304 1305 /** 1306 * The user-added tags associated with a video 1307 * <P>Type: TEXT</P> 1308 */ 1309 public static final String TAGS = "tags"; 1310 1311 /** 1312 * The YouTube category of the video 1313 * <P>Type: TEXT</P> 1314 */ 1315 public static final String CATEGORY = "category"; 1316 1317 /** 1318 * The language of the video 1319 * <P>Type: TEXT</P> 1320 */ 1321 public static final String LANGUAGE = "language"; 1322 1323 /** 1324 * The latitude where the image was captured. 1325 * <P>Type: DOUBLE</P> 1326 */ 1327 public static final String LATITUDE = "latitude"; 1328 1329 /** 1330 * The longitude where the image was captured. 1331 * <P>Type: DOUBLE</P> 1332 */ 1333 public static final String LONGITUDE = "longitude"; 1334 1335 /** 1336 * The date & time that the image was taken in units 1337 * of milliseconds since jan 1, 1970. 1338 * <P>Type: INTEGER</P> 1339 */ 1340 public static final String DATE_TAKEN = "datetaken"; 1341 1342 /** 1343 * The mini thumb id. 1344 * <P>Type: INTEGER</P> 1345 */ 1346 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; 1347 1348 /** 1349 * The bucket id of the video. This is a read-only property that 1350 * is automatically computed from the DATA column. 1351 * <P>Type: TEXT</P> 1352 */ 1353 public static final String BUCKET_ID = "bucket_id"; 1354 1355 /** 1356 * The bucket display name of the video. This is a read-only property that 1357 * is automatically computed from the DATA column. 1358 * <P>Type: TEXT</P> 1359 */ 1360 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; 1361 1362 /** 1363 * The bookmark for the video. Time in ms. Represents the location in the video that the 1364 * video should start playing at the next time it is opened. If the value is null or 1365 * out of the range 0..DURATION-1 then the video should start playing from the 1366 * beginning. 1367 * <P>Type: INTEGER</P> 1368 */ 1369 public static final String BOOKMARK = "bookmark"; 1370 } 1371 1372 public static final class Media implements VideoColumns { 1373 /** 1374 * Get the content:// style URI for the video media table on the 1375 * given volume. 1376 * 1377 * @param volumeName the name of the volume to get the URI for 1378 * @return the URI to the video media table on the given volume 1379 */ 1380 public static Uri getContentUri(String volumeName) { 1381 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + 1382 "/video/media"); 1383 } 1384 1385 /** 1386 * The content:// style URI for the internal storage. 1387 */ 1388 public static final Uri INTERNAL_CONTENT_URI = 1389 getContentUri("internal"); 1390 1391 /** 1392 * The content:// style URI for the "primary" external storage 1393 * volume. 1394 */ 1395 public static final Uri EXTERNAL_CONTENT_URI = 1396 getContentUri("external"); 1397 1398 /** 1399 * The MIME type for this table. 1400 */ 1401 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video"; 1402 1403 /** 1404 * The default sort order for this table 1405 */ 1406 public static final String DEFAULT_SORT_ORDER = TITLE; 1407 } 1408 } 1409 1410 /** 1411 * Uri for querying the state of the media scanner. 1412 */ 1413 public static Uri getMediaScannerUri() { 1414 return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner"); 1415 } 1416 1417 /** 1418 * Name of current volume being scanned by the media scanner. 1419 */ 1420 public static final String MEDIA_SCANNER_VOLUME = "volume"; 1421} 1422