-Refactored Playback manager to expose only readonly methods.

-Removed swap and move operations.
-Code clean up.
This commit is contained in:
John Zhen M 2017-09-10 17:43:21 -07:00 committed by John Zhen Mo
parent 9413856463
commit f8abf92a66
12 changed files with 128 additions and 226 deletions

View file

@ -382,9 +382,6 @@ public class BackgroundPlayer extends Service {
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(final StreamInfo info, final int sortedStreamsIndex) {
super.sync(info, sortedStreamsIndex); super.sync(info, sortedStreamsIndex);
setVideoTitle(info.name);
setUploaderName(info.uploader_name);
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName()); notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());

View file

@ -93,7 +93,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull; import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.SerialDisposable;
import io.reactivex.functions.Consumer; import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate; import io.reactivex.functions.Predicate;
@ -136,11 +135,6 @@ public abstract class BasePlayer implements Player.EventListener,
public static final String RESTORE_QUEUE_INDEX = "restore_queue_index"; public static final String RESTORE_QUEUE_INDEX = "restore_queue_index";
public static final String RESTORE_WINDOW_POS = "restore_window_pos"; public static final String RESTORE_WINDOW_POS = "restore_window_pos";
protected String videoUrl = "";
protected String videoTitle = "";
protected String videoThumbnailUrl = "";
protected String uploaderName = "";
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Playlist // Playlist
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -148,8 +142,11 @@ public abstract class BasePlayer implements Player.EventListener,
protected PlaybackManager playbackManager; protected PlaybackManager playbackManager;
protected PlayQueue playQueue; protected PlayQueue playQueue;
protected int queueStartPos = 0; private boolean isRecovery = false;
protected long videoStartPos = -1; private int queuePos = 0;
private long videoPos = -1;
protected StreamInfo currentInfo;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Player // Player
@ -246,8 +243,10 @@ public abstract class BasePlayer implements Player.EventListener,
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
if (intent == null) return; if (intent == null) return;
queueStartPos = intent.getIntExtra(RESTORE_QUEUE_INDEX, 0); setRecovery(
videoStartPos = intent.getLongExtra(START_POSITION, 0); intent.getIntExtra(RESTORE_QUEUE_INDEX, 0),
intent.getLongExtra(START_POSITION, 0)
);
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed())); setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
switch (intent.getStringExtra(INTENT_TYPE)) { switch (intent.getStringExtra(INTENT_TYPE)) {
@ -535,21 +534,37 @@ public abstract class BasePlayer implements Player.EventListener,
public void onTimelineChanged(Timeline timeline, Object manifest) { public void onTimelineChanged(Timeline timeline, Object manifest) {
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount()); if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) { final int currentSourceIndex = playbackManager.getCurrentSourceIndex();
if (timeline.getWindowCount() > playbackManager.getCurrentSourceIndex()) {
if (DEBUG) Log.d(TAG, "Rewinding to correct window"); // Check timeline has window
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex()); if (simpleExoPlayer.getCurrentTimeline().getWindowCount() <= currentSourceIndex) return;
}
// Check if window is ready
Timeline.Window window = new Timeline.Window();
simpleExoPlayer.getCurrentTimeline().getWindow(currentSourceIndex, window);
if (window.isDynamic) return;
// Check if already playing correct window
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
if (isCurrentWindowCorrect && getCurrentState() == STATE_PLAYING) return;
// Check if recovering on correct item
if (isRecovery && queuePos == playQueue.getIndex() && isCurrentWindowCorrect) {
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)videoPos));
simpleExoPlayer.seekTo(currentSourceIndex, videoPos);
isRecovery = false;
} else if (!isCurrentWindowCorrect) { // Or if on wrong window
final long startPos = currentInfo != null ? currentInfo.start_position : 0;
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
} }
if (!simpleExoPlayer.isCurrentWindowDynamic() && simpleExoPlayer.isCurrentWindowSeekable()) { // Good to go...
simpleExoPlayer.setPlayWhenReady(true); simpleExoPlayer.setPlayWhenReady(true);
} }
}
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
Log.w(TAG, "onTracksChanged() called, unsupported operation. Is this expected?");
} }
@Override @Override
@ -604,8 +619,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void onPlayerError(ExoPlaybackException error) { public void onPlayerError(ExoPlaybackException error) {
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]"); if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
playbackManager.report(error); playQueue.remove(playQueue.getIndex());
onError(error); onError(error);
} }
@ -615,7 +629,9 @@ public abstract class BasePlayer implements Player.EventListener,
int newIndex = simpleExoPlayer.getCurrentWindowIndex(); int newIndex = simpleExoPlayer.getCurrentWindowIndex();
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]"); if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
playbackManager.refresh(newIndex); if (newIndex == playbackManager.getCurrentSourceIndex() + 1) {
playQueue.offsetIndex(+1);
}
} }
@Override @Override
@ -633,6 +649,7 @@ public abstract class BasePlayer implements Player.EventListener,
if (DEBUG) Log.d(TAG, "Blocking..."); if (DEBUG) Log.d(TAG, "Blocking...");
simpleExoPlayer.stop(); simpleExoPlayer.stop();
isPrepared = false;
changeState(STATE_BUFFERING); changeState(STATE_BUFFERING);
} }
@ -642,13 +659,8 @@ public abstract class BasePlayer implements Player.EventListener,
if (simpleExoPlayer == null) return; if (simpleExoPlayer == null) return;
if (DEBUG) Log.d(TAG, "Unblocking..."); if (DEBUG) Log.d(TAG, "Unblocking...");
if (queueStartPos != playQueue.getIndex()) { simpleExoPlayer.prepare(playbackManager.getMediaSource(), true, true);
queueStartPos = playQueue.getIndex(); isPrepared = true;
videoStartPos = 0;
}
simpleExoPlayer.prepare(playbackManager.getMediaSource());
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), videoStartPos);
} }
@Override @Override
@ -656,13 +668,10 @@ public abstract class BasePlayer implements Player.EventListener,
if (simpleExoPlayer == null) return; if (simpleExoPlayer == null) return;
if (DEBUG) Log.d(TAG, "Syncing..."); if (DEBUG) Log.d(TAG, "Syncing...");
videoUrl = info.url; currentInfo = info;
videoThumbnailUrl = info.thumbnail_url;
videoTitle = info.name;
onTimelineChanged(simpleExoPlayer.getCurrentTimeline(), null); onTimelineChanged(simpleExoPlayer.getCurrentTimeline(), null);
initThumbnail(videoThumbnailUrl); initThumbnail(info.thumbnail_url);
} }
@Override @Override
@ -774,7 +783,11 @@ public abstract class BasePlayer implements Player.EventListener,
} }
public void triggerProgressUpdate() { public void triggerProgressUpdate() {
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage()); onUpdateProgress(
(int) simpleExoPlayer.getCurrentPosition(),
(int) simpleExoPlayer.getDuration(),
simpleExoPlayer.getBufferedPercentage()
);
} }
public void animateAudio(final float from, final float to, int duration) { public void animateAudio(final float from, final float to, int duration) {
@ -823,35 +836,19 @@ public abstract class BasePlayer implements Player.EventListener,
} }
public String getVideoUrl() { public String getVideoUrl() {
return videoUrl; return currentInfo.url;
} }
public void setVideoUrl(String videoUrl) { public long getVideoPos() {
this.videoUrl = videoUrl; return videoPos;
}
public long getVideoStartPos() {
return videoStartPos;
}
public void setVideoStartPos(long videoStartPos) {
this.videoStartPos = videoStartPos;
} }
public String getVideoTitle() { public String getVideoTitle() {
return videoTitle; return currentInfo.name;
}
public void setVideoTitle(String videoTitle) {
this.videoTitle = videoTitle;
} }
public String getUploaderName() { public String getUploaderName() {
return uploaderName; return currentInfo.uploader_name;
}
public void setUploaderName(String uploaderName) {
this.uploaderName = uploaderName;
} }
public boolean isCompleted() { public boolean isCompleted() {
@ -866,14 +863,6 @@ public abstract class BasePlayer implements Player.EventListener,
isPrepared = prepared; isPrepared = prepared;
} }
public String getVideoThumbnailUrl() {
return videoThumbnailUrl;
}
public void setVideoThumbnailUrl(String videoThumbnailUrl) {
this.videoThumbnailUrl = videoThumbnailUrl;
}
public float getPlaybackSpeed() { public float getPlaybackSpeed() {
return simpleExoPlayer.getPlaybackParameters().speed; return simpleExoPlayer.getPlaybackParameters().speed;
} }
@ -897,4 +886,15 @@ public abstract class BasePlayer implements Player.EventListener,
public boolean isProgressLoopRunning() { public boolean isProgressLoopRunning() {
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed(); return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
} }
public boolean getRecovery() {
return isRecovery;
}
public void setRecovery(final int queuePos, final long windowPos) {
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
this.isRecovery = true;
this.queuePos = queuePos;
this.videoPos = windowPos;
}
} }

View file

@ -111,7 +111,10 @@ public class MainVideoPlayer extends Activity {
if (DEBUG) Log.d(TAG, "onStop() called"); if (DEBUG) Log.d(TAG, "onStop() called");
activityPaused = true; activityPaused = true;
if (playerImpl.getPlayer() != null) { if (playerImpl.getPlayer() != null) {
playerImpl.setVideoStartPos((int) playerImpl.getPlayer().getCurrentPosition()); playerImpl.setRecovery(
playerImpl.getCurrentQueueIndex(),
(int) playerImpl.getPlayer().getCurrentPosition()
);
playerImpl.destroyPlayer(); playerImpl.destroyPlayer();
} }
} }
@ -224,13 +227,6 @@ public class MainVideoPlayer extends Activity {
screenRotationButton.setOnClickListener(this); screenRotationButton.setOnClickListener(this);
} }
@Override
public void handleIntent(Intent intent) {
super.handleIntent(intent);
titleTextView.setText(getVideoTitle());
channelTextView.setText(getUploaderName());
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Playback Listener // Playback Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -263,7 +259,8 @@ public class MainVideoPlayer extends Activity {
return; return;
} }
context.startService(NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, this)); final Intent intent = NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, this);
context.startService(intent);
destroyPlayer(); destroyPlayer();
((View) getControlAnimationView().getParent()).setVisibility(View.GONE); ((View) getControlAnimationView().getParent()).setVisibility(View.GONE);

View file

@ -50,15 +50,11 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
@ -68,19 +64,16 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.old.PlayVideoActivity; import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.player.playback.PlaybackManager; import org.schabi.newpipe.player.playback.PlaybackManager;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
@ -745,9 +738,6 @@ public class PopupVideoPlayer extends Service {
} }
public void onReceive(final StreamInfo info) { public void onReceive(final StreamInfo info) {
if (info.start_position > 0) playerImpl.setVideoStartPos(info.start_position * 1000);
else playerImpl.setVideoStartPos(-1);
mainHandler.post(new Runnable() { mainHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {

View file

@ -234,8 +234,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
final Serializable serializable = intent.getSerializableExtra(PLAY_QUEUE); final Serializable serializable = intent.getSerializableExtra(PLAY_QUEUE);
if (!(serializable instanceof PlayQueue)) return; if (!(serializable instanceof PlayQueue)) return;
selectedIndexStream = intent.getIntExtra(INDEX_SEL_VIDEO_STREAM, -1);
playQueue = (PlayQueue) serializable; playQueue = (PlayQueue) serializable;
playQueue.init(); playQueue.init();
@ -406,12 +404,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public void onPrepared(boolean playWhenReady) { public void onPrepared(boolean playWhenReady) {
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]"); if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
if (videoStartPos > 0) {
playbackSeekBar.setProgress((int) videoStartPos);
playbackCurrentTime.setText(getTimeString((int) videoStartPos));
videoStartPos = -1;
}
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration()); playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration())); playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
playbackSpeed.setText(formatSpeed(getPlaybackSpeed())); playbackSpeed.setText(formatSpeed(getPlaybackSpeed()));
@ -453,6 +445,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
protected void onFullScreenButtonClicked() { protected void onFullScreenButtonClicked() {
if (!isPlayerReady()) return; if (!isPlayerReady()) return;
changeState(STATE_BUFFERING);
} }
@Override @Override
@ -492,11 +486,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]"); Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
if (qualityPopupMenuGroupId == menuItem.getGroupId()) { if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
if (selectedIndexStream == menuItem.getItemId()) return true; if (selectedIndexStream == menuItem.getItemId() || getRecovery()) return true;
queueStartPos = playQueue.getIndex(); final int index = playQueue.getIndex();
videoStartPos = simpleExoPlayer.getCurrentPosition(); setRecovery(index, simpleExoPlayer.getCurrentPosition());
playbackManager.updateCurrent(menuItem.getItemId()); playQueue.updateIndex(index, menuItem.getItemId());
qualityTextView.setText(menuItem.getTitle()); qualityTextView.setText(menuItem.getTitle());
return true; return true;

View file

@ -12,7 +12,6 @@ import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.events.PlayQueueMessage; import org.schabi.newpipe.playlist.events.PlayQueueMessage;
import org.schabi.newpipe.playlist.events.RemoveEvent; import org.schabi.newpipe.playlist.events.RemoveEvent;
import org.schabi.newpipe.playlist.events.MoveEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -29,7 +28,7 @@ public class PlaybackManager {
private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode()); private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode());
// One-side rolling window size for default loading // One-side rolling window size for default loading
// Effectively loads WINDOW_SIZE * 2 streams // Effectively loads WINDOW_SIZE * 2 streams
private static final int WINDOW_SIZE = 2; private static final int WINDOW_SIZE = 3;
private final PlaybackListener playbackListener; private final PlaybackListener playbackListener;
private final PlayQueue playQueue; private final PlayQueue playQueue;
@ -77,37 +76,6 @@ public class PlaybackManager {
return sources; return sources;
} }
/*
* Called when the player has transitioned to another stream.
* */
public void refresh(final int newSourceIndex) {
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1 && newSourceIndex == getCurrentSourceIndex() + 1) {
playQueue.offsetIndex(+1);
}
}
public void report(final Exception error) {
// ignore error checking for now, just remove the current index
if (error == null) return;
tryBlock();
final int index = playQueue.getIndex();
playQueue.remove(index);
resetSources();
load();
}
public void updateCurrent(final int newSortedStreamsIndex) {
tryBlock();
PlayQueueItem item = playQueue.getCurrent();
item.setSortedQualityIndex(newSortedStreamsIndex);
resetSources();
load();
}
public void dispose() { public void dispose() {
if (playQueueReactor != null) playQueueReactor.cancel(); if (playQueueReactor != null) playQueueReactor.cancel();
@ -146,14 +114,19 @@ public class PlaybackManager {
case APPEND: case APPEND:
break; break;
case SELECT: case SELECT:
onSelect(); if (isBlocked) break;
if (isCurrentIndexLoaded()) sync(); else tryBlock();
break; break;
case REMOVE: case REMOVE:
final RemoveEvent removeEvent = (RemoveEvent) event; final RemoveEvent removeEvent = (RemoveEvent) event;
if (removeEvent.isCurrent()) tryBlock();
remove(removeEvent.index()); remove(removeEvent.index());
break; break;
case MOVE: case UPDATE:
throw new UnsupportedOperationException("Move not yet supported"); case SHUFFLE:
tryBlock();
resetSources();
break;
default: default:
break; break;
} }
@ -208,30 +181,6 @@ public class PlaybackManager {
return false; return false;
} }
/*
* Responds to a SELECT event.
*
* If the player is being blocked, then nothing should happen.
*
* Otherwise:
*
* When the selected item is already loaded, then we simply synchronize and
* start loading some more items.
*
* When the current item has not been fully loaded, then the player will be
* blocked. The sources will be reset and reloaded, to conserve memory.
* */
private void onSelect() {
if (isBlocked) return;
if (isCurrentIndexLoaded()) {
sync();
} else {
tryBlock();
resetSources();
}
}
private void sync() { private void sync() {
final PlayQueueItem currentItem = playQueue.getCurrent(); final PlayQueueItem currentItem = playQueue.getCurrent();
@ -319,13 +268,14 @@ public class PlaybackManager {
if (queueIndex < 0) return; if (queueIndex < 0) return;
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex); final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
if (sourceIndex != -1) { if (sourceIndex == -1) return;
sourceToQueueIndex.remove(sourceIndex); sourceToQueueIndex.remove(sourceIndex);
sources.removeMediaSource(sourceIndex); sources.removeMediaSource(sourceIndex);
// Will be slow on really large arrays, fast enough for typical use case // Will be slow on really large arrays, fast enough for typical use case
for (int i = sourceIndex; i < sourceToQueueIndex.size(); i++) { for (int i = sourceIndex; i < sourceToQueueIndex.size(); i++) {
sourceToQueueIndex.set(i, sourceToQueueIndex.get(i) - 1); sourceToQueueIndex.set(i, sourceToQueueIndex.get(i) - 1);
} }
} }
} }
}

View file

@ -10,14 +10,13 @@ import org.schabi.newpipe.playlist.events.InitEvent;
import org.schabi.newpipe.playlist.events.PlayQueueMessage; import org.schabi.newpipe.playlist.events.PlayQueueMessage;
import org.schabi.newpipe.playlist.events.RemoveEvent; import org.schabi.newpipe.playlist.events.RemoveEvent;
import org.schabi.newpipe.playlist.events.SelectEvent; import org.schabi.newpipe.playlist.events.SelectEvent;
import org.schabi.newpipe.playlist.events.MoveEvent; import org.schabi.newpipe.playlist.events.UpdateEvent;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import io.reactivex.BackpressureStrategy; import io.reactivex.BackpressureStrategy;
@ -96,7 +95,7 @@ public abstract class PlayQueue implements Serializable {
} }
public int indexOf(final PlayQueueItem item) { public int indexOf(final PlayQueueItem item) {
// reference equality, can't think of a better way to do this // referential equality, can't think of a better way to do this
// todo: better than this // todo: better than this
return streams.indexOf(item); return streams.indexOf(item);
} }
@ -134,6 +133,13 @@ public abstract class PlayQueue implements Serializable {
setIndex(getIndex() + offset); setIndex(getIndex() + offset);
} }
public synchronized void updateIndex(final int index, final int selectedQuality) {
if (index < 0 || index >= streams.size()) return;
get(index).setSortedQualityIndex(selectedQuality);
broadcast(new UpdateEvent(index));
}
protected synchronized void append(final PlayQueueItem item) { protected synchronized void append(final PlayQueueItem item) {
streams.add(item); streams.add(item);
broadcast(new AppendEvent(1)); broadcast(new AppendEvent(1));
@ -158,29 +164,6 @@ public abstract class PlayQueue implements Serializable {
broadcast(new RemoveEvent(index, isCurrent)); broadcast(new RemoveEvent(index, isCurrent));
} }
protected synchronized void swap(final int source, final int target) {
if (source < 0 || target < 0) return;
final List<PlayQueueItem> items = streams;
if (source < items.size() && target < items.size()) {
// Swap two items
final PlayQueueItem sourceItem = items.get(source);
final PlayQueueItem targetItem = items.get(target);
items.set(target, sourceItem);
items.set(source, targetItem);
// If the current playing index is one of the swapped indices, change that as well
final int index = queueIndex.get();
if (index == source || index == target) {
final int newIndex = index == source ? target : source;
queueIndex.set(newIndex);
}
broadcast(new MoveEvent(source, target));
}
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Rx Broadcast // Rx Broadcast
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View file

@ -82,10 +82,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
playQueue.remove(index); playQueue.remove(index);
} }
public void swap(final int source, final int target) {
playQueue.swap(source, target);
}
private void startReactor() { private void startReactor() {
final Observer<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() { final Observer<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() {
@Override @Override

View file

@ -1,25 +0,0 @@
package org.schabi.newpipe.playlist.events;
public class MoveEvent implements PlayQueueMessage {
final private int from;
final private int to;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.MOVE;
}
public MoveEvent(final int from, final int to) {
this.from = from;
this.to = to;
}
public int getFrom() {
return from;
}
public int getTo() {
return to;
}
}

View file

@ -3,10 +3,7 @@ package org.schabi.newpipe.playlist.events;
public enum PlayQueueEvent { public enum PlayQueueEvent {
INIT, INIT,
// sent when the user is seamlessly transitioned by exoplayer to the next stream // sent when the index is changed
NEXT,
// sent when the user transitions to an unbuffered period
SELECT, SELECT,
// sent when more streams are added to the play queue // sent when more streams are added to the play queue
@ -16,6 +13,12 @@ public enum PlayQueueEvent {
REMOVE, REMOVE,
// sent when two streams swap place in the play queue // sent when two streams swap place in the play queue
MOVE MOVE,
// sent when a stream is updated
UPDATE,
// send when queue is shuffled
SHUFFLE
} }

View file

@ -0,0 +1,18 @@
package org.schabi.newpipe.playlist.events;
public class UpdateEvent implements PlayQueueMessage {
final private int updatedIndex;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.UPDATE;
}
public UpdateEvent(final int updatedIndex) {
this.updatedIndex = updatedIndex;
}
public int index() {
return updatedIndex;
}
}

View file

@ -71,7 +71,6 @@ public class NavigationHelper {
return new Intent(context, targetClazz) return new Intent(context, targetClazz)
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.PLAYER_INTENT) .putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.PLAYER_INTENT)
.putExtra(VideoPlayer.PLAY_QUEUE, instance.getPlayQueue()) .putExtra(VideoPlayer.PLAY_QUEUE, instance.getPlayQueue())
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, instance.getSelectedStreamIndex())
.putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, instance.getCurrentQueueIndex()) .putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, instance.getCurrentQueueIndex())
.putExtra(BasePlayer.START_POSITION, instance.getPlayer().getCurrentPosition()) .putExtra(BasePlayer.START_POSITION, instance.getPlayer().getCurrentPosition())
.putExtra(BasePlayer.PLAYBACK_SPEED, instance.getPlaybackSpeed()); .putExtra(BasePlayer.PLAYBACK_SPEED, instance.getPlaybackSpeed());