-Added debouncing to index change reactor.
-Fixed repeat mode on background notification.
This commit is contained in:
parent
7d7a6f7ccc
commit
eb15c04254
|
@ -311,7 +311,7 @@ public class BackgroundPlayer extends Service {
|
|||
super.onRepeatClicked();
|
||||
|
||||
int opacity = 255;
|
||||
switch (currentRepeatMode) {
|
||||
switch (simpleExoPlayer.getRepeatMode()) {
|
||||
case Player.REPEAT_MODE_OFF:
|
||||
opacity = 77;
|
||||
break;
|
||||
|
@ -338,17 +338,17 @@ public class BackgroundPlayer extends Service {
|
|||
|
||||
@Override
|
||||
public void onFastRewind() {
|
||||
if (isPlayerBuffering()) return;
|
||||
if (!isPlayerReady()) return;
|
||||
|
||||
playQueue.setIndex(playQueue.getIndex() - 1);
|
||||
playQueue.offsetIndex(-1);
|
||||
triggerProgressUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFastForward() {
|
||||
if (isPlayerBuffering()) return;
|
||||
if (!isPlayerReady()) return;
|
||||
|
||||
playQueue.setIndex(playQueue.getIndex() + 1);
|
||||
playQueue.offsetIndex(+1);
|
||||
triggerProgressUpdate();
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,8 @@ import org.schabi.newpipe.R;
|
|||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
|
@ -96,10 +98,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public abstract class BasePlayer implements Player.EventListener,
|
||||
AudioManager.OnAudioFocusChangeListener, MediaSourceManager.PlaybackListener {
|
||||
AudioManager.OnAudioFocusChangeListener, PlaybackListener {
|
||||
// TODO: Check api version for deprecated audio manager methods
|
||||
|
||||
public static final boolean DEBUG = true;
|
||||
public static final boolean DEBUG = false;
|
||||
public static final String TAG = "BasePlayer";
|
||||
|
||||
protected Context context;
|
||||
|
@ -139,7 +141,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
// Playlist
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected MediaSourceManager playbackManager;
|
||||
protected PlaybackManager playbackManager;
|
||||
protected PlayQueue playQueue;
|
||||
|
||||
protected int restoreQueueIndex;
|
||||
|
@ -270,7 +272,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
playQueue = new ExternalPlayQueue(serviceId, nextPageUrl, info, index);
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
playbackManager = new PlaybackManager(this, playQueue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -281,7 +283,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
playQueue = new SinglePlayQueue((StreamInfo) serializable, PlayQueueItem.DEFAULT_QUALITY);
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
playbackManager = new PlaybackManager(this, playQueue);
|
||||
}
|
||||
|
||||
public void initThumbnail() {
|
||||
|
@ -494,9 +496,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
// Repeat
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected int currentRepeatMode = Player.REPEAT_MODE_OFF;
|
||||
|
||||
|
||||
public void onRepeatClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
|
||||
|
||||
|
@ -569,6 +568,10 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
|
||||
break;
|
||||
case Player.STATE_ENDED: // 4
|
||||
if (playQueue.getIndex() < playQueue.size() - 1) {
|
||||
playQueue.offsetIndex(+1);
|
||||
break;
|
||||
}
|
||||
changeState(STATE_COMPLETED);
|
||||
isPrepared = false;
|
||||
break;
|
||||
|
@ -589,9 +592,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
int newIndex = simpleExoPlayer.getCurrentWindowIndex();
|
||||
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
|
||||
|
||||
if (newIndex == playbackManager.getCurrentSourceIndex() + 1) {
|
||||
playbackManager.refresh(newIndex);
|
||||
}
|
||||
playbackManager.refresh(newIndex);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -601,7 +602,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
@Override
|
||||
public void block() {
|
||||
if (simpleExoPlayer == null) return;
|
||||
Log.d(TAG, "Blocking...");
|
||||
if (DEBUG) Log.d(TAG, "Blocking...");
|
||||
|
||||
simpleExoPlayer.stop();
|
||||
|
||||
|
@ -611,7 +612,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
@Override
|
||||
public void unblock() {
|
||||
if (simpleExoPlayer == null) return;
|
||||
Log.d(TAG, "Unblocking...");
|
||||
if (DEBUG) Log.d(TAG, "Unblocking...");
|
||||
|
||||
if (restoreQueueIndex != playQueue.getIndex()) {
|
||||
restoreQueueIndex = playQueue.getIndex();
|
||||
|
@ -626,7 +627,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
@Override
|
||||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
if (simpleExoPlayer == null) return;
|
||||
Log.d(TAG, "Syncing...");
|
||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||
|
||||
videoUrl = info.url;
|
||||
videoThumbnailUrl = info.thumbnail_url;
|
||||
|
@ -635,11 +636,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
initThumbnail();
|
||||
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
|
||||
Log.w(TAG, "Rewinding to correct window");
|
||||
if (DEBUG) Log.w(TAG, "Rewinding to correct window");
|
||||
if (simpleExoPlayer.getCurrentTimeline().getWindowCount() > playbackManager.getCurrentSourceIndex()) {
|
||||
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex());
|
||||
} else {
|
||||
Toast.makeText(context, "Player out of sync", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(context, "Play Queue out of sync", Toast.LENGTH_SHORT).show();
|
||||
simpleExoPlayer.seekToDefaultPosition();
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +650,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
Log.d(TAG, "Shutting down...");
|
||||
if (DEBUG) Log.d(TAG, "Shutting down...");
|
||||
|
||||
playbackManager.dispose();
|
||||
playQueue.dispose();
|
||||
|
@ -898,7 +899,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
return playQueue;
|
||||
}
|
||||
|
||||
public boolean isPlayerBuffering() {
|
||||
return currentState == STATE_BUFFERING;
|
||||
public boolean isPlayerReady() {
|
||||
return currentState == STATE_PLAYING || currentState == STATE_COMPLETED || currentState == STATE_PAUSED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -471,13 +471,13 @@ public class MainVideoPlayer extends Activity {
|
|||
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.isPlayerBuffering()) return false;
|
||||
if (!playerImpl.isPlayerReady()) return false;
|
||||
|
||||
if (e.getX() > playerImpl.getRootView().getWidth() / 2)
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);
|
||||
playerImpl.playQueue.offsetIndex(+1);
|
||||
//playerImpl.onFastForward();
|
||||
else
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1);
|
||||
playerImpl.playQueue.offsetIndex(-1);
|
||||
//playerImpl.onFastRewind();
|
||||
|
||||
return true;
|
||||
|
|
|
@ -580,14 +580,14 @@ public class PopupVideoPlayer extends Service {
|
|||
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() || playerImpl.isPlayerBuffering()) return false;
|
||||
if (!playerImpl.isPlaying() || !playerImpl.isPlayerReady()) return false;
|
||||
|
||||
if (e.getX() > popupWidth / 2) {
|
||||
//playerImpl.onFastForward();
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);
|
||||
playerImpl.playQueue.offsetIndex(+1);
|
||||
} else {
|
||||
//playerImpl.onFastRewind();
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1);
|
||||
playerImpl.playQueue.offsetIndex(-1);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.schabi.newpipe.extractor.MediaFormat;
|
|||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
|
@ -66,7 +67,6 @@ import org.schabi.newpipe.util.ListHelper;
|
|||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
|
@ -226,7 +226,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
playQueue = new SinglePlayQueue((StreamInfo) serializable, sortedStreamsIndex);
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
playbackManager = new PlaybackManager(this, playQueue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -239,7 +239,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
playQueue = (PlayQueue) serializable;
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
playbackManager = new PlaybackManager(this, playQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.schabi.newpipe.player.playback;
|
||||
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
|
||||
public interface PlaybackListener {
|
||||
/*
|
||||
* Called when the stream at the current queue index is not ready yet.
|
||||
* Signals to the listener to block the player from playing anything.
|
||||
* */
|
||||
void block();
|
||||
|
||||
/*
|
||||
* Called when the stream at the current queue index is ready.
|
||||
* Signals to the listener to resume the player.
|
||||
* May be called at any time, even when the player is unblocked.
|
||||
* */
|
||||
void unblock();
|
||||
|
||||
/*
|
||||
* Called when the queue index is refreshed.
|
||||
* Signals to the listener to synchronize the player's window to the manager's
|
||||
* window.
|
||||
*
|
||||
* CAN ONLY BE CALLED ONCE UNBLOCKED!
|
||||
* */
|
||||
void sync(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
/*
|
||||
* Requests the listener to resolve a stream info into a media source respective
|
||||
* of the listener's implementation (background, popup or main video player),
|
||||
* */
|
||||
MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
void shutdown();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.schabi.newpipe.player;
|
||||
package org.schabi.newpipe.player.playback;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
|
@ -25,11 +25,11 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
class MediaSourceManager {
|
||||
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
||||
public class PlaybackManager {
|
||||
private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode());
|
||||
// One-side rolling window size for default loading
|
||||
// Effectively loads WINDOW_SIZE * 2 streams
|
||||
private static final int WINDOW_SIZE = 3;
|
||||
private static final int WINDOW_SIZE = 2;
|
||||
|
||||
private final PlaybackListener playbackListener;
|
||||
private final PlayQueue playQueue;
|
||||
|
@ -41,43 +41,13 @@ class MediaSourceManager {
|
|||
private List<Integer> sourceToQueueIndex;
|
||||
|
||||
private Subscription playQueueReactor;
|
||||
private Subscription loadingReactor;
|
||||
private Disposable syncReactor;
|
||||
private CompositeDisposable disposables;
|
||||
|
||||
private boolean isBlocked;
|
||||
|
||||
interface PlaybackListener {
|
||||
/*
|
||||
* Called when the stream at the current queue index is not ready yet.
|
||||
* Signals to the listener to block the player from playing anything.
|
||||
* */
|
||||
void block();
|
||||
|
||||
/*
|
||||
* Called when the stream at the current queue index is ready.
|
||||
* Signals to the listener to resume the player.
|
||||
* May be called at any time, even when the player is unblocked.
|
||||
* */
|
||||
void unblock();
|
||||
|
||||
/*
|
||||
* Called when the queue index is refreshed.
|
||||
* Signals to the listener to synchronize the player's window to the manager's
|
||||
* window.
|
||||
* */
|
||||
void sync(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
/*
|
||||
* Requests the listener to resolve a stream info into a media source respective
|
||||
* of the listener's implementation (background, popup or main video player),
|
||||
* */
|
||||
MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
|
||||
MediaSourceManager(@NonNull final MediaSourceManager.PlaybackListener listener,
|
||||
@NonNull final PlayQueue playQueue) {
|
||||
public PlaybackManager(@NonNull final PlaybackListener listener,
|
||||
@NonNull final PlayQueue playQueue) {
|
||||
this.playbackListener = listener;
|
||||
this.playQueue = playQueue;
|
||||
|
||||
|
@ -98,25 +68,28 @@ class MediaSourceManager {
|
|||
/*
|
||||
* Returns the media source index of the currently playing stream.
|
||||
* */
|
||||
int getCurrentSourceIndex() {
|
||||
public int getCurrentSourceIndex() {
|
||||
return sourceToQueueIndex.indexOf(playQueue.getIndex());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
DynamicConcatenatingMediaSource getMediaSource() {
|
||||
public DynamicConcatenatingMediaSource getMediaSource() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the player has transitioned to another stream.
|
||||
* */
|
||||
void refresh(final int newSourceIndex) {
|
||||
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1) {
|
||||
playQueue.setIndex(sourceToQueueIndex.indexOf(newSourceIndex));
|
||||
public void refresh(final int newSourceIndex) {
|
||||
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1 && newSourceIndex == getCurrentSourceIndex() + 1) {
|
||||
playQueue.offsetIndex(+1);
|
||||
|
||||
// free up some memory
|
||||
if (sourceToQueueIndex.size() > 1) remove(sourceToQueueIndex.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
void report(final Exception error) {
|
||||
public void report(final Exception error) {
|
||||
// ignore error checking for now, just remove the current index
|
||||
if (error == null || !tryBlock()) return;
|
||||
|
||||
|
@ -127,27 +100,28 @@ class MediaSourceManager {
|
|||
load();
|
||||
}
|
||||
|
||||
int queueIndexOf(final int sourceIndex) {
|
||||
return sourceIndex < sourceToQueueIndex.size() ? sourceToQueueIndex.get(sourceIndex) : -1;
|
||||
}
|
||||
|
||||
void updateCurrent(final int newSortedStreamsIndex) {
|
||||
public void updateCurrent(final int newSortedStreamsIndex) {
|
||||
if (!tryBlock()) return;
|
||||
|
||||
PlayQueueItem item = playQueue.getCurrent();
|
||||
item.setSortedQualityIndex(newSortedStreamsIndex);
|
||||
|
||||
resetSources();
|
||||
load();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (loadingReactor != null) loadingReactor.cancel();
|
||||
public void dispose() {
|
||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||
if (disposables != null) disposables.dispose();
|
||||
if (syncReactor != null) syncReactor.dispose();
|
||||
if (sources != null) sources.releaseSource();
|
||||
if (sourceToQueueIndex != null) sourceToQueueIndex.clear();
|
||||
|
||||
loadingReactor = null;
|
||||
playQueueReactor = null;
|
||||
disposables = null;
|
||||
syncReactor = null;
|
||||
sources = null;
|
||||
sourceToQueueIndex = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -182,8 +156,8 @@ class MediaSourceManager {
|
|||
case SWAP:
|
||||
final SwapEvent swapEvent = (SwapEvent) event;
|
||||
swap(swapEvent.getFrom(), swapEvent.getTo());
|
||||
load();
|
||||
break;
|
||||
case NEXT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -240,14 +214,18 @@ class MediaSourceManager {
|
|||
|
||||
/*
|
||||
* Responds to a SELECT event.
|
||||
* When a change occur, the manager prepares by loading more.
|
||||
* If the current item has not been fully loaded,
|
||||
* If the selected item is already loaded, then we simply synchronize and
|
||||
* start loading some more items.
|
||||
*
|
||||
* If 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 (isCurrentIndexLoaded()) {
|
||||
if (isCurrentIndexLoaded() && !isBlocked) {
|
||||
sync();
|
||||
} else {
|
||||
tryBlock();
|
||||
resetSources();
|
||||
}
|
||||
|
||||
load();
|
||||
|
@ -256,14 +234,14 @@ class MediaSourceManager {
|
|||
private void sync() {
|
||||
final PlayQueueItem currentItem = playQueue.getCurrent();
|
||||
|
||||
final Consumer<StreamInfo> onSuccess = new Consumer<StreamInfo>() {
|
||||
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
|
||||
@Override
|
||||
public void accept(StreamInfo streamInfo) throws Exception {
|
||||
playbackListener.sync(streamInfo, currentItem.getSortedQualityIndex());
|
||||
}
|
||||
};
|
||||
|
||||
currentItem.getStream().subscribe(onSuccess);
|
||||
currentItem.getStream().subscribe(syncPlayback);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
|
@ -287,11 +265,13 @@ class MediaSourceManager {
|
|||
item.getStream().subscribe(new SingleObserver<StreamInfo>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (disposables != null) {
|
||||
disposables.add(d);
|
||||
} else {
|
||||
if (disposables == null) {
|
||||
d.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposables.size() > 8) disposables.clear();
|
||||
disposables.add(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -321,16 +301,6 @@ class MediaSourceManager {
|
|||
// Media Source List Manipulation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void reset(final int queueIndex) {
|
||||
if (queueIndex < 0) return;
|
||||
|
||||
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
|
||||
if (sourceIndex != -1) {
|
||||
sourceToQueueIndex.remove(sourceIndex);
|
||||
sources.removeMediaSource(sourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert source into playlist with position in respect to the play queue
|
||||
// If the play queue index already exists, then the insert is ignored
|
||||
private void insert(final int queueIndex, final MediaSource source) {
|
|
@ -5,12 +5,8 @@ import android.util.Log;
|
|||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.playlist.events.AppendEvent;
|
||||
import org.schabi.newpipe.playlist.events.InitEvent;
|
||||
import org.schabi.newpipe.playlist.events.NextEvent;
|
||||
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
|
@ -21,6 +17,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.reactivex.BackpressureStrategy;
|
||||
|
@ -29,12 +26,15 @@ import io.reactivex.subjects.BehaviorSubject;
|
|||
|
||||
public abstract class PlayQueue implements Serializable {
|
||||
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
|
||||
public static final boolean DEBUG = true;
|
||||
private final int INDEX_CHANGE_DEBOUNCE = 350;
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
|
||||
private final ArrayList<PlayQueueItem> streams;
|
||||
private final AtomicInteger queueIndex;
|
||||
|
||||
private transient BehaviorSubject<PlayQueueMessage> eventBroadcast;
|
||||
private transient BehaviorSubject<PlayQueueMessage> streamsEventBroadcast;
|
||||
private transient BehaviorSubject<PlayQueueMessage> indexEventBroadcast;
|
||||
private transient Flowable<PlayQueueMessage> broadcastReceiver;
|
||||
private transient Subscription reportingReactor;
|
||||
|
||||
|
@ -54,16 +54,19 @@ public abstract class PlayQueue implements Serializable {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void init() {
|
||||
eventBroadcast = BehaviorSubject.create();
|
||||
broadcastReceiver = eventBroadcast
|
||||
.startWith(new InitEvent())
|
||||
.toFlowable(BackpressureStrategy.BUFFER);
|
||||
streamsEventBroadcast = BehaviorSubject.create();
|
||||
indexEventBroadcast = BehaviorSubject.create();
|
||||
|
||||
broadcastReceiver = Flowable.merge(
|
||||
streamsEventBroadcast.toFlowable(BackpressureStrategy.BUFFER),
|
||||
indexEventBroadcast.toFlowable(BackpressureStrategy.BUFFER).debounce(INDEX_CHANGE_DEBOUNCE, TimeUnit.MILLISECONDS)
|
||||
).startWith(new InitEvent());
|
||||
|
||||
if (DEBUG) broadcastReceiver.subscribe(getSelfReporter());
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
eventBroadcast.onComplete();
|
||||
streamsEventBroadcast.onComplete();
|
||||
|
||||
if (reportingReactor != null) reportingReactor.cancel();
|
||||
reportingReactor = null;
|
||||
|
@ -121,9 +124,15 @@ public abstract class PlayQueue implements Serializable {
|
|||
// Write ops
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public synchronized void setIndex(final int index) {
|
||||
private synchronized void setIndex(final int index) {
|
||||
if (index < 0 || index >= streams.size()) return;
|
||||
|
||||
queueIndex.set(Math.min(Math.max(0, index), streams.size() - 1));
|
||||
broadcast(new SelectEvent(index));
|
||||
indexEventBroadcast.onNext(new SelectEvent(index));
|
||||
}
|
||||
|
||||
public synchronized void offsetIndex(final int offset) {
|
||||
setIndex(getIndex() + offset);
|
||||
}
|
||||
|
||||
protected synchronized void append(final PlayQueueItem item) {
|
||||
|
@ -137,7 +146,9 @@ public abstract class PlayQueue implements Serializable {
|
|||
}
|
||||
|
||||
public synchronized void remove(final int index) {
|
||||
if (index >= streams.size()) return;
|
||||
if (index >= streams.size() || index < 0) return;
|
||||
|
||||
final boolean isCurrent = index == getIndex();
|
||||
|
||||
streams.remove(index);
|
||||
// Nudge the index if it becomes larger than the queue size
|
||||
|
@ -145,10 +156,12 @@ public abstract class PlayQueue implements Serializable {
|
|||
queueIndex.set(size() - 1);
|
||||
}
|
||||
|
||||
broadcast(new RemoveEvent(index));
|
||||
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
|
||||
|
@ -174,7 +187,7 @@ public abstract class PlayQueue implements Serializable {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void broadcast(final PlayQueueMessage event) {
|
||||
eventBroadcast.onNext(event);
|
||||
streamsEventBroadcast.onNext(event);
|
||||
}
|
||||
|
||||
private Subscriber<PlayQueueMessage> getSelfReporter() {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package org.schabi.newpipe.playlist.events;
|
||||
|
||||
|
||||
public class NextEvent implements PlayQueueMessage {
|
||||
final private int newIndex;
|
||||
|
||||
@Override
|
||||
public PlayQueueEvent type() {
|
||||
return PlayQueueEvent.NEXT;
|
||||
}
|
||||
|
||||
public NextEvent(final int newIndex) {
|
||||
this.newIndex = newIndex;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return newIndex;
|
||||
}
|
||||
}
|
|
@ -3,17 +3,23 @@ package org.schabi.newpipe.playlist.events;
|
|||
|
||||
public class RemoveEvent implements PlayQueueMessage {
|
||||
final private int index;
|
||||
final private boolean isCurrent;
|
||||
|
||||
@Override
|
||||
public PlayQueueEvent type() {
|
||||
return PlayQueueEvent.REMOVE;
|
||||
}
|
||||
|
||||
public RemoveEvent(final int index) {
|
||||
public RemoveEvent(final int index, final boolean isCurrent) {
|
||||
this.index = index;
|
||||
this.isCurrent = isCurrent;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return isCurrent;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue