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.
77      */
apply()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      */
clear()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      */
addEditableKey(int key)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      */
removeEditableKeys()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      */
getEditableKeys()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      */
putString(int key, String value)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      */
putLong(int key, long value)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      */
putBitmap(int key, Bitmap bitmap)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      */
putObject(int key, Object value)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      */
getLong(int key, long defaultValue)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      */
getString(int key, String defaultValue)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      */
getBitmap(int key, Bitmap defaultValue)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      */
getObject(int key, Object defaultValue)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
METADATA_KEYS_TYPE.put( MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG)445         METADATA_KEYS_TYPE.put(
446                 MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG)447         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG)448         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG)449         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG);
450         // keys with String values
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_STRING)451         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put( MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_STRING)452         METADATA_KEYS_TYPE.put(
453                 MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_STRING)454         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_STRING)455         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_STRING)456         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put( MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_STRING)457         METADATA_KEYS_TYPE.put(
458                 MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_STRING)459         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_STRING)460         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_STRING)461         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_STRING)462         METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_STRING);
463         // keys with Bitmap values
METADATA_KEYS_TYPE.put(BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP)464         METADATA_KEYS_TYPE.put(BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP);
465         // keys with Rating values
METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING)466         METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING);
METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING)467         METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING);
468     }
469 }
470