Merge pull request #3332 from ByteHamster/playbackservice

PlaybackService bug fixes
This commit is contained in:
H. Lehmann 2019-08-23 19:47:05 +02:00 committed by GitHub
commit e56da3179a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 42 deletions

View File

@ -195,10 +195,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* Is true if service is running.
*/
public static boolean isRunning = false;
/**
* Is true if service has received a valid start command.
*/
public static boolean started = false;
/**
* Is true if the service was running, but paused due to headphone disconnect
*/
@ -214,6 +210,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private PlaybackServiceMediaPlayer mediaPlayer;
private PlaybackServiceTaskManager taskManager;
private PlaybackServiceFlavorHelper flavorHelper;
private PlaybackServiceStateManager stateManager;
/**
* Used for Lollipop notifications, Android Wear, and Android Auto.
@ -268,8 +265,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service created.");
isRunning = true;
stateManager = new PlaybackServiceStateManager(this);
PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this);
startForeground(NOTIFICATION_ID, notificationBuilder.build());
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS"));
registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
@ -328,9 +326,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service is about to be destroyed");
stopForeground(true);
stateManager.stopForeground(true);
isRunning = false;
started = false;
currentMediaType = MediaType.UNKNOWN;
PreferenceManager.getDefaultSharedPreferences(this)
@ -351,11 +348,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.shutdown();
taskManager.shutdown();
}
private void stopService() {
stopForeground(true);
stopSelf();
}
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
@ -449,28 +441,34 @@ public class PlaybackService extends MediaBrowserServiceCompat {
super.onStartCommand(intent, flags, startId);
Log.d(TAG, "OnStartCommand called");
if (!stateManager.isInForeground()) {
PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this);
startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
if (keycode == -1 && playable == null && !castDisconnect) {
Log.e(TAG, "PlaybackService was started with no arguments");
stopService();
stateManager.stopService();
return Service.START_NOT_STICKY;
}
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
stopForeground(true);
stateManager.stopForeground(true);
} else {
if (keycode != -1) {
Log.d(TAG, "Received media button event");
boolean handled = handleKeycode(keycode, true);
if (!handled) {
stopService();
if (!handled && !stateManager.hasReceivedValidStartCommand()) {
stateManager.stopService();
return Service.START_NOT_STICKY;
}
} else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
started = true;
stateManager.validStartCommandWasReceived();
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true);
boolean allowStreamThisTime = intent.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false);
boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
@ -484,7 +482,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stream && !NetworkUtils.isStreamingAllowed() && !allowStreamThisTime) {
displayStreamingNotAllowedNotification(intent);
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return Service.START_NOT_STICKY;
}
mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
@ -590,10 +588,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case KeyEvent.KEYCODE_MEDIA_STOP:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, true);
started = false;
}
stopForeground(true); // gets rid of persistent notification
stateManager.stopForeground(true); // gets rid of persistent notification
return true;
default:
Log.d(TAG, "Unhandled key code: " + keycode);
@ -609,10 +606,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
if (playable != null) {
mediaPlayer.playMediaObject(playable, false, true, true);
started = true;
stateManager.validStartCommandWasReceived();
PlaybackService.this.updateMediaSessionMetadata(playable);
} else {
stopService();
stateManager.stopService();
}
}
@ -629,7 +626,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.pause(true, false);
mediaPlayer.resetVideoSurface();
setupNotification(getPlayable());
stopForeground(!UserPreferences.isPersistNotify());
stateManager.stopForeground(!UserPreferences.isPersistNotify());
}
private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
@ -699,7 +696,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
setupNotification(newInfo);
} else if (!UserPreferences.isPersistNotify() && !isCasting) {
// remove notification on pause
stopForeground(true);
stateManager.stopForeground(true);
}
writePlayerStatusPlaybackPreferences();
break;
@ -712,7 +709,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case PLAYING:
writePlayerStatusPlaybackPreferences();
setupNotification(newInfo);
started = true;
stateManager.validStartCommandWasReceived();
// set sleep timer if auto-enabled
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
@ -723,7 +720,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
break;
}
@ -736,7 +733,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void shouldStop() {
stopService();
stateManager.stopService();
}
@Override
@ -785,7 +782,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return true;
}
@ -870,7 +867,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.shouldStream(true)
.getIntent());
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return null;
}
return nextItem.getMedia();
@ -886,7 +883,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
taskManager.cancelPositionSaver();
writePlaybackPreferencesNoMediaPlaying();
if (!isCasting) {
stopForeground(true);
stateManager.stopForeground(true);
}
}
if (mediaType == null) {
@ -1218,7 +1215,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
}
}
if (!Thread.currentThread().isInterrupted() && started) {
if (!Thread.currentThread().isInterrupted() && stateManager.hasReceivedValidStartCommand()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT));
@ -1254,8 +1251,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (playable == null) {
Log.d(TAG, "setupNotification: playable is null" + Log.getStackTraceString(new Exception()));
if (!started) {
stopService();
if (!stateManager.hasReceivedValidStartCommand()) {
stateManager.stopService();
}
return;
}
@ -1266,8 +1263,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (mediaPlayer == null) {
Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
if (!started) {
stopService();
if (!stateManager.hasReceivedValidStartCommand()) {
stateManager.stopService();
}
return;
}
@ -1279,20 +1276,20 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (!notificationBuilder.isIconCached(playable)) {
// To make sure that the notification is shown instantly
notificationBuilder.loadDefaultIcon();
startForeground(NOTIFICATION_ID, notificationBuilder.build());
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
notificationBuilder.loadIcon(playable);
if (!Thread.currentThread().isInterrupted() && started) {
if (!Thread.currentThread().isInterrupted() && stateManager.hasReceivedValidStartCommand()) {
Notification notification = notificationBuilder.build();
if (playerStatus == PlayerStatus.PLAYING ||
playerStatus == PlayerStatus.PREPARING ||
playerStatus == PlayerStatus.SEEKING ||
isCasting) {
startForeground(NOTIFICATION_ID, notification);
stateManager.startForeground(NOTIFICATION_ID, notification);
} else {
stopForeground(false);
stateManager.stopForeground(false);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
@ -1481,7 +1478,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
stopService();
stateManager.stopService();
}
}
@ -1832,7 +1829,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
PlaybackService.this.setupNotification(info);
} else if (!UserPreferences.isPersistNotify()) {
PlaybackService.this.stopForeground(true);
stateManager.stopForeground(true);
}
}
}

View File

@ -0,0 +1,43 @@
package de.danoeh.antennapod.core.service.playback;
import android.app.Notification;
class PlaybackServiceStateManager {
private final PlaybackService playbackService;
private volatile boolean isInForeground = false;
private volatile boolean hasReceivedValidStartCommand = false;
PlaybackServiceStateManager(PlaybackService playbackService) {
this.playbackService = playbackService;
}
void startForeground(int notificationId, Notification notification) {
playbackService.startForeground(notificationId, notification);
isInForeground = true;
}
void stopService() {
stopForeground(true);
isInForeground = false;
playbackService.stopSelf();
}
void stopForeground(boolean removeNotification) {
playbackService.stopForeground(removeNotification);
isInForeground = false;
hasReceivedValidStartCommand = false;
}
boolean isInForeground() {
return isInForeground;
}
boolean hasReceivedValidStartCommand() {
return hasReceivedValidStartCommand;
}
void validStartCommandWasReceived() {
this.hasReceivedValidStartCommand = true;
}
}

View File

@ -210,7 +210,7 @@ public abstract class PlaybackController {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(optionalIntent -> {
boolean bound = false;
if (!PlaybackService.started) {
if (!PlaybackService.isRunning) {
if (optionalIntent.isPresent()) {
Log.d(TAG, "Calling start service");
ContextCompat.startForegroundService(activity, optionalIntent.get());