mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Utilize CommandButton for customizing Notification
PiperOrigin-RevId: 416421456
This commit is contained in:
parent
531ff0e19d
commit
df2e37acbe
1 changed files with 188 additions and 274 deletions
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package androidx.media3.session;
|
package androidx.media3.session;
|
||||||
|
|
||||||
|
import static androidx.media3.common.Player.COMMAND_INVALID;
|
||||||
|
import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_BACK;
|
import static androidx.media3.common.Player.COMMAND_SEEK_BACK;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD;
|
import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
|
||||||
|
|
@ -40,6 +42,7 @@ import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
|
@ -53,6 +56,8 @@ import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.common.util.BundleableUtil;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.NotificationUtil;
|
import androidx.media3.common.util.NotificationUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
|
@ -62,7 +67,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -180,12 +184,12 @@ public class PlayerNotificationManager {
|
||||||
*
|
*
|
||||||
* <p>If multiple {@link PlayerNotificationManager} instances are in use at the same time, the
|
* <p>If multiple {@link PlayerNotificationManager} instances are in use at the same time, the
|
||||||
* {@code instanceId} must be set as an intent extra with key {@link
|
* {@code instanceId} must be set as an intent extra with key {@link
|
||||||
* PlayerNotificationManager#EXTRA_INSTANCE_ID} to avoid sending the action to every custom
|
* PlayerNotificationManager#INTENT_EXTRA_INSTANCE_ID} to avoid sending the action to every
|
||||||
* action receiver. It's also necessary to ensure something is different about the actions. This
|
* custom action receiver. It's also necessary to ensure something is different about the
|
||||||
* may be any of the {@link Intent} attributes considered by {@link Intent#filterEquals}, or
|
* actions. This may be any of the {@link Intent} attributes considered by {@link
|
||||||
* different request code integers when creating the {@link PendingIntent}s with {@link
|
* Intent#filterEquals}, or different request code integers when creating the {@link
|
||||||
* PendingIntent#getBroadcast}. The easiest approach is to use the {@code instanceId} as the
|
* PendingIntent}s with {@link PendingIntent#getBroadcast}. The easiest approach is to use the
|
||||||
* request code.
|
* {@code instanceId} as the request code.
|
||||||
*
|
*
|
||||||
* @param context The {@link Context}.
|
* @param context The {@link Context}.
|
||||||
* @param instanceId The instance id of the {@link PlayerNotificationManager}.
|
* @param instanceId The instance id of the {@link PlayerNotificationManager}.
|
||||||
|
|
@ -251,12 +255,6 @@ public class PlayerNotificationManager {
|
||||||
protected int channelDescriptionResourceId;
|
protected int channelDescriptionResourceId;
|
||||||
protected int channelImportance;
|
protected int channelImportance;
|
||||||
protected int smallIconResourceId;
|
protected int smallIconResourceId;
|
||||||
protected int rewindActionIconResourceId;
|
|
||||||
protected int playActionIconResourceId;
|
|
||||||
protected int pauseActionIconResourceId;
|
|
||||||
protected int fastForwardActionIconResourceId;
|
|
||||||
protected int previousActionIconResourceId;
|
|
||||||
protected int nextActionIconResourceId;
|
|
||||||
@Nullable protected String groupKey;
|
@Nullable protected String groupKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -288,12 +286,6 @@ public class PlayerNotificationManager {
|
||||||
channelImportance = NotificationUtil.IMPORTANCE_LOW;
|
channelImportance = NotificationUtil.IMPORTANCE_LOW;
|
||||||
mediaDescriptionAdapter = new DefaultMediaDescriptionAdapter(/* pendingIntent= */ null);
|
mediaDescriptionAdapter = new DefaultMediaDescriptionAdapter(/* pendingIntent= */ null);
|
||||||
smallIconResourceId = R.drawable.media3_notification_small_icon;
|
smallIconResourceId = R.drawable.media3_notification_small_icon;
|
||||||
playActionIconResourceId = R.drawable.media3_notification_play;
|
|
||||||
pauseActionIconResourceId = R.drawable.media3_notification_pause;
|
|
||||||
rewindActionIconResourceId = R.drawable.media3_notification_seek_back;
|
|
||||||
fastForwardActionIconResourceId = R.drawable.media3_notification_seek_forward;
|
|
||||||
previousActionIconResourceId = R.drawable.media3_notification_seek_to_previous;
|
|
||||||
nextActionIconResourceId = R.drawable.media3_notification_seek_to_next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -375,79 +367,6 @@ public class PlayerNotificationManager {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link #ACTION_PLAY}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_play}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setPlayActionIconResourceId(int playActionIconResourceId) {
|
|
||||||
this.playActionIconResourceId = playActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link #ACTION_PAUSE}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_pause}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setPauseActionIconResourceId(int pauseActionIconResourceId) {
|
|
||||||
this.pauseActionIconResourceId = pauseActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link #ACTION_REWIND}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_rewind}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setRewindActionIconResourceId(int rewindActionIconResourceId) {
|
|
||||||
this.rewindActionIconResourceId = rewindActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link
|
|
||||||
* #ACTION_FAST_FORWARD}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_fastforward}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setFastForwardActionIconResourceId(int fastForwardActionIconResourceId) {
|
|
||||||
this.fastForwardActionIconResourceId = fastForwardActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link #ACTION_PREVIOUS}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_previous}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setPreviousActionIconResourceId(int previousActionIconResourceId) {
|
|
||||||
this.previousActionIconResourceId = previousActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource id of the drawable to be used as the icon of action {@link #ACTION_NEXT}.
|
|
||||||
*
|
|
||||||
* <p>The default is {@code R.drawable#media3_notification_next}.
|
|
||||||
*
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public Builder setNextActionIconResourceId(int nextActionIconResourceId) {
|
|
||||||
this.nextActionIconResourceId = nextActionIconResourceId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The key of the group the media notification should belong to.
|
* The key of the group the media notification should belong to.
|
||||||
*
|
*
|
||||||
|
|
@ -491,12 +410,6 @@ public class PlayerNotificationManager {
|
||||||
notificationListener,
|
notificationListener,
|
||||||
customActionReceiver,
|
customActionReceiver,
|
||||||
smallIconResourceId,
|
smallIconResourceId,
|
||||||
playActionIconResourceId,
|
|
||||||
pauseActionIconResourceId,
|
|
||||||
rewindActionIconResourceId,
|
|
||||||
fastForwardActionIconResourceId,
|
|
||||||
previousActionIconResourceId,
|
|
||||||
nextActionIconResourceId,
|
|
||||||
groupKey);
|
groupKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -522,30 +435,34 @@ public class PlayerNotificationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The action which starts playback. */
|
/** The action which is executed when a button in the notification is clicked. */
|
||||||
public static final String ACTION_PLAY = "androidx.media3.ui.notification.play";
|
private static final String INTENT_ACTION_COMMAND = "androidx.media3.session.command";
|
||||||
/** The action which pauses playback. */
|
|
||||||
public static final String ACTION_PAUSE = "androidx.media3.ui.notification.pause";
|
|
||||||
/** The action which skips to the previous media item. */
|
|
||||||
public static final String ACTION_PREVIOUS = "androidx.media3.ui.notification.prev";
|
|
||||||
/** The action which skips to the next media item. */
|
|
||||||
public static final String ACTION_NEXT = "androidx.media3.ui.notification.next";
|
|
||||||
/** The action which fast forwards. */
|
|
||||||
public static final String ACTION_FAST_FORWARD = "androidx.media3.ui.notification.ffwd";
|
|
||||||
/** The action which rewinds. */
|
|
||||||
public static final String ACTION_REWIND = "androidx.media3.ui.notification.rewind";
|
|
||||||
/** The extra key of the instance id of the player notification manager. */
|
|
||||||
public static final String EXTRA_INSTANCE_ID = "INSTANCE_ID";
|
|
||||||
/**
|
/**
|
||||||
* The action which is executed when the notification is dismissed. It cancels the notification
|
* The action which is executed when the notification is dismissed. It cancels the notification
|
||||||
* and calls {@link NotificationListener#onNotificationCancelled(int, boolean)}.
|
* and calls {@link NotificationListener#onNotificationCancelled(int, boolean)}.
|
||||||
*/
|
*/
|
||||||
private static final String ACTION_DISMISS = "androidx.media3.ui.notification.dismiss";
|
private static final String INTENT_ACTION_DISMISS =
|
||||||
|
"androidx.media3.session.notification.dismiss";
|
||||||
|
|
||||||
|
private static final String INTENT_EXTRA_PLAYER_COMMAND =
|
||||||
|
"androidx.media3.session.EXTRA_PLAYER_COMMAND";
|
||||||
|
private static final String INTENT_EXTRA_SESSION_COMMAND =
|
||||||
|
"androidx.media3.session.EXTRA_SESSION_COMMAND";
|
||||||
|
private static final String INTENT_EXTRA_INSTANCE_ID =
|
||||||
|
"androidx.media3.session.notificaiton.EXTRA_INSTANCE_ID";
|
||||||
|
private static final String INTENT_SCHEME = "media3";
|
||||||
|
|
||||||
|
private static final int PENDING_INTENT_FLAGS =
|
||||||
|
(Util.SDK_INT >= 23)
|
||||||
|
? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||||
|
: PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
|
private static final String TAG = "NotificationManager";
|
||||||
|
|
||||||
// Internal messages.
|
// Internal messages.
|
||||||
|
|
||||||
private static final int MSG_START_OR_UPDATE_NOTIFICATION = 0;
|
private static final int MSG_START_OR_UPDATE_NOTIFICATION = 1;
|
||||||
private static final int MSG_UPDATE_NOTIFICATION_BITMAP = 1;
|
private static final int MSG_UPDATE_NOTIFICATION_BITMAP = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visibility of notification on the lock screen. One of {@link
|
* Visibility of notification on the lock screen. One of {@link
|
||||||
|
|
@ -591,13 +508,17 @@ public class PlayerNotificationManager {
|
||||||
private final IntentFilter intentFilter;
|
private final IntentFilter intentFilter;
|
||||||
private final Player.Listener playerListener;
|
private final Player.Listener playerListener;
|
||||||
private final NotificationBroadcastReceiver notificationBroadcastReceiver;
|
private final NotificationBroadcastReceiver notificationBroadcastReceiver;
|
||||||
private final Map<String, NotificationCompat.Action> playbackActions;
|
|
||||||
private final Map<String, NotificationCompat.Action> customActions;
|
private final Map<String, NotificationCompat.Action> customActions;
|
||||||
private final PendingIntent dismissPendingIntent;
|
private final PendingIntent dismissPendingIntent;
|
||||||
private final int instanceId;
|
private final int instanceId;
|
||||||
|
private final CommandButton playButton;
|
||||||
|
private final CommandButton pauseButton;
|
||||||
|
private final CommandButton seekToPreviousButton;
|
||||||
|
private final CommandButton seekToNextButton;
|
||||||
|
private final CommandButton seekBackButton;
|
||||||
|
private final CommandButton seekForwardButton;
|
||||||
|
|
||||||
@Nullable private NotificationCompat.Builder builder;
|
@Nullable private NotificationCompat.Builder builder;
|
||||||
@Nullable private List<NotificationCompat.Action> builderActions;
|
|
||||||
@Nullable private Player player;
|
@Nullable private Player player;
|
||||||
private boolean isNotificationStarted;
|
private boolean isNotificationStarted;
|
||||||
private int currentNotificationTag;
|
private int currentNotificationTag;
|
||||||
|
|
@ -620,12 +541,6 @@ public class PlayerNotificationManager {
|
||||||
@Nullable NotificationListener notificationListener,
|
@Nullable NotificationListener notificationListener,
|
||||||
@Nullable CustomActionReceiver customActionReceiver,
|
@Nullable CustomActionReceiver customActionReceiver,
|
||||||
int smallIconResourceId,
|
int smallIconResourceId,
|
||||||
int playActionIconResourceId,
|
|
||||||
int pauseActionIconResourceId,
|
|
||||||
int rewindActionIconResourceId,
|
|
||||||
int fastForwardActionIconResourceId,
|
|
||||||
int previousActionIconResourceId,
|
|
||||||
int nextActionIconResourceId,
|
|
||||||
@Nullable String groupKey) {
|
@Nullable String groupKey) {
|
||||||
context = context.getApplicationContext();
|
context = context.getApplicationContext();
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
@ -655,29 +570,51 @@ public class PlayerNotificationManager {
|
||||||
badgeIconType = NotificationCompat.BADGE_ICON_SMALL;
|
badgeIconType = NotificationCompat.BADGE_ICON_SMALL;
|
||||||
visibility = NotificationCompat.VISIBILITY_PUBLIC;
|
visibility = NotificationCompat.VISIBILITY_PUBLIC;
|
||||||
|
|
||||||
// initialize actions
|
// initialize default buttons
|
||||||
playbackActions =
|
playButton =
|
||||||
createPlaybackActions(
|
new CommandButton.Builder()
|
||||||
context,
|
.setDisplayName(context.getText(R.string.media3_controls_play_description))
|
||||||
instanceId,
|
.setIconResId(R.drawable.media3_notification_play)
|
||||||
playActionIconResourceId,
|
.setPlayerCommand(COMMAND_PLAY_PAUSE)
|
||||||
pauseActionIconResourceId,
|
.build();
|
||||||
rewindActionIconResourceId,
|
pauseButton =
|
||||||
fastForwardActionIconResourceId,
|
new CommandButton.Builder()
|
||||||
previousActionIconResourceId,
|
.setDisplayName(context.getText(R.string.media3_controls_pause_description))
|
||||||
nextActionIconResourceId);
|
.setIconResId(R.drawable.media3_notification_pause)
|
||||||
for (String action : playbackActions.keySet()) {
|
.setPlayerCommand(COMMAND_PLAY_PAUSE)
|
||||||
intentFilter.addAction(action);
|
.build();
|
||||||
}
|
seekToPreviousButton =
|
||||||
|
new CommandButton.Builder()
|
||||||
|
.setDisplayName(context.getText(R.string.media3_controls_seek_to_previous_description))
|
||||||
|
.setIconResId(R.drawable.media3_notification_seek_to_previous)
|
||||||
|
.setPlayerCommand(COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.build();
|
||||||
|
seekToNextButton =
|
||||||
|
new CommandButton.Builder()
|
||||||
|
.setDisplayName(context.getText(R.string.media3_controls_seek_to_next_description))
|
||||||
|
.setIconResId(R.drawable.media3_notification_seek_to_next)
|
||||||
|
.setPlayerCommand(COMMAND_SEEK_TO_NEXT)
|
||||||
|
.build();
|
||||||
|
seekBackButton =
|
||||||
|
new CommandButton.Builder()
|
||||||
|
.setDisplayName(context.getText(R.string.media3_controls_seek_back_description))
|
||||||
|
.setIconResId(R.drawable.media3_notification_seek_back)
|
||||||
|
.setPlayerCommand(COMMAND_SEEK_BACK)
|
||||||
|
.build();
|
||||||
|
seekForwardButton =
|
||||||
|
new CommandButton.Builder()
|
||||||
|
.setDisplayName(context.getText(R.string.media3_controls_seek_forward_description))
|
||||||
|
.setIconResId(R.drawable.media3_notification_seek_forward)
|
||||||
|
.setPlayerCommand(COMMAND_SEEK_FORWARD)
|
||||||
|
.build();
|
||||||
|
intentFilter.addAction(INTENT_ACTION_COMMAND);
|
||||||
|
intentFilter.addAction(INTENT_ACTION_DISMISS);
|
||||||
|
intentFilter.addDataScheme(INTENT_SCHEME);
|
||||||
customActions =
|
customActions =
|
||||||
customActionReceiver != null
|
customActionReceiver != null
|
||||||
? customActionReceiver.createCustomActions(context, instanceId)
|
? customActionReceiver.createCustomActions(context, instanceId)
|
||||||
: Collections.emptyMap();
|
: Collections.emptyMap();
|
||||||
for (String action : customActions.keySet()) {
|
dismissPendingIntent = createBroadcastIntent(context, INTENT_ACTION_DISMISS, instanceId);
|
||||||
intentFilter.addAction(action);
|
|
||||||
}
|
|
||||||
dismissPendingIntent = createBroadcastIntent(ACTION_DISMISS, context, instanceId);
|
|
||||||
intentFilter.addAction(ACTION_DISMISS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -844,7 +781,7 @@ public class PlayerNotificationManager {
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The media is {@link Player#isPlaying() actively playing}.
|
* <li>The media is {@link Player#isPlaying() actively playing}.
|
||||||
* <li>The media is not {@link Player#isCurrentMediaItemDynamic() dynamically changing its
|
* <li>The media is not {@link Player#isCurrentWindowDynamic() dynamically changing its
|
||||||
* duration} (like for example a live stream).
|
* duration} (like for example a live stream).
|
||||||
* <li>The media is not {@link Player#isPlayingAd() interrupted by an ad}.
|
* <li>The media is not {@link Player#isPlayingAd() interrupted by an ad}.
|
||||||
* <li>The media is played at {@link Player#getPlaybackParameters() regular speed}.
|
* <li>The media is played at {@link Player#getPlaybackParameters() regular speed}.
|
||||||
|
|
@ -947,37 +884,29 @@ public class PlayerNotificationManager {
|
||||||
boolean ongoing,
|
boolean ongoing,
|
||||||
@Nullable Bitmap largeIcon) {
|
@Nullable Bitmap largeIcon) {
|
||||||
if (player.getPlaybackState() == Player.STATE_IDLE && player.getCurrentTimeline().isEmpty()) {
|
if (player.getPlaybackState() == Player.STATE_IDLE && player.getCurrentTimeline().isEmpty()) {
|
||||||
builderActions = null;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> actionNames = getActions(player);
|
if (builder == null) {
|
||||||
List<NotificationCompat.Action> actions = new ArrayList<>(actionNames.size());
|
|
||||||
for (int i = 0; i < actionNames.size(); i++) {
|
|
||||||
String actionName = actionNames.get(i);
|
|
||||||
@Nullable
|
|
||||||
NotificationCompat.Action action =
|
|
||||||
playbackActions.containsKey(actionName)
|
|
||||||
? playbackActions.get(actionName)
|
|
||||||
: customActions.get(actionName);
|
|
||||||
if (action != null) {
|
|
||||||
actions.add(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builder == null || !actions.equals(builderActions)) {
|
|
||||||
builder = new NotificationCompat.Builder(context, channelId);
|
builder = new NotificationCompat.Builder(context, channelId);
|
||||||
builderActions = actions;
|
}
|
||||||
for (int i = 0; i < actions.size(); i++) {
|
List<CommandButton> actionButtons = getActionButtons(player);
|
||||||
builder.addAction(actions.get(i));
|
for (int i = 0; i < actionButtons.size(); i++) {
|
||||||
}
|
CommandButton button = actionButtons.get(i);
|
||||||
|
NotificationCompat.Action action =
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
button.iconResId,
|
||||||
|
button.displayName,
|
||||||
|
createBroadcastIntent(context, button, instanceId));
|
||||||
|
builder.addAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaStyle mediaStyle = new MediaStyle();
|
MediaStyle mediaStyle = new MediaStyle();
|
||||||
if (mediaSessionToken != null) {
|
if (mediaSessionToken != null) {
|
||||||
mediaStyle.setMediaSession(mediaSessionToken);
|
mediaStyle.setMediaSession(mediaSessionToken);
|
||||||
}
|
}
|
||||||
mediaStyle.setShowActionsInCompactView(getActionIndicesForCompactView(actionNames, player));
|
mediaStyle.setShowActionsInCompactView(
|
||||||
|
getActionButtonIndicesForCompactView(actionButtons, player));
|
||||||
// Configure dismiss action prior to API 21 ('x' button).
|
// Configure dismiss action prior to API 21 ('x' button).
|
||||||
mediaStyle.setShowCancelButton(!ongoing);
|
mediaStyle.setShowCancelButton(!ongoing);
|
||||||
mediaStyle.setCancelButtonIntent(dismissPendingIntent);
|
mediaStyle.setCancelButtonIntent(dismissPendingIntent);
|
||||||
|
|
@ -1002,7 +931,7 @@ public class PlayerNotificationManager {
|
||||||
&& useChronometer
|
&& useChronometer
|
||||||
&& player.isPlaying()
|
&& player.isPlaying()
|
||||||
&& !player.isPlayingAd()
|
&& !player.isPlayingAd()
|
||||||
&& !player.isCurrentMediaItemDynamic()
|
&& !player.isCurrentWindowDynamic()
|
||||||
&& player.getPlaybackParameters().speed == 1f) {
|
&& player.getPlaybackParameters().speed == 1f) {
|
||||||
builder
|
builder
|
||||||
.setWhen(System.currentTimeMillis() - player.getContentPosition())
|
.setWhen(System.currentTimeMillis() - player.getContentPosition())
|
||||||
|
|
@ -1040,42 +969,39 @@ public class PlayerNotificationManager {
|
||||||
* omitted:
|
* omitted:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* +-----------------------------------------------------------------+
|
* +------------------------------------------------+
|
||||||
* | prev | << | play/pause | >> | next | custom actions |
|
* | prev | << | play/pause | >> | next |
|
||||||
* +-----------------------------------------------------------------+
|
* +------------------------------------------------+
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>This method can be safely overridden. However, the names must be of the playback actions
|
* <p>This method can be safely overridden.
|
||||||
* {@link #ACTION_PAUSE}, {@link #ACTION_PLAY}, {@link #ACTION_FAST_FORWARD}, {@link
|
|
||||||
* #ACTION_REWIND}, {@link #ACTION_NEXT} or {@link #ACTION_PREVIOUS}, or a key contained in the
|
|
||||||
* map returned by {@link CustomActionReceiver#createCustomActions(Context, int)}. Otherwise the
|
|
||||||
* action name is ignored.
|
|
||||||
*/
|
*/
|
||||||
protected List<String> getActions(Player player) {
|
// Disclaimer: Custom action support is temporarily removed, but will be added back in next CL.
|
||||||
|
protected List<CommandButton> getActionButtons(Player player) {
|
||||||
boolean enablePrevious = player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS);
|
boolean enablePrevious = player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS);
|
||||||
boolean enableRewind = player.isCommandAvailable(COMMAND_SEEK_BACK);
|
boolean enableRewind = player.isCommandAvailable(COMMAND_SEEK_BACK);
|
||||||
boolean enableFastForward = player.isCommandAvailable(COMMAND_SEEK_FORWARD);
|
boolean enableFastForward = player.isCommandAvailable(COMMAND_SEEK_FORWARD);
|
||||||
boolean enableNext = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT);
|
boolean enableNext = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT);
|
||||||
|
|
||||||
List<String> stringActions = new ArrayList<>();
|
List<CommandButton> buttons = new ArrayList<>();
|
||||||
if (enablePrevious) {
|
if (enablePrevious) {
|
||||||
stringActions.add(ACTION_PREVIOUS);
|
buttons.add(seekToPreviousButton);
|
||||||
}
|
}
|
||||||
if (enableRewind) {
|
if (enableRewind) {
|
||||||
stringActions.add(ACTION_REWIND);
|
buttons.add(seekBackButton);
|
||||||
}
|
}
|
||||||
if (shouldShowPauseButton(player)) {
|
if (shouldShowPauseButton(player)) {
|
||||||
stringActions.add(ACTION_PAUSE);
|
buttons.add(pauseButton);
|
||||||
} else {
|
} else {
|
||||||
stringActions.add(ACTION_PLAY);
|
buttons.add(playButton);
|
||||||
}
|
}
|
||||||
if (enableFastForward) {
|
if (enableFastForward) {
|
||||||
stringActions.add(ACTION_FAST_FORWARD);
|
buttons.add(seekForwardButton);
|
||||||
}
|
}
|
||||||
if (enableNext) {
|
if (enableNext) {
|
||||||
stringActions.add(ACTION_NEXT);
|
buttons.add(seekToNextButton);
|
||||||
}
|
}
|
||||||
return stringActions;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1084,29 +1010,41 @@ public class PlayerNotificationManager {
|
||||||
* <p>This method can be overridden. The indices must refer to the list of actions passed as the
|
* <p>This method can be overridden. The indices must refer to the list of actions passed as the
|
||||||
* first parameter.
|
* first parameter.
|
||||||
*
|
*
|
||||||
* @param actionNames The names of the actions included in the notification.
|
* @param actionButtons The buttons of the actions included in the notification.
|
||||||
* @param player The player for which a notification is being built.
|
* @param player The player for which a notification is being built.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
protected int[] getActionIndicesForCompactView(List<String> actionNames, Player player) {
|
protected int[] getActionButtonIndicesForCompactView(
|
||||||
int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE);
|
List<CommandButton> actionButtons, Player player) {
|
||||||
int playActionIndex = actionNames.indexOf(ACTION_PLAY);
|
int previousIndex = C.INDEX_UNSET;
|
||||||
int leftSideActionIndex = actionNames.indexOf(ACTION_PREVIOUS);
|
int nextIndex = C.INDEX_UNSET;
|
||||||
int rightSideActionIndex = actionNames.indexOf(ACTION_NEXT);
|
int playPauseIndex = C.INDEX_UNSET;
|
||||||
|
for (int i = 0; i < actionButtons.size(); i++) {
|
||||||
|
CommandButton button = actionButtons.get(i);
|
||||||
|
switch (button.playerCommand) {
|
||||||
|
case COMMAND_PLAY_PAUSE:
|
||||||
|
playPauseIndex = i;
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_TO_PREVIOUS:
|
||||||
|
previousIndex = i;
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_TO_NEXT:
|
||||||
|
nextIndex = i;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
int[] actionIndices = new int[3];
|
int[] actionIndices = new int[3];
|
||||||
int actionCounter = 0;
|
int actionCounter = 0;
|
||||||
if (leftSideActionIndex != -1) {
|
if (previousIndex != C.INDEX_UNSET) {
|
||||||
actionIndices[actionCounter++] = leftSideActionIndex;
|
actionIndices[actionCounter++] = previousIndex;
|
||||||
}
|
}
|
||||||
boolean shouldShowPauseButton = shouldShowPauseButton(player);
|
if (playPauseIndex != C.INDEX_UNSET) {
|
||||||
if (pauseActionIndex != -1 && shouldShowPauseButton) {
|
actionIndices[actionCounter++] = playPauseIndex;
|
||||||
actionIndices[actionCounter++] = pauseActionIndex;
|
|
||||||
} else if (playActionIndex != -1 && !shouldShowPauseButton) {
|
|
||||||
actionIndices[actionCounter++] = playActionIndex;
|
|
||||||
}
|
}
|
||||||
if (rightSideActionIndex != -1) {
|
if (nextIndex != C.INDEX_UNSET) {
|
||||||
actionIndices[actionCounter++] = rightSideActionIndex;
|
actionIndices[actionCounter++] = nextIndex;
|
||||||
}
|
}
|
||||||
return Arrays.copyOf(actionIndices, actionCounter);
|
return Arrays.copyOf(actionIndices, actionCounter);
|
||||||
}
|
}
|
||||||
|
|
@ -1155,68 +1093,30 @@ public class PlayerNotificationManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, NotificationCompat.Action> createPlaybackActions(
|
private static PendingIntent createBroadcastIntent(
|
||||||
Context context,
|
Context context, CommandButton button, int instanceId) {
|
||||||
int instanceId,
|
Intent intent = new Intent(INTENT_ACTION_COMMAND).setPackage(context.getPackageName());
|
||||||
int playActionIconResourceId,
|
intent.putExtra(INTENT_EXTRA_INSTANCE_ID, instanceId);
|
||||||
int pauseActionIconResourceId,
|
intent.putExtra(INTENT_EXTRA_PLAYER_COMMAND, button.playerCommand);
|
||||||
int rewindActionIconResourceId,
|
intent.putExtra(
|
||||||
int fastForwardActionIconResourceId,
|
INTENT_EXTRA_SESSION_COMMAND, BundleableUtil.toNullableBundle(button.sessionCommand));
|
||||||
int previousActionIconResourceId,
|
// Make intent distinguishable by Intent#filterEquals() due to the PendingIntent requirement.
|
||||||
int nextActionIconResourceId) {
|
Uri intentUri =
|
||||||
Map<String, NotificationCompat.Action> actions = new HashMap<>();
|
new Uri.Builder()
|
||||||
actions.put(
|
.scheme(INTENT_SCHEME)
|
||||||
ACTION_PLAY,
|
.appendPath(Integer.toString(instanceId))
|
||||||
new NotificationCompat.Action(
|
.appendPath(Integer.toString(button.playerCommand))
|
||||||
playActionIconResourceId,
|
.appendPath(button.sessionCommand == null ? "null" : button.sessionCommand.customAction)
|
||||||
context.getString(R.string.media3_controls_play_description),
|
.build();
|
||||||
createBroadcastIntent(ACTION_PLAY, context, instanceId)));
|
intent.setData(intentUri);
|
||||||
actions.put(
|
return PendingIntent.getBroadcast(context, instanceId, intent, PENDING_INTENT_FLAGS);
|
||||||
ACTION_PAUSE,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
pauseActionIconResourceId,
|
|
||||||
context.getString(R.string.media3_controls_pause_description),
|
|
||||||
createBroadcastIntent(ACTION_PAUSE, context, instanceId)));
|
|
||||||
actions.put(
|
|
||||||
ACTION_REWIND,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
rewindActionIconResourceId,
|
|
||||||
context.getString(R.string.media3_controls_seek_back_description),
|
|
||||||
createBroadcastIntent(ACTION_REWIND, context, instanceId)));
|
|
||||||
actions.put(
|
|
||||||
ACTION_FAST_FORWARD,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
fastForwardActionIconResourceId,
|
|
||||||
context.getString(R.string.media3_controls_seek_forward_description),
|
|
||||||
createBroadcastIntent(ACTION_FAST_FORWARD, context, instanceId)));
|
|
||||||
actions.put(
|
|
||||||
ACTION_PREVIOUS,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
previousActionIconResourceId,
|
|
||||||
context.getString(R.string.media3_controls_seek_to_previous_description),
|
|
||||||
createBroadcastIntent(ACTION_PREVIOUS, context, instanceId)));
|
|
||||||
actions.put(
|
|
||||||
ACTION_NEXT,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
nextActionIconResourceId,
|
|
||||||
context.getString(R.string.media3_controls_seek_to_next_description),
|
|
||||||
createBroadcastIntent(ACTION_NEXT, context, instanceId)));
|
|
||||||
return actions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PendingIntent createBroadcastIntent(
|
private static PendingIntent createBroadcastIntent(
|
||||||
String action, Context context, int instanceId) {
|
Context context, String action, int instanceId) {
|
||||||
Intent intent = new Intent(action).setPackage(context.getPackageName());
|
Intent intent = new Intent(action).setPackage(context.getPackageName());
|
||||||
intent.putExtra(EXTRA_INSTANCE_ID, instanceId);
|
intent.putExtra(INTENT_EXTRA_INSTANCE_ID, instanceId);
|
||||||
|
return PendingIntent.getBroadcast(context, instanceId, intent, PENDING_INTENT_FLAGS);
|
||||||
int pendingFlags;
|
|
||||||
if (Util.SDK_INT >= 23) {
|
|
||||||
pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
|
||||||
} else {
|
|
||||||
pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, instanceId, intent, pendingFlags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("nullness:argument")
|
@SuppressWarnings("nullness:argument")
|
||||||
|
|
@ -1251,28 +1151,42 @@ public class PlayerNotificationManager {
|
||||||
Player player = PlayerNotificationManager.this.player;
|
Player player = PlayerNotificationManager.this.player;
|
||||||
if (player == null
|
if (player == null
|
||||||
|| !isNotificationStarted
|
|| !isNotificationStarted
|
||||||
|| intent.getIntExtra(EXTRA_INSTANCE_ID, instanceId) != instanceId) {
|
|| intent.getIntExtra(INTENT_EXTRA_INSTANCE_ID, instanceId) != instanceId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
if (ACTION_PLAY.equals(action)) {
|
if (INTENT_ACTION_COMMAND.equals(action)) {
|
||||||
if (player.getPlaybackState() == Player.STATE_IDLE) {
|
@Player.Command
|
||||||
player.prepare();
|
int playerCommand = intent.getIntExtra(INTENT_EXTRA_PLAYER_COMMAND, COMMAND_INVALID);
|
||||||
} else if (player.getPlaybackState() == Player.STATE_ENDED) {
|
switch (playerCommand) {
|
||||||
player.seekToDefaultPosition(player.getCurrentMediaItemIndex());
|
case COMMAND_PLAY_PAUSE:
|
||||||
|
if (!player.getPlayWhenReady()) {
|
||||||
|
if (player.getPlaybackState() == Player.STATE_IDLE) {
|
||||||
|
player.prepare();
|
||||||
|
} else if (player.getPlaybackState() == Player.STATE_ENDED) {
|
||||||
|
player.seekToDefaultPosition(player.getCurrentWindowIndex());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_TO_PREVIOUS:
|
||||||
|
player.seekToPrevious();
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_BACK:
|
||||||
|
player.seekBack();
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_FORWARD:
|
||||||
|
player.seekForward();
|
||||||
|
break;
|
||||||
|
case COMMAND_SEEK_TO_NEXT:
|
||||||
|
player.seekToNext();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "Unsupported player command, playerCommand=" + playerCommand);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
player.play();
|
} else if (INTENT_ACTION_DISMISS.equals(action)) {
|
||||||
} else if (ACTION_PAUSE.equals(action)) {
|
|
||||||
player.pause();
|
|
||||||
} else if (ACTION_PREVIOUS.equals(action)) {
|
|
||||||
player.seekToPrevious();
|
|
||||||
} else if (ACTION_REWIND.equals(action)) {
|
|
||||||
player.seekBack();
|
|
||||||
} else if (ACTION_FAST_FORWARD.equals(action)) {
|
|
||||||
player.seekForward();
|
|
||||||
} else if (ACTION_NEXT.equals(action)) {
|
|
||||||
player.seekToNext();
|
|
||||||
} else if (ACTION_DISMISS.equals(action)) {
|
|
||||||
stopNotification(/* dismissedByUser= */ true);
|
stopNotification(/* dismissedByUser= */ true);
|
||||||
} else if (action != null
|
} else if (action != null
|
||||||
&& customActionReceiver != null
|
&& customActionReceiver != null
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue