Revise new public API for ratings in RemoteControlClient
Refactor RemoteControlClient.MetadataEditor to move functionality in new abstract class MediaMetadataEditor, so it can be also used on the "display" side of the RemoteControl functionality. Compatibility of these changes has been tested against existing Play applications which use the RemoteControlClient API. Move the new constants and method definitions related to the ratings feature to MediaMetadataEditor. This changes the yet-to-be-published ratings API, so this doesn't break compatibility. Rating feature is handled by the new Rating class with a set of constructors that handle the different rating models. Bug 8440498 Change-Id: I9b6dc2204bfc48594bad5cd6449d357f8a485da0
This commit is contained in:
@@ -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