implement basic cast session join

This commit is contained in:
Domingos Lopes 2016-03-28 22:43:22 -04:00
parent e70f4d5389
commit b41eba90bd
5 changed files with 117 additions and 75 deletions

View File

@ -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);

View File

@ -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();
}

View File

@ -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());
}
};
}

View File

@ -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);

View File

@ -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);
}
}