Add helper methods to create a pending intent for media button event
Introduced helper methods to create a broadcast pending intent which sends
a media button event to the media button receiver with specified media action.
It will be helpful for developers because it can save lots of code.
Bug: 22718016
Change-Id: I08d2ffd4c666c22d4b57aa5a1e6b57dc8d02382f
diff --git a/api/current.txt b/api/current.txt
index 67359fa..53434f5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5312,6 +5312,8 @@
ctor public MediaSessionCompat(android.content.Context, java.lang.String);
ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+ method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+ method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
method public android.support.v4.media.session.MediaControllerCompat getController();
method public java.lang.Object getMediaSession();
method public java.lang.Object getRemoteControlClient();
@@ -5414,6 +5416,7 @@
method public java.lang.Object getPlaybackState();
method public long getPosition();
method public int getState();
+ method public static int toKeyCode(long);
method public void writeToParcel(android.os.Parcel, int);
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index e66b894..117eae2 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -44,6 +44,7 @@
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.RatingCompat;
import android.support.v4.media.VolumeProviderCompat;
+import android.support.v4.media.session.PlaybackStateCompat.MediaKeyActions;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -160,6 +161,94 @@
"android.support.v4.media.session.action.ARGUMENT_EXTRAS";
/**
+ * Creates a broadcast pending intent that will send a media button event. The {@code action}
+ * will be translated to the appropriate {@link KeyEvent}, and it will be sent to the
+ * registered media button receiver in the given context. The {@code action} should be one of
+ * the following:
+ * <ul>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_STOP}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li>
+ * </ul>
+ *
+ * @param context The context of the application.
+ * @param action The action to be sent via the pending intent.
+ * @return Created pending intent, or null if cannot find a unique registered media button
+ * receiver or if the {@code action} is unsupported/invalid.
+ */
+ public static PendingIntent buildMediaButtonPendingIntent(Context context,
+ @MediaKeyActions long action) {
+ ComponentName mbrComponent = getMediaButtonReceiverComponent(context);
+ if (mbrComponent == null) {
+ Log.w(TAG, "A unique media button receiver could not be found in the given context, so "
+ + "couldn't build a pending intent.");
+ return null;
+ }
+ return buildMediaButtonPendingIntent(context, mbrComponent, action);
+ }
+
+ /**
+ * Creates a broadcast pending intent that will send a media button event. The {@code action}
+ * will be translated to the appropriate {@link KeyEvent}, and sent to the provided media
+ * button receiver via the pending intent. The {@code action} should be one of the following:
+ * <ul>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_STOP}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li>
+ * </ul>
+ *
+ * @param context The context of the application.
+ * @param mbrComponent The full component name of a media button receiver where you want to send
+ * this intent.
+ * @param action The action to be sent via the pending intent.
+ * @return Created pending intent, or null if cannot find a unique registered media button
+ * receiver or if the {@code action} is unsupported/invalid.
+ */
+ public static PendingIntent buildMediaButtonPendingIntent(Context context,
+ ComponentName mbrComponent, @MediaKeyActions long action) {
+ if (mbrComponent == null) {
+ Log.w(TAG, "The component name of media button receiver should be provided.");
+ return null;
+ }
+ int keyCode = PlaybackStateCompat.toKeyCode(action);
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ Log.w(TAG,
+ "Cannot build a media button pending intent with the given action: " + action);
+ return null;
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.setComponent(mbrComponent);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ return PendingIntent.getBroadcast(context, keyCode, intent, 0);
+ }
+
+ private static ComponentName getMediaButtonReceiverComponent(Context context) {
+ Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ queryIntent.setPackage(context.getPackageName());
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0);
+ if (resolveInfos.size() == 1) {
+ ResolveInfo resolveInfo = resolveInfos.get(0);
+ return new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ } else if (resolveInfos.size() > 1) {
+ Log.w(TAG, "More than one BroadcastReceiver that handles "
+ + Intent.ACTION_MEDIA_BUTTON + " was found, returning null.");
+ }
+ return null;
+ }
+
+ /**
* Creates a new session. You must call {@link #release()} when finished with the session.
* <p>
* The session will automatically be registered with the system but will not be published
@@ -1220,22 +1309,10 @@
public MediaSessionImplBase(Context context, String tag, ComponentName mbrComponent,
PendingIntent mbrIntent) {
if (mbrComponent == null) {
- Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- queryIntent.setPackage(context.getPackageName());
- PackageManager pm = context.getPackageManager();
- List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0);
- // If none are found, assume we are running on a newer platform version that does
- // not require a media button receiver ComponentName. Later code will double check
- // this assumption and throw an error if needed
- if (resolveInfos.size() == 1) {
- ResolveInfo resolveInfo = resolveInfos.get(0);
- mbrComponent = new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name);
- } else if (resolveInfos.size() > 1) {
- Log.w(TAG, "More than one BroadcastReceiver that handles "
- + Intent.ACTION_MEDIA_BUTTON + " was found, using null. Provide a "
- + "specific ComponentName to use as this session's media button "
- + "receiver");
+ mbrComponent = getMediaButtonReceiverComponent(context);
+ if (mbrComponent == null) {
+ Log.w(TAG, "Couldn't find a unique registered media button receiver in the "
+ + "given context.");
}
}
if (mbrComponent != null && mbrIntent == null) {
diff --git a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
index e3bda8c..8f9d3b0 100644
--- a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -24,6 +24,7 @@
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import android.view.KeyEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,6 +50,14 @@
public @interface Actions {}
/**
+ * @hide
+ */
+ @IntDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
+ ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_PLAY_PAUSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaKeyActions {}
+
+ /**
* Indicates this session supports the stop command.
*
* @see Builder#setActions(long)
@@ -285,6 +294,49 @@
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
+ // KeyEvent constants only available on API 11+
+ private static final int KEYCODE_MEDIA_PAUSE = 127;
+ private static final int KEYCODE_MEDIA_PLAY = 126;
+
+ /**
+ * Translates a given action into a matched key code defined in {@link KeyEvent}. The given
+ * action should be one of the following:
+ * <ul>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_STOP}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li>
+ * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li>
+ * </ul>
+ *
+ * @param action The action to be translated.
+ *
+ * @return the key code matched to the given action.
+ */
+ public static int toKeyCode(@MediaKeyActions long action) {
+ if (action == ACTION_PLAY) {
+ return KEYCODE_MEDIA_PLAY;
+ } else if (action == ACTION_PAUSE) {
+ return KEYCODE_MEDIA_PAUSE;
+ } else if (action == ACTION_SKIP_TO_NEXT) {
+ return KeyEvent.KEYCODE_MEDIA_NEXT;
+ } else if (action == ACTION_SKIP_TO_PREVIOUS) {
+ return KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ } else if (action == ACTION_STOP) {
+ return KeyEvent.KEYCODE_MEDIA_STOP;
+ } else if (action == ACTION_FAST_FORWARD) {
+ return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
+ } else if (action == ACTION_REWIND) {
+ return KeyEvent.KEYCODE_MEDIA_REWIND;
+ } else if (action == ACTION_PLAY_PAUSE) {
+ return KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+ }
+ return KeyEvent.KEYCODE_UNKNOWN;
+ }
+
private final int mState;
private final long mPosition;
private final long mBufferedPosition;