Public API for RemoteController
Public API, under system|signature permission for access to currently playing metadata and playback state. Public API for sending media key events. Bug 8209392 Change-Id: I39b9309ca3fb1bc305492bad98740df0ae0842b2
This commit is contained in:
@ -80,6 +80,7 @@ package android {
|
||||
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
|
||||
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
|
||||
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
|
||||
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
|
||||
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
|
||||
field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
|
||||
field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
|
||||
@ -12008,6 +12009,7 @@ package android.media {
|
||||
method public void adjustStreamVolume(int, int, int);
|
||||
method public void adjustSuggestedStreamVolume(int, int, int);
|
||||
method public void adjustVolume(int, int);
|
||||
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
|
||||
method public int getMode();
|
||||
method public java.lang.String getParameters(java.lang.String);
|
||||
method public java.lang.String getProperty(java.lang.String);
|
||||
@ -12029,6 +12031,7 @@ package android.media {
|
||||
method public void registerMediaButtonEventReceiver(android.content.ComponentName);
|
||||
method public void registerMediaButtonEventReceiver(android.app.PendingIntent);
|
||||
method public void registerRemoteControlClient(android.media.RemoteControlClient);
|
||||
method public boolean registerRemoteController(android.media.RemoteController);
|
||||
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
|
||||
method public deprecated void setBluetoothA2dpOn(boolean);
|
||||
method public void setBluetoothScoOn(boolean);
|
||||
@ -12050,6 +12053,7 @@ package android.media {
|
||||
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
|
||||
method public void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
|
||||
method public void unregisterRemoteControlClient(android.media.RemoteControlClient);
|
||||
method public void unregisterRemoteController(android.media.RemoteController);
|
||||
field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
|
||||
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
|
||||
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
|
||||
@ -13276,6 +13280,36 @@ package android.media {
|
||||
method public abstract void onPlaybackPositionUpdate(long);
|
||||
}
|
||||
|
||||
public final class RemoteController {
|
||||
ctor public RemoteController(android.content.Context) throws java.lang.IllegalArgumentException;
|
||||
ctor public RemoteController(android.content.Context, android.os.Looper) throws java.lang.IllegalArgumentException;
|
||||
method public int clearArtworkConfiguration();
|
||||
method public android.media.RemoteController.MetadataEditor editMetadata();
|
||||
method public int seekTo(long);
|
||||
method public int sendMediaKeyEvent(android.view.KeyEvent);
|
||||
method public int setArtworkConfiguration(int, int);
|
||||
method public void setOnClientUpdateListener(android.media.RemoteController.OnClientUpdateListener);
|
||||
method public int setSynchronizationMode(int);
|
||||
field public static final int ERROR = -1; // 0xffffffff
|
||||
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
|
||||
field public static final int POSITION_SYNCHRONIZATION_CHECK = 1; // 0x1
|
||||
field public static final int POSITION_SYNCHRONIZATION_NONE = 0; // 0x0
|
||||
field public static final int SUCCESS = 0; // 0x0
|
||||
}
|
||||
|
||||
public class RemoteController.MetadataEditor extends android.media.MediaMetadataEditor {
|
||||
method public synchronized void apply();
|
||||
}
|
||||
|
||||
public static abstract class RemoteController.OnClientUpdateListener {
|
||||
ctor public RemoteController.OnClientUpdateListener();
|
||||
method public void onClientChange(boolean);
|
||||
method public void onClientMetadataUpdate(android.media.RemoteController.MetadataEditor);
|
||||
method public void onClientPlaybackStateUpdate(int);
|
||||
method public void onClientPlaybackStateUpdate(int, long, long, float);
|
||||
method public void onClientTransportControlUpdate(int);
|
||||
}
|
||||
|
||||
public final class ResourceBusyException extends android.media.MediaDrmException {
|
||||
ctor public ResourceBusyException(java.lang.String);
|
||||
}
|
||||
@ -31607,6 +31641,8 @@ package android.widget {
|
||||
ctor public NumberPicker(android.content.Context);
|
||||
ctor public NumberPicker(android.content.Context, android.util.AttributeSet);
|
||||
ctor public NumberPicker(android.content.Context, android.util.AttributeSet, int);
|
||||
method public int computeVerticalScrollOffset();
|
||||
method public int computeVerticalScrollRange();
|
||||
method public java.lang.String[] getDisplayedValues();
|
||||
method public int getMaxValue();
|
||||
method public int getMinValue();
|
||||
|
@ -2107,7 +2107,7 @@
|
||||
android:description="@string/permdesc_captureSecureVideoOutput"
|
||||
android:protectionLevel="signature|system" />
|
||||
|
||||
<!--@hide Allows an application to know what content is playing and control its playback.
|
||||
<!-- Allows an application to know what content is playing and control its playback.
|
||||
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
|
||||
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
|
||||
android:label="@string/permlab_mediaContentControl"
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.app.PendingIntent;
|
||||
@ -443,10 +444,29 @@ public class AudioManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param KeyEvent
|
||||
* Sends a simulated key event for a media button.
|
||||
* To simulate a key press, you must first send a KeyEvent built with a
|
||||
* {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
|
||||
* action.
|
||||
* <p>The key event will be sent to the current media key event consumer which registered with
|
||||
* {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}.
|
||||
* @param keyEvent a {@link KeyEvent} instance whose key code is one of
|
||||
* {@link KeyEvent#KEYCODE_MUTE},
|
||||
* {@link KeyEvent#KEYCODE_HEADSETHOOK},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PLAY},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_STOP},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_NEXT},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_REWIND},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_RECORD},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_EJECT},
|
||||
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
|
||||
*/
|
||||
protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
|
||||
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
service.dispatchMediaKeyEvent(keyEvent);
|
||||
@ -2247,9 +2267,11 @@ public class AudioManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param rctlr
|
||||
* Registers a {@link RemoteController} instance for it to receive media metadata updates
|
||||
* and playback state information from applications using {@link RemoteControlClient}, and
|
||||
* control their playback.
|
||||
* <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
|
||||
* @param rctlr the object to register.
|
||||
* @return true if the {@link RemoteController} was successfully registered, false if an
|
||||
* error occurred, due to an internal system error, or insufficient permissions.
|
||||
*/
|
||||
@ -2272,9 +2294,9 @@ public class AudioManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param rctlr
|
||||
* Unregisters a {@link RemoteController}, causing it to no longer receive media metadata and
|
||||
* playback state information, and no longer be capable of controlling playback.
|
||||
* @param rctlr the object to unregister.
|
||||
*/
|
||||
public void unregisterRemoteController(RemoteController rctlr) {
|
||||
if (rctlr == null) {
|
||||
|
@ -524,10 +524,20 @@ public class MediaFocusControl implements OnFinished {
|
||||
//==========================================================================================
|
||||
// RemoteControl
|
||||
//==========================================================================================
|
||||
/**
|
||||
* No-op if the key code for keyEvent is not a valid media key
|
||||
* (see {@link #isValidMediaKeyEvent(KeyEvent)})
|
||||
* @param keyEvent the key event to send
|
||||
*/
|
||||
protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
|
||||
filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op if the key code for keyEvent is not a valid media key
|
||||
* (see {@link #isValidMediaKeyEvent(KeyEvent)})
|
||||
* @param keyEvent the key event to send
|
||||
*/
|
||||
protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
|
||||
filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.PendingIntent.CanceledException;
|
||||
import android.content.Context;
|
||||
@ -33,8 +34,6 @@ import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* The RemoteController class is used to control media playback, display and update media metadata
|
||||
* and playback status, published by applications using the {@link RemoteControlClient} class.
|
||||
* <p>
|
||||
@ -45,23 +44,21 @@ import android.view.KeyEvent;
|
||||
* the {@link OnClientUpdateListener} abstract class. Override its methods to receive the
|
||||
* information published by the active {@link RemoteControlClient} instances.
|
||||
* By default an {@link OnClientUpdateListener} implementation will not receive bitmaps for album
|
||||
* art. Use {@link #setBitmapConfiguration(boolean, int, int)} to receive images as well.
|
||||
* art. Use {@link #setArtworkConfiguration(int, int)} to receive images as well.
|
||||
* <p>
|
||||
* A RemoteController can also be used without being registered, when it is only meant to send
|
||||
* media key events (for play or stop events for instance),
|
||||
* with {@link #sendMediaKeyEvent(KeyEvent)}.
|
||||
* Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
|
||||
*/
|
||||
public class RemoteController
|
||||
public final class RemoteController
|
||||
{
|
||||
private final static int MAX_BITMAP_DIMENSION = 512;
|
||||
private final static int TRANSPORT_UNKNOWN = 0;
|
||||
private RcDisplay mRcd;
|
||||
private final static String TAG = "RemoteController";
|
||||
private final static boolean DEBUG = false;
|
||||
private final static Object mGenLock = new Object();
|
||||
private final static Object mInfoLock = new Object();
|
||||
private Context mContext;
|
||||
private AudioManager mAudioManager;
|
||||
private final RcDisplay mRcd;
|
||||
private final Context mContext;
|
||||
private final AudioManager mAudioManager;
|
||||
private MetadataEditor mMetadataEditor;
|
||||
|
||||
/**
|
||||
@ -79,93 +76,101 @@ public class RemoteController
|
||||
private int mLastTransportControlFlags = TRANSPORT_UNKNOWN;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param ctxt non-null {@link Context}
|
||||
* Class constructor.
|
||||
* @param context non-null the {@link Context}, must be non-null
|
||||
* @throws java.lang.IllegalArgumentException
|
||||
*/
|
||||
public RemoteController(Context ctxt) throws IllegalArgumentException {
|
||||
if (ctxt == null) {
|
||||
public RemoteController(Context context) throws IllegalArgumentException {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* @param looper the {@link Looper} on which to run the event loop,
|
||||
* or null to use the current thread's looper.
|
||||
* @param context the {@link Context}, must be non-null
|
||||
* @throws java.lang.IllegalArgumentException
|
||||
*/
|
||||
public RemoteController(Context context, Looper looper) throws IllegalArgumentException {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Invalid null Context");
|
||||
}
|
||||
Looper looper;
|
||||
if ((looper = Looper.myLooper()) != null) {
|
||||
mEventHandler = new EventHandler(this, looper);
|
||||
} else if ((looper = Looper.getMainLooper()) != null) {
|
||||
if (looper != null) {
|
||||
mEventHandler = new EventHandler(this, looper);
|
||||
} else {
|
||||
mEventHandler = null;
|
||||
Log.e(TAG, "RemoteController() couldn't find main application thread");
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null) {
|
||||
mEventHandler = new EventHandler(this, l);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Calling thread not associated with a looper");
|
||||
}
|
||||
}
|
||||
mContext = ctxt;
|
||||
mContext = context;
|
||||
mRcd = new RcDisplay();
|
||||
mAudioManager = (AudioManager) ctxt.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param looper
|
||||
* @param ctxt non-null {@link Context}
|
||||
* @throws java.lang.IllegalArgumentException
|
||||
*/
|
||||
public RemoteController(Looper looper, Context ctxt) throws IllegalArgumentException {
|
||||
if (ctxt == null) {
|
||||
throw new IllegalArgumentException("Invalid null Context");
|
||||
}
|
||||
mEventHandler = new EventHandler(this, looper);
|
||||
mContext = ctxt;
|
||||
mRcd = new RcDisplay();
|
||||
mAudioManager = (AudioManager) ctxt.getSystemService(Context.AUDIO_SERVICE);
|
||||
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* An abstract class definition for the callbacks to be invoked whenever media events, metadata
|
||||
* and playback status are available.
|
||||
*/
|
||||
public static abstract class OnClientUpdateListener {
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param clearing
|
||||
* The method called whenever all information previously received through the other
|
||||
* methods of the listener, is no longer valid and is about to be refreshed.
|
||||
* This is typically called whenever a new {@link RemoteControlClient} has been selected
|
||||
* by the system to have its media information published.
|
||||
* @param clearing true if there is no selected RemoteControlClient and no information
|
||||
* is available.
|
||||
*/
|
||||
public void onClientReset(boolean clearing) { }
|
||||
public void onClientChange(boolean clearing) { }
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param state
|
||||
* The method called whenever the playback state has changed.
|
||||
* It is called when no information is known about the playback progress in the media and
|
||||
* the playback speed.
|
||||
* @param state one of the playback states authorized
|
||||
* in {@link RemoteControlClient#setPlaybackState(int)}.
|
||||
*/
|
||||
public void onClientPlaybackStateUpdate(int state) { }
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param state
|
||||
* @param stateChangeTimeMs
|
||||
* @param currentPosMs
|
||||
* @param speed
|
||||
* The method called whenever the playback state has changed, and playback position and
|
||||
* speed are known.
|
||||
* @param state one of the playback states authorized
|
||||
* in {@link RemoteControlClient#setPlaybackState(int)}.
|
||||
* @param stateChangeTimeMs the system time at which the state change was reported,
|
||||
* expressed in ms.
|
||||
* @param currentPosMs a positive value for the current media playback position expressed
|
||||
* in ms, a negative value if the position is temporarily unknown.
|
||||
* @param speed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
|
||||
* 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
|
||||
* playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).
|
||||
*/
|
||||
public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
|
||||
long currentPosMs, float speed) { }
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param transportControlFlags
|
||||
* @param posCapabilities
|
||||
* The method called whenever the transport control flags have changed.
|
||||
* @param transportControlFlags one of the flags authorized
|
||||
* in {@link RemoteControlClient#setTransportControlFlags(int)}.
|
||||
*/
|
||||
public void onClientTransportControlUpdate(int transportControlFlags) { }
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param metadataEditor
|
||||
* The method called whenever new metadata is available.
|
||||
* See the {@link MediaMetadataEditor#putLong(int, long)},
|
||||
* {@link MediaMetadataEditor#putString(int, String)},
|
||||
* {@link MediaMetadataEditor#putBitmap(int, Bitmap)}, and
|
||||
* {@link MediaMetadataEditor#putObject(int, Object)} methods for the various keys that
|
||||
* can be queried.
|
||||
* @param metadataEditor the container of the new metadata.
|
||||
*/
|
||||
public void onClientMetadataUpdate(MetadataEditor metadataEditor) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param l
|
||||
* Sets the listener to be called whenever new client information is available.
|
||||
* This method can only be called on a registered RemoteController.
|
||||
* @param l the update listener to be called.
|
||||
*/
|
||||
public void setOnClientUpdateListener(OnClientUpdateListener l) {
|
||||
synchronized(mInfoLock) {
|
||||
@ -195,61 +200,55 @@ public class RemoteController
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Send a simulated key event for a media button.
|
||||
* May be used without registering the RemoteController
|
||||
* with {@link AudioManager#registerRemoteController(RemoteController)}. To simulate a key
|
||||
* press, you must first send a KeyEvent built with a {@link KeyEvent#ACTION_DOWN} action, then
|
||||
* another event with the {@link KeyEvent#ACTION_UP} action.
|
||||
* <p> When used from a registered RemoteController, the key event will be sent to the
|
||||
* application currently promoted to publish its media metadata and playback state (there may be
|
||||
* none under some circumstances). With an unregistered RemoteController, the key event will be
|
||||
* sent to the current media key event consumer
|
||||
* (see {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}).
|
||||
* Send a simulated key event for a media button to be received by the current client.
|
||||
* To simulate a key press, you must first send a KeyEvent built with
|
||||
* a {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
|
||||
* action.
|
||||
* <p>The key event will be sent to the registered receiver
|
||||
* (see {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}) whose associated
|
||||
* {@link RemoteControlClient}'s metadata and playback state is published (there may be
|
||||
* none under some circumstances).
|
||||
* @param keyEvent a {@link KeyEvent} instance whose key code is one of
|
||||
* {@link KeyEvent.KEYCODE_MUTE},
|
||||
* {@link KeyEvent.KEYCODE_HEADSETHOOK},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_PLAY},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_PAUSE},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_STOP},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_NEXT},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_PREVIOUS},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_REWIND},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_RECORD},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_FAST_FORWARD},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_CLOSE},
|
||||
* {@link KeyEvent.KEYCODE_MEDIA_EJECT},
|
||||
* or {@link KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK}.
|
||||
* {@link KeyEvent#KEYCODE_MUTE},
|
||||
* {@link KeyEvent#KEYCODE_HEADSETHOOK},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PLAY},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_STOP},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_NEXT},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_REWIND},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_RECORD},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
|
||||
* {@link KeyEvent#KEYCODE_MEDIA_EJECT},
|
||||
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
|
||||
*/
|
||||
public int sendMediaKeyEvent(KeyEvent keyEvent) {
|
||||
if (!MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode())) {
|
||||
Log.e(TAG, "Cannot use sendMediaKeyEvent() for a non-media key event");
|
||||
return ERROR_BAD_VALUE;
|
||||
}
|
||||
boolean registered = false;
|
||||
final PendingIntent pi;
|
||||
synchronized(mInfoLock) {
|
||||
registered = mIsRegistered;
|
||||
if (!mIsRegistered) {
|
||||
Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
|
||||
return ERROR;
|
||||
}
|
||||
pi = mClientPendingIntentCurrent;
|
||||
}
|
||||
if (registered) {
|
||||
if (pi != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
|
||||
try {
|
||||
pi.send(mContext, 0, intent);
|
||||
} catch (CanceledException e) {
|
||||
Log.e(TAG, "Error sending intent for media button down: ", e);
|
||||
return ERROR;
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "No-op when sending key click, no receiver right now");
|
||||
if (pi != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
|
||||
try {
|
||||
pi.send(mContext, 0, intent);
|
||||
} catch (CanceledException e) {
|
||||
Log.e(TAG, "Error sending intent for media button down: ", e);
|
||||
return ERROR;
|
||||
}
|
||||
} else {
|
||||
mAudioManager.dispatchMediaKeyEvent(keyEvent);
|
||||
Log.i(TAG, "No-op when sending key click, no receiver right now");
|
||||
return ERROR;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@ -257,29 +256,23 @@ public class RemoteController
|
||||
|
||||
// Error codes
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Successful operation.
|
||||
*/
|
||||
public static final int SUCCESS = 0;
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Unspecified error.
|
||||
*/
|
||||
public static final int ERROR = -1;
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Operation failed due to bad parameter value.
|
||||
*/
|
||||
public static final int ERROR_BAD_VALUE = -2;
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* @param timeMs
|
||||
* Sets the new playback position.
|
||||
* This method can only be called on a registered RemoteController.
|
||||
* @param timeMs a 0 or positive value for the new playback position, expressed in ms.
|
||||
* @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE}
|
||||
*/
|
||||
public int seekTo(long timeMs) {
|
||||
@ -303,7 +296,7 @@ public class RemoteController
|
||||
* @param height
|
||||
* @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE}
|
||||
*/
|
||||
public int setBitmapConfiguration(boolean wantBitmap, int width, int height) {
|
||||
public int setArtworkConfiguration(boolean wantBitmap, int width, int height) {
|
||||
synchronized (mInfoLock) {
|
||||
if (!mIsRegistered) {
|
||||
Log.e(TAG, "Cannot specify bitmap configuration on unregistered RemoteController");
|
||||
@ -326,31 +319,28 @@ public class RemoteController
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* must be called on a registered RemoteController
|
||||
* @param width
|
||||
* @param height
|
||||
* Set the maximum artwork image dimensions to be received in the metadata.
|
||||
* No bitmaps will be received unless this has been specified.
|
||||
* This method can only be called on a registered RemoteController.
|
||||
* @param width the maximum width in pixels
|
||||
* @param height the maximum height in pixels
|
||||
* @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE}
|
||||
*/
|
||||
public int setBitmapConfiguration(int width, int height) {
|
||||
return setBitmapConfiguration(true, width, height);
|
||||
public int setArtworkConfiguration(int width, int height) {
|
||||
return setArtworkConfiguration(true, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* must be called on a registered RemoteController
|
||||
* Prevents this RemoteController from receiving artwork images.
|
||||
* This method can only be called on a registered RemoteController.
|
||||
* @return {@link #SUCCESS}, {@link #ERROR}
|
||||
*/
|
||||
public int setBitmapConfigurationNone() {
|
||||
return setBitmapConfiguration(false, -1, -1);
|
||||
public int clearArtworkConfiguration() {
|
||||
return setArtworkConfiguration(false, -1, -1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Default playback position synchronization mode where the RemoteControlClient is not
|
||||
* asked regularly for its playback position to see if it has drifted from the estimated
|
||||
* position.
|
||||
@ -358,8 +348,6 @@ public class RemoteController
|
||||
public static final int POSITION_SYNCHRONIZATION_NONE = 0;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* The playback position synchronization mode where the RemoteControlClient instances which
|
||||
* expose their playback position to the framework, will be regularly polled to check
|
||||
* whether any drift has been noticed between their estimated position and the one they report.
|
||||
@ -371,8 +359,6 @@ public class RemoteController
|
||||
public static final int POSITION_SYNCHRONIZATION_CHECK = 1;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Set the playback position synchronization mode.
|
||||
* Must be called on a registered RemoteController.
|
||||
* @param sync {@link #POSITION_SYNCHRONIZATION_NONE} or {@link #POSITION_SYNCHRONIZATION_CHECK}
|
||||
@ -394,10 +380,9 @@ public class RemoteController
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Creates a {@link MetadataEditor} for updating metadata values of the editable keys of
|
||||
* the current {@link RemoteControlClient}.
|
||||
* This method can only be called on a registered RemoteController.
|
||||
* @return a new MetadataEditor instance.
|
||||
*/
|
||||
public MetadataEditor editMetadata() {
|
||||
@ -412,9 +397,7 @@ public class RemoteController
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Used to read the metadata published by a {@link RemoteControlClient}, or send a
|
||||
* A class to read the metadata published by a {@link RemoteControlClient}, or send a
|
||||
* {@link RemoteControlClient} new values for keys that can be edited.
|
||||
*/
|
||||
public class MetadataEditor extends MediaMetadataEditor {
|
||||
@ -436,8 +419,9 @@ public class RemoteController
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* CANDIDATE FOR PUBLIC API
|
||||
* Applies all of the metadata changes that have been set since the MediaMetadataEditor
|
||||
* instance was created with {@link RemoteController#editMetadata()}
|
||||
* or since {@link #clear()} was called.
|
||||
*/
|
||||
public synchronized void apply() {
|
||||
// "applying" a metadata bundle in RemoteController is only for sending edited
|
||||
@ -474,9 +458,7 @@ public class RemoteController
|
||||
//==================================================
|
||||
// Implementation of IRemoteControlDisplay interface
|
||||
private class RcDisplay extends IRemoteControlDisplay.Stub {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public void setCurrentClientId(int genId, PendingIntent clientMediaIntent,
|
||||
boolean clearing) {
|
||||
boolean isNew = false;
|
||||
@ -491,14 +473,11 @@ public class RemoteController
|
||||
genId /*arg1*/, 0, clientMediaIntent /*obj*/, 0 /*delay*/);
|
||||
}
|
||||
if (isNew || clearing) {
|
||||
sendMsg(mEventHandler, MSG_CLIENT_RESET, SENDMSG_REPLACE,
|
||||
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
|
||||
genId /*arg1*/, clearing ? 1 : 0, null /*obj*/, 0 /*delay*/);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setPlaybackState(int genId, int state,
|
||||
long stateChangeTimeMs, long currentPosMs, float speed) {
|
||||
if (DEBUG) {
|
||||
@ -521,9 +500,6 @@ public class RemoteController
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setTransportControlInfo(int genId, int transportControlFlags,
|
||||
int posCapabilities) {
|
||||
synchronized(mGenLock) {
|
||||
@ -536,9 +512,6 @@ public class RemoteController
|
||||
null /*obj*/, 0 /*delay*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setMetadata(int genId, Bundle metadata) {
|
||||
if (DEBUG) { Log.e(TAG, "setMetadata("+genId+")"); }
|
||||
if (metadata == null) {
|
||||
@ -554,9 +527,6 @@ public class RemoteController
|
||||
metadata /*obj*/, 0 /*delay*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setArtwork(int genId, Bitmap artwork) {
|
||||
if (DEBUG) { Log.v(TAG, "setArtwork("+genId+")"); }
|
||||
if (artwork == null) {
|
||||
@ -574,9 +544,6 @@ public class RemoteController
|
||||
metadata /*obj*/, 0 /*delay*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setAllMetadata(int genId, Bundle metadata, Bitmap artwork) {
|
||||
if (DEBUG) { Log.e(TAG, "setAllMetadata("+genId+")"); }
|
||||
if ((metadata == null) && (artwork == null)) {
|
||||
@ -602,12 +569,12 @@ public class RemoteController
|
||||
|
||||
//==================================================
|
||||
// Event handling
|
||||
private EventHandler mEventHandler;
|
||||
private final EventHandler mEventHandler;
|
||||
private final static int MSG_NEW_PENDING_INTENT = 0;
|
||||
private final static int MSG_NEW_PLAYBACK_INFO = 1;
|
||||
private final static int MSG_NEW_TRANSPORT_INFO = 2;
|
||||
private final static int MSG_NEW_METADATA = 3; // msg always has non-null obj parameter
|
||||
private final static int MSG_CLIENT_RESET = 4;
|
||||
private final static int MSG_CLIENT_CHANGE = 4;
|
||||
|
||||
private class EventHandler extends Handler {
|
||||
|
||||
@ -630,8 +597,8 @@ public class RemoteController
|
||||
case MSG_NEW_METADATA:
|
||||
onNewMetadata(msg.arg1, (Bundle)msg.obj);
|
||||
break;
|
||||
case MSG_CLIENT_RESET:
|
||||
onClientReset(msg.arg1, msg.arg2 == 1);
|
||||
case MSG_CLIENT_CHANGE:
|
||||
onClientChange(msg.arg1, msg.arg2 == 1);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "unknown event " + msg.what);
|
||||
@ -743,7 +710,7 @@ public class RemoteController
|
||||
}
|
||||
}
|
||||
|
||||
private void onClientReset(int genId, boolean clearing) {
|
||||
private void onClientChange(int genId, boolean clearing) {
|
||||
synchronized(mGenLock) {
|
||||
if (mClientGenerationIdCurrent != genId) {
|
||||
return;
|
||||
@ -754,7 +721,7 @@ public class RemoteController
|
||||
l = mOnClientUpdateListener;
|
||||
}
|
||||
if (l != null) {
|
||||
l.onClientReset(clearing);
|
||||
l.onClientChange(clearing);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user