diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 6b99f86e0..9f7cbe58f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -93,6 +93,7 @@ public final class BackgroundPlayer extends Service { /*////////////////////////////////////////////////////////////////////////// // Notification //////////////////////////////////////////////////////////////////////////*/ + private static final int NOTIFICATION_ID = 123789; private NotificationManager notificationManager; private NotificationCompat.Builder notBuilder; @@ -101,6 +102,8 @@ public final class BackgroundPlayer extends Service { private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha"; private final String setImageResourceMethodName = "setImageResource"; + private boolean shouldUpdateOnProgress; + /*////////////////////////////////////////////////////////////////////////// // Service's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @@ -117,6 +120,7 @@ public final class BackgroundPlayer extends Service { basePlayerImpl.setup(); mBinder = new LocalBinder(); + shouldUpdateOnProgress = true; } @Override @@ -166,12 +170,9 @@ public final class BackgroundPlayer extends Service { private void onScreenOnOff(boolean on) { if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]"); + shouldUpdateOnProgress = on; if (on) { - if (basePlayerImpl.isPlaying() && !basePlayerImpl.isProgressLoopRunning()) { - basePlayerImpl.startProgressLoop(); - } - } else { - basePlayerImpl.stopProgressLoop(); + basePlayerImpl.triggerProgressUpdate(); } } @@ -324,14 +325,6 @@ public final class BackgroundPlayer extends Service { @Override public void onPrepared(boolean playWhenReady) { super.onPrepared(playWhenReady); - if (simpleExoPlayer.getDuration() < 15000) { - FAST_FORWARD_REWIND_AMOUNT = 2000; - } else if (simpleExoPlayer.getDuration() > 60 * 60 * 1000) { - FAST_FORWARD_REWIND_AMOUNT = 60000; - } else { - FAST_FORWARD_REWIND_AMOUNT = 10000; - } - PROGRESS_LOOP_INTERVAL = 1000; simpleExoPlayer.setVolume(1f); } @@ -343,6 +336,10 @@ public final class BackgroundPlayer extends Service { @Override public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) { + updateProgress(currentProgress, duration, bufferPercent); + + if (!shouldUpdateOnProgress) return; + resetNotification(); if (bigNotRemoteView != null) { if (currentItem != null) { @@ -361,7 +358,6 @@ public final class BackgroundPlayer extends Service { } updateNotification(-1); - updateProgress(currentProgress, duration, bufferPercent); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 6a4fc820e..20ad90aa1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -122,21 +122,8 @@ public abstract class BasePlayer implements Player.EventListener, // Intent //////////////////////////////////////////////////////////////////////////*/ - public static final String INTENT_TYPE = "intent_type"; - public static final String SINGLE_STREAM = "single"; - public static final String EXTERNAL_PLAYLIST = "external"; - public static final String INTERNAL_PLAYLIST = "internal"; - - public static final String VIDEO_URL = "video_url"; - public static final String VIDEO_TITLE = "video_title"; - public static final String VIDEO_THUMBNAIL_URL = "video_thumbnail_url"; - public static final String START_POSITION = "start_position"; - public static final String CHANNEL_NAME = "channel_name"; public static final String PLAYBACK_SPEED = "playback_speed"; - public static final String PLAY_QUEUE = "play_queue"; - public static final String RESTORE_QUEUE_INDEX = "restore_queue_index"; - public static final String RESTORE_WINDOW_POS = "restore_window_pos"; public static final String APPEND_ONLY = "append_only"; /*////////////////////////////////////////////////////////////////////////// @@ -149,10 +136,6 @@ public abstract class BasePlayer implements Player.EventListener, protected MediaSourceManager playbackManager; protected PlayQueue playQueue; - private boolean isRecovery = false; - private int queuePos = 0; - private long videoPos = -1; - protected StreamInfo currentInfo; protected PlayQueueItem currentItem; @@ -160,9 +143,10 @@ public abstract class BasePlayer implements Player.EventListener, // Player //////////////////////////////////////////////////////////////////////////*/ - public int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds - public int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds - public static final String CACHE_FOLDER_NAME = "exoplayer"; + protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds + protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds + protected final static int PROGRESS_LOOP_INTERVAL = 500; + protected final static String CACHE_FOLDER_NAME = "exoplayer"; protected SimpleExoPlayer simpleExoPlayer; protected boolean isPrepared = false; @@ -172,7 +156,6 @@ public abstract class BasePlayer implements Player.EventListener, protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); - protected int PROGRESS_LOOP_INTERVAL = 500; protected Disposable progressUpdateReactor; //////////////////////////////////////////////////////////////////////////*/ @@ -269,13 +252,6 @@ public abstract class BasePlayer implements Player.EventListener, return; } - // Resolve playback details - if (intent.hasExtra(RESTORE_QUEUE_INDEX) && intent.hasExtra(START_POSITION)) { - setRecovery( - intent.getIntExtra(RESTORE_QUEUE_INDEX, 0), - intent.getLongExtra(START_POSITION, 0) - ); - } setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed())); // Re-initialization @@ -579,6 +555,7 @@ public abstract class BasePlayer implements Player.EventListener, if (playQueue == null) return; + setRecovery(); if (playQueue.isShuffled()) { playQueue.unshuffle(); } else { @@ -590,26 +567,31 @@ public abstract class BasePlayer implements Player.EventListener, // ExoPlayer Listener //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onTimelineChanged(Timeline timeline, Object manifest) { - if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount()); - + private void recover() { final int currentSourceIndex = playQueue.getIndex(); + final PlayQueueItem currentSourceItem = playQueue.getItem(); // Check if already playing correct window final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex; // Check if recovering - if (isCurrentWindowCorrect && isRecovery && queuePos == playQueue.getIndex()) { + if (isCurrentWindowCorrect && currentSourceItem != null && + currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) { + // todo: figure out exactly why this is the case /* Rounding time to nearest second as certain media cannot guarantee a sub-second seek will complete and the player might get stuck in buffering state forever */ - final long roundedPos = (videoPos / 1000) * 1000; + final long roundedPos = (currentSourceItem.getRecoveryPosition() / 1000) * 1000; if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)roundedPos)); - simpleExoPlayer.seekTo(roundedPos); - isRecovery = false; + simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition()); + currentSourceItem.resetRecoveryPosition(); } + } + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount()); if (playbackManager != null) { playbackManager.load(); @@ -653,6 +635,8 @@ public abstract class BasePlayer implements Player.EventListener, } break; case Player.STATE_READY: //3 + recover(); + if (!isPrepared) { isPrepared = true; onPrepared(playWhenReady); @@ -664,8 +648,7 @@ public abstract class BasePlayer implements Player.EventListener, case Player.STATE_ENDED: // 4 // Ensure the current window has actually ended // since single windows that are still loading may produce an ended state - if (simpleExoPlayer.isCurrentWindowSeekable() && - simpleExoPlayer.getCurrentPosition() >= simpleExoPlayer.getDuration()) { + if (simpleExoPlayer.getDuration() > 0 && simpleExoPlayer.getCurrentPosition() >= simpleExoPlayer.getDuration()) { changeState(STATE_COMPLETED); isPrepared = false; } @@ -812,11 +795,8 @@ public abstract class BasePlayer implements Player.EventListener, else audioManager.abandonAudioFocus(this); if (getCurrentState() == STATE_COMPLETED) { - if (playQueue.getIndex() == 0) { - simpleExoPlayer.seekToDefaultPosition(); - } else { - playQueue.setIndex(0); - } + playQueue.setIndex(0); + simpleExoPlayer.seekToDefaultPosition(); } simpleExoPlayer.setPlayWhenReady(!isPlaying()); @@ -877,10 +857,6 @@ public abstract class BasePlayer implements Player.EventListener, simpleExoPlayer.seekTo(progress); } - public boolean isPlaying() { - return simpleExoPlayer.getPlaybackState() == Player.STATE_READY && simpleExoPlayer.getPlayWhenReady(); - } - /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ @@ -921,24 +897,6 @@ public abstract class BasePlayer implements Player.EventListener, progressUpdateReactor = null; } - protected void tryDeleteCacheFiles(Context context) { - File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME); - - if (cacheDir.exists()) { - try { - if (cacheDir.isDirectory()) { - for (File file : cacheDir.listFiles()) { - try { - if (DEBUG) Log.d(TAG, "tryDeleteCacheFiles: " + file.getAbsolutePath() + " deleted = " + file.delete()); - } catch (Exception ignored) { - } - } - } - } catch (Exception ignored) { - } - } - } - public void triggerProgressUpdate() { onUpdateProgress( (int) simpleExoPlayer.getCurrentPosition(), @@ -992,10 +950,6 @@ public abstract class BasePlayer implements Player.EventListener, return currentState; } - public long getVideoPos() { - return videoPos; - } - public String getVideoUrl() { return currentItem == null ? null : currentItem.getUrl(); } @@ -1012,12 +966,8 @@ public abstract class BasePlayer implements Player.EventListener, return simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == Player.STATE_ENDED; } - public boolean isPrepared() { - return isPrepared; - } - - public void setPrepared(boolean prepared) { - isPrepared = prepared; + public boolean isPlaying() { + return simpleExoPlayer.getPlaybackState() == Player.STATE_READY && simpleExoPlayer.getPlayWhenReady(); } public float getPlaybackSpeed() { @@ -1045,18 +995,10 @@ public abstract class BasePlayer implements Player.EventListener, simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch)); } - public int getCurrentQueueIndex() { - return playQueue != null ? playQueue.getIndex() : -1; - } - public int getCurrentResolutionTarget() { return trackSelector != null ? trackSelector.getParameters().maxVideoHeight : Integer.MAX_VALUE; } - public long getPlayerCurrentPosition() { - return simpleExoPlayer != null ? simpleExoPlayer.getCurrentPosition() : 0L; - } - public PlayQueue getPlayQueue() { return playQueue; } @@ -1069,14 +1011,19 @@ public abstract class BasePlayer implements Player.EventListener, return progressUpdateReactor != null && !progressUpdateReactor.isDisposed(); } - public boolean getRecovery() { - return isRecovery; + public void setRecovery() { + if (playQueue == null || simpleExoPlayer == null) return; + + final int queuePos = playQueue.getIndex(); + final long windowPos = simpleExoPlayer.getCurrentPosition(); + + setRecovery(queuePos, windowPos); } public void setRecovery(final int queuePos, final long windowPos) { + if (playQueue.size() <= queuePos) return; + if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos); - this.isRecovery = true; - this.queuePos = queuePos; - this.videoPos = windowPos; + playQueue.getItem(queuePos).setRecoveryPosition(windowPos); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index c20b5d3e2..2b3a7cef3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -126,10 +126,7 @@ public final class MainVideoPlayer extends Activity { if (playerImpl.getPlayer() != null) { playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady(); - playerImpl.setRecovery( - playerImpl.getCurrentQueueIndex(), - (int) playerImpl.getPlayer().getCurrentPosition() - ); + playerImpl.setRecovery(); playerImpl.destroyPlayer(); } } @@ -224,6 +221,8 @@ public final class MainVideoPlayer extends Activity { private ImageButton screenRotationButton; private ImageButton playPauseButton; + private ImageButton playPreviousButton; + private ImageButton playNextButton; private RelativeLayout queueLayout; private ImageButton itemsListCloseButton; @@ -248,6 +247,8 @@ public final class MainVideoPlayer extends Activity { this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton); this.playPauseButton = rootView.findViewById(R.id.playPauseButton); + this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton); + this.playNextButton = rootView.findViewById(R.id.playNextButton); getRootView().setKeepScreenOn(true); } @@ -264,6 +265,8 @@ public final class MainVideoPlayer extends Activity { queueButton.setOnClickListener(this); repeatButton.setOnClickListener(this); playPauseButton.setOnClickListener(this); + playPreviousButton.setOnClickListener(this); + playNextButton.setOnClickListener(this); screenRotationButton.setOnClickListener(this); } @@ -315,13 +318,12 @@ public final class MainVideoPlayer extends Activity { return; } + setRecovery(); final Intent intent = NavigationHelper.getPlayerIntent( context, PopupVideoPlayer.class, this.getPlayQueue(), this.getCurrentResolutionTarget(), - this.getCurrentQueueIndex(), - this.getPlayerCurrentPosition(), this.getPlaybackSpeed() ); context.startService(intent); @@ -340,6 +342,12 @@ public final class MainVideoPlayer extends Activity { } else if (v.getId() == playPauseButton.getId()) { onVideoPlayPause(); + } else if (v.getId() == playPreviousButton.getId()) { + onPlayPrevious(); + + } else if (v.getId() == playNextButton.getId()) { + onPlayNext(); + } else if (v.getId() == screenRotationButton.getId()) { onScreenRotationClicked(); @@ -367,6 +375,7 @@ public final class MainVideoPlayer extends Activity { hideSystemUi(); getControlsRoot().setVisibility(View.INVISIBLE); queueLayout.setVisibility(View.VISIBLE); + itemsList.smoothScrollToPosition(playQueue.getIndex()); } private void onQueueClosed() { @@ -410,18 +419,24 @@ public final class MainVideoPlayer extends Activity { // States //////////////////////////////////////////////////////////////////////////*/ + private void animatePlayButtons(final boolean show, final int duration) { + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration); + animateView(playPreviousButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration); + animateView(playNextButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration); + } + @Override public void onBlocked() { super.onBlocked(); playPauseButton.setImageResource(R.drawable.ic_pause_white); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); + animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); } @Override public void onBuffering() { super.onBuffering(); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); + animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); } @@ -432,7 +447,7 @@ public final class MainVideoPlayer extends Activity { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_pause_white); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200); + animatePlayButtons(true, 200); } }); showSystemUi(); @@ -446,7 +461,7 @@ public final class MainVideoPlayer extends Activity { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200); + animatePlayButtons(true, 200); } }); @@ -457,7 +472,7 @@ public final class MainVideoPlayer extends Activity { @Override public void onPausedSeek() { super.onPausedSeek(); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); + animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); } @@ -469,7 +484,7 @@ public final class MainVideoPlayer extends Activity { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_replay_white); - animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300); + animatePlayButtons(true, 300); } }); @@ -619,15 +634,12 @@ public final class MainVideoPlayer extends Activity { @Override public boolean onDoubleTap(MotionEvent e) { if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); - //if (!playerImpl.isPlaying()) return false; - if (!playerImpl.isPlayerReady()) return false; + if (!playerImpl.isPlaying()) return false; if (e.getX() > playerImpl.getRootView().getWidth() / 2) - playerImpl.onPlayNext(); - //playerImpl.onFastForward(); + playerImpl.onFastForward(); else - playerImpl.onPlayPrevious(); - //playerImpl.onFastRewind(); + playerImpl.onFastRewind(); return true; } diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index d4e5f19ce..c48e3e3b2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -435,6 +435,8 @@ public final class PopupVideoPlayer extends Service { super.onFullScreenButtonClicked(); if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); + + playerImpl.setRecovery(); Intent intent; if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) { intent = NavigationHelper.getPlayerIntent( @@ -442,8 +444,6 @@ public final class PopupVideoPlayer extends Service { MainVideoPlayer.class, this.getPlayQueue(), this.getCurrentResolutionTarget(), - this.getCurrentQueueIndex(), - this.getPlayerCurrentPosition(), this.getPlaybackSpeed() ); if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false); @@ -703,11 +703,9 @@ public final class PopupVideoPlayer extends Service { if (!playerImpl.isPlaying() || !playerImpl.isPlayerReady()) return false; if (e.getX() > popupWidth / 2) { - //playerImpl.onFastForward(); - playerImpl.onPlayNext(); + playerImpl.onFastForward(); } else { - //playerImpl.onFastRewind(); - playerImpl.onPlayPrevious(); + playerImpl.onFastRewind(); } return true; diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 5de90dbbe..2c20e0740 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -382,11 +382,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity } else if (view.getId() == backwardButton.getId()) { player.onPlayPrevious(); - scrollToSelected(); } else if (view.getId() == playPauseButton.getId()) { player.onVideoPlayPause(); - scrollToSelected(); } else if (view.getId() == forwardButton.getId()) { player.onPlayNext(); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index 38542aa86..e61fc2975 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -89,16 +89,12 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. // Intent //////////////////////////////////////////////////////////////////////////*/ - public static final String VIDEO_STREAMS_LIST = "video_streams_list"; - public static final String VIDEO_ONLY_AUDIO_STREAM = "video_only_audio_stream"; - public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream"; public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe"; - - public static final String PLAYER_INTENT = "player_intent"; public static final String MAX_RESOLUTION = "max_resolution"; private ArrayList availableStreams; - private int selectedStreamIndex; + private int selectedStreamIndex; + /*////////////////////////////////////////////////////////////////////////// // Player //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index ff139e9d7..65d04c33c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -153,6 +153,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { } private void onPlayQueueChanged(final PlayQueueMessage event) { + if (playQueue.isEmpty()) { + playbackListener.shutdown(); + } + // why no pattern matching in Java =( switch (event.type()) { case INIT: @@ -168,6 +172,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { case REMOVE: final RemoveEvent removeEvent = (RemoveEvent) event; remove(removeEvent.index()); + sync(); break; case MOVE: final MoveEvent moveEvent = (MoveEvent) event; @@ -181,8 +186,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { if (!isPlayQueueReady()) { tryBlock(); playQueue.fetch(); - } else if (playQueue.isEmpty()) { - playbackListener.shutdown(); } else { load(); // All event warrants a load } @@ -219,6 +222,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { private void sync() { final PlayQueueItem currentItem = playQueue.getItem(); + if (currentItem == null) return; final Consumer syncPlayback = new Consumer() { @Override diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index 33017c4a7..89a074b94 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -50,10 +50,6 @@ public abstract class PlayQueue implements Serializable { private transient Flowable broadcastReceiver; private transient Subscription reportingReactor; - PlayQueue() { - this(0, Collections.emptyList()); - } - PlayQueue(final int index, final List startWith) { streams = new ArrayList<>(); streams.addAll(startWith); @@ -81,12 +77,9 @@ public abstract class PlayQueue implements Serializable { } /** - * Dispose this play queue by stopping all message buses and clearing the playlist. + * Dispose the play queue by stopping all message buses. * */ public void dispose() { - if (backup != null) backup.clear(); - if (streams != null) streams.clear(); - if (eventBroadcast != null) eventBroadcast.onComplete(); if (reportingReactor != null) reportingReactor.cancel(); @@ -265,11 +258,12 @@ public abstract class PlayQueue implements Serializable { private synchronized void removeInternal(final int index) { final int currentIndex = queueIndex.get(); + final int size = size(); if (currentIndex > index) { queueIndex.decrementAndGet(); - } else if (currentIndex >= size()) { - queueIndex.set(0); + } else if (currentIndex >= size) { + queueIndex.set(currentIndex % (size - 1)); } if (backup != null) { @@ -300,9 +294,8 @@ public abstract class PlayQueue implements Serializable { * Shuffles the current play queue. * * This method first backs up the existing play queue and item being played. - * Then a newly shuffled play queue will be generated along with the index of - * the previously playing item if it is found in the shuffled play queue. If - * not found, the current index will reset to 0. + * Then a newly shuffled play queue will be generated along with currently + * playing item placed at the beginning of the queue. * * Will emit a {@link ReorderEvent} in any context. * */ @@ -315,10 +308,9 @@ public abstract class PlayQueue implements Serializable { final int newIndex = streams.indexOf(current); if (newIndex != -1) { - queueIndex.set(newIndex); - } else { - queueIndex.set(0); + streams.add(0, streams.remove(newIndex)); } + queueIndex.set(0); broadcast(new ReorderEvent()); } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java index 05f01d1dd..322fbe2e7 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java @@ -15,6 +15,8 @@ import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; public class PlayQueueItem implements Serializable { + final public static int DEFAULT_QUALITY = Integer.MIN_VALUE; + final public static long RECOVERY_UNSET = Long.MIN_VALUE; final private String title; final private String url; @@ -23,28 +25,32 @@ public class PlayQueueItem implements Serializable { final private String thumbnailUrl; final private String uploader; + private int qualityIndex; + private long recoveryPosition; private Throwable error; private transient Single stream; - PlayQueueItem(final StreamInfo streamInfo) { - this.title = streamInfo.name; - this.url = streamInfo.url; - this.serviceId = streamInfo.service_id; - this.duration = streamInfo.duration; - this.thumbnailUrl = streamInfo.thumbnail_url; - this.uploader = streamInfo.uploader_name; - - this.stream = Single.just(streamInfo); + PlayQueueItem(@NonNull final StreamInfo info) { + this(info.name, info.url, info.service_id, info.duration, info.thumbnail_url, info.uploader_name); + this.stream = Single.just(info); } - PlayQueueItem(final StreamInfoItem streamInfoItem) { - this.title = streamInfoItem.name; - this.url = streamInfoItem.url; - this.serviceId = streamInfoItem.service_id; - this.duration = streamInfoItem.duration; - this.thumbnailUrl = streamInfoItem.thumbnail_url; - this.uploader = streamInfoItem.uploader_name; + PlayQueueItem(@NonNull final StreamInfoItem item) { + this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name); + } + + private PlayQueueItem(final String name, final String url, final int serviceId, + final long duration, final String thumbnailUrl, final String uploader) { + this.title = name; + this.url = url; + this.serviceId = serviceId; + this.duration = duration; + this.thumbnailUrl = thumbnailUrl; + this.uploader = uploader; + + resetQualityIndex(); + resetRecoveryPosition(); } @NonNull @@ -97,4 +103,32 @@ public class PlayQueueItem implements Serializable { .observeOn(AndroidSchedulers.mainThread()) .doOnError(onError); } + + //////////////////////////////////////////////////////////////////////////// + // Item States + //////////////////////////////////////////////////////////////////////////// + + public int getQualityIndex() { + return qualityIndex; + } + + public long getRecoveryPosition() { + return recoveryPosition; + } + + public void setQualityIndex(int qualityIndex) { + this.qualityIndex = qualityIndex; + } + + public void setRecoveryPosition(long recoveryPosition) { + this.recoveryPosition = recoveryPosition; + } + + public void resetQualityIndex() { + this.qualityIndex = DEFAULT_QUALITY; + } + + public void resetRecoveryPosition() { + this.recoveryPosition = RECOVERY_UNSET; + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 9e92c128e..b5a2db8cf 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -77,12 +77,8 @@ public class NavigationHelper { final Class targetClazz, final PlayQueue playQueue, final int maxResolution, - final int restoringIndex, - final long startPosition, final float playbackSpeed) { return getPlayerIntent(context, targetClazz, playQueue, maxResolution) - .putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, restoringIndex) - .putExtra(BasePlayer.START_POSITION, startPosition) .putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed); } diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml index 24a1fafc4..30146f928 100644 --- a/app/src/main/res/layout/activity_main_player.xml +++ b/app/src/main/res/layout/activity_main_player.xml @@ -275,6 +275,34 @@ android:src="@drawable/ic_pause_white" tools:ignore="ContentDescription"/> + + + + diff --git a/app/src/main/res/layout/player_notification.xml b/app/src/main/res/layout/player_notification.xml index 958b9bf3d..157615bb7 100644 --- a/app/src/main/res/layout/player_notification.xml +++ b/app/src/main/res/layout/player_notification.xml @@ -12,6 +12,7 @@ android:layout_height="64dp" android:background="@color/background_notification_color" android:clickable="true" + android:focusable="true" android:gravity="center_vertical" android:orientation="horizontal"> @@ -58,6 +59,7 @@ android:layout_height="match_parent" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="5dp" android:scaleType="fitCenter" android:src="@drawable/ic_repeat_white" @@ -69,9 +71,10 @@ android:layout_height="match_parent" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="5dp" android:scaleType="fitCenter" - android:src="@drawable/ic_action_av_fast_rewind" + android:src="@drawable/exo_controls_previous" tools:ignore="ContentDescription"/> @@ -89,9 +93,10 @@ android:layout_height="match_parent" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="5dp" android:scaleType="fitCenter" - android:src="@drawable/ic_action_av_fast_forward" + android:src="@drawable/exo_controls_next" tools:ignore="ContentDescription"/> @@ -26,6 +27,7 @@ android:layout_alignParentRight="true" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="8dp" android:scaleType="fitCenter" android:src="@drawable/ic_close_white_24dp" @@ -82,9 +84,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" + android:layout_marginStart="8dp" android:layout_marginTop="2dp" android:layout_alignTop="@+id/notificationProgressBar" android:layout_toRightOf="@+id/notificationCover" + android:layout_toEndOf="@+id/notificationCover" android:ellipsize="end" android:maxLines="1" android:textSize="12sp" @@ -109,6 +113,7 @@ android:layout_centerVertical="true" android:background="#00000000" android:clickable="true" + android:focusable="true" android:scaleType="fitXY" android:src="@drawable/ic_repeat_white" tools:ignore="ContentDescription"/> @@ -122,9 +127,10 @@ android:layout_toLeftOf="@+id/notificationPlayPause" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="2dp" android:scaleType="fitCenter" - android:src="@drawable/ic_action_av_fast_rewind" + android:src="@drawable/exo_controls_previous" tools:ignore="ContentDescription"/> @@ -150,107 +157,10 @@ android:layout_marginRight="8dp" android:background="#00000000" android:clickable="true" + android:focusable="true" android:padding="2dp" android:scaleType="fitCenter" - android:src="@drawable/ic_action_av_fast_forward" + android:src="@drawable/exo_controls_next" tools:ignore="ContentDescription"/> - - - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5b3712602..31547873d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -303,5 +303,5 @@ Remove Details Audio Settings - Hold To Append + Hold To Enqueue