mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Prevent NPE in PlayerNotificationManager.
The app can set the player to null while messages from the player are still in flight. This may cause NPEs. Issue:#4238 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=196504077
This commit is contained in:
parent
1af9334188
commit
b3c3717007
2 changed files with 103 additions and 97 deletions
|
|
@ -36,6 +36,7 @@ dependencies {
|
||||||
implementation project(modulePrefix + 'library-core')
|
implementation project(modulePrefix + 'library-core')
|
||||||
implementation 'com.android.support:support-media-compat:' + supportLibraryVersion
|
implementation 'com.android.support:support-media-compat:' + supportLibraryVersion
|
||||||
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
|
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||||
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A notification manager to start, update and cancel a media style notification reflecting the
|
* A notification manager to start, update and cancel a media style notification reflecting the
|
||||||
|
|
@ -205,7 +206,9 @@ public class PlayerNotificationManager {
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (notificationTag == currentNotificationTag && isNotificationStarted) {
|
if (player != null
|
||||||
|
&& notificationTag == currentNotificationTag
|
||||||
|
&& isNotificationStarted) {
|
||||||
updateNotification(bitmap);
|
updateNotification(bitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +263,7 @@ public class PlayerNotificationManager {
|
||||||
private final String channelId;
|
private final String channelId;
|
||||||
private final int notificationId;
|
private final int notificationId;
|
||||||
private final MediaDescriptionAdapter mediaDescriptionAdapter;
|
private final MediaDescriptionAdapter mediaDescriptionAdapter;
|
||||||
private final CustomActionReceiver customActionReceiver;
|
private final @Nullable CustomActionReceiver customActionReceiver;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
private final NotificationManagerCompat notificationManager;
|
private final NotificationManagerCompat notificationManager;
|
||||||
private final IntentFilter intentFilter;
|
private final IntentFilter intentFilter;
|
||||||
|
|
@ -269,12 +272,12 @@ public class PlayerNotificationManager {
|
||||||
private final Map<String, NotificationCompat.Action> playbackActions;
|
private final Map<String, NotificationCompat.Action> playbackActions;
|
||||||
private final Map<String, NotificationCompat.Action> customActions;
|
private final Map<String, NotificationCompat.Action> customActions;
|
||||||
|
|
||||||
private Player player;
|
private @Nullable Player player;
|
||||||
private ControlDispatcher controlDispatcher;
|
private ControlDispatcher controlDispatcher;
|
||||||
private boolean isNotificationStarted;
|
private boolean isNotificationStarted;
|
||||||
private int currentNotificationTag;
|
private int currentNotificationTag;
|
||||||
private NotificationListener notificationListener;
|
private @Nullable NotificationListener notificationListener;
|
||||||
private MediaSessionCompat.Token mediaSessionToken;
|
private @Nullable MediaSessionCompat.Token mediaSessionToken;
|
||||||
private boolean useNavigationActions;
|
private boolean useNavigationActions;
|
||||||
private boolean usePlayPauseActions;
|
private boolean usePlayPauseActions;
|
||||||
private @Nullable String stopAction;
|
private @Nullable String stopAction;
|
||||||
|
|
@ -365,6 +368,20 @@ public class PlayerNotificationManager {
|
||||||
playerListener = new PlayerListener();
|
playerListener = new PlayerListener();
|
||||||
notificationBroadcastReceiver = new NotificationBroadcastReceiver();
|
notificationBroadcastReceiver = new NotificationBroadcastReceiver();
|
||||||
intentFilter = new IntentFilter();
|
intentFilter = new IntentFilter();
|
||||||
|
useNavigationActions = true;
|
||||||
|
usePlayPauseActions = true;
|
||||||
|
ongoing = true;
|
||||||
|
colorized = true;
|
||||||
|
useChronometer = true;
|
||||||
|
color = Color.TRANSPARENT;
|
||||||
|
smallIconResourceId = R.drawable.exo_notification_small_icon;
|
||||||
|
defaults = 0;
|
||||||
|
priority = NotificationCompat.PRIORITY_LOW;
|
||||||
|
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
|
||||||
|
rewindMs = DEFAULT_REWIND_MS;
|
||||||
|
stopAction = ACTION_STOP;
|
||||||
|
badgeIconType = NotificationCompat.BADGE_ICON_SMALL;
|
||||||
|
visibility = NotificationCompat.VISIBILITY_PUBLIC;
|
||||||
|
|
||||||
// initialize actions
|
// initialize actions
|
||||||
playbackActions = createPlaybackActions(context);
|
playbackActions = createPlaybackActions(context);
|
||||||
|
|
@ -378,22 +395,7 @@ public class PlayerNotificationManager {
|
||||||
for (String action : customActions.keySet()) {
|
for (String action : customActions.keySet()) {
|
||||||
intentFilter.addAction(action);
|
intentFilter.addAction(action);
|
||||||
}
|
}
|
||||||
|
stopPendingIntent = Assertions.checkNotNull(playbackActions.get(ACTION_STOP)).actionIntent;
|
||||||
setStopAction(ACTION_STOP);
|
|
||||||
|
|
||||||
useNavigationActions = true;
|
|
||||||
usePlayPauseActions = true;
|
|
||||||
ongoing = true;
|
|
||||||
colorized = true;
|
|
||||||
useChronometer = true;
|
|
||||||
color = Color.TRANSPARENT;
|
|
||||||
smallIconResourceId = R.drawable.exo_notification_small_icon;
|
|
||||||
defaults = 0;
|
|
||||||
priority = NotificationCompat.PRIORITY_LOW;
|
|
||||||
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
|
|
||||||
rewindMs = DEFAULT_REWIND_MS;
|
|
||||||
setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
|
|
||||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -512,10 +514,9 @@ public class PlayerNotificationManager {
|
||||||
}
|
}
|
||||||
this.stopAction = stopAction;
|
this.stopAction = stopAction;
|
||||||
if (ACTION_STOP.equals(stopAction)) {
|
if (ACTION_STOP.equals(stopAction)) {
|
||||||
stopPendingIntent = playbackActions.get(ACTION_STOP).actionIntent;
|
stopPendingIntent = Assertions.checkNotNull(playbackActions.get(ACTION_STOP)).actionIntent;
|
||||||
} else if (stopAction != null) {
|
} else if (stopAction != null) {
|
||||||
Assertions.checkArgument(customActions.containsKey(stopAction));
|
stopPendingIntent = Assertions.checkNotNull(customActions.get(stopAction)).actionIntent;
|
||||||
stopPendingIntent = customActions.get(stopAction).actionIntent;
|
|
||||||
} else {
|
} else {
|
||||||
stopPendingIntent = null;
|
stopPendingIntent = null;
|
||||||
}
|
}
|
||||||
|
|
@ -698,13 +699,15 @@ public class PlayerNotificationManager {
|
||||||
maybeUpdateNotification();
|
maybeUpdateNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification updateNotification(Bitmap bitmap) {
|
@RequiresNonNull("player")
|
||||||
|
private Notification updateNotification(@Nullable Bitmap bitmap) {
|
||||||
Notification notification = createNotification(player, bitmap);
|
Notification notification = createNotification(player, bitmap);
|
||||||
notificationManager.notify(notificationId, notification);
|
notificationManager.notify(notificationId, notification);
|
||||||
return notification;
|
return notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startOrUpdateNotification() {
|
private void startOrUpdateNotification() {
|
||||||
|
if (player != null) {
|
||||||
Notification notification = updateNotification(null);
|
Notification notification = updateNotification(null);
|
||||||
if (!isNotificationStarted) {
|
if (!isNotificationStarted) {
|
||||||
isNotificationStarted = true;
|
isNotificationStarted = true;
|
||||||
|
|
@ -714,9 +717,10 @@ public class PlayerNotificationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void maybeUpdateNotification() {
|
private void maybeUpdateNotification() {
|
||||||
if (isNotificationStarted) {
|
if (isNotificationStarted && player != null) {
|
||||||
updateNotification(null);
|
updateNotification(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -732,64 +736,6 @@ public class PlayerNotificationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, NotificationCompat.Action> createPlaybackActions(Context context) {
|
|
||||||
Map<String, NotificationCompat.Action> actions = new HashMap<>();
|
|
||||||
Intent playIntent = new Intent(ACTION_PLAY).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_PLAY,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_play,
|
|
||||||
context.getString(R.string.exo_controls_play_description),
|
|
||||||
PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent pauseIntent = new Intent(ACTION_PAUSE).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_PAUSE,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_pause,
|
|
||||||
context.getString(R.string.exo_controls_pause_description),
|
|
||||||
PendingIntent.getBroadcast(
|
|
||||||
context, 0, pauseIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent stopIntent = new Intent(ACTION_STOP).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_STOP,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_stop,
|
|
||||||
context.getString(R.string.exo_controls_stop_description),
|
|
||||||
PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent rewindIntent = new Intent(ACTION_REWIND).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_REWIND,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_rewind,
|
|
||||||
context.getString(R.string.exo_controls_rewind_description),
|
|
||||||
PendingIntent.getBroadcast(
|
|
||||||
context, 0, rewindIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent fastForwardIntent = new Intent(ACTION_FAST_FORWARD).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_FAST_FORWARD,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_fastforward,
|
|
||||||
context.getString(R.string.exo_controls_fastforward_description),
|
|
||||||
PendingIntent.getBroadcast(
|
|
||||||
context, 0, fastForwardIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent previousIntent = new Intent(ACTION_PREVIOUS).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_PREVIOUS,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_previous,
|
|
||||||
context.getString(R.string.exo_controls_previous_description),
|
|
||||||
PendingIntent.getBroadcast(
|
|
||||||
context, 0, previousIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
Intent nextIntent = new Intent(ACTION_NEXT).setPackage(context.getPackageName());
|
|
||||||
actions.put(
|
|
||||||
ACTION_NEXT,
|
|
||||||
new NotificationCompat.Action(
|
|
||||||
R.drawable.exo_notification_next,
|
|
||||||
context.getString(R.string.exo_controls_next_description),
|
|
||||||
PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the notification given the current player state.
|
* Creates the notification given the current player state.
|
||||||
*
|
*
|
||||||
|
|
@ -821,7 +767,7 @@ public class PlayerNotificationManager {
|
||||||
// Configure stop action (eg. when user dismisses the notification when !isOngoing).
|
// Configure stop action (eg. when user dismisses the notification when !isOngoing).
|
||||||
boolean useStopAction = stopAction != null && !isPlayingAd;
|
boolean useStopAction = stopAction != null && !isPlayingAd;
|
||||||
mediaStyle.setShowCancelButton(useStopAction);
|
mediaStyle.setShowCancelButton(useStopAction);
|
||||||
if (useStopAction) {
|
if (useStopAction && stopPendingIntent != null) {
|
||||||
builder.setDeleteIntent(stopPendingIntent);
|
builder.setDeleteIntent(stopPendingIntent);
|
||||||
mediaStyle.setCancelButtonIntent(stopPendingIntent);
|
mediaStyle.setCancelButtonIntent(stopPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
@ -905,7 +851,7 @@ public class PlayerNotificationManager {
|
||||||
if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) {
|
if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) {
|
||||||
stringActions.add(ACTION_NEXT);
|
stringActions.add(ACTION_NEXT);
|
||||||
}
|
}
|
||||||
if (!customActions.isEmpty()) {
|
if (customActionReceiver != null) {
|
||||||
stringActions.addAll(customActionReceiver.getCustomActions(player));
|
stringActions.addAll(customActionReceiver.getCustomActions(player));
|
||||||
}
|
}
|
||||||
if (ACTION_STOP.equals(stopAction)) {
|
if (ACTION_STOP.equals(stopAction)) {
|
||||||
|
|
@ -932,6 +878,64 @@ public class PlayerNotificationManager {
|
||||||
return new int[] {actionIndex};
|
return new int[] {actionIndex};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, NotificationCompat.Action> createPlaybackActions(Context context) {
|
||||||
|
Map<String, NotificationCompat.Action> actions = new HashMap<>();
|
||||||
|
Intent playIntent = new Intent(ACTION_PLAY).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_PLAY,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_play,
|
||||||
|
context.getString(R.string.exo_controls_play_description),
|
||||||
|
PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent pauseIntent = new Intent(ACTION_PAUSE).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_PAUSE,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_pause,
|
||||||
|
context.getString(R.string.exo_controls_pause_description),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context, 0, pauseIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent stopIntent = new Intent(ACTION_STOP).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_STOP,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_stop,
|
||||||
|
context.getString(R.string.exo_controls_stop_description),
|
||||||
|
PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent rewindIntent = new Intent(ACTION_REWIND).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_REWIND,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_rewind,
|
||||||
|
context.getString(R.string.exo_controls_rewind_description),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context, 0, rewindIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent fastForwardIntent = new Intent(ACTION_FAST_FORWARD).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_FAST_FORWARD,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_fastforward,
|
||||||
|
context.getString(R.string.exo_controls_fastforward_description),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context, 0, fastForwardIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent previousIntent = new Intent(ACTION_PREVIOUS).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_PREVIOUS,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_previous,
|
||||||
|
context.getString(R.string.exo_controls_previous_description),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context, 0, previousIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
Intent nextIntent = new Intent(ACTION_NEXT).setPackage(context.getPackageName());
|
||||||
|
actions.put(
|
||||||
|
ACTION_NEXT,
|
||||||
|
new NotificationCompat.Action(
|
||||||
|
R.drawable.exo_notification_next,
|
||||||
|
context.getString(R.string.exo_controls_next_description),
|
||||||
|
PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
private class PlayerListener extends Player.DefaultEventListener {
|
private class PlayerListener extends Player.DefaultEventListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -946,7 +950,7 @@ public class PlayerNotificationManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
|
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
|
||||||
if (player.getPlaybackState() == Player.STATE_IDLE) {
|
if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startOrUpdateNotification();
|
startOrUpdateNotification();
|
||||||
|
|
@ -954,7 +958,7 @@ public class PlayerNotificationManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||||
if (player.getPlaybackState() == Player.STATE_IDLE) {
|
if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startOrUpdateNotification();
|
startOrUpdateNotification();
|
||||||
|
|
@ -967,7 +971,7 @@ public class PlayerNotificationManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRepeatModeChanged(int repeatMode) {
|
public void onRepeatModeChanged(int repeatMode) {
|
||||||
if (player.getPlaybackState() == Player.STATE_IDLE) {
|
if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startOrUpdateNotification();
|
startOrUpdateNotification();
|
||||||
|
|
@ -985,7 +989,8 @@ public class PlayerNotificationManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (!isNotificationStarted) {
|
Player player = PlayerNotificationManager.this.player;
|
||||||
|
if (player == null || !isNotificationStarted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
@ -1013,7 +1018,7 @@ public class PlayerNotificationManager {
|
||||||
} else if (ACTION_STOP.equals(action)) {
|
} else if (ACTION_STOP.equals(action)) {
|
||||||
controlDispatcher.dispatchStop(player, true);
|
controlDispatcher.dispatchStop(player, true);
|
||||||
stopNotification();
|
stopNotification();
|
||||||
} else if (customActions.containsKey(action)) {
|
} else if (customActionReceiver != null && customActions.containsKey(action)) {
|
||||||
customActionReceiver.onCustomAction(player, action, intent);
|
customActionReceiver.onCustomAction(player, action, intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue