1 /* 2 * Copyright (C) 2014 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 package android.support.v4.media; 17 18 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 19 20 import android.graphics.Bitmap; 21 import android.net.Uri; 22 import android.os.Build; 23 import android.os.Bundle; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.support.annotation.Nullable; 27 import android.support.annotation.RestrictTo; 28 import android.text.TextUtils; 29 30 /** 31 * A simple set of metadata for a media item suitable for display. This can be 32 * created using the Builder or retrieved from existing metadata using 33 * {@link MediaMetadataCompat#getDescription()}. 34 */ 35 public final class MediaDescriptionCompat implements Parcelable { 36 /** 37 * Used as a long extra field to indicate the bluetooth folder type of the media item as 38 * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for 39 * {@link MediaBrowserCompat.MediaItem} with 40 * {@link MediaBrowserCompat.MediaItem#FLAG_BROWSABLE}. The value should be one of the 41 * following: 42 * <ul> 43 * <li>{@link #BT_FOLDER_TYPE_MIXED}</li> 44 * <li>{@link #BT_FOLDER_TYPE_TITLES}</li> 45 * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li> 46 * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li> 47 * <li>{@link #BT_FOLDER_TYPE_GENRES}</li> 48 * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li> 49 * <li>{@link #BT_FOLDER_TYPE_YEARS}</li> 50 * </ul> 51 * 52 * @see #getExtras() 53 */ 54 public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE"; 55 56 /** 57 * The type of folder that is unknown or contains media elements of mixed types as specified in 58 * the section 6.10.2.2 of the Bluetooth AVRCP 1.5. 59 */ 60 public static final long BT_FOLDER_TYPE_MIXED = 0; 61 62 /** 63 * The type of folder that contains media elements only as specified in the section 6.10.2.2 of 64 * the Bluetooth AVRCP 1.5. 65 */ 66 public static final long BT_FOLDER_TYPE_TITLES = 1; 67 68 /** 69 * The type of folder that contains folders categorized by album as specified in the section 70 * 6.10.2.2 of the Bluetooth AVRCP 1.5. 71 */ 72 public static final long BT_FOLDER_TYPE_ALBUMS = 2; 73 74 /** 75 * The type of folder that contains folders categorized by artist as specified in the section 76 * 6.10.2.2 of the Bluetooth AVRCP 1.5. 77 */ 78 public static final long BT_FOLDER_TYPE_ARTISTS = 3; 79 80 /** 81 * The type of folder that contains folders categorized by genre as specified in the section 82 * 6.10.2.2 of the Bluetooth AVRCP 1.5. 83 */ 84 public static final long BT_FOLDER_TYPE_GENRES = 4; 85 86 /** 87 * The type of folder that contains folders categorized by playlist as specified in the section 88 * 6.10.2.2 of the Bluetooth AVRCP 1.5. 89 */ 90 public static final long BT_FOLDER_TYPE_PLAYLISTS = 5; 91 92 /** 93 * The type of folder that contains folders categorized by year as specified in the section 94 * 6.10.2.2 of the Bluetooth AVRCP 1.5. 95 */ 96 public static final long BT_FOLDER_TYPE_YEARS = 6; 97 98 /** 99 * Used as a long extra field to indicate the download status of the media item. The value 100 * should be one of the following: 101 * <ul> 102 * <li>{@link #STATUS_NOT_DOWNLOADED}</li> 103 * <li>{@link #STATUS_DOWNLOADING}</li> 104 * <li>{@link #STATUS_DOWNLOADED}</li> 105 * </ul> 106 * 107 * @see #getExtras() 108 */ 109 public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS"; 110 111 /** 112 * The status value to indicate the media item is not downloaded. 113 * 114 * @see #EXTRA_DOWNLOAD_STATUS 115 */ 116 public static final long STATUS_NOT_DOWNLOADED = 0; 117 118 /** 119 * The status value to indicate the media item is being downloaded. 120 * 121 * @see #EXTRA_DOWNLOAD_STATUS 122 */ 123 public static final long STATUS_DOWNLOADING = 1; 124 125 /** 126 * The status value to indicate the media item is downloaded for later offline playback. 127 * 128 * @see #EXTRA_DOWNLOAD_STATUS 129 */ 130 public static final long STATUS_DOWNLOADED = 2; 131 132 /** 133 * Custom key to store a media URI on API 21-22 devices (before it became part of the 134 * framework class) when parceling/converting to and from framework objects. 135 * 136 * @hide 137 */ 138 @RestrictTo(LIBRARY_GROUP) 139 public static final String DESCRIPTION_KEY_MEDIA_URI = 140 "android.support.v4.media.description.MEDIA_URI"; 141 /** 142 * Custom key to store whether the original Bundle provided by the developer was null 143 * 144 * @hide 145 */ 146 @RestrictTo(LIBRARY_GROUP) 147 public static final String DESCRIPTION_KEY_NULL_BUNDLE_FLAG = 148 "android.support.v4.media.description.NULL_BUNDLE_FLAG"; 149 /** 150 * A unique persistent id for the content or null. 151 */ 152 private final String mMediaId; 153 /** 154 * A primary title suitable for display or null. 155 */ 156 private final CharSequence mTitle; 157 /** 158 * A subtitle suitable for display or null. 159 */ 160 private final CharSequence mSubtitle; 161 /** 162 * A description suitable for display or null. 163 */ 164 private final CharSequence mDescription; 165 /** 166 * A bitmap icon suitable for display or null. 167 */ 168 private final Bitmap mIcon; 169 /** 170 * A Uri for an icon suitable for display or null. 171 */ 172 private final Uri mIconUri; 173 /** 174 * Extras for opaque use by apps/system. 175 */ 176 private final Bundle mExtras; 177 /** 178 * A Uri to identify this content. 179 */ 180 private final Uri mMediaUri; 181 182 /** 183 * A cached copy of the equivalent framework object. 184 */ 185 private Object mDescriptionObj; 186 MediaDescriptionCompat(String mediaId, CharSequence title, CharSequence subtitle, CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri)187 MediaDescriptionCompat(String mediaId, CharSequence title, CharSequence subtitle, 188 CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) { 189 mMediaId = mediaId; 190 mTitle = title; 191 mSubtitle = subtitle; 192 mDescription = description; 193 mIcon = icon; 194 mIconUri = iconUri; 195 mExtras = extras; 196 mMediaUri = mediaUri; 197 } 198 MediaDescriptionCompat(Parcel in)199 MediaDescriptionCompat(Parcel in) { 200 mMediaId = in.readString(); 201 mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 202 mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 203 mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 204 mIcon = in.readParcelable(null); 205 mIconUri = in.readParcelable(null); 206 mExtras = in.readBundle(); 207 mMediaUri = in.readParcelable(null); 208 } 209 210 /** 211 * Returns the media id or null. See 212 * {@link MediaMetadataCompat#METADATA_KEY_MEDIA_ID}. 213 */ 214 @Nullable getMediaId()215 public String getMediaId() { 216 return mMediaId; 217 } 218 219 /** 220 * Returns a title suitable for display or null. 221 * 222 * @return A title or null. 223 */ 224 @Nullable getTitle()225 public CharSequence getTitle() { 226 return mTitle; 227 } 228 229 /** 230 * Returns a subtitle suitable for display or null. 231 * 232 * @return A subtitle or null. 233 */ 234 @Nullable getSubtitle()235 public CharSequence getSubtitle() { 236 return mSubtitle; 237 } 238 239 /** 240 * Returns a description suitable for display or null. 241 * 242 * @return A description or null. 243 */ 244 @Nullable getDescription()245 public CharSequence getDescription() { 246 return mDescription; 247 } 248 249 /** 250 * Returns a bitmap icon suitable for display or null. 251 * 252 * @return An icon or null. 253 */ 254 @Nullable getIconBitmap()255 public Bitmap getIconBitmap() { 256 return mIcon; 257 } 258 259 /** 260 * Returns a Uri for an icon suitable for display or null. 261 * 262 * @return An icon uri or null. 263 */ 264 @Nullable getIconUri()265 public Uri getIconUri() { 266 return mIconUri; 267 } 268 269 /** 270 * Returns any extras that were added to the description. 271 * 272 * @return A bundle of extras or null. 273 */ 274 @Nullable getExtras()275 public Bundle getExtras() { 276 return mExtras; 277 } 278 279 /** 280 * Returns a Uri representing this content or null. 281 * 282 * @return A media Uri or null. 283 */ 284 @Nullable getMediaUri()285 public Uri getMediaUri() { 286 return mMediaUri; 287 } 288 289 @Override describeContents()290 public int describeContents() { 291 return 0; 292 } 293 294 @Override writeToParcel(Parcel dest, int flags)295 public void writeToParcel(Parcel dest, int flags) { 296 if (Build.VERSION.SDK_INT < 21) { 297 dest.writeString(mMediaId); 298 TextUtils.writeToParcel(mTitle, dest, flags); 299 TextUtils.writeToParcel(mSubtitle, dest, flags); 300 TextUtils.writeToParcel(mDescription, dest, flags); 301 dest.writeParcelable(mIcon, flags); 302 dest.writeParcelable(mIconUri, flags); 303 dest.writeBundle(mExtras); 304 dest.writeParcelable(mMediaUri, flags); 305 } else { 306 MediaDescriptionCompatApi21.writeToParcel(getMediaDescription(), dest, flags); 307 } 308 } 309 310 @Override toString()311 public String toString() { 312 return mTitle + ", " + mSubtitle + ", " + mDescription; 313 } 314 315 /** 316 * Gets the underlying framework {@link android.media.MediaDescription} 317 * object. 318 * <p> 319 * This method is only supported on 320 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. 321 * </p> 322 * 323 * @return An equivalent {@link android.media.MediaDescription} object, or 324 * null if none. 325 */ getMediaDescription()326 public Object getMediaDescription() { 327 if (mDescriptionObj != null || Build.VERSION.SDK_INT < 21) { 328 return mDescriptionObj; 329 } 330 Object bob = MediaDescriptionCompatApi21.Builder.newInstance(); 331 MediaDescriptionCompatApi21.Builder.setMediaId(bob, mMediaId); 332 MediaDescriptionCompatApi21.Builder.setTitle(bob, mTitle); 333 MediaDescriptionCompatApi21.Builder.setSubtitle(bob, mSubtitle); 334 MediaDescriptionCompatApi21.Builder.setDescription(bob, mDescription); 335 MediaDescriptionCompatApi21.Builder.setIconBitmap(bob, mIcon); 336 MediaDescriptionCompatApi21.Builder.setIconUri(bob, mIconUri); 337 // Media URI was not added until API 23, so add it to the Bundle of extras to 338 // ensure the data is not lost - this ensures that 339 // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns 340 // an equivalent MediaDescriptionCompat on all API levels 341 Bundle extras = mExtras; 342 if (Build.VERSION.SDK_INT < 23 && mMediaUri != null) { 343 if (extras == null) { 344 extras = new Bundle(); 345 extras.putBoolean(DESCRIPTION_KEY_NULL_BUNDLE_FLAG, true); 346 } 347 extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri); 348 } 349 MediaDescriptionCompatApi21.Builder.setExtras(bob, extras); 350 if (Build.VERSION.SDK_INT >= 23) { 351 MediaDescriptionCompatApi23.Builder.setMediaUri(bob, mMediaUri); 352 } 353 mDescriptionObj = MediaDescriptionCompatApi21.Builder.build(bob); 354 355 return mDescriptionObj; 356 } 357 358 /** 359 * Creates an instance from a framework 360 * {@link android.media.MediaDescription} object. 361 * <p> 362 * This method is only supported on API 21+. 363 * </p> 364 * 365 * @param descriptionObj A {@link android.media.MediaDescription} object, or 366 * null if none. 367 * @return An equivalent {@link MediaMetadataCompat} object, or null if 368 * none. 369 */ fromMediaDescription(Object descriptionObj)370 public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) { 371 if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) { 372 Builder bob = new Builder(); 373 bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj)); 374 bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj)); 375 bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj)); 376 bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj)); 377 bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj)); 378 bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj)); 379 Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj); 380 Uri mediaUri = extras == null ? null : 381 (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI); 382 if (mediaUri != null) { 383 if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) { 384 // The extras were only created for the media URI, so we set it back to null to 385 // ensure mediaDescriptionCompat.getExtras() equals 386 // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras() 387 extras = null; 388 } else { 389 // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet() 390 // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) 391 // .getExtras().keySet() 392 extras.remove(DESCRIPTION_KEY_MEDIA_URI); 393 extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG); 394 } 395 } 396 bob.setExtras(extras); 397 if (mediaUri != null) { 398 bob.setMediaUri(mediaUri); 399 } else if (Build.VERSION.SDK_INT >= 23) { 400 bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj)); 401 } 402 MediaDescriptionCompat descriptionCompat = bob.build(); 403 descriptionCompat.mDescriptionObj = descriptionObj; 404 405 return descriptionCompat; 406 } else { 407 return null; 408 } 409 } 410 411 public static final Parcelable.Creator<MediaDescriptionCompat> CREATOR = 412 new Parcelable.Creator<MediaDescriptionCompat>() { 413 @Override 414 public MediaDescriptionCompat createFromParcel(Parcel in) { 415 if (Build.VERSION.SDK_INT < 21) { 416 return new MediaDescriptionCompat(in); 417 } else { 418 return fromMediaDescription(MediaDescriptionCompatApi21.fromParcel(in)); 419 } 420 } 421 422 @Override 423 public MediaDescriptionCompat[] newArray(int size) { 424 return new MediaDescriptionCompat[size]; 425 } 426 }; 427 428 /** 429 * Builder for {@link MediaDescriptionCompat} objects. 430 */ 431 public static final class Builder { 432 private String mMediaId; 433 private CharSequence mTitle; 434 private CharSequence mSubtitle; 435 private CharSequence mDescription; 436 private Bitmap mIcon; 437 private Uri mIconUri; 438 private Bundle mExtras; 439 private Uri mMediaUri; 440 441 /** 442 * Creates an initially empty builder. 443 */ Builder()444 public Builder() { 445 } 446 447 /** 448 * Sets the media id. 449 * 450 * @param mediaId The unique id for the item or null. 451 * @return this 452 */ setMediaId(@ullable String mediaId)453 public Builder setMediaId(@Nullable String mediaId) { 454 mMediaId = mediaId; 455 return this; 456 } 457 458 /** 459 * Sets the title. 460 * 461 * @param title A title suitable for display to the user or null. 462 * @return this 463 */ setTitle(@ullable CharSequence title)464 public Builder setTitle(@Nullable CharSequence title) { 465 mTitle = title; 466 return this; 467 } 468 469 /** 470 * Sets the subtitle. 471 * 472 * @param subtitle A subtitle suitable for display to the user or null. 473 * @return this 474 */ setSubtitle(@ullable CharSequence subtitle)475 public Builder setSubtitle(@Nullable CharSequence subtitle) { 476 mSubtitle = subtitle; 477 return this; 478 } 479 480 /** 481 * Sets the description. 482 * 483 * @param description A description suitable for display to the user or 484 * null. 485 * @return this 486 */ setDescription(@ullable CharSequence description)487 public Builder setDescription(@Nullable CharSequence description) { 488 mDescription = description; 489 return this; 490 } 491 492 /** 493 * Sets the icon. 494 * 495 * @param icon A {@link Bitmap} icon suitable for display to the user or 496 * null. 497 * @return this 498 */ setIconBitmap(@ullable Bitmap icon)499 public Builder setIconBitmap(@Nullable Bitmap icon) { 500 mIcon = icon; 501 return this; 502 } 503 504 /** 505 * Sets the icon uri. 506 * 507 * @param iconUri A {@link Uri} for an icon suitable for display to the 508 * user or null. 509 * @return this 510 */ setIconUri(@ullable Uri iconUri)511 public Builder setIconUri(@Nullable Uri iconUri) { 512 mIconUri = iconUri; 513 return this; 514 } 515 516 /** 517 * Sets a bundle of extras. 518 * 519 * @param extras The extras to include with this description or null. 520 * @return this 521 */ setExtras(@ullable Bundle extras)522 public Builder setExtras(@Nullable Bundle extras) { 523 mExtras = extras; 524 return this; 525 } 526 527 /** 528 * Sets the media uri. 529 * 530 * @param mediaUri The content's {@link Uri} for the item or null. 531 * @return this 532 */ setMediaUri(@ullable Uri mediaUri)533 public Builder setMediaUri(@Nullable Uri mediaUri) { 534 mMediaUri = mediaUri; 535 return this; 536 } 537 538 /** 539 * Creates a {@link MediaDescriptionCompat} instance with the specified 540 * fields. 541 * 542 * @return A MediaDescriptionCompat instance. 543 */ build()544 public MediaDescriptionCompat build() { 545 return new MediaDescriptionCompat(mMediaId, mTitle, mSubtitle, mDescription, mIcon, 546 mIconUri, mExtras, mMediaUri); 547 } 548 } 549 } 550