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