diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java index cfa2ab316..ed8ca5a73 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.BackpressureStrategy; @@ -41,21 +42,27 @@ public abstract class PlayQueue implements Serializable { public static final boolean DEBUG = MainActivity.DEBUG; @NonNull private final AtomicInteger queueIndex; - private final List history = new ArrayList<>(); + private final List history; - private List backup; + // volatile is needed for the isShuffled method + private volatile List backup; private List streams; private transient BehaviorSubject eventBroadcast; - private transient Flowable broadcastReceiver; - private transient boolean disposed = false; + private transient volatile Flowable broadcastReceiver; + + // volatile is needed for the isDisposed method + private transient volatile boolean disposed = false; PlayQueue(final int index, final List startWith) { - streams = new ArrayList<>(startWith); - if (streams.size() > index) { - history.add(streams.get(index)); + List h = new ArrayList<>(); + if (startWith.size() > index) { + h.add(startWith.get(index)); } + history = h; + + streams = new ArrayList<>(startWith); queueIndex = new AtomicInteger(index); } @@ -70,18 +77,19 @@ public abstract class PlayQueue implements Serializable { * Also starts a self reporter for logging if debug mode is enabled. *

*/ - public void init() { - eventBroadcast = BehaviorSubject.create(); + public synchronized void init() { // todo: cas mechanics + BehaviorSubject b = BehaviorSubject.create(); - broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER) + broadcastReceiver = b.toFlowable(BackpressureStrategy.BUFFER) .observeOn(AndroidSchedulers.mainThread()) .startWithItem(new InitEvent()); + eventBroadcast = b; } /** * Dispose the play queue by stopping all message buses. */ - public void dispose() { + public synchronized void dispose() { // todo: cas mechanics if (eventBroadcast != null) { eventBroadcast.onComplete(); } @@ -169,7 +177,7 @@ public abstract class PlayQueue implements Serializable { * @return the current item that should be played, or null if the queue is empty */ @Nullable - public PlayQueueItem getItem() { + public synchronized PlayQueueItem getItem() { return getItem(getIndex()); } @@ -178,7 +186,7 @@ public abstract class PlayQueue implements Serializable { * @return the item at the given index, or null if the index is out of bounds */ @Nullable - public PlayQueueItem getItem(final int index) { + public synchronized PlayQueueItem getItem(final int index) { if (index < 0 || index >= streams.size()) { return null; } @@ -192,14 +200,14 @@ public abstract class PlayQueue implements Serializable { * @param item the item to find the index of * @return the index of the given item */ - public int indexOf(@NonNull final PlayQueueItem item) { + public synchronized int indexOf(@NonNull final PlayQueueItem item) { return streams.indexOf(item); } /** * @return the current size of play queue. */ - public int size() { + public synchronized int size() { return streams.size(); } @@ -208,7 +216,7 @@ public abstract class PlayQueue implements Serializable { * * @return whether the play queue is empty */ - public boolean isEmpty() { + public synchronized boolean isEmpty() { return streams.isEmpty(); } @@ -225,7 +233,7 @@ public abstract class PlayQueue implements Serializable { * @return an immutable view of the play queue */ @NonNull - public List getStreams() { + public synchronized List getStreams() { // todo: iterator race return Collections.unmodifiableList(streams); } @@ -522,25 +530,29 @@ public abstract class PlayQueue implements Serializable { if (other == null) { return false; } - if (size() != other.size()) { - return false; - } - for (int i = 0; i < size(); i++) { - final PlayQueueItem stream = streams.get(i); - final PlayQueueItem otherStream = other.streams.get(i); - // Check is based on serviceId and URL - if (stream.getServiceId() != otherStream.getServiceId() - || !stream.getUrl().equals(otherStream.getUrl())) { + synchronized (this) { + if (size() != other.size()) { return false; } + for (int i = 0; i < size(); i++) { + final PlayQueueItem stream = streams.get(i); + final PlayQueueItem otherStream = other.streams.get(i); + // Check is based on serviceId and URL + if (stream.getServiceId() != otherStream.getServiceId() + || !stream.getUrl().equals(otherStream.getUrl())) { + return false; + } + } + return true; } - return true; } public boolean equalStreamsAndIndex(@Nullable final PlayQueue other) { if (equalStreams(other)) { - //noinspection ConstantConditions - return other.getIndex() == getIndex(); //NOSONAR: other is not null + synchronized (this) { + //noinspection ConstantConditions + return other.getIndex() == getIndex(); //NOSONAR: other is not null + } } return false; }