Allow an app to decide to not start the service

Once a service is started as a foreground service, it must
be started into the foreground. This means an app can not
suppress a play command arriving from the `MediaButtonReceiver`
once the receiver has started the service.

This change adds a method to the `MediaButtonReceiver` that
allows app to suppress starting the service to not get into
this situation of wanting to suppress the play command after
the service is already started.

Issue: androidx/media#1528
PiperOrigin-RevId: 650280025
This commit is contained in:
bachinger 2024-07-08 09:41:55 -07:00 committed by Copybara-Service
parent 2377d7556f
commit 06d61ffaaa
2 changed files with 45 additions and 4 deletions

View File

@ -45,6 +45,12 @@
* Muxers:
* IMA extension:
* Session:
* Add `MediaButtonReceiver.shouldStartForegroundService(Intent)` to allow
apps to suppress a play command coming in for playback resumption by
overriding this method. By default, the service is always started and
playback can't be suppressed without the system crashing the service
with a `ForegroundServiceDidNotStartInTimeException`
([#1528](https://github.com/google/ExoPlayer/issues/1528)).
* UI:
* Downloads:
* OkHttp Extension:

View File

@ -118,11 +118,16 @@ public class MediaButtonReceiver extends BroadcastReceiver {
return;
}
@Nullable
KeyEvent keyEvent = checkNotNull(intent.getExtras()).getParcelable(Intent.EXTRA_KEY_EVENT);
if (keyEvent == null
|| keyEvent.getAction() != KeyEvent.ACTION_DOWN
|| keyEvent.getRepeatCount() != 0) {
// Only handle the intent once with the earliest key event that arrives.
return;
}
if (Util.SDK_INT >= 26) {
@Nullable
KeyEvent keyEvent = checkNotNull(intent.getExtras()).getParcelable(Intent.EXTRA_KEY_EVENT);
if (keyEvent != null
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_MEDIA_PLAY
if (keyEvent.getKeyCode() != KeyEvent.KEYCODE_MEDIA_PLAY
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
// Starting with Android 8 (API 26), the service must be started immediately in the
// foreground when being started. Also starting with Android 8, the system sends media
@ -143,6 +148,14 @@ public class MediaButtonReceiver extends BroadcastReceiver {
ComponentName mediaButtonServiceComponentName = getServiceComponentByAction(context, action);
if (mediaButtonServiceComponentName != null) {
intent.setComponent(mediaButtonServiceComponentName);
if (!shouldStartForegroundService(intent)) {
Log.i(
TAG,
"onReceive(Intent) does not start the media button event target service into the"
+ " foreground on app request: "
+ mediaButtonServiceComponentName.getClassName());
return;
}
try {
ContextCompat.startForegroundService(context, intent);
} catch (/* ForegroundServiceStartNotAllowedException */ IllegalStateException e) {
@ -161,6 +174,28 @@ public class MediaButtonReceiver extends BroadcastReceiver {
"Could not find any Service that handles any of the actions " + Arrays.toString(ACTIONS));
}
/**
* Returns whether to start the {@linkplain Intent#getComponent() media button event target
* service} into the foreground.
*
* <p>Returns true by default. Apps can override this method to decide to not start a service when
* receiving an event with {@link KeyEvent#KEYCODE_MEDIA_PLAY} or {@link
* KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} that should be suppressed.
*
* <p>Note: Once the service is started into the foreground by the receiver, the app must start
* playback to get into the foreground or the system will crash the service with a {@code
* ForegroundServiceDidNotStartInTimeException} or an {@link IllegalStateException}.
*
* @param intent The intent that {@linkplain #onReceive(Context, Intent) was received by the media
* button event receiver}.
* @return true if the service should be {@linkplain ContextCompat#startForegroundService(Context,
* Intent) started as a foreground service}. If false is returned the service is not started
* and the receiver call is a no-op.
*/
protected boolean shouldStartForegroundService(Intent intent) {
return true;
}
/**
* This method is called when an exception is thrown when calling {@link
* Context#startForegroundService(Intent)} as a result of receiving a media button event.