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