diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 6fd0e7d682..967b039757 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -75,6 +75,8 @@
`DownloadNotificationHelper`.
* Move creation of dialogs for `TrackSelectionView`s to
`TrackSelectionDialogBuilder` and add option to select multiple overrides.
+* MediaSessionConnector: Let apps intercept media button events
+ ([#5179](https://github.com/google/ExoPlayer/issues/5179)).
### 2.9.5 ###
diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
index 9074f9e16d..6d80c1001f 100644
--- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
+++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.ext.mediasession;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
@@ -76,6 +77,9 @@ import java.util.Map;
* is recommended for most use cases.
*
To enable editing of the media queue, you can set a {@link QueueEditor} by calling {@link
* #setQueueEditor(QueueEditor)}.
+ * A {@link MediaButtonEventHandler} can be set by calling {@link
+ * #setMediaButtonEventHandler(MediaButtonEventHandler)}. By default media button events are
+ * handled by {@link MediaSessionCompat.Callback#onMediaButtonEvent(Intent)}.
* An {@link ErrorMessageProvider} for providing human readable error messages and
* corresponding error codes can be set by calling {@link
* #setErrorMessageProvider(ErrorMessageProvider)}.
@@ -300,6 +304,21 @@ public final class MediaSessionConnector {
void onSetRating(Player player, RatingCompat rating, Bundle extras);
}
+ /** Handles a media button event. */
+ public interface MediaButtonEventHandler {
+ /**
+ * See {@link MediaSessionCompat.Callback#onMediaButtonEvent(Intent)}.
+ *
+ * @param player The {@link Player}.
+ * @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
+ * changes to the player.
+ * @param mediaButtonEvent The {@link Intent}.
+ * @return True if the event was handled, false otherwise.
+ */
+ boolean onMediaButtonEvent(
+ Player player, ControlDispatcher controlDispatcher, Intent mediaButtonEvent);
+ }
+
/**
* Provides a {@link PlaybackStateCompat.CustomAction} to be published and handles the action when
* sent by a media controller.
@@ -357,6 +376,7 @@ public final class MediaSessionConnector {
@Nullable private QueueNavigator queueNavigator;
@Nullable private QueueEditor queueEditor;
@Nullable private RatingCallback ratingCallback;
+ @Nullable private MediaButtonEventHandler mediaButtonEventHandler;
private long enabledPlaybackActions;
private int rewindMs;
@@ -432,6 +452,18 @@ public final class MediaSessionConnector {
}
}
+ /**
+ * Sets the {@link MediaButtonEventHandler}. Pass {@code null} if the media button event should be
+ * handled by {@link MediaSessionCompat.Callback#onMediaButtonEvent(Intent)}.
+ *
+ * @param mediaButtonEventHandler The {@link MediaButtonEventHandler}, or null to let the event be
+ * handled by {@link MediaSessionCompat.Callback#onMediaButtonEvent(Intent)}.
+ */
+ public void setMediaButtonEventHandler(
+ @Nullable MediaButtonEventHandler mediaButtonEventHandler) {
+ this.mediaButtonEventHandler = mediaButtonEventHandler;
+ }
+
/**
* Sets the enabled playback actions.
*
@@ -753,6 +785,10 @@ public final class MediaSessionConnector {
return player != null && queueEditor != null;
}
+ private boolean canDispatchMediaButtonEvent() {
+ return player != null && mediaButtonEventHandler != null;
+ }
+
private void stopPlayerForPrepare(boolean playWhenReady) {
if (player != null) {
player.stop();
@@ -1169,5 +1205,14 @@ public final class MediaSessionConnector {
queueEditor.onRemoveQueueItem(player, description);
}
}
+
+ @Override
+ public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
+ boolean isHandled =
+ canDispatchMediaButtonEvent()
+ && mediaButtonEventHandler.onMediaButtonEvent(
+ player, controlDispatcher, mediaButtonEvent);
+ return isHandled || super.onMediaButtonEvent(mediaButtonEvent);
+ }
}
}