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