Home | History | Annotate | Download | only in radio
      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.content.ContentResolver;
     21 import android.graphics.Bitmap;
     22 import android.graphics.BitmapFactory;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.text.TextUtils;
     28 import android.util.ArrayMap;
     29 import android.util.Log;
     30 import android.util.SparseArray;
     31 
     32 import java.util.ArrayList;
     33 import java.util.Set;
     34 
     35 /**
     36  * Contains meta data about a radio program such as station name, song title, artist etc...
     37  * @hide
     38  */
     39 @SystemApi
     40 public final class RadioMetadata implements Parcelable {
     41     private static final String TAG = "RadioMetadata";
     42 
     43     /**
     44      * The RDS Program Information.
     45      */
     46     public static final String METADATA_KEY_RDS_PI = "android.hardware.radio.metadata.RDS_PI";
     47 
     48     /**
     49      * The RDS Program Service.
     50      */
     51     public static final String METADATA_KEY_RDS_PS = "android.hardware.radio.metadata.RDS_PS";
     52 
     53     /**
     54      * The RDS PTY.
     55      */
     56     public static final String METADATA_KEY_RDS_PTY = "android.hardware.radio.metadata.RDS_PTY";
     57 
     58     /**
     59      * The RBDS PTY.
     60      */
     61     public static final String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY";
     62 
     63     /**
     64      * The RBDS Radio Text.
     65      */
     66     public static final String METADATA_KEY_RDS_RT = "android.hardware.radio.metadata.RDS_RT";
     67 
     68     /**
     69      * The song title.
     70      */
     71     public static final String METADATA_KEY_TITLE = "android.hardware.radio.metadata.TITLE";
     72 
     73     /**
     74      * The artist name.
     75      */
     76     public static final String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST";
     77 
     78     /**
     79      * The album name.
     80      */
     81     public static final String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM";
     82 
     83     /**
     84      * The music genre.
     85      */
     86     public static final String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE";
     87 
     88     /**
     89      * The radio station icon {@link Bitmap}.
     90      */
     91     public static final String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON";
     92 
     93     /**
     94      * The artwork for the song/album {@link Bitmap}.
     95      */
     96     public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
     97 
     98 
     99     private static final int METADATA_TYPE_INVALID = -1;
    100     private static final int METADATA_TYPE_INT = 0;
    101     private static final int METADATA_TYPE_TEXT = 1;
    102     private static final int METADATA_TYPE_BITMAP = 2;
    103 
    104     private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
    105 
    106     static {
    107         METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
    108         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_TEXT);
    109         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT);
    110         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT);
    111         METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT);
    112         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT);
    113         METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
    114         METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
    115         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
    116         METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
    117         METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
    118         METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
    119     }
    120 
    121     // keep in sync with: system/media/radio/include/system/radio_metadata.h
    122     private static final int NATIVE_KEY_INVALID     = -1;
    123     private static final int NATIVE_KEY_RDS_PI      = 0;
    124     private static final int NATIVE_KEY_RDS_PS      = 1;
    125     private static final int NATIVE_KEY_RDS_PTY     = 2;
    126     private static final int NATIVE_KEY_RBDS_PTY    = 3;
    127     private static final int NATIVE_KEY_RDS_RT      = 4;
    128     private static final int NATIVE_KEY_TITLE       = 5;
    129     private static final int NATIVE_KEY_ARTIST      = 6;
    130     private static final int NATIVE_KEY_ALBUM       = 7;
    131     private static final int NATIVE_KEY_GENRE       = 8;
    132     private static final int NATIVE_KEY_ICON        = 9;
    133     private static final int NATIVE_KEY_ART         = 10;
    134 
    135     private static final SparseArray<String> NATIVE_KEY_MAPPING;
    136 
    137     static {
    138         NATIVE_KEY_MAPPING = new SparseArray<String>();
    139         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI);
    140         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS);
    141         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY);
    142         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY);
    143         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT);
    144         NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE);
    145         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST);
    146         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM);
    147         NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE);
    148         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON);
    149         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART);
    150     }
    151 
    152     private final Bundle mBundle;
    153 
    154     RadioMetadata() {
    155         mBundle = new Bundle();
    156     }
    157 
    158     private RadioMetadata(Bundle bundle) {
    159         mBundle = new Bundle(bundle);
    160     }
    161 
    162     private RadioMetadata(Parcel in) {
    163         mBundle = in.readBundle();
    164     }
    165 
    166     /**
    167      * Returns {@code true} if the given key is contained in the meta data
    168      *
    169      * @param key a String key
    170      * @return {@code true} if the key exists in this meta data, {@code false} otherwise
    171      */
    172     public boolean containsKey(String key) {
    173         return mBundle.containsKey(key);
    174     }
    175 
    176     /**
    177      * Returns the text value associated with the given key as a String, or null
    178      * if the key is not found in the meta data.
    179      *
    180      * @param key The key the value is stored under
    181      * @return a String value, or null
    182      */
    183     public String getString(String key) {
    184         return mBundle.getString(key);
    185     }
    186 
    187     /**
    188      * Returns the value associated with the given key,
    189      * or 0 if the key is not found in the meta data.
    190      *
    191      * @param key The key the value is stored under
    192      * @return an int value
    193      */
    194     public int getInt(String key) {
    195         return mBundle.getInt(key, 0);
    196     }
    197 
    198     /**
    199      * Returns a {@link Bitmap} for the given key or null if the key is not found in the meta data.
    200      *
    201      * @param key The key the value is stored under
    202      * @return a {@link Bitmap} or null
    203      */
    204     public Bitmap getBitmap(String key) {
    205         Bitmap bmp = null;
    206         try {
    207             bmp = mBundle.getParcelable(key);
    208         } catch (Exception e) {
    209             // ignore, value was not a bitmap
    210             Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
    211         }
    212         return bmp;
    213     }
    214 
    215     @Override
    216     public int describeContents() {
    217         return 0;
    218     }
    219 
    220     @Override
    221     public void writeToParcel(Parcel dest, int flags) {
    222         dest.writeBundle(mBundle);
    223     }
    224 
    225     /**
    226      * Returns the number of fields in this meta data.
    227      *
    228      * @return the number of fields in the meta data.
    229      */
    230     public int size() {
    231         return mBundle.size();
    232     }
    233 
    234     /**
    235      * Returns a Set containing the Strings used as keys in this meta data.
    236      *
    237      * @return a Set of String keys
    238      */
    239     public Set<String> keySet() {
    240         return mBundle.keySet();
    241     }
    242 
    243     /**
    244      * Helper for getting the String key used by {@link RadioMetadata} from the
    245      * corrsponding native integer key.
    246      *
    247      * @param editorKey The key used by the editor
    248      * @return the key used by this class or null if no mapping exists
    249      * @hide
    250      */
    251     public static String getKeyFromNativeKey(int nativeKey) {
    252         return NATIVE_KEY_MAPPING.get(nativeKey, null);
    253     }
    254 
    255     public static final Parcelable.Creator<RadioMetadata> CREATOR =
    256             new Parcelable.Creator<RadioMetadata>() {
    257                 @Override
    258                 public RadioMetadata createFromParcel(Parcel in) {
    259                     return new RadioMetadata(in);
    260                 }
    261 
    262                 @Override
    263                 public RadioMetadata[] newArray(int size) {
    264                     return new RadioMetadata[size];
    265                 }
    266             };
    267 
    268     /**
    269      * Use to build RadioMetadata objects.
    270      */
    271     public static final class Builder {
    272         private final Bundle mBundle;
    273 
    274         /**
    275          * Create an empty Builder. Any field that should be included in the
    276          * {@link RadioMetadata} must be added.
    277          */
    278         public Builder() {
    279             mBundle = new Bundle();
    280         }
    281 
    282         /**
    283          * Create a Builder using a {@link RadioMetadata} instance to set the
    284          * initial values. All fields in the source meta data will be included in
    285          * the new meta data. Fields can be overwritten by adding the same key.
    286          *
    287          * @param source
    288          */
    289         public Builder(RadioMetadata source) {
    290             mBundle = new Bundle(source.mBundle);
    291         }
    292 
    293         /**
    294          * Create a Builder using a {@link RadioMetadata} instance to set
    295          * initial values, but replace bitmaps with a scaled down copy if they
    296          * are larger than maxBitmapSize.
    297          *
    298          * @param source The original meta data to copy.
    299          * @param maxBitmapSize The maximum height/width for bitmaps contained
    300          *            in the meta data.
    301          * @hide
    302          */
    303         public Builder(RadioMetadata source, int maxBitmapSize) {
    304             this(source);
    305             for (String key : mBundle.keySet()) {
    306                 Object value = mBundle.get(key);
    307                 if (value != null && value instanceof Bitmap) {
    308                     Bitmap bmp = (Bitmap) value;
    309                     if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
    310                         putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
    311                     }
    312                 }
    313             }
    314         }
    315 
    316         /**
    317          * Put a String value into the meta data. Custom keys may be used, but if
    318          * the METADATA_KEYs defined in this class are used they may only be one
    319          * of the following:
    320          * <ul>
    321          * <li>{@link #METADATA_KEY_RDS_PI}</li>
    322          * <li>{@link #METADATA_KEY_RDS_PS}</li>
    323          * <li>{@link #METADATA_KEY_RDS_RT}</li>
    324          * <li>{@link #METADATA_KEY_TITLE}</li>
    325          * <li>{@link #METADATA_KEY_ARTIST}</li>
    326          * <li>{@link #METADATA_KEY_ALBUM}</li>
    327          * <li>{@link #METADATA_KEY_GENRE}</li>
    328          * </ul>
    329          *
    330          * @param key The key for referencing this value
    331          * @param value The String value to store
    332          * @return the same Builder instance
    333          */
    334         public Builder putString(String key, String value) {
    335             if (!METADATA_KEYS_TYPE.containsKey(key) ||
    336                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
    337                 throw new IllegalArgumentException("The " + key
    338                         + " key cannot be used to put a String");
    339             }
    340             mBundle.putString(key, value);
    341             return this;
    342         }
    343 
    344         /**
    345          * Put an int value into the meta data. Custom keys may be used, but if
    346          * the METADATA_KEYs defined in this class are used they may only be one
    347          * of the following:
    348          * <ul>
    349          * <li>{@link #METADATA_KEY_RDS_PTY}</li>
    350          * <li>{@link #METADATA_KEY_RBDS_PTY}</li>
    351          * </ul>
    352          *
    353          * @param key The key for referencing this value
    354          * @param value The int value to store
    355          * @return the same Builder instance
    356          */
    357         public Builder putInt(String key, int value) {
    358             if (!METADATA_KEYS_TYPE.containsKey(key) ||
    359                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
    360                 throw new IllegalArgumentException("The " + key
    361                         + " key cannot be used to put a long");
    362             }
    363             mBundle.putInt(key, value);
    364             return this;
    365         }
    366 
    367         /**
    368          * Put a {@link Bitmap} into the meta data. Custom keys may be used, but
    369          * if the METADATA_KEYs defined in this class are used they may only be
    370          * one of the following:
    371          * <ul>
    372          * <li>{@link #METADATA_KEY_ICON}</li>
    373          * <li>{@link #METADATA_KEY_ART}</li>
    374          * </ul>
    375          * <p>
    376          *
    377          * @param key The key for referencing this value
    378          * @param value The Bitmap to store
    379          * @return the same Builder instance
    380          */
    381         public Builder putBitmap(String key, Bitmap value) {
    382             if (!METADATA_KEYS_TYPE.containsKey(key) ||
    383                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
    384                 throw new IllegalArgumentException("The " + key
    385                         + " key cannot be used to put a Bitmap");
    386             }
    387             mBundle.putParcelable(key, value);
    388             return this;
    389         }
    390 
    391         /**
    392          * Creates a {@link RadioMetadata} instance with the specified fields.
    393          *
    394          * @return a new {@link RadioMetadata} object
    395          */
    396         public RadioMetadata build() {
    397             return new RadioMetadata(mBundle);
    398         }
    399 
    400         private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
    401             float maxSizeF = maxSize;
    402             float widthScale = maxSizeF / bmp.getWidth();
    403             float heightScale = maxSizeF / bmp.getHeight();
    404             float scale = Math.min(widthScale, heightScale);
    405             int height = (int) (bmp.getHeight() * scale);
    406             int width = (int) (bmp.getWidth() * scale);
    407             return Bitmap.createScaledBitmap(bmp, width, height, true);
    408         }
    409     }
    410 
    411     int putIntFromNative(int nativeKey, int value) {
    412         String key = getKeyFromNativeKey(nativeKey);
    413         if (!METADATA_KEYS_TYPE.containsKey(key) ||
    414                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
    415             return -1;
    416         }
    417         mBundle.putInt(key, value);
    418         return 0;
    419     }
    420 
    421     int putStringFromNative(int nativeKey, String value) {
    422         String key = getKeyFromNativeKey(nativeKey);
    423         if (!METADATA_KEYS_TYPE.containsKey(key) ||
    424                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
    425             return -1;
    426         }
    427         mBundle.putString(key, value);
    428         return 0;
    429     }
    430 
    431     int putBitmapFromNative(int nativeKey, byte[] value) {
    432         String key = getKeyFromNativeKey(nativeKey);
    433         if (!METADATA_KEYS_TYPE.containsKey(key) ||
    434                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
    435             return -1;
    436         }
    437         Bitmap bmp = null;
    438         try {
    439             bmp = BitmapFactory.decodeByteArray(value, 0, value.length);
    440         } catch (Exception e) {
    441         } finally {
    442             if (bmp == null) {
    443                 return -1;
    444             }
    445             mBundle.putParcelable(key, bmp);
    446             return 0;
    447         }
    448     }
    449 }
    450