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