-Fixed main video player losing state when killed in background.

-Disabled auto queuing when repeating is enabled.
-Added method to use startForegroundService instead of startService in sdk 26 and up.
This commit is contained in:
John Zhen Mo 2018-03-05 19:03:49 -08:00
parent d01aeab242
commit b34160eeec
4 changed files with 125 additions and 76 deletions

View file

@ -68,7 +68,6 @@ import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.playlist.PlayQueue; import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueAdapter; import org.schabi.newpipe.playlist.PlayQueueAdapter;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.SerializedCache;
import java.io.IOException; import java.io.IOException;
@ -230,17 +229,19 @@ public abstract class BasePlayer implements
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()); final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch()); final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
// Re-initialization // Good to go...
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch);
}
protected void initPlayback(@NonNull final PlayQueue queue,
@Player.RepeatMode final int repeatMode,
final float playbackSpeed,
final float playbackPitch) {
destroyPlayer(); destroyPlayer();
initPlayer(); initPlayer();
setRepeatMode(repeatMode); setRepeatMode(repeatMode);
setPlaybackParameters(playbackSpeed, playbackPitch); setPlaybackParameters(playbackSpeed, playbackPitch);
// Good to go...
initPlayback(queue);
}
protected void initPlayback(final PlayQueue queue) {
playQueue = queue; playQueue = queue;
playQueue.init(); playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue); playbackManager = new MediaSourceManager(this, playQueue);
@ -840,8 +841,9 @@ public abstract class BasePlayer implements
simpleExoPlayer.seekTo(currentPlayQueueIndex, startPos); simpleExoPlayer.seekTo(currentPlayQueueIndex, startPos);
} }
// when starting playback on the last item, maybe auto queue // when starting playback on the last item when not repeating, maybe auto queue
if (info != null && currentPlayQueueIndex == playQueue.size() - 1 && if (info != null && currentPlayQueueIndex == playQueue.size() - 1 &&
getRepeatMode() == Player.REPEAT_MODE_OFF &&
PlayerHelper.isAutoQueueEnabled(context)) { PlayerHelper.isAutoQueueEnabled(context)) {
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams()); final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams());
if (autoQueue != null) playQueue.append(autoQueue.getStreams()); if (autoQueue != null) playQueue.append(autoQueue.getStreams());
@ -1077,11 +1079,12 @@ public abstract class BasePlayer implements
&& simpleExoPlayer.getPlayWhenReady(); && simpleExoPlayer.getPlayWhenReady();
} }
@Player.RepeatMode
public int getRepeatMode() { public int getRepeatMode() {
return simpleExoPlayer.getRepeatMode(); return simpleExoPlayer.getRepeatMode();
} }
public void setRepeatMode(final int repeatMode) { public void setRepeatMode(@Player.RepeatMode final int repeatMode) {
simpleExoPlayer.setRepeatMode(repeatMode); simpleExoPlayer.setRepeatMode(repeatMode);
} }

View file

@ -58,6 +58,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.PlayQueueItemBuilder; import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
import org.schabi.newpipe.playlist.PlayQueueItemHolder; import org.schabi.newpipe.playlist.PlayQueueItemHolder;
@ -65,24 +66,27 @@ import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.util.List; import java.util.List;
import java.util.Queue;
import java.util.UUID;
import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING; import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION; import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME; import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
import static org.schabi.newpipe.util.StateSaver.KEY_SAVED_STATE;
/** /**
* Activity Player implementing VideoPlayer * Activity Player implementing VideoPlayer
* *
* @author mauriciocolli * @author mauriciocolli
*/ */
public final class MainVideoPlayer extends Activity { public final class MainVideoPlayer extends Activity implements StateSaver.WriteRead {
private static final String TAG = ".MainVideoPlayer"; private static final String TAG = ".MainVideoPlayer";
private static final boolean DEBUG = BasePlayer.DEBUG; private static final boolean DEBUG = BasePlayer.DEBUG;
private static final String PLAYER_STATE_INTENT = "player_state_intent";
private GestureDetector gestureDetector; private GestureDetector gestureDetector;
@ -91,6 +95,8 @@ public final class MainVideoPlayer extends Activity {
private SharedPreferences defaultPreferences; private SharedPreferences defaultPreferences;
@Nullable private StateSaver.SavedState savedState;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Activity LifeCycle // Activity LifeCycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -104,42 +110,28 @@ public final class MainVideoPlayer extends Activity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
setVolumeControlStream(AudioManager.STREAM_MUSIC); setVolumeControlStream(AudioManager.STREAM_MUSIC);
final Intent intent;
if (savedInstanceState != null && savedInstanceState.getParcelable(PLAYER_STATE_INTENT) != null) {
intent = savedInstanceState.getParcelable(PLAYER_STATE_INTENT);
} else {
intent = getIntent();
}
if (intent == null) {
Toast.makeText(this, R.string.general_error, Toast.LENGTH_SHORT).show();
finish();
return;
}
changeSystemUi(); changeSystemUi();
setContentView(R.layout.activity_main_player); setContentView(R.layout.activity_main_player);
playerImpl = new VideoPlayerImpl(this); playerImpl = new VideoPlayerImpl(this);
playerImpl.setup(findViewById(android.R.id.content)); playerImpl.setup(findViewById(android.R.id.content));
playerImpl.handleIntent(intent);
if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) {
return; // We have saved states, stop here to restore it
}
final Intent intent = getIntent();
if (intent != null) {
playerImpl.handleIntent(intent);
} else {
Toast.makeText(this, R.string.general_error, Toast.LENGTH_SHORT).show();
finish();
}
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onRestoreInstanceState(@NonNull Bundle bundle) {
super.onSaveInstanceState(outState); super.onRestoreInstanceState(bundle);
if (this.playerImpl == null) return; savedState = StateSaver.tryToRestore(bundle, this);
playerImpl.setRecovery();
final Intent intent = NavigationHelper.getPlayerIntent(
getApplicationContext(),
this.getClass(),
this.playerImpl.getPlayQueue(),
this.playerImpl.getRepeatMode(),
this.playerImpl.getPlaybackSpeed(),
this.playerImpl.getPlaybackPitch(),
this.playerImpl.getPlaybackQuality()
);
outState.putParcelable(PLAYER_STATE_INTENT, intent);
} }
@Override @Override
@ -149,31 +141,12 @@ public final class MainVideoPlayer extends Activity {
playerImpl.handleIntent(intent); playerImpl.handleIntent(intent);
} }
@Override
public void onBackPressed() {
if (DEBUG) Log.d(TAG, "onBackPressed() called");
super.onBackPressed();
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
}
@Override
protected void onPause() {
super.onPause();
if (DEBUG) Log.d(TAG, "onPause() called");
if (playerImpl.getPlayer() != null && playerImpl.isPlaying() && !activityPaused) {
playerImpl.wasPlaying = true;
playerImpl.onVideoPlayPause();
}
activityPaused = true;
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (DEBUG) Log.d(TAG, "onResume() called"); if (DEBUG) Log.d(TAG, "onResume() called");
if (playerImpl.getPlayer() != null && playerImpl.wasPlaying() if (playerImpl.getPlayer() != null && activityPaused && playerImpl.wasPlaying()
&& !playerImpl.isPlaying() && activityPaused) { && !playerImpl.isPlaying()) {
playerImpl.onVideoPlayPause(); playerImpl.onVideoPlayPause();
} }
activityPaused = false; activityPaused = false;
@ -181,15 +154,15 @@ public final class MainVideoPlayer extends Activity {
if(globalScreenOrientationLocked()) { if(globalScreenOrientationLocked()) {
boolean lastOrientationWasLandscape boolean lastOrientationWasLandscape
= defaultPreferences.getBoolean(getString(R.string.last_orientation_landscape_key), false); = defaultPreferences.getBoolean(getString(R.string.last_orientation_landscape_key), false);
setLandScape(lastOrientationWasLandscape); setLandscape(lastOrientationWasLandscape);
} }
} }
@Override @Override
protected void onDestroy() { public void onBackPressed() {
super.onDestroy(); if (DEBUG) Log.d(TAG, "onBackPressed() called");
if (DEBUG) Log.d(TAG, "onDestroy() called"); super.onBackPressed();
if (playerImpl != null) playerImpl.destroy(); if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
} }
@Override @Override
@ -202,8 +175,71 @@ public final class MainVideoPlayer extends Activity {
} }
} }
@Override
protected void onPause() {
super.onPause();
if (DEBUG) Log.d(TAG, "onPause() called");
if (playerImpl != null && playerImpl.getPlayer() != null && !activityPaused) {
playerImpl.wasPlaying = playerImpl.isPlaying();
if (playerImpl.isPlaying()) playerImpl.onVideoPlayPause();
}
activityPaused = true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (playerImpl == null) return;
playerImpl.setRecovery();
savedState = StateSaver.tryToSave(isChangingConfigurations(), savedState,
outState, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (DEBUG) Log.d(TAG, "onDestroy() called");
if (playerImpl != null) playerImpl.destroy();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Utils // State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override
public String generateSuffix() {
return "." + UUID.randomUUID().toString() + ".player";
}
@Override
public void writeTo(Queue<Object> objectsToSave) {
if (objectsToSave == null) return;
objectsToSave.add(playerImpl.getPlayQueue());
objectsToSave.add(playerImpl.getRepeatMode());
objectsToSave.add(playerImpl.getPlaybackSpeed());
objectsToSave.add(playerImpl.getPlaybackPitch());
objectsToSave.add(playerImpl.getPlaybackQuality());
}
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
@NonNull final PlayQueue queue = (PlayQueue) savedObjects.poll();
final int repeatMode = (int) savedObjects.poll();
final float playbackSpeed = (float) savedObjects.poll();
final float playbackPitch = (float) savedObjects.poll();
@NonNull final String playbackQuality = (String) savedObjects.poll();
playerImpl.setPlaybackQuality(playbackQuality);
playerImpl.initPlayback(queue, repeatMode, playbackSpeed, playbackPitch);
StateSaver.onDestroy(savedState);
}
/*//////////////////////////////////////////////////////////////////////////
// View
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
/** /**
@ -256,17 +292,17 @@ public final class MainVideoPlayer extends Activity {
} }
private void toggleOrientation() { private void toggleOrientation() {
setLandScape(!isLandScape()); setLandscape(!isLandscape());
defaultPreferences.edit() defaultPreferences.edit()
.putBoolean(getString(R.string.last_orientation_landscape_key), !isLandScape()) .putBoolean(getString(R.string.last_orientation_landscape_key), !isLandscape())
.apply(); .apply();
} }
private boolean isLandScape() { private boolean isLandscape() {
return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels; return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels;
} }
private void setLandScape(boolean v) { private void setLandscape(boolean v) {
setRequestedOrientation(v setRequestedOrientation(v
? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);

View file

@ -139,12 +139,12 @@ public class NavigationHelper {
} }
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
context.startService(getPlayerIntent(context, PopupVideoPlayer.class, queue)); startService(context, getPlayerIntent(context, PopupVideoPlayer.class, queue));
} }
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) { public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) {
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
context.startService(getPlayerIntent(context, BackgroundPlayer.class, queue)); startService(context, getPlayerIntent(context, BackgroundPlayer.class, queue));
} }
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) { public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) {
@ -158,7 +158,8 @@ public class NavigationHelper {
} }
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend)); startService(context,
getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend));
} }
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) { public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
@ -167,7 +168,16 @@ public class NavigationHelper {
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) { public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend)); startService(context,
getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend));
}
public static void startService(@NonNull final Context context, @NonNull final Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////

View file

@ -75,7 +75,7 @@
<string name="use_inexact_seek_title">Use fast inexact seek</string> <string name="use_inexact_seek_title">Use fast inexact seek</string>
<string name="use_inexact_seek_summary">Inexact seek allows the player to seek to positions faster with reduced precision</string> <string name="use_inexact_seek_summary">Inexact seek allows the player to seek to positions faster with reduced precision</string>
<string name="auto_queue_title">Auto-queue next stream</string> <string name="auto_queue_title">Auto-queue next stream</string>
<string name="auto_queue_summary">Automatically append a related stream when playback starts on the last stream in play queue.</string> <string name="auto_queue_summary">Automatically append a related stream when playback starts on the last stream in a non-repeating play queue.</string>
<string name="player_gesture_controls_title">Player gesture controls</string> <string name="player_gesture_controls_title">Player gesture controls</string>
<string name="player_gesture_controls_summary">Use gestures to control the brightness and volume of the player</string> <string name="player_gesture_controls_summary">Use gestures to control the brightness and volume of the player</string>
<string name="show_search_suggestions_title">Search suggestions</string> <string name="show_search_suggestions_title">Search suggestions</string>