| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media; |
| |
| import android.graphics.Bitmap; |
| import android.media.session.MediaSession; |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| import android.util.Log; |
| import android.util.SparseIntArray; |
| |
| /** |
| * An abstract class for editing and storing metadata that can be published by |
| * {@link RemoteControlClient}. See the {@link RemoteControlClient#editMetadata(boolean)} |
| * method to instantiate a {@link RemoteControlClient.MetadataEditor} object. |
| * |
| * @deprecated Use {@link MediaMetadata} instead together with {@link MediaSession}. |
| */ |
| @Deprecated public abstract class MediaMetadataEditor { |
| |
| private final static String TAG = "MediaMetadataEditor"; |
| /** |
| * @hide |
| */ |
| protected MediaMetadataEditor() { |
| } |
| |
| // Public keys for metadata used by RemoteControlClient and RemoteController. |
| // Note that these keys are defined here, and not in MediaMetadataRetriever |
| // because they are not supported by the MediaMetadataRetriever features. |
| /** |
| * The metadata key for the content artwork / album art. |
| */ |
| public final static int BITMAP_KEY_ARTWORK = |
| RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK; |
| |
| /** |
| * The metadata key for the content's average rating, not the user's rating. |
| * The value associated with this key is a {@link Rating} instance. |
| * @see #RATING_KEY_BY_USER |
| */ |
| public final static int RATING_KEY_BY_OTHERS = 101; |
| |
| /** |
| * The metadata key for the content's user rating. |
| * The value associated with this key is a {@link Rating} instance. |
| * This key can be flagged as "editable" (with {@link #addEditableKey(int)}) to enable |
| * receiving user rating values through the |
| * {@link android.media.RemoteControlClient.OnMetadataUpdateListener} interface. |
| */ |
| public final static int RATING_KEY_BY_USER = 0x10000001; |
| |
| /** |
| * @hide |
| * Editable key mask |
| */ |
| public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF; |
| |
| |
| /** |
| * Applies all of the metadata changes that have been set since the MediaMetadataEditor instance |
| * was created or since {@link #clear()} was called. |
| */ |
| public abstract void apply(); |
| |
| |
| /** |
| * @hide |
| * Mask of editable keys. |
| */ |
| protected long mEditableKeys; |
| |
| /** |
| * @hide |
| */ |
| protected boolean mMetadataChanged = false; |
| |
| /** |
| * @hide |
| */ |
| protected boolean mApplied = false; |
| |
| /** |
| * @hide |
| */ |
| protected boolean mArtworkChanged = false; |
| |
| /** |
| * @hide |
| */ |
| protected Bitmap mEditorArtwork; |
| |
| /** |
| * @hide |
| */ |
| protected Bundle mEditorMetadata; |
| |
| /** |
| * @hide |
| */ |
| protected MediaMetadata.Builder mMetadataBuilder; |
| |
| /** |
| * Clears all the pending metadata changes set since the MediaMetadataEditor instance was |
| * created or since this method was last called. |
| * Note that clearing the metadata doesn't reset the editable keys |
| * (use {@link #removeEditableKeys()} instead). |
| */ |
| public synchronized void clear() { |
| if (mApplied) { |
| Log.e(TAG, "Can't clear a previously applied MediaMetadataEditor"); |
| return; |
| } |
| mEditorMetadata.clear(); |
| mEditorArtwork = null; |
| mMetadataBuilder = new MediaMetadata.Builder(); |
| } |
| |
| /** |
| * Flags the given key as being editable. |
| * This should only be used by metadata publishers, such as {@link RemoteControlClient}, |
| * which will declare the metadata field as eligible to be updated, with new values |
| * received through the {@link RemoteControlClient.OnMetadataUpdateListener} interface. |
| * @param key the type of metadata that can be edited. The supported key is |
| * {@link #RATING_KEY_BY_USER}. |
| */ |
| public synchronized void addEditableKey(int key) { |
| if (mApplied) { |
| Log.e(TAG, "Can't change editable keys of a previously applied MetadataEditor"); |
| return; |
| } |
| // only one editable key at the moment, so we're not wasting memory on an array |
| // of editable keys to check the validity of the key, just hardcode the supported key. |
| if (key == RATING_KEY_BY_USER) { |
| mEditableKeys |= (KEY_EDITABLE_MASK & key); |
| mMetadataChanged = true; |
| } else { |
| Log.e(TAG, "Metadata key " + key + " cannot be edited"); |
| } |
| } |
| |
| /** |
| * Causes all metadata fields to be read-only. |
| */ |
| public synchronized void removeEditableKeys() { |
| if (mApplied) { |
| Log.e(TAG, "Can't remove all editable keys of a previously applied MetadataEditor"); |
| return; |
| } |
| if (mEditableKeys != 0) { |
| mEditableKeys = 0; |
| mMetadataChanged = true; |
| } |
| } |
| |
| /** |
| * Retrieves the keys flagged as editable. |
| * @return null if there are no editable keys, or an array containing the keys. |
| */ |
| public synchronized int[] getEditableKeys() { |
| // only one editable key supported here |
| if (mEditableKeys == RATING_KEY_BY_USER) { |
| int[] keys = { RATING_KEY_BY_USER }; |
| return keys; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Adds textual information. |
| * Note that none of the information added after {@link #apply()} has been called, |
| * will be available to consumers of metadata stored by the MediaMetadataEditor. |
| * @param key The identifier of a the metadata field to set. Valid values are |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. |
| * @param value The text for the given key, or {@code null} to signify there is no valid |
| * information for the field. |
| * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put |
| * calls together. |
| */ |
| public synchronized MediaMetadataEditor putString(int key, String value) |
| throws IllegalArgumentException { |
| if (mApplied) { |
| Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); |
| return this; |
| } |
| if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) { |
| throw(new IllegalArgumentException("Invalid type 'String' for key "+ key)); |
| } |
| mEditorMetadata.putString(String.valueOf(key), value); |
| mMetadataChanged = true; |
| return this; |
| } |
| |
| /** |
| * Adds numerical information. |
| * Note that none of the information added after {@link #apply()} has been called |
| * will be available to consumers of metadata stored by the MediaMetadataEditor. |
| * @param key the identifier of a the metadata field to set. Valid values are |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value |
| * expressed in milliseconds), |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. |
| * @param value The long value for the given key |
| * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put |
| * calls together. |
| * @throws IllegalArgumentException |
| */ |
| public synchronized MediaMetadataEditor putLong(int key, long value) |
| throws IllegalArgumentException { |
| if (mApplied) { |
| Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); |
| return this; |
| } |
| if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) { |
| throw(new IllegalArgumentException("Invalid type 'long' for key "+ key)); |
| } |
| mEditorMetadata.putLong(String.valueOf(key), value); |
| mMetadataChanged = true; |
| return this; |
| } |
| |
| /** |
| * Adds image. |
| * @param key the identifier of the bitmap to set. The only valid value is |
| * {@link #BITMAP_KEY_ARTWORK} |
| * @param bitmap The bitmap for the artwork, or null if there isn't any. |
| * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put |
| * calls together. |
| * @throws IllegalArgumentException |
| * @see android.graphics.Bitmap |
| */ |
| public synchronized MediaMetadataEditor putBitmap(int key, Bitmap bitmap) |
| throws IllegalArgumentException { |
| if (mApplied) { |
| Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); |
| return this; |
| } |
| if (key != BITMAP_KEY_ARTWORK) { |
| throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); |
| } |
| mEditorArtwork = bitmap; |
| mArtworkChanged = true; |
| return this; |
| } |
| |
| /** |
| * Adds information stored as an instance. |
| * Note that none of the information added after {@link #apply()} has been called |
| * will be available to consumers of metadata stored by the MediaMetadataEditor. |
| * @param key the identifier of a the metadata field to set. Valid keys for a: |
| * <ul> |
| * <li>{@link Bitmap} object are {@link #BITMAP_KEY_ARTWORK},</li> |
| * <li>{@link String} object are the same as for {@link #putString(int, String)}</li> |
| * <li>{@link Long} object are the same as for {@link #putLong(int, long)}</li> |
| * <li>{@link Rating} object are {@link #RATING_KEY_BY_OTHERS} |
| * and {@link #RATING_KEY_BY_USER}.</li> |
| * </ul> |
| * @param value the metadata to add. |
| * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put |
| * calls together. |
| * @throws IllegalArgumentException |
| */ |
| public synchronized MediaMetadataEditor putObject(int key, Object value) |
| throws IllegalArgumentException { |
| if (mApplied) { |
| Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor"); |
| return this; |
| } |
| switch(METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) { |
| case METADATA_TYPE_LONG: |
| if (value instanceof Long) { |
| return putLong(key, ((Long)value).longValue()); |
| } else { |
| throw(new IllegalArgumentException("Not a non-null Long for key "+ key)); |
| } |
| case METADATA_TYPE_STRING: |
| if ((value == null) || (value instanceof String)) { |
| return putString(key, (String) value); |
| } else { |
| throw(new IllegalArgumentException("Not a String for key "+ key)); |
| } |
| case METADATA_TYPE_RATING: |
| mEditorMetadata.putParcelable(String.valueOf(key), (Parcelable)value); |
| mMetadataChanged = true; |
| break; |
| case METADATA_TYPE_BITMAP: |
| if ((value == null) || (value instanceof Bitmap)) { |
| return putBitmap(key, (Bitmap) value); |
| } else { |
| throw(new IllegalArgumentException("Not a Bitmap for key "+ key)); |
| } |
| default: |
| throw(new IllegalArgumentException("Invalid key "+ key)); |
| } |
| return this; |
| } |
| |
| |
| /** |
| * Returns the long value for the key. |
| * @param key one of the keys supported in {@link #putLong(int, long)} |
| * @param defaultValue the value returned if the key is not present |
| * @return the long value for the key, or the supplied default value if the key is not present |
| * @throws IllegalArgumentException |
| */ |
| public synchronized long getLong(int key, long defaultValue) |
| throws IllegalArgumentException { |
| if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) { |
| throw(new IllegalArgumentException("Invalid type 'long' for key "+ key)); |
| } |
| return mEditorMetadata.getLong(String.valueOf(key), defaultValue); |
| } |
| |
| /** |
| * Returns the {@link String} value for the key. |
| * @param key one of the keys supported in {@link #putString(int, String)} |
| * @param defaultValue the value returned if the key is not present |
| * @return the {@link String} value for the key, or the supplied default value if the key is |
| * not present |
| * @throws IllegalArgumentException |
| */ |
| public synchronized String getString(int key, String defaultValue) |
| throws IllegalArgumentException { |
| if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) { |
| throw(new IllegalArgumentException("Invalid type 'String' for key "+ key)); |
| } |
| return mEditorMetadata.getString(String.valueOf(key), defaultValue); |
| } |
| |
| /** |
| * Returns the {@link Bitmap} value for the key. |
| * @param key the {@link #BITMAP_KEY_ARTWORK} key |
| * @param defaultValue the value returned if the key is not present |
| * @return the {@link Bitmap} value for the key, or the supplied default value if the key is |
| * not present |
| * @throws IllegalArgumentException |
| */ |
| public synchronized Bitmap getBitmap(int key, Bitmap defaultValue) |
| throws IllegalArgumentException { |
| if (key != BITMAP_KEY_ARTWORK) { |
| throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); |
| } |
| return (mEditorArtwork != null ? mEditorArtwork : defaultValue); |
| } |
| |
| /** |
| * Returns an object representation of the value for the key |
| * @param key one of the keys supported in {@link #putObject(int, Object)} |
| * @param defaultValue the value returned if the key is not present |
| * @return the object for the key, as a {@link Long}, {@link Bitmap}, {@link String}, or |
| * {@link Rating} depending on the key value, or the supplied default value if the key is |
| * not present |
| * @throws IllegalArgumentException |
| */ |
| public synchronized Object getObject(int key, Object defaultValue) |
| throws IllegalArgumentException { |
| switch (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) { |
| case METADATA_TYPE_LONG: |
| if (mEditorMetadata.containsKey(String.valueOf(key))) { |
| return mEditorMetadata.getLong(String.valueOf(key)); |
| } else { |
| return defaultValue; |
| } |
| case METADATA_TYPE_STRING: |
| if (mEditorMetadata.containsKey(String.valueOf(key))) { |
| return mEditorMetadata.getString(String.valueOf(key)); |
| } else { |
| return defaultValue; |
| } |
| case METADATA_TYPE_RATING: |
| if (mEditorMetadata.containsKey(String.valueOf(key))) { |
| return mEditorMetadata.getParcelable(String.valueOf(key)); |
| } else { |
| return defaultValue; |
| } |
| case METADATA_TYPE_BITMAP: |
| // only one key for Bitmap supported, value is not stored in mEditorMetadata Bundle |
| if (key == BITMAP_KEY_ARTWORK) { |
| return (mEditorArtwork != null ? mEditorArtwork : defaultValue); |
| } // else: fall through to invalid key handling |
| default: |
| throw(new IllegalArgumentException("Invalid key "+ key)); |
| } |
| } |
| |
| |
| /** |
| * @hide |
| */ |
| protected static final int METADATA_TYPE_INVALID = -1; |
| /** |
| * @hide |
| */ |
| protected static final int METADATA_TYPE_LONG = 0; |
| |
| /** |
| * @hide |
| */ |
| protected static final int METADATA_TYPE_STRING = 1; |
| |
| /** |
| * @hide |
| */ |
| protected static final int METADATA_TYPE_BITMAP = 2; |
| |
| /** |
| * @hide |
| */ |
| protected static final int METADATA_TYPE_RATING = 3; |
| |
| /** |
| * @hide |
| */ |
| protected static final SparseIntArray METADATA_KEYS_TYPE; |
| |
| static { |
| METADATA_KEYS_TYPE = new SparseIntArray(17); |
| // NOTE: if adding to the list below, make sure you increment the array initialization size |
| // keys with long values |
| METADATA_KEYS_TYPE.put( |
| MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG); |
| // keys with String values |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put( |
| MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put( |
| MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_STRING); |
| METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_STRING); |
| // keys with Bitmap values |
| METADATA_KEYS_TYPE.put(BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP); |
| // keys with Rating values |
| METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING); |
| METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING); |
| } |
| } |