am f03ceff2: Merge "Revise new public API for ratings in RemoteControlClient" into klp-dev
* commit 'f03ceff2f506133a238848c2f5db4322285cc2b7': Revise new public API for ratings in RemoteControlClient
This commit is contained in:
@@ -5987,6 +5987,7 @@ package android.content {
|
||||
field public static final int MODE_PRIVATE = 0; // 0x0
|
||||
field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
|
||||
field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
|
||||
field public static final java.lang.String NETWORKMANAGEMENT_SERVICE = "network_management";
|
||||
field public static final java.lang.String NFC_SERVICE = "nfc";
|
||||
field public static final java.lang.String NOTIFICATION_SERVICE = "notification";
|
||||
field public static final java.lang.String NSD_SERVICE = "servicediscovery";
|
||||
@@ -12770,6 +12771,25 @@ package android.media {
|
||||
field public static final java.lang.String KEY_WIDTH = "width";
|
||||
}
|
||||
|
||||
public abstract class MediaMetadataEditor {
|
||||
method public synchronized void addEditableKey(int);
|
||||
method public abstract void apply();
|
||||
method public synchronized void clear();
|
||||
method public synchronized android.graphics.Bitmap getBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized int[] getEditableKeys();
|
||||
method public synchronized long getLong(int, long) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized java.lang.Object getObject(int, java.lang.Object) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized java.lang.String getString(int, java.lang.String) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.MediaMetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.MediaMetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.MediaMetadataEditor putObject(int, java.lang.Object) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.MediaMetadataEditor putString(int, java.lang.String) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized void removeEditableKeys();
|
||||
field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
|
||||
field public static final int RATING_KEY_BY_OTHERS = 101; // 0x65
|
||||
field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
|
||||
}
|
||||
|
||||
public class MediaMetadataRetriever {
|
||||
ctor public MediaMetadataRetriever();
|
||||
method public java.lang.String extractMetadata(int);
|
||||
@@ -13186,6 +13206,29 @@ package android.media {
|
||||
ctor public NotProvisionedException(java.lang.String);
|
||||
}
|
||||
|
||||
public final class Rating implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public float getPercentRating();
|
||||
method public int getRatingStyle();
|
||||
method public float getStarRating();
|
||||
method public boolean hasHeart();
|
||||
method public boolean isRated();
|
||||
method public boolean isThumbUp();
|
||||
method public static android.media.Rating newHeartRating(boolean);
|
||||
method public static android.media.Rating newPercentageRating(float);
|
||||
method public static android.media.Rating newStarRating(int, float);
|
||||
method public static android.media.Rating newThumbRating(boolean);
|
||||
method public static android.media.Rating newUnratedRating(int);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
field public static final int RATING_3_STARS = 3; // 0x3
|
||||
field public static final int RATING_4_STARS = 4; // 0x4
|
||||
field public static final int RATING_5_STARS = 5; // 0x5
|
||||
field public static final int RATING_HEART = 1; // 0x1
|
||||
field public static final int RATING_PERCENTAGE = 6; // 0x6
|
||||
field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
|
||||
}
|
||||
|
||||
public class RemoteControlClient {
|
||||
ctor public RemoteControlClient(android.app.PendingIntent);
|
||||
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
|
||||
@@ -13217,21 +13260,9 @@ package android.media {
|
||||
field public static final int PLAYSTATE_STOPPED = 1; // 0x1
|
||||
}
|
||||
|
||||
public class RemoteControlClient.MetadataEditor {
|
||||
method public synchronized void addEditableKey(int);
|
||||
public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
|
||||
method public synchronized void apply();
|
||||
method public synchronized void clear();
|
||||
method public synchronized void clearEditableKeys();
|
||||
method public synchronized android.media.RemoteControlClient.MetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.RemoteControlClient.MetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
|
||||
method public synchronized android.media.RemoteControlClient.MetadataEditor putString(int, java.lang.String) throws java.lang.IllegalArgumentException;
|
||||
field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
|
||||
field public static final int LONG_KEY_RATING_BY_OTHERS = 102; // 0x66
|
||||
field public static final int LONG_KEY_RATING_BY_USER = 268435457; // 0x10000001
|
||||
field public static final int LONG_KEY_RATING_TYPE = 101; // 0x65
|
||||
field public static final long RATING_HEART = -1L; // 0xffffffffffffffffL
|
||||
field public static final long RATING_NOT_RATED = -101L; // 0xffffffffffffff9bL
|
||||
field public static final long RATING_THUMB_UP_DOWN = -2L; // 0xfffffffffffffffeL
|
||||
}
|
||||
|
||||
public static abstract interface RemoteControlClient.OnGetPlaybackPositionListener {
|
||||
@@ -13239,9 +13270,7 @@ package android.media {
|
||||
}
|
||||
|
||||
public static abstract interface RemoteControlClient.OnMetadataUpdateListener {
|
||||
method public abstract void onMetadataUpdateBitmap(int, android.graphics.Bitmap);
|
||||
method public abstract void onMetadataUpdateLong(int, long);
|
||||
method public abstract void onMetadataUpdateString(int, java.lang.String);
|
||||
method public abstract void onMetadataUpdate(int, java.lang.Object);
|
||||
}
|
||||
|
||||
public static abstract interface RemoteControlClient.OnPlaybackPositionUpdateListener {
|
||||
|
||||
@@ -17,6 +17,7 @@ package android.media;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.IRemoteControlDisplay;
|
||||
import android.media.Rating;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -49,5 +50,5 @@ oneway interface IRemoteControlClient
|
||||
void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
|
||||
void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
|
||||
void seekTo(int clientGeneration, long timeMs);
|
||||
void updateMetadata(int clientGeneration, int key, long value);
|
||||
void updateMetadata(int clientGeneration, int key, in Rating value);
|
||||
}
|
||||
@@ -2093,8 +2093,11 @@ public class MediaFocusControl implements OnFinished {
|
||||
if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
|
||||
try {
|
||||
switch (key) {
|
||||
case RemoteControlClient.MetadataEditor.LONG_KEY_RATING_BY_USER:
|
||||
mCurrentRcClient.updateMetadata(genId, key, value);
|
||||
case RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER:
|
||||
// TODO handle rating update, placeholder code here that sends
|
||||
// an unrated percent-based rating
|
||||
mCurrentRcClient.updateMetadata(genId, key,
|
||||
Rating.newUnratedRating(Rating.RATING_PERCENTAGE));
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
|
||||
|
||||
462
media/java/android/media/MediaMetadataEditor.java
Normal file
462
media/java/android/media/MediaMetadataEditor.java
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* 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.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 obj 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);
|
||||
}
|
||||
}
|
||||
19
media/java/android/media/Rating.aidl
Normal file
19
media/java/android/media/Rating.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
parcelable Rating;
|
||||
274
media/java/android/media/Rating.java
Normal file
274
media/java/android/media/Rating.java
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A class to encapsulate rating information used as content metadata.
|
||||
* A rating is defined by its rating style (see {@link #RATING_HEART},
|
||||
* {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
|
||||
* {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
|
||||
* be defined as "unrated"), both of which are defined when the rating instance is constructed
|
||||
* through one of the factory methods.
|
||||
*/
|
||||
public final class Rating implements Parcelable {
|
||||
|
||||
private final static String TAG = "Rating";
|
||||
|
||||
/**
|
||||
* A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
|
||||
* indicate the content referred to is a favorite (or not).
|
||||
*/
|
||||
public final static int RATING_HEART = 1;
|
||||
|
||||
/**
|
||||
* A rating style for "thumb up" vs "thumb down".
|
||||
*/
|
||||
public final static int RATING_THUMB_UP_DOWN = 2;
|
||||
|
||||
/**
|
||||
* A rating style with 0 to 3 stars.
|
||||
*/
|
||||
public final static int RATING_3_STARS = 3;
|
||||
|
||||
/**
|
||||
* A rating style with 0 to 4 stars.
|
||||
*/
|
||||
public final static int RATING_4_STARS = 4;
|
||||
|
||||
/**
|
||||
* A rating style with 0 to 5 stars.
|
||||
*/
|
||||
public final static int RATING_5_STARS = 5;
|
||||
|
||||
/**
|
||||
* A rating style expressed as a percentage.
|
||||
*/
|
||||
public final static int RATING_PERCENTAGE = 6;
|
||||
|
||||
private final static float RATING_NOT_RATED = -1.0f;
|
||||
|
||||
private final int mRatingStyle;
|
||||
|
||||
private final float mRatingValue;
|
||||
|
||||
private Rating(int ratingStyle, float rating) {
|
||||
mRatingStyle = ratingStyle;
|
||||
mRatingValue = rating;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return mRatingStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mRatingStyle);
|
||||
dest.writeFloat(mRatingValue);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Rating> CREATOR
|
||||
= new Parcelable.Creator<Rating>() {
|
||||
/**
|
||||
* Rebuilds a Rating previously stored with writeToParcel().
|
||||
* @param p Parcel object to read the Rating from
|
||||
* @return a new Rating created from the data in the parcel
|
||||
*/
|
||||
public Rating createFromParcel(Parcel p) {
|
||||
return new Rating(p.readInt(), p.readFloat());
|
||||
}
|
||||
public Rating[] newArray(int size) {
|
||||
return new Rating[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a Rating instance with no rating.
|
||||
* Create and return a new Rating instance with no rating known for the given
|
||||
* rating style.
|
||||
* @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
|
||||
* {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
|
||||
* or {@link #RATING_PERCENTAGE}.
|
||||
* @return null if an invalid rating style is passed, a new Rating instance otherwise.
|
||||
*/
|
||||
public static Rating newUnratedRating(int ratingStyle) {
|
||||
switch(ratingStyle) {
|
||||
case RATING_HEART:
|
||||
case RATING_THUMB_UP_DOWN:
|
||||
case RATING_3_STARS:
|
||||
case RATING_4_STARS:
|
||||
case RATING_5_STARS:
|
||||
case RATING_PERCENTAGE:
|
||||
return new Rating(ratingStyle, RATING_NOT_RATED);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Rating instance with a heart-based rating.
|
||||
* Create and return a new Rating instance with a rating style of {@link #RATING_HEART},
|
||||
* and a heart-based rating.
|
||||
* @param hasHeart true for a "heart selected" rating, false for "heart unselected".
|
||||
* @return a new Rating instance.
|
||||
*/
|
||||
public static Rating newHeartRating(boolean hasHeart) {
|
||||
return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Rating instance with a thumb-based rating.
|
||||
* Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN}
|
||||
* rating style, and a "thumb up" or "thumb down" rating.
|
||||
* @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
|
||||
* @return a new Rating instance.
|
||||
*/
|
||||
public static Rating newThumbRating(boolean thumbIsUp) {
|
||||
return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Rating instance with a star-based rating.
|
||||
* Create and return a new Rating instance with one of the star-base rating styles
|
||||
* and the given integer or fractional number of stars. Non integer values can for instance
|
||||
* be used to represent an average rating value, which might not be an integer number of stars.
|
||||
* @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
|
||||
* {@link #RATING_5_STARS}.
|
||||
* @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
|
||||
* the rating style.
|
||||
* @return null if the rating style is invalid, or the rating is out of range,
|
||||
* a new Rating instance otherwise.
|
||||
*/
|
||||
public static Rating newStarRating(int starRatingStyle, float starRating) {
|
||||
float maxRating = -1.0f;
|
||||
switch(starRatingStyle) {
|
||||
case RATING_3_STARS:
|
||||
maxRating = 3.0f;
|
||||
break;
|
||||
case RATING_4_STARS:
|
||||
maxRating = 4.0f;
|
||||
break;
|
||||
case RATING_5_STARS:
|
||||
maxRating = 5.0f;
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
|
||||
return null;
|
||||
}
|
||||
if ((starRating < 0.0f) || (starRating > maxRating)) {
|
||||
Log.e(TAG, "Trying to set out of range star-based rating");
|
||||
return null;
|
||||
}
|
||||
return new Rating(starRatingStyle, starRating);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Rating instance with a percentage-based rating.
|
||||
* Create and return a new Rating instance with a {@link #RATING_PERCENTAGE}
|
||||
* rating style, and a rating of the given percentage.
|
||||
* @param percent the value of the rating
|
||||
* @return null if the rating is out of range, a new Rating instance otherwise.
|
||||
*/
|
||||
public static Rating newPercentageRating(float percent) {
|
||||
if ((percent < 0.0f) || (percent > 100.0f)) {
|
||||
Log.e(TAG, "Invalid percentage-based rating value");
|
||||
return null;
|
||||
} else {
|
||||
return new Rating(RATING_PERCENTAGE, percent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is a rating value available.
|
||||
* @return true if the instance was not created with {@link #newUnratedRating(int)}.
|
||||
*/
|
||||
public boolean isRated() {
|
||||
return mRatingValue >= 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rating style.
|
||||
* @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
|
||||
* {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
|
||||
* or {@link #RATING_PERCENTAGE}.
|
||||
*/
|
||||
public int getRatingStyle() {
|
||||
return mRatingStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the rating is "heart selected".
|
||||
* @return true if the rating is "heart selected", false if the rating is "heart unselected",
|
||||
* if the rating style is not {@link #RATING_HEART} or if it is unrated.
|
||||
*/
|
||||
public boolean hasHeart() {
|
||||
if (mRatingStyle != RATING_HEART) {
|
||||
return false;
|
||||
} else {
|
||||
return (mRatingValue == 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the rating is "thumb up".
|
||||
* @return true if the rating is "thumb up", false if the rating is "thumb down",
|
||||
* if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
|
||||
*/
|
||||
public boolean isThumbUp() {
|
||||
if (mRatingStyle != RATING_THUMB_UP_DOWN) {
|
||||
return false;
|
||||
} else {
|
||||
return (mRatingValue == 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the star-based rating value.
|
||||
* @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
|
||||
* not star-based, or if it is unrated.
|
||||
*/
|
||||
public float getStarRating() {
|
||||
switch (mRatingStyle) {
|
||||
case RATING_3_STARS:
|
||||
case RATING_4_STARS:
|
||||
case RATING_5_STARS:
|
||||
if (isRated()) {
|
||||
return mRatingValue;
|
||||
}
|
||||
default:
|
||||
return -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the percentage-based rating value.
|
||||
* @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
|
||||
* not percentage-based, or if it is unrated.
|
||||
*/
|
||||
public float getPercentRating() {
|
||||
if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
|
||||
return -1.0f;
|
||||
} else {
|
||||
return mRatingValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
@@ -385,27 +386,6 @@ public class RemoteControlClient
|
||||
mEventHandler = new EventHandler(this, looper);
|
||||
}
|
||||
|
||||
private static final int[] METADATA_KEYS_TYPE_STRING = {
|
||||
MediaMetadataRetriever.METADATA_KEY_ALBUM,
|
||||
MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
|
||||
MediaMetadataRetriever.METADATA_KEY_TITLE,
|
||||
MediaMetadataRetriever.METADATA_KEY_ARTIST,
|
||||
MediaMetadataRetriever.METADATA_KEY_AUTHOR,
|
||||
MediaMetadataRetriever.METADATA_KEY_COMPILATION,
|
||||
MediaMetadataRetriever.METADATA_KEY_COMPOSER,
|
||||
MediaMetadataRetriever.METADATA_KEY_DATE,
|
||||
MediaMetadataRetriever.METADATA_KEY_GENRE,
|
||||
MediaMetadataRetriever.METADATA_KEY_TITLE,
|
||||
MediaMetadataRetriever.METADATA_KEY_WRITER };
|
||||
private static final int[] METADATA_KEYS_TYPE_LONG = {
|
||||
MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
|
||||
MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
|
||||
MediaMetadataRetriever.METADATA_KEY_DURATION,
|
||||
MediaMetadataRetriever.METADATA_KEY_YEAR,
|
||||
MetadataEditor.LONG_KEY_RATING_TYPE,
|
||||
MetadataEditor.LONG_KEY_RATING_BY_OTHERS,
|
||||
MetadataEditor.LONG_KEY_RATING_BY_USER};
|
||||
|
||||
/**
|
||||
* Class used to modify metadata in a {@link RemoteControlClient} object.
|
||||
* Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor,
|
||||
@@ -414,28 +394,7 @@ public class RemoteControlClient
|
||||
* for the associated client. Once the metadata has been "applied", you cannot reuse this
|
||||
* instance of the MetadataEditor.
|
||||
*/
|
||||
public class MetadataEditor {
|
||||
/**
|
||||
* Mask of editable keys.
|
||||
*/
|
||||
private long mEditableKeys;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected boolean mMetadataChanged;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected boolean mArtworkChanged;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected Bitmap mEditorArtwork;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected Bundle mEditorMetadata;
|
||||
private boolean mApplied = false;
|
||||
public class MetadataEditor extends MediaMetadataEditor {
|
||||
|
||||
// only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
|
||||
private MetadataEditor() { }
|
||||
@@ -450,73 +409,10 @@ public class RemoteControlClient
|
||||
* The metadata key for the content artwork / album art.
|
||||
*/
|
||||
public final static int BITMAP_KEY_ARTWORK = 100;
|
||||
/**
|
||||
* The metadata key qualifying the content rating.
|
||||
* The value associated with this key may be: {@link #RATING_HEART},
|
||||
* {@link #RATING_THUMB_UP_DOWN}, or a non-null positive integer expressing a maximum
|
||||
* number of "stars" for the rating, for which a typical value is 3 or 5.
|
||||
*/
|
||||
public final static int LONG_KEY_RATING_TYPE = 101;
|
||||
/**
|
||||
* The metadata key for the content's average rating, not the user's rating.
|
||||
* The value associated with this key may be: an integer value between 0 and 100,
|
||||
* or {@link #RATING_NOT_RATED} to express that no average rating is available.
|
||||
* <p></p>
|
||||
* Note that a rating value up to 100 is not incompatible with a rating type using up
|
||||
* to 5 stars for instance, as the average may be an non-integer number of stars.
|
||||
* <p></p>
|
||||
* When the rating type is:
|
||||
* <ul>
|
||||
* <li>{@link #RATING_HEART}, a rating of 51 to 100 means "heart selected",</li>
|
||||
* <li>{@link #RATING_THUMB_UP_DOWN}, a rating of 0 to 50 means "thumb down",
|
||||
* 51 to 100 means "thumb up"</li>
|
||||
* <li>a non-null positive integer, the rating value is mapped to the number of stars, e.g.
|
||||
* with a maximum of 5 stars, a rating of 0 maps to 0 stars, 1 to 20 maps to 1 star,
|
||||
* 21 to 40 maps to 2 stars, etc.</li>
|
||||
* </ul>
|
||||
* @see #LONG_KEY_RATING_BY_USER
|
||||
*/
|
||||
public final static int LONG_KEY_RATING_BY_OTHERS = 102;
|
||||
|
||||
// editable keys
|
||||
/**
|
||||
* @hide
|
||||
* Editable key mask
|
||||
*/
|
||||
public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF;
|
||||
/**
|
||||
* The metadata key for the content's user rating.
|
||||
* The value associated with this key may be: an integer value between 0 and 100,
|
||||
* or {@link #RATING_NOT_RATED} to express that the user hasn't rated this content.
|
||||
* Rules for the interpretation of the rating value according to the rating style are
|
||||
* the same as for {@link #LONG_KEY_RATING_BY_OTHERS}.
|
||||
* 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 LONG_KEY_RATING_BY_USER = 0x10000001;
|
||||
|
||||
/**
|
||||
* A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
|
||||
* indicate the content referred to is a favorite (or not).
|
||||
* @see #LONG_KEY_RATING_TYPE
|
||||
*/
|
||||
public final static long RATING_HEART = -1;
|
||||
/**
|
||||
* A rating style for "thumb up" vs "thumb down".
|
||||
* @see #LONG_KEY_RATING_TYPE
|
||||
*/
|
||||
public final static long RATING_THUMB_UP_DOWN = -2;
|
||||
/**
|
||||
* A rating value indicating no rating is available.
|
||||
* @see #LONG_KEY_RATING_BY_OTHERS
|
||||
* @see #LONG_KEY_RATING_BY_USER
|
||||
*/
|
||||
public final static long RATING_NOT_RATED = -101;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* TODO(jmtrivi) have lockscreen and music move to the new key name
|
||||
* TODO(jmtrivi) have lockscreen move to the new key name and remove
|
||||
*/
|
||||
public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK;
|
||||
|
||||
@@ -543,15 +439,7 @@ public class RemoteControlClient
|
||||
*/
|
||||
public synchronized MetadataEditor putString(int key, String value)
|
||||
throws IllegalArgumentException {
|
||||
if (mApplied) {
|
||||
Log.e(TAG, "Can't edit a previously applied MetadataEditor");
|
||||
return this;
|
||||
}
|
||||
if (!validTypeForKey(key, METADATA_KEYS_TYPE_STRING)) {
|
||||
throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
|
||||
}
|
||||
mEditorMetadata.putString(String.valueOf(key), value);
|
||||
mMetadataChanged = true;
|
||||
super.putString(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -564,9 +452,7 @@ public class RemoteControlClient
|
||||
* {@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},
|
||||
* {@link #LONG_KEY_RATING_BY_OTHERS}, {@link #LONG_KEY_RATING_BY_USER},
|
||||
* {@link #LONG_KEY_RATING_TYPE}.
|
||||
* {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
|
||||
* @param value The long value for the given key
|
||||
* @return Returns a reference to the same MetadataEditor object, so you can chain put
|
||||
* calls together.
|
||||
@@ -574,15 +460,7 @@ public class RemoteControlClient
|
||||
*/
|
||||
public synchronized MetadataEditor putLong(int key, long value)
|
||||
throws IllegalArgumentException {
|
||||
if (mApplied) {
|
||||
Log.e(TAG, "Can't edit a previously applied MetadataEditor");
|
||||
return this;
|
||||
}
|
||||
if (!validTypeForKey(key, METADATA_KEYS_TYPE_LONG)) {
|
||||
throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
|
||||
}
|
||||
mEditorMetadata.putLong(String.valueOf(key), value);
|
||||
mMetadataChanged = true;
|
||||
super.putLong(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -596,69 +474,22 @@ public class RemoteControlClient
|
||||
* @throws IllegalArgumentException
|
||||
* @see android.graphics.Bitmap
|
||||
*/
|
||||
@Override
|
||||
public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
|
||||
throws IllegalArgumentException {
|
||||
if (mApplied) {
|
||||
Log.e(TAG, "Can't edit a previously applied MetadataEditor");
|
||||
return this;
|
||||
}
|
||||
if (key != BITMAP_KEY_ARTWORK) {
|
||||
throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
|
||||
}
|
||||
mEditorArtwork = bitmap;
|
||||
mArtworkChanged = true;
|
||||
super.putBitmap(key, bitmap);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the metadata that has been set since the MetadataEditor instance was
|
||||
* created with {@link RemoteControlClient#editMetadata(boolean)}.
|
||||
* Clears all the metadata that has been set since the MetadataEditor instance was created
|
||||
* (with {@link RemoteControlClient#editMetadata(boolean)}).
|
||||
* Note that clearing the metadata doesn't reset the editable keys
|
||||
* (use {@link #clearEditableKeys()} instead).
|
||||
* (use {@link MediaMetadataEditor#removeEditableKeys()} instead).
|
||||
*/
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
if (mApplied) {
|
||||
Log.e(TAG, "Can't clear a previously applied MetadataEditor");
|
||||
return;
|
||||
}
|
||||
mEditorMetadata.clear();
|
||||
mEditorArtwork = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag the given key as being editable.
|
||||
* This 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 #LONG_KEY_RATING_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 == MetadataEditor.LONG_KEY_RATING_BY_USER) {
|
||||
mEditableKeys |= (MetadataEditor.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 clearEditableKeys() {
|
||||
if (mApplied) {
|
||||
Log.e(TAG, "Can't clear editable keys of a previously applied MetadataEditor");
|
||||
return;
|
||||
}
|
||||
if (mEditableKeys != 0) {
|
||||
mEditableKeys = 0;
|
||||
mMetadataChanged = true;
|
||||
}
|
||||
super.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -881,30 +712,17 @@ public class RemoteControlClient
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when one of the metadata values has
|
||||
* been updated.
|
||||
* Implement this interface to receive metadata updates after registering your listener
|
||||
* through {@link RemoteControlClient#setMetadataUpdateListener(OnMetadataUpdateListener)}.
|
||||
*/
|
||||
public interface OnMetadataUpdateListener {
|
||||
/**
|
||||
* Called on the implementer to notify that the metadata field for the given key has
|
||||
* been updated to the new value of type <code>long</long>.
|
||||
* @param key the identifier of the updated metadata field of type <code>long</long>.
|
||||
* @param newValue the new <code>long</long> value for the key
|
||||
* been updated to the new value.
|
||||
* @param key the identifier of the updated metadata field.
|
||||
* @param newValue the Object storing the new value for the key.
|
||||
*/
|
||||
void onMetadataUpdateLong(int key, long newValue);
|
||||
/**
|
||||
* Called on the implementer to notify that the metadata field for the given key has
|
||||
* been updated to the new <code>String</long>.
|
||||
* @param key the identifier of the updated metadata field of type <code>String</long>.
|
||||
* @param newValue the new <code>String</long> value for the key
|
||||
*/
|
||||
void onMetadataUpdateString(int key, String newValue);
|
||||
/**
|
||||
* Called on the implementer to notify that the metadata field for the given key has
|
||||
* been updated to the new {@link android.graphics.Bitmap}.
|
||||
* @param key the identifier of the updated metadata field of type
|
||||
* {@link android.graphics.Bitmap}.
|
||||
* @param newValue the new {@link android.graphics.Bitmap} for the key
|
||||
*/
|
||||
void onMetadataUpdateBitmap(int key, Bitmap newValue);
|
||||
public abstract void onMetadataUpdate(int key, Object newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1338,12 +1156,11 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMetadata(int generationId, int key, long value) {
|
||||
public void updateMetadata(int generationId, int key, Rating value) {
|
||||
// only post messages, we can't block here
|
||||
if (mEventHandler != null) {
|
||||
mEventHandler.sendMessage(mEventHandler.obtainMessage(
|
||||
MSG_UPDATE_METADATA_LONG, generationId /* arg1 */, key /* arg2*/,
|
||||
new Long(value)));
|
||||
MSG_UPDATE_METADATA, generationId /* arg1 */, key /* arg2*/, value));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1389,7 +1206,7 @@ public class RemoteControlClient
|
||||
private final static int MSG_SEEK_TO = 10;
|
||||
private final static int MSG_POSITION_DRIFT_CHECK = 11;
|
||||
private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;
|
||||
private final static int MSG_UPDATE_METADATA_LONG = 13;
|
||||
private final static int MSG_UPDATE_METADATA = 13;
|
||||
|
||||
private class EventHandler extends Handler {
|
||||
public EventHandler(RemoteControlClient rcc, Looper looper) {
|
||||
@@ -1443,8 +1260,8 @@ public class RemoteControlClient
|
||||
case MSG_DISPLAY_WANTS_POS_SYNC:
|
||||
onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
|
||||
break;
|
||||
case MSG_UPDATE_METADATA_LONG:
|
||||
onUpdateMetadata(msg.arg1, msg.arg2, ((Long)msg.obj).longValue());
|
||||
case MSG_UPDATE_METADATA:
|
||||
onUpdateMetadata(msg.arg1, msg.arg2, msg.obj);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
|
||||
@@ -1725,10 +1542,10 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
private void onUpdateMetadata(int generationId, int key, long value) {
|
||||
private void onUpdateMetadata(int generationId, int key, Object value) {
|
||||
synchronized (mCacheLock) {
|
||||
if ((mCurrentClientGenId == generationId) && (mMetadataUpdateListener != null)) {
|
||||
mMetadataUpdateListener.onMetadataUpdateLong(key, value);
|
||||
mMetadataUpdateListener.onMetadataUpdate(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1771,24 +1588,6 @@ public class RemoteControlClient
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast routine to go through an array of allowed keys and return whether the key is part
|
||||
* of that array
|
||||
* @param key the key value
|
||||
* @param validKeys the array of valid keys for a given type
|
||||
* @return true if the key is part of the array, false otherwise
|
||||
*/
|
||||
private static boolean validTypeForKey(int key, int[] validKeys) {
|
||||
try {
|
||||
for (int i = 0 ; ; i++) {
|
||||
if (key == validKeys[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether, for the given playback state, the playback position is expected to
|
||||
|
||||
Reference in New Issue
Block a user