Skip to content

Commit dbb3020

Browse files
committed
Allow opting out of media button playback resumption at runtime
This is a step towards Issue: #770
1 parent 8faee05 commit dbb3020

File tree

4 files changed

+69
-59
lines changed

4 files changed

+69
-59
lines changed

libraries/session/src/main/java/androidx/media3/session/MediaSession.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,24 @@ public final void setSessionActivity(
857857
impl.setSessionActivity(controller, activityPendingIntent);
858858
}
859859

860+
/**
861+
* Sets whether to opt out from media button (e.g. Bluetooth) playback resumption.
862+
*
863+
* <p>The default value is {@code false}.
864+
*
865+
* <p>This method will throw {@link IllegalStateException} if called when media button playback
866+
* resumption was never opted into in the first place (by adding {@link MediaButtonReceiver} to
867+
* the manifest).
868+
*
869+
* @param optOutOfMediaButtonPlaybackResumption Whether to opt out of media button playback
870+
* resumption.
871+
*/
872+
@UnstableApi
873+
public void setOptOutOfMediaButtonPlaybackResumption(
874+
boolean optOutOfMediaButtonPlaybackResumption) {
875+
impl.setOptOutOfMediaButtonPlaybackResumption(optOutOfMediaButtonPlaybackResumption);
876+
}
877+
860878
/**
861879
* Sets the underlying {@link Player} for this session to dispatch incoming events to.
862880
*

libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,12 @@ public MediaSession.ConnectionResult onConnectOnHandler(ControllerInfo controlle
889889
return connectionResult;
890890
}
891891

892+
public void setOptOutOfMediaButtonPlaybackResumption(
893+
boolean optOutOfMediaButtonPlaybackResumption) {
894+
sessionLegacyStub.setOptOutOfMediaButtonPlaybackResumption(
895+
optOutOfMediaButtonPlaybackResumption);
896+
}
897+
892898
public void onPostConnectOnHandler(ControllerInfo controller) {
893899
if (isMediaNotificationControllerConnected && isSystemUiController(controller)) {
894900
// Hide System UI. Apps can use the media notification controller to maintain the platform

libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import android.os.SystemClock;
6969
import android.text.TextUtils;
7070
import android.view.KeyEvent;
71+
import androidx.annotation.NonNull;
7172
import androidx.annotation.Nullable;
7273
import androidx.annotation.RequiresApi;
7374
import androidx.core.util.ObjectsCompat;
@@ -136,6 +137,7 @@
136137
private final MediaSessionCompat sessionCompat;
137138
@Nullable private final MediaButtonReceiver runtimeBroadcastReceiver;
138139
@Nullable private final ComponentName broadcastReceiverComponentName;
140+
private boolean optOutOfMediaButtonPlaybackResumption;
139141
@Nullable private VolumeProviderCompat volumeProviderCompat;
140142
private final boolean playIfSuppressed;
141143

@@ -191,7 +193,7 @@ public MediaSessionLegacyStub(
191193
broadcastReceiverComponentName = queryPackageManagerForMediaButtonReceiver(context);
192194
@Nullable ComponentName receiverComponentName = broadcastReceiverComponentName;
193195
boolean isReceiverComponentAService = false;
194-
if (receiverComponentName == null || SDK_INT < 31) {
196+
if (receiverComponentName == null && SDK_INT < 26) {
195197
// Below API 26, media button events are sent to the receiver at runtime also. We always want
196198
// these to arrive at the service at runtime. release() then set the receiver for restart if
197199
// available.
@@ -201,37 +203,35 @@ public MediaSessionLegacyStub(
201203
receiverComponentName =
202204
getServiceComponentByAction(context, MediaSessionService.SERVICE_INTERFACE);
203205
}
204-
isReceiverComponentAService =
205-
receiverComponentName != null
206-
&& !Objects.equals(receiverComponentName, broadcastReceiverComponentName);
206+
isReceiverComponentAService = receiverComponentName != null;
207207
}
208-
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionUri);
209-
PendingIntent mediaButtonIntent;
210-
if (receiverComponentName == null) {
208+
final PendingIntent mediaButtonIntent;
209+
if (receiverComponentName == null && SDK_INT < 26) {
211210
// Neither a media button receiver from the app manifest nor a service available that could
212211
// handle media button events. Create a runtime receiver and a pending intent for it.
213212
runtimeBroadcastReceiver = new MediaButtonReceiver();
214213
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
215214
filter.addDataScheme(castNonNull(sessionUri.getScheme()));
216215
Util.registerReceiverNotExported(context, runtimeBroadcastReceiver, filter);
217216
// Create a pending intent to be broadcast to the receiver.
217+
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionUri);
218218
intent.setPackage(context.getPackageName());
219219
mediaButtonIntent =
220220
PendingIntent.getBroadcast(
221221
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE);
222-
// Creates a fake ComponentName for MediaSessionCompat in pre-L or without a service.
223-
receiverComponentName = new ComponentName(context, context.getClass());
224222
} else {
225-
intent.setComponent(receiverComponentName);
226-
mediaButtonIntent =
227-
isReceiverComponentAService
228-
? (SDK_INT >= 26
229-
? PendingIntent.getForegroundService(
230-
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE)
231-
: PendingIntent.getService(
232-
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE))
233-
: PendingIntent.getBroadcast(
234-
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE);
223+
if (receiverComponentName != null && SDK_INT < 31) {
224+
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionUri);
225+
intent.setComponent(receiverComponentName);
226+
mediaButtonIntent =
227+
isReceiverComponentAService
228+
? PendingIntent.getService(
229+
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE)
230+
: PendingIntent.getBroadcast(
231+
context, /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE);
232+
} else {
233+
mediaButtonIntent = null;
234+
}
235235
runtimeBroadcastReceiver = null;
236236
}
237237

@@ -243,7 +243,6 @@ public MediaSessionLegacyStub(
243243
new MediaSessionCompat(
244244
context,
245245
sessionCompatId,
246-
SDK_INT < 31 ? receiverComponentName : null,
247246
SDK_INT < 31 ? mediaButtonIntent : null,
248247
/* sessionInfo= */ tokenExtras);
249248
if (SDK_INT >= 31 && broadcastReceiverComponentName != null) {
@@ -261,6 +260,15 @@ public MediaSessionLegacyStub(
261260
sessionCompat.setCallback(thisRef, handler);
262261
}
263262

263+
public void setOptOutOfMediaButtonPlaybackResumption(
264+
boolean optOutOfMediaButtonPlaybackResumption) {
265+
if (broadcastReceiverComponentName == null) {
266+
throw new IllegalStateException(
267+
"Media button playback resumption is not enabled, cannot opt out.");
268+
}
269+
this.optOutOfMediaButtonPlaybackResumption = optOutOfMediaButtonPlaybackResumption;
270+
}
271+
264272
/**
265273
* Sets the available commands for the platform session.
266274
*
@@ -460,22 +468,18 @@ public void start() {
460468

461469
@SuppressWarnings("PendingIntentMutability") // We can't use SaferPendingIntent.
462470
public void release() {
463-
if (SDK_INT < 31) {
464-
if (broadcastReceiverComponentName == null) {
465-
// No broadcast receiver available. Playback resumption not supported.
466-
setMediaButtonReceiver(sessionCompat, /* mediaButtonReceiverIntent= */ null);
467-
} else {
468-
// Override the runtime receiver with the broadcast receiver for playback resumption.
469-
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionImpl.getUri());
470-
intent.setComponent(broadcastReceiverComponentName);
471-
PendingIntent mediaButtonReceiverIntent =
472-
PendingIntent.getBroadcast(
473-
sessionImpl.getContext(),
474-
/* requestCode= */ 0,
475-
intent,
476-
PENDING_INTENT_FLAG_MUTABLE);
477-
setMediaButtonReceiver(sessionCompat, mediaButtonReceiverIntent);
478-
}
471+
if ((broadcastReceiverComponentName == null && SDK_INT < 26)
472+
|| optOutOfMediaButtonPlaybackResumption) {
473+
// No broadcast receiver available. Playback resumption not supported.
474+
setMediaButtonReceiver(sessionCompat, /* mediaButtonReceiverIntent= */ null);
475+
} else if (SDK_INT < 26) {
476+
// Override the runtime receiver with the broadcast receiver for playback resumption.
477+
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionImpl.getUri());
478+
intent.setComponent(broadcastReceiverComponentName);
479+
PendingIntent mediaButtonReceiverIntent =
480+
PendingIntent.getBroadcast(
481+
sessionImpl.getContext(), /* requestCode= */ 0, intent, PENDING_INTENT_FLAG_MUTABLE);
482+
setMediaButtonReceiver(sessionCompat, mediaButtonReceiverIntent);
479483
}
480484
if (runtimeBroadcastReceiver != null) {
481485
sessionImpl.getContext().unregisterReceiver(runtimeBroadcastReceiver);
@@ -2080,7 +2084,7 @@ public void onReceive(Context context, Intent intent) {
20802084
@RequiresApi(31)
20812085
private static final class Api31 {
20822086
public static void setMediaButtonBroadcastReceiver(
2083-
MediaSessionCompat mediaSessionCompat, ComponentName broadcastReceiver) {
2087+
MediaSessionCompat mediaSessionCompat, @NonNull ComponentName broadcastReceiver) {
20842088
try {
20852089
((android.media.session.MediaSession) checkNotNull(mediaSessionCompat.getMediaSession()))
20862090
.setMediaButtonBroadcastReceiver(broadcastReceiver);

libraries/session/src/main/java/androidx/media3/session/legacy/MediaSessionCompat.java

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -325,32 +325,12 @@ public class MediaSessionCompat {
325325
public MediaSessionCompat(
326326
Context context,
327327
String tag,
328-
@Nullable ComponentName mbrComponent,
329328
@Nullable PendingIntent mbrIntent,
330329
@Nullable Bundle sessionInfo) {
331330
if (TextUtils.isEmpty(tag)) {
332331
throw new IllegalArgumentException("tag must not be null or empty");
333332
}
334333

335-
if (mbrComponent == null) {
336-
mbrComponent = MediaButtonReceiver.getMediaButtonReceiverComponent(context);
337-
if (mbrComponent == null) {
338-
Log.i(TAG, "Couldn't find a unique registered media button receiver in the given context.");
339-
}
340-
}
341-
if (mbrComponent != null && mbrIntent == null) {
342-
// construct a PendingIntent for the media button
343-
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
344-
// the associated intent will be handled by the component being registered
345-
mediaButtonIntent.setComponent(mbrComponent);
346-
mbrIntent =
347-
PendingIntent.getBroadcast(
348-
context,
349-
0 /* requestCode, ignored */,
350-
mediaButtonIntent,
351-
Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0);
352-
}
353-
354334
if (Build.VERSION.SDK_INT >= 29) {
355335
impl = new MediaSessionImplApi29(context, tag, sessionInfo);
356336
} else if (Build.VERSION.SDK_INT >= 28) {
@@ -364,7 +344,9 @@ public MediaSessionCompat(
364344
Looper myLooper = Looper.myLooper();
365345
Handler handler = new Handler(myLooper != null ? myLooper : Looper.getMainLooper());
366346
setCallback(new Callback() {}, handler);
367-
impl.setMediaButtonReceiver(mbrIntent);
347+
if (mbrIntent != null) {
348+
impl.setMediaButtonReceiver(mbrIntent);
349+
}
368350

369351
controller = new MediaControllerCompat(context, this);
370352
}
@@ -404,7 +386,7 @@ public void setSessionActivity(@Nullable PendingIntent pi) {
404386
*
405387
* @param mbr The {@link PendingIntent} to send the media button event to.
406388
*/
407-
public void setMediaButtonReceiver(PendingIntent mbr) {
389+
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
408390
impl.setMediaButtonReceiver(mbr);
409391
}
410392

0 commit comments

Comments
 (0)