1 /* 2 * Copyright (C) 2013 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 android.media; 18 19 import android.graphics.Bitmap; 20 import android.os.Bundle; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.util.Log; 24 import android.util.SparseIntArray; 25 26 /** 27 * An abstract class for editing and storing metadata that can be published by 28 * {@link RemoteControlClient}. See the {@link RemoteControlClient#editMetadata(boolean)} 29 * method to instantiate a {@link RemoteControlClient.MetadataEditor} object. 30 */ 31 public abstract class MediaMetadataEditor { 32 33 private final static String TAG = "MediaMetadataEditor"; 34 /** 35 * @hide 36 */ 37 protected MediaMetadataEditor() { 38 } 39 40 // Public keys for metadata used by RemoteControlClient and RemoteController. 41 // Note that these keys are defined here, and not in MediaMetadataRetriever 42 // because they are not supported by the MediaMetadataRetriever features. 43 /** 44 * The metadata key for the content artwork / album art. 45 */ 46 public final static int BITMAP_KEY_ARTWORK = 47 RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK; 48 49 /** 50 * The metadata key for the content's average rating, not the user's rating. 51 * The value associated with this key is a {@link Rating} instance. 52 * @see #RATING_KEY_BY_USER 53 */ 54 public final static int RATING_KEY_BY_OTHERS = 101; 55 56 /** 57 * The metadata key for the content's user rating. 58 * The value associated with this key is a {@link Rating} instance. 59 * This key can be flagged as "editable" (with {@link #addEditableKey(int)}) to enable 60 * receiving user rating values through the 61 * {@link android.media.RemoteControlClient.OnMetadataUpdateListener} interface. 62 */ 63 public final static int RATING_KEY_BY_USER = 0x10000001; 64 65 /** 66 * @hide 67 * Editable key mask 68 */ 69 public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF; 70 71 72 /** 73 * Applies all of the metadata changes that have been set since the MediaMetadataEditor instance 74 * was created or since {@link #clear()} was called. 75 */ 76 public abstract void apply(); 77 78 79 /** 80 * @hide 81 * Mask of editable keys. 82 */ 83 protected long mEditableKeys; 84 85 /** 86 * @hide 87 */ 88 protected boolean mMetadataChanged = false; 89 90 /** 91 * @hide 92 */ 93 protected boolean mApplied = false; 94 95 /** 96 * @hide 97 */ 98 protected boolean mArtworkChanged = false; 99 100 /** 101 * @hide 102 */ 103 protected Bitmap mEditorArtwork; 104 105 /** 106 * @hide 107 */ 108 protected Bundle mEditorMetadata; 109 110 111 /** 112 * Clears all the pending metadata changes set since the MediaMetadataEditor instance was 113 * created or since this method was last called. 114 * Note that clearing the metadata doesn't reset the editable keys 115 * (use {@link #removeEditableKeys()} instead). 116 */ 117 public synchronized void clear() { 118 if (mApplied) { 119 Log.e(TAG, "Can't clear a previously applied MediaMetadataEditor"); 120 return; 121 } 122 mEditorMetadata.clear(); 123 mEditorArtwork = null; 124 } 125 126 /** 127 * Flags the given key as being editable. 128 * This should only be used by metadata publishers, such as {@link RemoteControlClient}, 129 * which will declare the metadata field as eligible to be updated, with new values 130 * received through the {@link RemoteControlClient.OnMetadataUpdateListener} interface. 131 * @param key the type of metadata that can be edited. The supported key is 132 * {@link #RATING_KEY_BY_USER}. 133 */ 134 public synchronized void addEditableKey(int key) { 135 if (mApplied) { 136 Log.e(TAG, "Can't change editable keys of a previously applied MetadataEditor"); 137 return; 138 } 139 // only one editable key at the moment, so we're not wasting memory on an array 140 // of editable keys to check the validity of the key, just hardcode the supported key. 141 if (key == RATING_KEY_BY_USER) { 142 mEditableKeys |= (KEY_EDITABLE_MASK & key); 143 mMetadataChanged = true; 144 } else { 145 Log.e(TAG, "Metadata key " + key + " cannot be edited"); 146 } 147 } 148 149 /** 150 * Causes all metadata fields to be read-only. 151 */ 152 public synchronized void removeEditableKeys() { 153 if (mApplied) { 154 Log.e(TAG, "Can't remove all editable keys of a previously applied MetadataEditor"); 155 return; 156 } 157 if (mEditableKeys != 0) { 158 mEditableKeys = 0; 159 mMetadataChanged = true; 160 } 161 } 162 163 /** 164 * Retrieves the keys flagged as editable. 165 * @return null if there are no editable keys, or an array containing the keys. 166 */ 167 public synchronized int[] getEditableKeys() { 168 // only one editable key supported here 169 if (mEditableKeys == RATING_KEY_BY_USER) { 170 int[] keys = { RATING_KEY_BY_USER }; 171 return keys; 172 } else { 173 return null; 174 } 175 } 176 177 /** 178 * Adds textual information. 179 * Note that none of the information added after {@link #apply()} has been called, 180 * will be available to consumers of metadata stored by the MediaMetadataEditor. 181 * @param key The identifier of a the metadata field to set. Valid values are 182 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, 183 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, 184 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, 185 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, 186 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, 187 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, 188 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, 189 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, 190 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, 191 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. 192 * @param value The text for the given key, or {@code null} to signify there is no valid 193 * information for the field. 194 * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put 195 * calls together. 196 */ 197 public synchronized MediaMetadataEditor putString(int key, String value) 198 throws IllegalArgumentException { 199 if (mApplied) { 200 Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); 201 return this; 202 } 203 if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) { 204 throw(new IllegalArgumentException("Invalid type 'String' for key "+ key)); 205 } 206 mEditorMetadata.putString(String.valueOf(key), value); 207 mMetadataChanged = true; 208 return this; 209 } 210 211 /** 212 * Adds numerical information. 213 * Note that none of the information added after {@link #apply()} has been called 214 * will be available to consumers of metadata stored by the MediaMetadataEditor. 215 * @param key the identifier of a the metadata field to set. Valid values are 216 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, 217 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, 218 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value 219 * expressed in milliseconds), 220 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. 221 * @param value The long value for the given key 222 * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put 223 * calls together. 224 * @throws IllegalArgumentException 225 */ 226 public synchronized MediaMetadataEditor putLong(int key, long value) 227 throws IllegalArgumentException { 228 if (mApplied) { 229 Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); 230 return this; 231 } 232 if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) { 233 throw(new IllegalArgumentException("Invalid type 'long' for key "+ key)); 234 } 235 mEditorMetadata.putLong(String.valueOf(key), value); 236 mMetadataChanged = true; 237 return this; 238 } 239 240 /** 241 * Adds image. 242 * @param key the identifier of the bitmap to set. The only valid value is 243 * {@link #BITMAP_KEY_ARTWORK} 244 * @param bitmap The bitmap for the artwork, or null if there isn't any. 245 * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put 246 * calls together. 247 * @throws IllegalArgumentException 248 * @see android.graphics.Bitmap 249 */ 250 public synchronized MediaMetadataEditor putBitmap(int key, Bitmap bitmap) 251 throws IllegalArgumentException { 252 if (mApplied) { 253 Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); 254 return this; 255 } 256 if (key != BITMAP_KEY_ARTWORK) { 257 throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); 258 } 259 mEditorArtwork = bitmap; 260 mArtworkChanged = true; 261 return this; 262 } 263 264 /** 265 * Adds information stored as an instance. 266 * Note that none of the information added after {@link #apply()} has been called 267 * will be available to consumers of metadata stored by the MediaMetadataEditor. 268 * @param key the identifier of a the metadata field to set. Valid keys for a: 269 * <ul> 270 * <li>{@link Bitmap} object are {@link #BITMAP_KEY_ARTWORK},</li> 271 * <li>{@link String} object are the same as for {@link #putString(int, String)}</li> 272 * <li>{@link Long} object are the same as for {@link #putLong(int, long)}</li> 273 * <li>{@link Rating} object are {@link #RATING_KEY_BY_OTHERS} 274 * and {@link #RATING_KEY_BY_USER}.</li> 275 * </ul> 276 * @param value the metadata to add. 277 * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put 278 * calls together. 279 * @throws IllegalArgumentException 280 */ 281 public synchronized MediaMetadataEditor putObject(int key, Object value) 282 throws IllegalArgumentException { 283 if (mApplied) { 284 Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); 285 return this; 286 } 287 switch(METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) { 288 case METADATA_TYPE_LONG: 289 if (value instanceof Long) { 290 return putLong(key, ((Long)value).longValue()); 291 } else { 292 throw(new IllegalArgumentException("Not a non-null Long for key "+ key)); 293 } 294 case METADATA_TYPE_STRING: 295 if ((value == null) || (value instanceof String)) { 296 return putString(key, (String) value); 297 } else { 298 throw(new IllegalArgumentException("Not a String for key "+ key)); 299 } 300 case METADATA_TYPE_RATING: 301 mEditorMetadata.putParcelable(String.valueOf(key), (Parcelable)value); 302 mMetadataChanged = true; 303 break; 304 case METADATA_TYPE_BITMAP: 305 if ((value == null) || (value instanceof Bitmap)) { 306 return putBitmap(key, (Bitmap) value); 307 } else { 308 throw(new IllegalArgumentException("Not a Bitmap for key "+ key)); 309 } 310 default: 311 throw(new IllegalArgumentException("Invalid key "+ key)); 312 } 313 return this; 314 } 315 316 317 /** 318 * Returns the long value for the key. 319 * @param key one of the keys supported in {@link #putLong(int, long)} 320 * @param defaultValue the value returned if the key is not present 321 * @return the long value for the key, or the supplied default value if the key is not present 322 * @throws IllegalArgumentException 323 */ 324 public synchronized long getLong(int key, long defaultValue) 325 throws IllegalArgumentException { 326 if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) { 327 throw(new IllegalArgumentException("Invalid type 'long' for key "+ key)); 328 } 329 return mEditorMetadata.getLong(String.valueOf(key), defaultValue); 330 } 331 332 /** 333 * Returns the {@link String} value for the key. 334 * @param key one of the keys supported in {@link #putString(int, String)} 335 * @param defaultValue the value returned if the key is not present 336 * @return the {@link String} value for the key, or the supplied default value if the key is 337 * not present 338 * @throws IllegalArgumentException 339 */ 340 public synchronized String getString(int key, String defaultValue) 341 throws IllegalArgumentException { 342 if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) { 343 throw(new IllegalArgumentException("Invalid type 'String' for key "+ key)); 344 } 345 return mEditorMetadata.getString(String.valueOf(key), defaultValue); 346 } 347 348 /** 349 * Returns the {@link Bitmap} value for the key. 350 * @param key the {@link #BITMAP_KEY_ARTWORK} key 351 * @param defaultValue the value returned if the key is not present 352 * @return the {@link Bitmap} value for the key, or the supplied default value if the key is 353 * not present 354 * @throws IllegalArgumentException 355 */ 356 public synchronized Bitmap getBitmap(int key, Bitmap defaultValue) 357 throws IllegalArgumentException { 358 if (key != BITMAP_KEY_ARTWORK) { 359 throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); 360 } 361 return (mEditorArtwork != null ? mEditorArtwork : defaultValue); 362 } 363 364 /** 365 * Returns an object representation of the value for the key 366 * @param key one of the keys supported in {@link #putObject(int, Object)} 367 * @param defaultValue the value returned if the key is not present 368 * @return the object for the key, as a {@link Long}, {@link Bitmap}, {@link String}, or 369 * {@link Rating} depending on the key value, or the supplied default value if the key is 370 * not present 371 * @throws IllegalArgumentException 372 */ 373 public synchronized Object getObject(int key, Object defaultValue) 374 throws IllegalArgumentException { 375 switch (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) { 376 case METADATA_TYPE_LONG: 377 if (mEditorMetadata.containsKey(String.valueOf(key))) { 378 return mEditorMetadata.getLong(String.valueOf(key)); 379 } else { 380 return defaultValue; 381 } 382 case METADATA_TYPE_STRING: 383 if (mEditorMetadata.containsKey(String.valueOf(key))) { 384 return mEditorMetadata.getString(String.valueOf(key)); 385 } else { 386 return defaultValue; 387 } 388 case METADATA_TYPE_RATING: 389 if (mEditorMetadata.containsKey(String.valueOf(key))) { 390 return mEditorMetadata.getParcelable(String.valueOf(key)); 391 } else { 392 return defaultValue; 393 } 394 case METADATA_TYPE_BITMAP: 395 // only one key for Bitmap supported, value is not stored in mEditorMetadata Bundle 396 if (key == BITMAP_KEY_ARTWORK) { 397 return (mEditorArtwork != null ? mEditorArtwork : defaultValue); 398 } // else: fall through to invalid key handling 399 default: 400 throw(new IllegalArgumentException("Invalid key "+ key)); 401 } 402 } 403 404 405 /** 406 * @hide 407 */ 408 protected static final int METADATA_TYPE_INVALID = -1; 409 /** 410 * @hide 411 */ 412 protected static final int METADATA_TYPE_LONG = 0; 413 414 /** 415 * @hide 416 */ 417 protected static final int METADATA_TYPE_STRING = 1; 418 419 /** 420 * @hide 421 */ 422 protected static final int METADATA_TYPE_BITMAP = 2; 423 424 /** 425 * @hide 426 */ 427 protected static final int METADATA_TYPE_RATING = 3; 428 429 /** 430 * @hide 431 */ 432 protected static final SparseIntArray METADATA_KEYS_TYPE; 433 434 static { 435 METADATA_KEYS_TYPE = new SparseIntArray(17); 436 // NOTE: if adding to the list below, make sure you increment the array initialization size 437 // keys with long values 438 METADATA_KEYS_TYPE.put( 439 MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG); 440 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); 441 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG); 442 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG); 443 // keys with String values 444 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_STRING); 445 METADATA_KEYS_TYPE.put( 446 MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_STRING); 447 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_STRING); 448 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_STRING); 449 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_STRING); 450 METADATA_KEYS_TYPE.put( 451 MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_STRING); 452 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_STRING); 453 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_STRING); 454 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_STRING); 455 METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_STRING); 456 // keys with Bitmap values 457 METADATA_KEYS_TYPE.put(BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP); 458 // keys with Rating values 459 METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING); 460 METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING); 461 } 462 } 463