Home | History | Annotate | Download | only in media
      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