1 /* 2 * Copyright 2018 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 17 package com.android.media; 18 19 import static android.media.MediaMetadata2.*; 20 21 import android.annotation.Nullable; 22 import android.graphics.Bitmap; 23 import android.media.MediaMetadata2; 24 import android.media.MediaMetadata2.BitmapKey; 25 import android.media.MediaMetadata2.Builder; 26 import android.media.MediaMetadata2.LongKey; 27 import android.media.MediaMetadata2.RatingKey; 28 import android.media.MediaMetadata2.TextKey; 29 import android.media.Rating2; 30 import android.media.update.MediaMetadata2Provider; 31 import android.os.Bundle; 32 import android.util.ArrayMap; 33 import android.util.Log; 34 35 import java.util.Set; 36 37 public class MediaMetadata2Impl implements MediaMetadata2Provider { 38 private static final String TAG = "MediaMetadata2"; 39 40 static final int METADATA_TYPE_LONG = 0; 41 static final int METADATA_TYPE_TEXT = 1; 42 static final int METADATA_TYPE_BITMAP = 2; 43 static final int METADATA_TYPE_RATING = 3; 44 static final int METADATA_TYPE_FLOAT = 4; 45 static final ArrayMap<String, Integer> METADATA_KEYS_TYPE; 46 47 static { 48 METADATA_KEYS_TYPE = new ArrayMap<String, Integer>(); METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT)49 METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT)50 METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG)51 METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT)52 METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT)53 METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT)54 METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT)55 METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT)56 METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT)57 METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG)58 METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT)59 METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG)60 METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG)61 METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG)62 METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT)63 METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP)64 METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT)65 METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP)66 METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT)67 METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING)68 METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING); METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING)69 METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING); METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT)70 METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT)71 METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT)72 METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP)73 METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT)74 METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT)75 METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG)76 METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT)77 METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG)78 METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG); METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG)79 METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG); 80 } 81 82 private static final @TextKey 83 String[] PREFERRED_DESCRIPTION_ORDER = { 84 METADATA_KEY_TITLE, 85 METADATA_KEY_ARTIST, 86 METADATA_KEY_ALBUM, 87 METADATA_KEY_ALBUM_ARTIST, 88 METADATA_KEY_WRITER, 89 METADATA_KEY_AUTHOR, 90 METADATA_KEY_COMPOSER 91 }; 92 93 private static final @BitmapKey 94 String[] PREFERRED_BITMAP_ORDER = { 95 METADATA_KEY_DISPLAY_ICON, 96 METADATA_KEY_ART, 97 METADATA_KEY_ALBUM_ART 98 }; 99 100 private static final @TextKey 101 String[] PREFERRED_URI_ORDER = { 102 METADATA_KEY_DISPLAY_ICON_URI, 103 METADATA_KEY_ART_URI, 104 METADATA_KEY_ALBUM_ART_URI 105 }; 106 107 private final MediaMetadata2 mInstance; 108 private final Bundle mBundle; 109 MediaMetadata2Impl(Bundle bundle)110 public MediaMetadata2Impl(Bundle bundle) { 111 mInstance = new MediaMetadata2(this); 112 mBundle = bundle; 113 } 114 getInstance()115 public MediaMetadata2 getInstance() { 116 return mInstance; 117 } 118 119 @Override containsKey_impl(String key)120 public boolean containsKey_impl(String key) { 121 if (key == null) { 122 throw new IllegalArgumentException("key shouldn't be null"); 123 } 124 return mBundle.containsKey(key); 125 } 126 127 @Override getText_impl(@extKey String key)128 public CharSequence getText_impl(@TextKey String key) { 129 if (key == null) { 130 throw new IllegalArgumentException("key shouldn't be null"); 131 } 132 return mBundle.getCharSequence(key); 133 } 134 135 @Override getMediaId_impl()136 public @Nullable String getMediaId_impl() { 137 return mInstance.getString(METADATA_KEY_MEDIA_ID); 138 } 139 140 @Override getString_impl(@extKey String key)141 public String getString_impl(@TextKey String key) { 142 if (key == null) { 143 throw new IllegalArgumentException("key shouldn't be null"); 144 } 145 CharSequence text = mBundle.getCharSequence(key); 146 if (text != null) { 147 return text.toString(); 148 } 149 return null; 150 } 151 152 @Override getLong_impl(@ongKey String key)153 public long getLong_impl(@LongKey String key) { 154 if (key == null) { 155 throw new IllegalArgumentException("key shouldn't be null"); 156 } 157 return mBundle.getLong(key, 0); 158 } 159 160 @Override getRating_impl(@atingKey String key)161 public Rating2 getRating_impl(@RatingKey String key) { 162 if (key == null) { 163 throw new IllegalArgumentException("key shouldn't be null"); 164 } 165 // TODO(jaewan): Add backward compatibility 166 Rating2 rating = null; 167 try { 168 rating = Rating2.fromBundle(mBundle.getBundle(key)); 169 } catch (Exception e) { 170 // ignore, value was not a rating 171 Log.w(TAG, "Failed to retrieve a key as Rating.", e); 172 } 173 return rating; 174 } 175 176 @Override getFloat_impl(@loatKey String key)177 public float getFloat_impl(@FloatKey String key) { 178 if (key == null) { 179 throw new IllegalArgumentException("key shouldn't be null"); 180 } 181 return mBundle.getFloat(key); 182 } 183 184 @Override getBitmap_impl(@itmapKey String key)185 public Bitmap getBitmap_impl(@BitmapKey String key) { 186 if (key == null) { 187 throw new IllegalArgumentException("key shouldn't be null"); 188 } 189 Bitmap bmp = null; 190 try { 191 bmp = mBundle.getParcelable(key); 192 } catch (Exception e) { 193 // ignore, value was not a bitmap 194 Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); 195 } 196 return bmp; 197 } 198 199 @Override getExtras_impl()200 public Bundle getExtras_impl() { 201 try { 202 return mBundle.getBundle(METADATA_KEY_EXTRAS); 203 } catch (Exception e) { 204 // ignore, value was not an bundle 205 Log.w(TAG, "Failed to retrieve an extra"); 206 } 207 return null; 208 } 209 210 @Override size_impl()211 public int size_impl() { 212 return mBundle.size(); 213 } 214 215 @Override keySet_impl()216 public Set<String> keySet_impl() { 217 return mBundle.keySet(); 218 } 219 220 @Override toBundle_impl()221 public Bundle toBundle_impl() { 222 return mBundle; 223 } 224 fromBundle_impl(Bundle bundle)225 public static MediaMetadata2 fromBundle_impl(Bundle bundle) { 226 return (bundle == null) ? null : new MediaMetadata2Impl(bundle).getInstance(); 227 } 228 229 public static final class BuilderImpl implements MediaMetadata2Provider.BuilderProvider { 230 private final MediaMetadata2.Builder mInstance; 231 private final Bundle mBundle; 232 BuilderImpl(MediaMetadata2.Builder instance)233 public BuilderImpl(MediaMetadata2.Builder instance) { 234 mInstance = instance; 235 mBundle = new Bundle(); 236 } 237 BuilderImpl(MediaMetadata2.Builder instance, MediaMetadata2 source)238 public BuilderImpl(MediaMetadata2.Builder instance, MediaMetadata2 source) { 239 if (source == null) { 240 throw new IllegalArgumentException("source shouldn't be null"); 241 } 242 mInstance = instance; 243 mBundle = new Bundle(source.toBundle()); 244 } 245 BuilderImpl(int maxBitmapSize)246 public BuilderImpl(int maxBitmapSize) { 247 mInstance = new MediaMetadata2.Builder(this); 248 mBundle = new Bundle(); 249 250 for (String key : mBundle.keySet()) { 251 Object value = mBundle.get(key); 252 if (value instanceof Bitmap) { 253 Bitmap bmp = (Bitmap) value; 254 if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { 255 mInstance.putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); 256 } 257 } 258 } 259 } 260 261 @Override putText_impl(@extKey String key, CharSequence value)262 public Builder putText_impl(@TextKey String key, CharSequence value) { 263 if (key == null) { 264 throw new IllegalArgumentException("key shouldn't be null"); 265 } 266 if (METADATA_KEYS_TYPE.containsKey(key)) { 267 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 268 throw new IllegalArgumentException("The " + key 269 + " key cannot be used to put a CharSequence"); 270 } 271 } 272 mBundle.putCharSequence(key, value); 273 return mInstance; 274 } 275 276 @Override putString_impl(@extKey String key, String value)277 public Builder putString_impl(@TextKey String key, String value) { 278 if (key == null) { 279 throw new IllegalArgumentException("key shouldn't be null"); 280 } 281 if (METADATA_KEYS_TYPE.containsKey(key)) { 282 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 283 throw new IllegalArgumentException("The " + key 284 + " key cannot be used to put a String"); 285 } 286 } 287 mBundle.putCharSequence(key, value); 288 return mInstance; 289 } 290 291 @Override putLong_impl(@ongKey String key, long value)292 public Builder putLong_impl(@LongKey String key, long value) { 293 if (key == null) { 294 throw new IllegalArgumentException("key shouldn't be null"); 295 } 296 if (METADATA_KEYS_TYPE.containsKey(key)) { 297 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { 298 throw new IllegalArgumentException("The " + key 299 + " key cannot be used to put a long"); 300 } 301 } 302 mBundle.putLong(key, value); 303 return mInstance; 304 } 305 306 @Override putRating_impl(@atingKey String key, Rating2 value)307 public Builder putRating_impl(@RatingKey String key, Rating2 value) { 308 if (key == null) { 309 throw new IllegalArgumentException("key shouldn't be null"); 310 } 311 if (METADATA_KEYS_TYPE.containsKey(key)) { 312 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { 313 throw new IllegalArgumentException("The " + key 314 + " key cannot be used to put a Rating"); 315 } 316 } 317 mBundle.putBundle(key, value.toBundle()); 318 return mInstance; 319 } 320 321 @Override putBitmap_impl(@itmapKey String key, Bitmap value)322 public Builder putBitmap_impl(@BitmapKey String key, Bitmap value) { 323 if (key == null) { 324 throw new IllegalArgumentException("key shouldn't be null"); 325 } 326 if (METADATA_KEYS_TYPE.containsKey(key)) { 327 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 328 throw new IllegalArgumentException("The " + key 329 + " key cannot be used to put a Bitmap"); 330 } 331 } 332 mBundle.putParcelable(key, value); 333 return mInstance; 334 } 335 336 @Override putFloat_impl(@loatKey String key, float value)337 public Builder putFloat_impl(@FloatKey String key, float value) { 338 if (key == null) { 339 throw new IllegalArgumentException("key shouldn't be null"); 340 } 341 if (METADATA_KEYS_TYPE.containsKey(key)) { 342 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_FLOAT) { 343 throw new IllegalArgumentException("The " + key 344 + " key cannot be used to put a float"); 345 } 346 } 347 mBundle.putFloat(key, value); 348 return mInstance; 349 } 350 351 @Override setExtras_impl(Bundle bundle)352 public Builder setExtras_impl(Bundle bundle) { 353 mBundle.putBundle(METADATA_KEY_EXTRAS, bundle); 354 return mInstance; 355 } 356 357 @Override build_impl()358 public MediaMetadata2 build_impl() { 359 return new MediaMetadata2Impl(mBundle).getInstance(); 360 } 361 scaleBitmap(Bitmap bmp, int maxSize)362 private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { 363 float maxSizeF = maxSize; 364 float widthScale = maxSizeF / bmp.getWidth(); 365 float heightScale = maxSizeF / bmp.getHeight(); 366 float scale = Math.min(widthScale, heightScale); 367 int height = (int) (bmp.getHeight() * scale); 368 int width = (int) (bmp.getWidth() * scale); 369 return Bitmap.createScaledBitmap(bmp, width, height, true); 370 } 371 } 372 } 373 374