1 /* 2 * Copyright (C) 2015 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.hardware.radio; 17 18 import android.annotation.NonNull; 19 import android.annotation.SystemApi; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapFactory; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 import android.util.SparseArray; 28 29 import java.util.Set; 30 31 /** 32 * Contains meta data about a radio program such as station name, song title, artist etc... 33 * @hide 34 */ 35 @SystemApi 36 public final class RadioMetadata implements Parcelable { 37 private static final String TAG = "BroadcastRadio.metadata"; 38 39 /** 40 * The RDS Program Information. 41 */ 42 public static final String METADATA_KEY_RDS_PI = "android.hardware.radio.metadata.RDS_PI"; 43 44 /** 45 * The RDS Program Service. 46 */ 47 public static final String METADATA_KEY_RDS_PS = "android.hardware.radio.metadata.RDS_PS"; 48 49 /** 50 * The RDS PTY. 51 */ 52 public static final String METADATA_KEY_RDS_PTY = "android.hardware.radio.metadata.RDS_PTY"; 53 54 /** 55 * The RBDS PTY. 56 */ 57 public static final String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY"; 58 59 /** 60 * The RBDS Radio Text. 61 */ 62 public static final String METADATA_KEY_RDS_RT = "android.hardware.radio.metadata.RDS_RT"; 63 64 /** 65 * The song title. 66 */ 67 public static final String METADATA_KEY_TITLE = "android.hardware.radio.metadata.TITLE"; 68 69 /** 70 * The artist name. 71 */ 72 public static final String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST"; 73 74 /** 75 * The album name. 76 */ 77 public static final String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM"; 78 79 /** 80 * The music genre. 81 */ 82 public static final String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE"; 83 84 /** 85 * The radio station icon {@link Bitmap}. 86 */ 87 public static final String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON"; 88 89 /** 90 * The artwork for the song/album {@link Bitmap}. 91 */ 92 public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART"; 93 94 /** 95 * The clock. 96 */ 97 public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK"; 98 99 /** 100 * Technology-independent program name (station name). 101 */ 102 public static final String METADATA_KEY_PROGRAM_NAME = 103 "android.hardware.radio.metadata.PROGRAM_NAME"; 104 105 /** 106 * DAB ensemble name. 107 */ 108 public static final String METADATA_KEY_DAB_ENSEMBLE_NAME = 109 "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME"; 110 111 /** 112 * DAB ensemble name - short version (up to 8 characters). 113 */ 114 public static final String METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT = 115 "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME_SHORT"; 116 117 /** 118 * DAB service name. 119 */ 120 public static final String METADATA_KEY_DAB_SERVICE_NAME = 121 "android.hardware.radio.metadata.DAB_SERVICE_NAME"; 122 123 /** 124 * DAB service name - short version (up to 8 characters). 125 */ 126 public static final String METADATA_KEY_DAB_SERVICE_NAME_SHORT = 127 "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT"; 128 129 /** 130 * DAB component name. 131 */ 132 public static final String METADATA_KEY_DAB_COMPONENT_NAME = 133 "android.hardware.radio.metadata.DAB_COMPONENT_NAME"; 134 135 /** 136 * DAB component name. 137 */ 138 public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT = 139 "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT"; 140 141 142 private static final int METADATA_TYPE_INVALID = -1; 143 private static final int METADATA_TYPE_INT = 0; 144 private static final int METADATA_TYPE_TEXT = 1; 145 private static final int METADATA_TYPE_BITMAP = 2; 146 private static final int METADATA_TYPE_CLOCK = 3; 147 148 private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE; 149 150 static { 151 METADATA_KEYS_TYPE = new ArrayMap<String, Integer>(); METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_INT)152 METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_INT); METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT)153 METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT)154 METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT); METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT)155 METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT); METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT)156 METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT)157 METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT)158 METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT)159 METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT)160 METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP)161 METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP)162 METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK)163 METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK); METADATA_KEYS_TYPE.put(METADATA_KEY_PROGRAM_NAME, METADATA_TYPE_TEXT)164 METADATA_KEYS_TYPE.put(METADATA_KEY_PROGRAM_NAME, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME, METADATA_TYPE_TEXT)165 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT, METADATA_TYPE_TEXT)166 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME, METADATA_TYPE_TEXT)167 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT)168 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT)169 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT)170 METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT); 171 } 172 173 // keep in sync with: system/media/radio/include/system/radio_metadata.h 174 private static final int NATIVE_KEY_INVALID = -1; 175 private static final int NATIVE_KEY_RDS_PI = 0; 176 private static final int NATIVE_KEY_RDS_PS = 1; 177 private static final int NATIVE_KEY_RDS_PTY = 2; 178 private static final int NATIVE_KEY_RBDS_PTY = 3; 179 private static final int NATIVE_KEY_RDS_RT = 4; 180 private static final int NATIVE_KEY_TITLE = 5; 181 private static final int NATIVE_KEY_ARTIST = 6; 182 private static final int NATIVE_KEY_ALBUM = 7; 183 private static final int NATIVE_KEY_GENRE = 8; 184 private static final int NATIVE_KEY_ICON = 9; 185 private static final int NATIVE_KEY_ART = 10; 186 private static final int NATIVE_KEY_CLOCK = 11; 187 188 private static final SparseArray<String> NATIVE_KEY_MAPPING; 189 190 static { 191 NATIVE_KEY_MAPPING = new SparseArray<String>(); NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI)192 NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI); NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS)193 NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS); NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY)194 NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY); NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY)195 NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY); NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT)196 NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT); NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE)197 NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST)198 NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM)199 NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM); NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE)200 NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON)201 NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART)202 NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART); NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK)203 NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK); 204 } 205 206 /** 207 * Provides a Clock that can be used to describe time as provided by the Radio. 208 * 209 * The clock is defined by the seconds since epoch at the UTC + 0 timezone 210 * and timezone offset from UTC + 0 represented in number of minutes. 211 * 212 * @hide 213 */ 214 @SystemApi 215 public static final class Clock implements Parcelable { 216 private final long mUtcEpochSeconds; 217 private final int mTimezoneOffsetMinutes; 218 describeContents()219 public int describeContents() { 220 return 0; 221 } 222 writeToParcel(Parcel out, int flags)223 public void writeToParcel(Parcel out, int flags) { 224 out.writeLong(mUtcEpochSeconds); 225 out.writeInt(mTimezoneOffsetMinutes); 226 } 227 228 public static final Parcelable.Creator<Clock> CREATOR 229 = new Parcelable.Creator<Clock>() { 230 public Clock createFromParcel(Parcel in) { 231 return new Clock(in); 232 } 233 234 public Clock[] newArray(int size) { 235 return new Clock[size]; 236 } 237 }; 238 Clock(long utcEpochSeconds, int timezoneOffsetMinutes)239 public Clock(long utcEpochSeconds, int timezoneOffsetMinutes) { 240 mUtcEpochSeconds = utcEpochSeconds; 241 mTimezoneOffsetMinutes = timezoneOffsetMinutes; 242 } 243 Clock(Parcel in)244 private Clock(Parcel in) { 245 mUtcEpochSeconds = in.readLong(); 246 mTimezoneOffsetMinutes = in.readInt(); 247 } 248 getUtcEpochSeconds()249 public long getUtcEpochSeconds() { 250 return mUtcEpochSeconds; 251 } 252 getTimezoneOffsetMinutes()253 public int getTimezoneOffsetMinutes() { 254 return mTimezoneOffsetMinutes; 255 } 256 } 257 258 private final Bundle mBundle; 259 RadioMetadata()260 RadioMetadata() { 261 mBundle = new Bundle(); 262 } 263 RadioMetadata(Bundle bundle)264 private RadioMetadata(Bundle bundle) { 265 mBundle = new Bundle(bundle); 266 } 267 RadioMetadata(Parcel in)268 private RadioMetadata(Parcel in) { 269 mBundle = in.readBundle(); 270 } 271 272 @Override toString()273 public String toString() { 274 StringBuilder sb = new StringBuilder("RadioMetadata["); 275 276 final String removePrefix = "android.hardware.radio.metadata"; 277 278 boolean first = true; 279 for (String key : mBundle.keySet()) { 280 if (first) first = false; 281 else sb.append(", "); 282 283 String keyDisp = key; 284 if (key.startsWith(removePrefix)) keyDisp = key.substring(removePrefix.length()); 285 286 sb.append(keyDisp); 287 sb.append('='); 288 sb.append(mBundle.get(key)); 289 } 290 291 sb.append("]"); 292 return sb.toString(); 293 } 294 295 /** 296 * Returns {@code true} if the given key is contained in the meta data 297 * 298 * @param key a String key 299 * @return {@code true} if the key exists in this meta data, {@code false} otherwise 300 */ containsKey(String key)301 public boolean containsKey(String key) { 302 return mBundle.containsKey(key); 303 } 304 305 /** 306 * Returns the text value associated with the given key as a String, or null 307 * if the key is not found in the meta data. 308 * 309 * @param key The key the value is stored under 310 * @return a String value, or null 311 */ getString(String key)312 public String getString(String key) { 313 return mBundle.getString(key); 314 } 315 putInt(Bundle bundle, String key, int value)316 private static void putInt(Bundle bundle, String key, int value) { 317 int type = METADATA_KEYS_TYPE.getOrDefault(key, METADATA_TYPE_INVALID); 318 if (type != METADATA_TYPE_INT && type != METADATA_TYPE_BITMAP) { 319 throw new IllegalArgumentException("The " + key + " key cannot be used to put an int"); 320 } 321 bundle.putInt(key, value); 322 } 323 324 /** 325 * Returns the value associated with the given key, 326 * or 0 if the key is not found in the meta data. 327 * 328 * @param key The key the value is stored under 329 * @return an int value 330 */ getInt(String key)331 public int getInt(String key) { 332 return mBundle.getInt(key, 0); 333 } 334 335 /** 336 * Returns a {@link Bitmap} for the given key or null if the key is not found in the meta data. 337 * 338 * @param key The key the value is stored under 339 * @return a {@link Bitmap} or null 340 * @deprecated Use getBitmapId(String) instead 341 */ 342 @Deprecated getBitmap(String key)343 public Bitmap getBitmap(String key) { 344 Bitmap bmp = null; 345 try { 346 bmp = mBundle.getParcelable(key); 347 } catch (Exception e) { 348 // ignore, value was not a bitmap 349 Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); 350 } 351 return bmp; 352 } 353 354 /** 355 * Retrieves an identifier for a bitmap. 356 * 357 * The format of an identifier is opaque to the application, 358 * with a special case of value 0 being invalid. 359 * An identifier for a given image-tuner pair is unique, so an application 360 * may cache images and determine if there is a necessity to fetch them 361 * again - if identifier changes, it means the image has changed. 362 * 363 * Only bitmap keys may be used with this method: 364 * <ul> 365 * <li>{@link #METADATA_KEY_ICON}</li> 366 * <li>{@link #METADATA_KEY_ART}</li> 367 * </ul> 368 * 369 * @param key The key the value is stored under. 370 * @return a bitmap identifier or 0 if it's missing. 371 * @hide This API is not thoroughly elaborated yet 372 */ getBitmapId(@onNull String key)373 public int getBitmapId(@NonNull String key) { 374 if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) return 0; 375 return getInt(key); 376 } 377 getClock(String key)378 public Clock getClock(String key) { 379 Clock clock = null; 380 try { 381 clock = mBundle.getParcelable(key); 382 } catch (Exception e) { 383 // ignore, value was not a clock. 384 Log.w(TAG, "Failed to retrieve a key as Clock.", e); 385 } 386 return clock; 387 } 388 389 @Override describeContents()390 public int describeContents() { 391 return 0; 392 } 393 394 @Override writeToParcel(Parcel dest, int flags)395 public void writeToParcel(Parcel dest, int flags) { 396 dest.writeBundle(mBundle); 397 } 398 399 /** 400 * Returns the number of fields in this meta data. 401 * 402 * @return the number of fields in the meta data. 403 */ size()404 public int size() { 405 return mBundle.size(); 406 } 407 408 /** 409 * Returns a Set containing the Strings used as keys in this meta data. 410 * 411 * @return a Set of String keys 412 */ keySet()413 public Set<String> keySet() { 414 return mBundle.keySet(); 415 } 416 417 /** 418 * Helper for getting the String key used by {@link RadioMetadata} from the 419 * corrsponding native integer key. 420 * 421 * @param editorKey The key used by the editor 422 * @return the key used by this class or null if no mapping exists 423 * @hide 424 */ getKeyFromNativeKey(int nativeKey)425 public static String getKeyFromNativeKey(int nativeKey) { 426 return NATIVE_KEY_MAPPING.get(nativeKey, null); 427 } 428 429 public static final Parcelable.Creator<RadioMetadata> CREATOR = 430 new Parcelable.Creator<RadioMetadata>() { 431 @Override 432 public RadioMetadata createFromParcel(Parcel in) { 433 return new RadioMetadata(in); 434 } 435 436 @Override 437 public RadioMetadata[] newArray(int size) { 438 return new RadioMetadata[size]; 439 } 440 }; 441 442 /** 443 * Use to build RadioMetadata objects. 444 */ 445 public static final class Builder { 446 private final Bundle mBundle; 447 448 /** 449 * Create an empty Builder. Any field that should be included in the 450 * {@link RadioMetadata} must be added. 451 */ Builder()452 public Builder() { 453 mBundle = new Bundle(); 454 } 455 456 /** 457 * Create a Builder using a {@link RadioMetadata} instance to set the 458 * initial values. All fields in the source meta data will be included in 459 * the new meta data. Fields can be overwritten by adding the same key. 460 * 461 * @param source 462 */ Builder(RadioMetadata source)463 public Builder(RadioMetadata source) { 464 mBundle = new Bundle(source.mBundle); 465 } 466 467 /** 468 * Create a Builder using a {@link RadioMetadata} instance to set 469 * initial values, but replace bitmaps with a scaled down copy if they 470 * are larger than maxBitmapSize. 471 * 472 * @param source The original meta data to copy. 473 * @param maxBitmapSize The maximum height/width for bitmaps contained 474 * in the meta data. 475 * @hide 476 */ Builder(RadioMetadata source, int maxBitmapSize)477 public Builder(RadioMetadata source, int maxBitmapSize) { 478 this(source); 479 for (String key : mBundle.keySet()) { 480 Object value = mBundle.get(key); 481 if (value != null && value instanceof Bitmap) { 482 Bitmap bmp = (Bitmap) value; 483 if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { 484 putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); 485 } 486 } 487 } 488 } 489 490 /** 491 * Put a String value into the meta data. Custom keys may be used, but if 492 * the METADATA_KEYs defined in this class are used they may only be one 493 * of the following: 494 * <ul> 495 * <li>{@link #METADATA_KEY_RDS_PS}</li> 496 * <li>{@link #METADATA_KEY_RDS_RT}</li> 497 * <li>{@link #METADATA_KEY_TITLE}</li> 498 * <li>{@link #METADATA_KEY_ARTIST}</li> 499 * <li>{@link #METADATA_KEY_ALBUM}</li> 500 * <li>{@link #METADATA_KEY_GENRE}</li> 501 * </ul> 502 * 503 * @param key The key for referencing this value 504 * @param value The String value to store 505 * @return the same Builder instance 506 */ putString(String key, String value)507 public Builder putString(String key, String value) { 508 if (!METADATA_KEYS_TYPE.containsKey(key) || 509 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 510 throw new IllegalArgumentException("The " + key 511 + " key cannot be used to put a String"); 512 } 513 mBundle.putString(key, value); 514 return this; 515 } 516 517 /** 518 * Put an int value into the meta data. Custom keys may be used, but if 519 * the METADATA_KEYs defined in this class are used they may only be one 520 * of the following: 521 * <ul> 522 * <li>{@link #METADATA_KEY_RDS_PI}</li> 523 * <li>{@link #METADATA_KEY_RDS_PTY}</li> 524 * <li>{@link #METADATA_KEY_RBDS_PTY}</li> 525 * </ul> 526 * or any bitmap represented by its identifier. 527 * 528 * @param key The key for referencing this value 529 * @param value The int value to store 530 * @return the same Builder instance 531 */ putInt(String key, int value)532 public Builder putInt(String key, int value) { 533 RadioMetadata.putInt(mBundle, key, value); 534 return this; 535 } 536 537 /** 538 * Put a {@link Bitmap} into the meta data. Custom keys may be used, but 539 * if the METADATA_KEYs defined in this class are used they may only be 540 * one of the following: 541 * <ul> 542 * <li>{@link #METADATA_KEY_ICON}</li> 543 * <li>{@link #METADATA_KEY_ART}</li> 544 * </ul> 545 * <p> 546 * 547 * @param key The key for referencing this value 548 * @param value The Bitmap to store 549 * @return the same Builder instance 550 */ putBitmap(String key, Bitmap value)551 public Builder putBitmap(String key, Bitmap value) { 552 if (!METADATA_KEYS_TYPE.containsKey(key) || 553 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 554 throw new IllegalArgumentException("The " + key 555 + " key cannot be used to put a Bitmap"); 556 } 557 mBundle.putParcelable(key, value); 558 return this; 559 } 560 561 /** 562 * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the 563 * METADATA_KEYs defined in this class are used they may only be one of the following: 564 * <ul> 565 * <li>{@link #MEADATA_KEY_CLOCK}</li> 566 * </ul> 567 * 568 * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone. 569 * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes. 570 * @return the same Builder instance. 571 */ putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes)572 public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) { 573 if (!METADATA_KEYS_TYPE.containsKey(key) || 574 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { 575 throw new IllegalArgumentException("The " + key 576 + " key cannot be used to put a RadioMetadata.Clock."); 577 } 578 mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes)); 579 return this; 580 } 581 582 /** 583 * Creates a {@link RadioMetadata} instance with the specified fields. 584 * 585 * @return a new {@link RadioMetadata} object 586 */ build()587 public RadioMetadata build() { 588 return new RadioMetadata(mBundle); 589 } 590 scaleBitmap(Bitmap bmp, int maxSize)591 private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { 592 float maxSizeF = maxSize; 593 float widthScale = maxSizeF / bmp.getWidth(); 594 float heightScale = maxSizeF / bmp.getHeight(); 595 float scale = Math.min(widthScale, heightScale); 596 int height = (int) (bmp.getHeight() * scale); 597 int width = (int) (bmp.getWidth() * scale); 598 return Bitmap.createScaledBitmap(bmp, width, height, true); 599 } 600 } 601 putIntFromNative(int nativeKey, int value)602 int putIntFromNative(int nativeKey, int value) { 603 String key = getKeyFromNativeKey(nativeKey); 604 try { 605 putInt(mBundle, key, value); 606 return 0; 607 } catch (IllegalArgumentException ex) { 608 return -1; 609 } 610 } 611 putStringFromNative(int nativeKey, String value)612 int putStringFromNative(int nativeKey, String value) { 613 String key = getKeyFromNativeKey(nativeKey); 614 if (!METADATA_KEYS_TYPE.containsKey(key) || 615 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 616 return -1; 617 } 618 mBundle.putString(key, value); 619 return 0; 620 } 621 putBitmapFromNative(int nativeKey, byte[] value)622 int putBitmapFromNative(int nativeKey, byte[] value) { 623 String key = getKeyFromNativeKey(nativeKey); 624 if (!METADATA_KEYS_TYPE.containsKey(key) || 625 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 626 return -1; 627 } 628 Bitmap bmp = null; 629 try { 630 bmp = BitmapFactory.decodeByteArray(value, 0, value.length); 631 if (bmp != null) { 632 mBundle.putParcelable(key, bmp); 633 return 0; 634 } 635 } catch (Exception e) { 636 } 637 return -1; 638 } 639 putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes)640 int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) { 641 String key = getKeyFromNativeKey(nativeKey); 642 if (!METADATA_KEYS_TYPE.containsKey(key) || 643 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { 644 return -1; 645 } 646 mBundle.putParcelable(key, new RadioMetadata.Clock( 647 utcEpochSeconds, timezoneOffsetInMinutes)); 648 return 0; 649 } 650 } 651