implement basic cast session join
This commit is contained in:
parent
e70f4d5389
commit
b41eba90bd
|
@ -82,7 +82,7 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(UserPreferences.PREF_CAST_ENABLED)) {
|
||||
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
|
||||
isCastEnabled = UserPreferences.isCastEnabled();
|
||||
Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + isCastEnabled);
|
||||
mMediaRouteActionProvider.setEnabled(isCastEnabled);
|
||||
|
|
|
@ -609,7 +609,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
@Override
|
||||
public void shutdownAsync() {
|
||||
public void shutdownQuietly() {
|
||||
executor.submit(this::shutdown);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ import de.danoeh.antennapod.core.util.playback.Playable;
|
|||
/**
|
||||
* Controls the MediaPlayer that plays a FeedMedia-file
|
||||
*/
|
||||
public class PlaybackService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
public class PlaybackService extends Service {
|
||||
public static final String FORCE_WIDGET_UPDATE = "de.danoeh.antennapod.FORCE_WIDGET_UPDATE";
|
||||
public static final String STOP_WIDGET_UPDATE = "de.danoeh.antennapod.STOP_WIDGET_UPDATE";
|
||||
/**
|
||||
|
@ -277,11 +277,17 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
registerReceiver(pauseResumeCurrentEpisodeReceiver, new IntentFilter(
|
||||
ACTION_RESUME_PLAY_CURRENT_EPISODE));
|
||||
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.registerOnSharedPreferenceChangeListener(prefListener);
|
||||
CastManager castMgr = CastManager.getInstance();
|
||||
castMgr.addCastConsumer(castConsumer);
|
||||
isCasting = castMgr.isConnected();
|
||||
if (isCasting) {
|
||||
onCastAppConnected(false);
|
||||
if (UserPreferences.isCastEnabled()) {
|
||||
onCastAppConnected(false);
|
||||
} else {
|
||||
castMgr.disconnect();
|
||||
}
|
||||
} else {
|
||||
mediaPlayer = new LocalPSMP(this, mediaPlayerCallback);
|
||||
}
|
||||
|
@ -306,8 +312,6 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
Log.e(TAG, "NullPointerException while setting up MediaSession");
|
||||
npe.printStackTrace();
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -319,7 +323,7 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
currentMediaType = MediaType.UNKNOWN;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
.unregisterOnSharedPreferenceChangeListener(prefListener);
|
||||
if (mediaSession != null) {
|
||||
mediaSession.release();
|
||||
}
|
||||
|
@ -344,13 +348,6 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if(key.equals(UserPreferences.PREF_LOCKSCREEN_BACKGROUND)) {
|
||||
updateMediaSessionMetadata(getPlayable());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
@ -1598,7 +1595,7 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
|
||||
}
|
||||
switchMediaPlayer(new LocalPSMP(PlaybackService.this, mediaPlayerCallback),
|
||||
info);
|
||||
info, false);
|
||||
if (info.playable != null) {
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
||||
info.playable.getMediaType() == MediaType.AUDIO ? EXTRA_CODE_AUDIO : EXTRA_CODE_VIDEO);
|
||||
|
@ -1611,31 +1608,42 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
};
|
||||
|
||||
private void onCastAppConnected(boolean wasLaunched) {
|
||||
//TODO deal with wasLaunched == false
|
||||
Log.d(TAG, "A cast device application was connected");
|
||||
Log.d(TAG, "A cast device application was " + (wasLaunched ? "launched" : "joined"));
|
||||
isCasting = true;
|
||||
if (mediaPlayer != null) {
|
||||
PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
||||
if (info.playerStatus == PlayerStatus.PLAYING) {
|
||||
// could be pause, but this way we make sure the new player will get the correct position, since pause runs asynchronously
|
||||
// could be pause, but this way we make sure the new player will get the correct position,
|
||||
// since pause runs asynchronously and we could be directing the new player to play even before
|
||||
// the old player gives us back the position.
|
||||
saveCurrentPosition(false, 0);
|
||||
}
|
||||
}
|
||||
switchMediaPlayer(new RemotePSMP(PlaybackService.this, mediaPlayerCallback),
|
||||
(mediaPlayer != null) ? mediaPlayer.getPSMPInfo() :
|
||||
new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null));
|
||||
new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null),
|
||||
wasLaunched);
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, EXTRA_CODE_CAST);
|
||||
registerWifiBroadcastReceiver();
|
||||
}
|
||||
|
||||
private void switchMediaPlayer(@NonNull PlaybackServiceMediaPlayer newPlayer,
|
||||
@NonNull PlaybackServiceMediaPlayer.PSMPInfo info) {
|
||||
@NonNull PlaybackServiceMediaPlayer.PSMPInfo info,
|
||||
boolean wasLaunched) {
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.endPlayback(true, true);
|
||||
mediaPlayer.shutdownAsync();
|
||||
mediaPlayer.shutdownQuietly();
|
||||
}
|
||||
mediaPlayer = newPlayer;
|
||||
Log.d(TAG, "switched to " + mediaPlayer.getClass().getSimpleName());
|
||||
if (!wasLaunched) {
|
||||
PlaybackServiceMediaPlayer.PSMPInfo candidate = mediaPlayer.getPSMPInfo();
|
||||
if (candidate.playable != null &&
|
||||
candidate.playerStatus.isAtLeast(PlayerStatus.PREPARING)) {
|
||||
// do not automatically send new media to cast device
|
||||
info.playable = null;
|
||||
}
|
||||
}
|
||||
if (info.playable != null) {
|
||||
mediaPlayer.playMediaObject(info.playable,
|
||||
!info.playable.localFileAvailable(),
|
||||
|
@ -1676,4 +1684,18 @@ public class PlaybackService extends Service implements SharedPreferences.OnShar
|
|||
mWifiBroadcastReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private SharedPreferences.OnSharedPreferenceChangeListener prefListener =
|
||||
(sharedPreferences, key) -> {
|
||||
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
|
||||
if (!UserPreferences.isCastEnabled()) {
|
||||
CastManager castManager = CastManager.getInstance();
|
||||
if (castManager.isConnecting() || castManager.isConnected()) {
|
||||
castManager.disconnect();
|
||||
}
|
||||
}
|
||||
} else if(key.equals(UserPreferences.PREF_LOCKSCREEN_BACKGROUND)) {
|
||||
updateMediaSessionMetadata(getPlayable());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
* Releases internally used resources. This method should only be called when the object is not used anymore.
|
||||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
public abstract void shutdownAsync();
|
||||
public abstract void shutdownQuietly();
|
||||
|
||||
public abstract void setVideoSurface(SurfaceHolder surface);
|
||||
|
||||
|
|
|
@ -67,6 +67,17 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
|
|||
executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
|
||||
(r, executor) -> Log.d(TAG, "Rejected execution of runnable"));
|
||||
|
||||
try {
|
||||
if (castMgr.isConnected() && castMgr.isRemoteMediaLoaded()) {
|
||||
// updates the state, but does not start playing new media if it was going to
|
||||
onRemoteMediaPlayerStatusUpdated(
|
||||
((playNextEpisode, wasSkipped, switchingPlayers) ->
|
||||
this.callback.endPlayback(false, wasSkipped, switchingPlayers)));
|
||||
}
|
||||
} catch (TransientNetworkDisconnectionException | NoConnectionException e) {
|
||||
Log.e(TAG, "Unable to do initial check for loaded media", e);
|
||||
}
|
||||
|
||||
castMgr.addCastConsumer(castConsumer);
|
||||
//TODO
|
||||
}
|
||||
|
@ -74,62 +85,12 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
|
|||
private CastConsumer castConsumer = new CastConsumerImpl() {
|
||||
@Override
|
||||
public void onRemoteMediaPlayerMetadataUpdated() {
|
||||
//TODO check this is indeed a correct behavior
|
||||
onRemoteMediaPlayerStatusUpdated();
|
||||
RemotePSMP.this.onRemoteMediaPlayerStatusUpdated(callback::endPlayback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteMediaPlayerStatusUpdated() {
|
||||
MediaStatus status = castMgr.getMediaStatus();
|
||||
if (status == null) {
|
||||
setBuffering(false);
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
|
||||
return;
|
||||
}
|
||||
Playable currentMedia = localVersion(status.getMediaInfo());
|
||||
long position = status.getStreamPosition();
|
||||
if (position > 0 && currentMedia.getPosition()==0) {
|
||||
currentMedia.setPosition((int) position);
|
||||
}
|
||||
int state = status.getPlayerState();
|
||||
setBuffering(state == MediaStatus.PLAYER_STATE_BUFFERING);
|
||||
switch (state) {
|
||||
case MediaStatus.PLAYER_STATE_PLAYING:
|
||||
setPlayerStatus(PlayerStatus.PLAYING, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_PAUSED:
|
||||
setPlayerStatus(PlayerStatus.PAUSED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_BUFFERING:
|
||||
setPlayerStatus(playerStatus, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_IDLE:
|
||||
int reason = status.getIdleReason();
|
||||
switch (reason) {
|
||||
case MediaStatus.IDLE_REASON_CANCELED:
|
||||
setPlayerStatus(PlayerStatus.STOPPED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_INTERRUPTED:
|
||||
setPlayerStatus(PlayerStatus.PREPARING, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_NONE:
|
||||
setPlayerStatus(PlayerStatus.INITIALIZED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_FINISHED:
|
||||
boolean playing = playerStatus == PlayerStatus.PLAYING;
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
callback.endPlayback(playing, false, false);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_ERROR:
|
||||
//Let's assume it's a media format error. Skipping...
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
callback.endPlayback(startWhenPrepared.get(), true, false);
|
||||
}
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_UNKNOWN:
|
||||
//is this right?
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
}
|
||||
RemotePSMP.this.onRemoteMediaPlayerStatusUpdated(callback::endPlayback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -189,6 +150,61 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void onRemoteMediaPlayerStatusUpdated(@NonNull EndPlaybackCall endPlaybackCall) {
|
||||
MediaStatus status = castMgr.getMediaStatus();
|
||||
if (status == null) {
|
||||
setBuffering(false);
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
|
||||
return;
|
||||
}
|
||||
Playable currentMedia = localVersion(status.getMediaInfo());
|
||||
if (currentMedia != null) {
|
||||
long position = status.getStreamPosition();
|
||||
if (position > 0 && currentMedia.getPosition() == 0) {
|
||||
currentMedia.setPosition((int) position);
|
||||
}
|
||||
}
|
||||
int state = status.getPlayerState();
|
||||
setBuffering(state == MediaStatus.PLAYER_STATE_BUFFERING);
|
||||
switch (state) {
|
||||
case MediaStatus.PLAYER_STATE_PLAYING:
|
||||
setPlayerStatus(PlayerStatus.PLAYING, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_PAUSED:
|
||||
setPlayerStatus(PlayerStatus.PAUSED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_BUFFERING:
|
||||
setPlayerStatus(playerStatus, currentMedia);
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_IDLE:
|
||||
int reason = status.getIdleReason();
|
||||
switch (reason) {
|
||||
case MediaStatus.IDLE_REASON_CANCELED:
|
||||
setPlayerStatus(PlayerStatus.STOPPED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_INTERRUPTED:
|
||||
setPlayerStatus(PlayerStatus.PREPARING, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_NONE:
|
||||
setPlayerStatus(PlayerStatus.INITIALIZED, currentMedia);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_FINISHED:
|
||||
boolean playing = playerStatus == PlayerStatus.PLAYING;
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
endPlaybackCall.endPlayback(playing, false, false);
|
||||
break;
|
||||
case MediaStatus.IDLE_REASON_ERROR:
|
||||
//Let's assume it's a media format error. Skipping...
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
endPlaybackCall.endPlayback(startWhenPrepared.get(), true, false);
|
||||
}
|
||||
break;
|
||||
case MediaStatus.PLAYER_STATE_UNKNOWN:
|
||||
//is this right?
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playMediaObject(@NonNull final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
Log.d(TAG, "playMediaObject() called");
|
||||
|
@ -461,7 +477,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void shutdownAsync() {
|
||||
public void shutdownQuietly() {
|
||||
executor.execute(this::shutdown);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
@ -516,4 +532,8 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
|
|||
protected boolean shouldLockWifi() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private interface EndPlaybackCall {
|
||||
boolean endPlayback(boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue