diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java index ac88c9cdba..69c8d3db09 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -37,6 +37,7 @@ import androidx.media3.common.Player; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; +import androidx.media3.session.MediaSessionService.ShowNotificationForEmptyPlayerMode; import androidx.media3.session.MediaSessionService.ShowNotificationForIdlePlayerMode; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.FutureCallback; @@ -79,6 +80,7 @@ private boolean isUserEngagedTimeoutEnabled; private long userEngagedTimeoutMs; @ShowNotificationForIdlePlayerMode int showNotificationForIdlePlayerMode; + @ShowNotificationForEmptyPlayerMode int showNotificationForEmptyPlayerMode; public MediaNotificationManager( MediaSessionService mediaSessionService, @@ -97,6 +99,8 @@ public MediaNotificationManager( userEngagedTimeoutMs = MediaSessionService.DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS; showNotificationForIdlePlayerMode = MediaSessionService.SHOW_NOTIFICATION_FOR_IDLE_PLAYER_AFTER_STOP_OR_ERROR; + showNotificationForEmptyPlayerMode = + MediaSessionService.SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER; } public void addSession(MediaSession session) { @@ -212,6 +216,16 @@ public void setShowNotificationForIdlePlayer( } } + public void setShowNotificationForEmptyPlayer( + @ShowNotificationForEmptyPlayerMode int showNotificationForEmptyPlayerMode) { + this.showNotificationForEmptyPlayerMode = showNotificationForEmptyPlayerMode; + List sessions = mediaSessionService.getSessions(); + for (int i = 0; i < sessions.size(); i++) { + mediaSessionService.onUpdateNotificationInternal( + sessions.get(i), /* startInForegroundWhenPaused= */ false); + } + } + @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_USER_ENGAGED_TIMEOUT) { @@ -322,10 +336,25 @@ private void removeNotification() { private boolean shouldShowNotification(MediaSession session) { MediaController controller = getConnectedControllerForSession(session); - if (controller == null || controller.getCurrentTimeline().isEmpty()) { + if (controller == null) { return false; } ControllerInfo controllerInfo = checkNotNull(controllerMap.get(session)); + if (controller.getCurrentTimeline().isEmpty()) { + switch (showNotificationForEmptyPlayerMode) { + case MediaSessionService.SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_ALWAYS: + break; + case MediaSessionService.SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER: + return false; + case MediaSessionService.SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_AFTER_STOP_OR_ERROR: + if (!controllerInfo.hasBeenPrepared) { + return false; + } + break; + default: + throw new IllegalStateException(); + } + } if (controller.getPlaybackState() != Player.STATE_IDLE) { // Playback first prepared or restarted, reset previous notification dismissed flag. controllerInfo.wasNotificationDismissed = false; diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index ec789b06ef..0ece951df2 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -194,8 +194,8 @@ default void onForegroundServiceStartNotAllowedException() {} public @interface ShowNotificationForIdlePlayerMode {} /** - * Always show a notification when the {@link Player} is in {@link Player#STATE_IDLE}, has media, - * and the notification wasn't explicitly dismissed. + * Always show a notification when the {@link Player} is in {@link Player#STATE_IDLE} and the + * notification wasn't explicitly dismissed. */ @UnstableApi public static final int SHOW_NOTIFICATION_FOR_IDLE_PLAYER_ALWAYS = 1; @@ -204,10 +204,45 @@ default void onForegroundServiceStartNotAllowedException() {} /** * Shows a notification when the {@link Player} is in {@link Player#STATE_IDLE} due to {@link - * Player#stop} or an error, has media, and the notification wasn't explicitly dismissed. + * Player#stop} or an error, and the notification wasn't explicitly dismissed. */ @UnstableApi public static final int SHOW_NOTIFICATION_FOR_IDLE_PLAYER_AFTER_STOP_OR_ERROR = 3; + /** + * The behavior for showing notifications when the {@link Player} has no media. + * + *

One of {@link #SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_ALWAYS}, {@link + * #SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER}, {@link + * #SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_AFTER_STOP_OR_ERROR}. + * + *

The default value is {@link #SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER}. + */ + @UnstableApi + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({ + SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_ALWAYS, + SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER, + SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_AFTER_STOP_OR_ERROR + }) + public @interface ShowNotificationForEmptyPlayerMode {} + + /** + * Always show a notification when the {@link Player} is empty and the notification wasn't + * explicitly dismissed. + */ + @UnstableApi public static final int SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_ALWAYS = 1; + + /** Never show a notification when the {@link Player} is empty. */ + @UnstableApi public static final int SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_NEVER = 2; + + /** + * Shows a notification when the {@link Player} is empty, in {@link Player#STATE_IDLE} due to + * {@link Player#stop} or an error, and the notification wasn't explicitly dismissed. + */ + @UnstableApi public static final int SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_AFTER_STOP_OR_ERROR = 3; + private static final String TAG = "MSessionService"; private final Object lock; @@ -547,6 +582,18 @@ public final void setShowNotificationForIdlePlayer( .setShowNotificationForIdlePlayer(showNotificationForIdlePlayerMode); } + /** + * Sets whether and when a notification for a {@link Player} that has no media should be shown. + * + * @param showNotificationForEmptyPlayerMode The {@link ShowNotificationForEmptyPlayerMode}. + */ + @UnstableApi + public final void setShowNotificationForEmptyPlayer( + @ShowNotificationForEmptyPlayerMode int showNotificationForEmptyPlayerMode) { + getMediaNotificationManager() + .setShowNotificationForEmptyPlayer(showNotificationForEmptyPlayerMode); + } + /** * Returns whether there is a session with ongoing user-engaged playback that is run in a * foreground service.