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 @android.annotation.NonNull 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 @NonNull 273 @Override toString()274 public String toString() { 275 StringBuilder sb = new StringBuilder("RadioMetadata["); 276 277 final String removePrefix = "android.hardware.radio.metadata"; 278 279 boolean first = true; 280 for (String key : mBundle.keySet()) { 281 if (first) first = false; 282 else sb.append(", "); 283 284 String keyDisp = key; 285 if (key.startsWith(removePrefix)) keyDisp = key.substring(removePrefix.length()); 286 287 sb.append(keyDisp); 288 sb.append('='); 289 sb.append(mBundle.get(key)); 290 } 291 292 sb.append("]"); 293 return sb.toString(); 294 } 295 296 /** 297 * Returns {@code true} if the given key is contained in the meta data 298 * 299 * @param key a String key 300 * @return {@code true} if the key exists in this meta data, {@code false} otherwise 301 */ containsKey(String key)302 public boolean containsKey(String key) { 303 return mBundle.containsKey(key); 304 } 305 306 /** 307 * Returns the text value associated with the given key as a String, or null 308 * if the key is not found in the meta data. 309 * 310 * @param key The key the value is stored under 311 * @return a String value, or null 312 */ getString(String key)313 public String getString(String key) { 314 return mBundle.getString(key); 315 } 316 putInt(Bundle bundle, String key, int value)317 private static void putInt(Bundle bundle, String key, int value) { 318 int type = METADATA_KEYS_TYPE.getOrDefault(key, METADATA_TYPE_INVALID); 319 if (type != METADATA_TYPE_INT && type != METADATA_TYPE_BITMAP) { 320 throw new IllegalArgumentException("The " + key + " key cannot be used to put an int"); 321 } 322 bundle.putInt(key, value); 323 } 324 325 /** 326 * Returns the value associated with the given key, 327 * or 0 if the key is not found in the meta data. 328 * 329 * @param key The key the value is stored under 330 * @return an int value 331 */ getInt(String key)332 public int getInt(String key) { 333 return mBundle.getInt(key, 0); 334 } 335 336 /** 337 * Returns a {@link Bitmap} for the given key or null if the key is not found in the meta data. 338 * 339 * @param key The key the value is stored under 340 * @return a {@link Bitmap} or null 341 * @deprecated Use getBitmapId(String) instead 342 */ 343 @Deprecated getBitmap(String key)344 public Bitmap getBitmap(String key) { 345 Bitmap bmp = null; 346 try { 347 bmp = mBundle.getParcelable(key); 348 } catch (Exception e) { 349 // ignore, value was not a bitmap 350 Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); 351 } 352 return bmp; 353 } 354 355 /** 356 * Retrieves an identifier for a bitmap. 357 * 358 * The format of an identifier is opaque to the application, 359 * with a special case of value 0 being invalid. 360 * An identifier for a given image-tuner pair is unique, so an application 361 * may cache images and determine if there is a necessity to fetch them 362 * again - if identifier changes, it means the image has changed. 363 * 364 * Only bitmap keys may be used with this method: 365 * <ul> 366 * <li>{@link #METADATA_KEY_ICON}</li> 367 * <li>{@link #METADATA_KEY_ART}</li> 368 * </ul> 369 * 370 * @param key The key the value is stored under. 371 * @return a bitmap identifier or 0 if it's missing. 372 * @hide This API is not thoroughly elaborated yet 373 */ getBitmapId(@onNull String key)374 public int getBitmapId(@NonNull String key) { 375 if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) return 0; 376 return getInt(key); 377 } 378 getClock(String key)379 public Clock getClock(String key) { 380 Clock clock = null; 381 try { 382 clock = mBundle.getParcelable(key); 383 } catch (Exception e) { 384 // ignore, value was not a clock. 385 Log.w(TAG, "Failed to retrieve a key as Clock.", e); 386 } 387 return clock; 388 } 389 390 @Override describeContents()391 public int describeContents() { 392 return 0; 393 } 394 395 @Override writeToParcel(Parcel dest, int flags)396 public void writeToParcel(Parcel dest, int flags) { 397 dest.writeBundle(mBundle); 398 } 399 400 /** 401 * Returns the number of fields in this meta data. 402 * 403 * @return the number of fields in the meta data. 404 */ size()405 public int size() { 406 return mBundle.size(); 407 } 408 409 /** 410 * Returns a Set containing the Strings used as keys in this meta data. 411 * 412 * @return a Set of String keys 413 */ keySet()414 public Set<String> keySet() { 415 return mBundle.keySet(); 416 } 417 418 /** 419 * Helper for getting the String key used by {@link RadioMetadata} from the 420 * corrsponding native integer key. 421 * 422 * @param editorKey The key used by the editor 423 * @return the key used by this class or null if no mapping exists 424 * @hide 425 */ getKeyFromNativeKey(int nativeKey)426 public static String getKeyFromNativeKey(int nativeKey) { 427 return NATIVE_KEY_MAPPING.get(nativeKey, null); 428 } 429 430 public static final @android.annotation.NonNull Parcelable.Creator<RadioMetadata> CREATOR = 431 new Parcelable.Creator<RadioMetadata>() { 432 @Override 433 public RadioMetadata createFromParcel(Parcel in) { 434 return new RadioMetadata(in); 435 } 436 437 @Override 438 public RadioMetadata[] newArray(int size) { 439 return new RadioMetadata[size]; 440 } 441 }; 442 443 /** 444 * Use to build RadioMetadata objects. 445 */ 446 public static final class Builder { 447 private final Bundle mBundle; 448 449 /** 450 * Create an empty Builder. Any field that should be included in the 451 * {@link RadioMetadata} must be added. 452 */ Builder()453 public Builder() { 454 mBundle = new Bundle(); 455 } 456 457 /** 458 * Create a Builder using a {@link RadioMetadata} instance to set the 459 * initial values. All fields in the source meta data will be included in 460 * the new meta data. Fields can be overwritten by adding the same key. 461 * 462 * @param source 463 */ Builder(RadioMetadata source)464 public Builder(RadioMetadata source) { 465 mBundle = new Bundle(source.mBundle); 466 } 467 468 /** 469 * Create a Builder using a {@link RadioMetadata} instance to set 470 * initial values, but replace bitmaps with a scaled down copy if they 471 * are larger than maxBitmapSize. 472 * 473 * @param source The original meta data to copy. 474 * @param maxBitmapSize The maximum height/width for bitmaps contained 475 * in the meta data. 476 * @hide 477 */ Builder(RadioMetadata source, int maxBitmapSize)478 public Builder(RadioMetadata source, int maxBitmapSize) { 479 this(source); 480 for (String key : mBundle.keySet()) { 481 Object value = mBundle.get(key); 482 if (value != null && value instanceof Bitmap) { 483 Bitmap bmp = (Bitmap) value; 484 if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { 485 putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); 486 } 487 } 488 } 489 } 490 491 /** 492 * Put a String value into the meta data. Custom keys may be used, but if 493 * the METADATA_KEYs defined in this class are used they may only be one 494 * of the following: 495 * <ul> 496 * <li>{@link #METADATA_KEY_RDS_PS}</li> 497 * <li>{@link #METADATA_KEY_RDS_RT}</li> 498 * <li>{@link #METADATA_KEY_TITLE}</li> 499 * <li>{@link #METADATA_KEY_ARTIST}</li> 500 * <li>{@link #METADATA_KEY_ALBUM}</li> 501 * <li>{@link #METADATA_KEY_GENRE}</li> 502 * </ul> 503 * 504 * @param key The key for referencing this value 505 * @param value The String value to store 506 * @return the same Builder instance 507 */ putString(String key, String value)508 public Builder putString(String key, String value) { 509 if (!METADATA_KEYS_TYPE.containsKey(key) || 510 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 511 throw new IllegalArgumentException("The " + key 512 + " key cannot be used to put a String"); 513 } 514 mBundle.putString(key, value); 515 return this; 516 } 517 518 /** 519 * Put an int value into the meta data. Custom keys may be used, but if 520 * the METADATA_KEYs defined in this class are used they may only be one 521 * of the following: 522 * <ul> 523 * <li>{@link #METADATA_KEY_RDS_PI}</li> 524 * <li>{@link #METADATA_KEY_RDS_PTY}</li> 525 * <li>{@link #METADATA_KEY_RBDS_PTY}</li> 526 * </ul> 527 * or any bitmap represented by its identifier. 528 * 529 * @param key The key for referencing this value 530 * @param value The int value to store 531 * @return the same Builder instance 532 */ putInt(String key, int value)533 public Builder putInt(String key, int value) { 534 RadioMetadata.putInt(mBundle, key, value); 535 return this; 536 } 537 538 /** 539 * Put a {@link Bitmap} into the meta data. Custom keys may be used, but 540 * if the METADATA_KEYs defined in this class are used they may only be 541 * one of the following: 542 * <ul> 543 * <li>{@link #METADATA_KEY_ICON}</li> 544 * <li>{@link #METADATA_KEY_ART}</li> 545 * </ul> 546 * <p> 547 * 548 * @param key The key for referencing this value 549 * @param value The Bitmap to store 550 * @return the same Builder instance 551 */ putBitmap(String key, Bitmap value)552 public Builder putBitmap(String key, Bitmap value) { 553 if (!METADATA_KEYS_TYPE.containsKey(key) || 554 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 555 throw new IllegalArgumentException("The " + key 556 + " key cannot be used to put a Bitmap"); 557 } 558 mBundle.putParcelable(key, value); 559 return this; 560 } 561 562 /** 563 * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the 564 * METADATA_KEYs defined in this class are used they may only be one of the following: 565 * <ul> 566 * <li>{@link #MEADATA_KEY_CLOCK}</li> 567 * </ul> 568 * 569 * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone. 570 * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes. 571 * @return the same Builder instance. 572 */ putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes)573 public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) { 574 if (!METADATA_KEYS_TYPE.containsKey(key) || 575 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { 576 throw new IllegalArgumentException("The " + key 577 + " key cannot be used to put a RadioMetadata.Clock."); 578 } 579 mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes)); 580 return this; 581 } 582 583 /** 584 * Creates a {@link RadioMetadata} instance with the specified fields. 585 * 586 * @return a new {@link RadioMetadata} object 587 */ build()588 public RadioMetadata build() { 589 return new RadioMetadata(mBundle); 590 } 591 scaleBitmap(Bitmap bmp, int maxSize)592 private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { 593 float maxSizeF = maxSize; 594 float widthScale = maxSizeF / bmp.getWidth(); 595 float heightScale = maxSizeF / bmp.getHeight(); 596 float scale = Math.min(widthScale, heightScale); 597 int height = (int) (bmp.getHeight() * scale); 598 int width = (int) (bmp.getWidth() * scale); 599 return Bitmap.createScaledBitmap(bmp, width, height, true); 600 } 601 } 602 putIntFromNative(int nativeKey, int value)603 int putIntFromNative(int nativeKey, int value) { 604 String key = getKeyFromNativeKey(nativeKey); 605 try { 606 putInt(mBundle, key, value); 607 return 0; 608 } catch (IllegalArgumentException ex) { 609 return -1; 610 } 611 } 612 putStringFromNative(int nativeKey, String value)613 int putStringFromNative(int nativeKey, String value) { 614 String key = getKeyFromNativeKey(nativeKey); 615 if (!METADATA_KEYS_TYPE.containsKey(key) || 616 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 617 return -1; 618 } 619 mBundle.putString(key, value); 620 return 0; 621 } 622 putBitmapFromNative(int nativeKey, byte[] value)623 int putBitmapFromNative(int nativeKey, byte[] value) { 624 String key = getKeyFromNativeKey(nativeKey); 625 if (!METADATA_KEYS_TYPE.containsKey(key) || 626 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 627 return -1; 628 } 629 Bitmap bmp = null; 630 try { 631 bmp = BitmapFactory.decodeByteArray(value, 0, value.length); 632 if (bmp != null) { 633 mBundle.putParcelable(key, bmp); 634 return 0; 635 } 636 } catch (Exception e) { 637 } 638 return -1; 639 } 640 putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes)641 int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) { 642 String key = getKeyFromNativeKey(nativeKey); 643 if (!METADATA_KEYS_TYPE.containsKey(key) || 644 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { 645 return -1; 646 } 647 mBundle.putParcelable(key, new RadioMetadata.Clock( 648 utcEpochSeconds, timezoneOffsetInMinutes)); 649 return 0; 650 } 651 } 652