* Added a distinction between SystemService sleeping (no wakelock, but appears

active) and shutdown (no notification or lockscreen controls).
* Fixed bug where recovering from an HTTP error would cause the track to reset
  playback at the beginning
This commit is contained in:
Casey Langen 2017-05-31 14:12:22 -07:00
parent ab11376356
commit 45a81cb138
3 changed files with 92 additions and 47 deletions

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util;
import io.casey.musikcube.remote.Application;
@ -35,6 +34,7 @@ public class ExoPlayerWrapper extends PlayerWrapper {
private SimpleExoPlayer player;
private boolean prefetch;
private Context context;
private long lastPosition = -1;
public ExoPlayerWrapper() {
this.context = Application.getInstance();
@ -100,7 +100,7 @@ public class ExoPlayerWrapper extends PlayerWrapper {
break;
case Error:
this.player.setPlayWhenReady(true);
this.player.setPlayWhenReady(this.lastPosition == -1);
this.player.prepare(this.source);
setState(State.Preparing);
break;
@ -113,6 +113,7 @@ public class ExoPlayerWrapper extends PlayerWrapper {
public void setPosition(int millis) {
Preconditions.throwIfNotOnMainThread();
this.lastPosition = -1;
if (this.player.getPlaybackState() != ExoPlayer.STATE_IDLE) {
if (this.player.isCurrentWindowSeekable()) {
this.player.seekTo(millis);
@ -201,6 +202,11 @@ public class ExoPlayerWrapper extends PlayerWrapper {
player.setVolume(getGlobalVolume());
if (lastPosition != -1) {
player.seekTo(lastPosition);
lastPosition = -1;
}
if (!prefetch) {
player.setPlayWhenReady(true);
setState(State.Playing);
@ -220,6 +226,8 @@ public class ExoPlayerWrapper extends PlayerWrapper {
public void onPlayerError(ExoPlaybackException error) {
Preconditions.throwIfNotOnMainThread();
lastPosition = player.getCurrentPosition();
switch (getState()) {
case Preparing:
case Prepared:

View File

@ -40,7 +40,7 @@ public class StreamingPlaybackService implements PlaybackService {
private static final int PREV_TRACK_GRACE_PERIOD_MILLIS = 3500;
private static final int MAX_TRACK_METADATA_CACHE_SIZE = 50;
private static final int PRECACHE_METADATA_SIZE = 10;
private static final int PAUSED_SERVICE_SHUTDOWN_DELAY_MS = 1000 * 60; /* 1 minute */
private static final int PAUSED_SERVICE_SLEEP_DELAY_MS = 1000 * 60 * 5; /* 5 minutes */
private WebSocketService wss;
private Set<EventListener> listeners = new HashSet<>();
@ -72,7 +72,8 @@ public class StreamingPlaybackService implements PlaybackService {
public void stopPlaybackAndReset() {
reset(currentPlayer);
reset(nextPlayer);
nextPlayerScheduled = false; this.currentPlayer = this.nextPlayer = null;
nextPlayerScheduled = false;
this.currentPlayer = this.nextPlayer = null;
this.currentMetadata = this.nextMetadata = null;
this.currentIndex = this.nextIndex = -1;
}
@ -226,7 +227,7 @@ public class StreamingPlaybackService implements PlaybackService {
@Override
public void pause() {
if (state != PlaybackState.Paused) {
schedulePausedShutdown();
schedulePausedSleep();
killAudioFocus();
if (context.currentPlayer != null) {
@ -240,7 +241,7 @@ public class StreamingPlaybackService implements PlaybackService {
@Override
public void resume() {
if (requestAudioFocus()) {
cancelScheduledPausedShutdown();
cancelScheduledPausedSleep();
context.currentPlayer.resume();
setState(PlaybackState.Playing);
pausedByTransientLoss = false;
@ -259,7 +260,7 @@ public class StreamingPlaybackService implements PlaybackService {
@Override
public void prev() {
if (requestAudioFocus()) {
cancelScheduledPausedShutdown();
cancelScheduledPausedSleep();
if (context.currentPlayer != null) {
if (context.currentPlayer.getPosition() > PREV_TRACK_GRACE_PERIOD_MILLIS) {
@ -274,7 +275,7 @@ public class StreamingPlaybackService implements PlaybackService {
@Override
public void next() {
if (requestAudioFocus()) {
cancelScheduledPausedShutdown();
cancelScheduledPausedSleep();
moveToNextTrack(true);
}
}
@ -503,7 +504,7 @@ public class StreamingPlaybackService implements PlaybackService {
case Playing:
setState(PlaybackState.Playing);
prefetchNextTrackAudio();
cancelScheduledPausedShutdown();
cancelScheduledPausedSleep();
precacheTrackMetadata(context.currentIndex, PRECACHE_METADATA_SIZE);
break;
@ -737,7 +738,7 @@ public class StreamingPlaybackService implements PlaybackService {
private void loadQueueAndPlay(final QueueParams params, int startIndex) {
setState(PlaybackState.Buffering);
cancelScheduledPausedShutdown();
cancelScheduledPausedSleep();
SystemService.wakeup();
this.pausedByTransientLoss = false;
@ -782,13 +783,13 @@ public class StreamingPlaybackService implements PlaybackService {
.subscribe();
}
private void cancelScheduledPausedShutdown() {
private void cancelScheduledPausedSleep() {
SystemService.wakeup();
handler.removeCallbacks(pauseServiceShutdownRunnable);
handler.removeCallbacks(pauseServiceSleepRunnable);
}
private void schedulePausedShutdown() {
handler.postDelayed(pauseServiceShutdownRunnable, PAUSED_SERVICE_SHUTDOWN_DELAY_MS);
private void schedulePausedSleep() {
handler.postDelayed(pauseServiceSleepRunnable, PAUSED_SERVICE_SLEEP_DELAY_MS);
}
private void precacheTrackMetadata(final int start, final int count) {
@ -881,7 +882,7 @@ public class StreamingPlaybackService implements PlaybackService {
}
};
private Runnable pauseServiceShutdownRunnable = () -> SystemService.shutdown();
private Runnable pauseServiceSleepRunnable = () -> SystemService.sleep();
private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = (flag) -> {
switch (flag) {

View File

@ -49,6 +49,7 @@ public class SystemService extends Service {
public static final String ACTION_NOTIFICATION_STOP = "io.casey.musikcube.remote.PAUSE_SHUT_DOWN";
public static String ACTION_WAKE_UP = "io.casey.musikcube.remote.WAKE_UP";
public static String ACTION_SHUT_DOWN = "io.casey.musikcube.remote.SHUT_DOWN";
public static String ACTION_SLEEP = "io.casey.musikcube.remote.SLEEP";
private final static long MEDIA_SESSION_ACTIONS =
PlaybackStateCompat.ACTION_PLAY_PAUSE |
@ -79,6 +80,11 @@ public class SystemService extends Service {
c.startService(new Intent(c, SystemService.class).setAction(ACTION_SHUT_DOWN));
}
public static void sleep() {
final Context c = Application.getInstance();
c.startService(new Intent(c, SystemService.class).setAction(ACTION_SLEEP));
}
@Override
public void onCreate() {
super.onCreate();
@ -99,42 +105,16 @@ public class SystemService extends Service {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_WAKE_UP.equals(action)) {
Log.d(TAG, "SystemService WAKE_UP");
if (playback == null) {
playback = PlaybackServiceFactory.streaming(this);
playback.connect(listener);
initMediaSession();
}
if (wakeLock == null) {
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "StreamingPlaybackService");
wakeLock.setReferenceCounted(false);
wakeLock.acquire();
}
wakeupNow();
}
else if (ACTION_SHUT_DOWN.equals(action)) {
Log.d(TAG, "SystemService SHUT_DOWN");
if (mediaSession != null) {
mediaSession.release();
}
if (playback != null) {
playback.disconnect(listener);
playback = null;
}
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
stopSelf();
shutdownNow();
}
else if (ACTION_SLEEP.equals(action)) {
sleepNow();
}
else if (handlePlaybackAction(action)) {
wakeupNow();
return super.onStartCommand(intent, flags, startId);
}
}
@ -148,6 +128,62 @@ public class SystemService extends Service {
return null;
}
private void wakeupNow() {
Log.d(TAG, "SystemService WAKE_UP");
final boolean sleeping = (playback == null || wakeLock == null);
if (playback == null) {
playback = PlaybackServiceFactory.streaming(this);
}
if (wakeLock == null) {
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "StreamingPlaybackService");
wakeLock.setReferenceCounted(false);
wakeLock.acquire();
}
if (sleeping) {
playback.connect(listener);
initMediaSession();
}
}
private void shutdownNow() {
Log.d(TAG, "SystemService SHUT_DOWN");
if (mediaSession != null) {
mediaSession.release();
}
if (playback != null) {
playback.disconnect(listener);
playback = null;
}
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
stopSelf();
}
private void sleepNow() {
Log.d(TAG, "SystemService SLEEP");
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
if (playback != null) {
playback.disconnect(listener);
}
}
private void initMediaSession() {
ComponentName receiver = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());