Added a queue system.
This commit is contained in:
469
interface/src/main/java/edu/regis/universeplayer/data/Queue.java
Normal file
469
interface/src/main/java/edu/regis/universeplayer/data/Queue.java
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A song queue contains a list of songs that are to play, along with a way to
|
||||
* order them
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class Queue extends ArrayList<Song>
|
||||
{
|
||||
private static Queue INSTANCE;
|
||||
|
||||
public static Queue getInstance()
|
||||
{
|
||||
if (INSTANCE == null)
|
||||
{
|
||||
INSTANCE = new Queue();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final ArrayList<Song> queueOrder = new ArrayList<>();
|
||||
private int currentIndex;
|
||||
private boolean shuffle;
|
||||
private boolean repeat;
|
||||
private final Random random = new Random();
|
||||
|
||||
private final LinkedList<SongChangeListener> songListeners = new LinkedList<>();
|
||||
private final LinkedList<QueueChangeListener> queueListeners = new LinkedList<>();
|
||||
|
||||
public Queue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the song scheduled to play.
|
||||
*
|
||||
* @return The currently playing song.
|
||||
*/
|
||||
public Song getCurrentSong()
|
||||
{
|
||||
if (this.currentIndex < 0 || this.currentIndex >= this.queueOrder.size())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return this.queueOrder.get(currentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the song index scheduled to play.
|
||||
*
|
||||
* @return The currently playing song index.
|
||||
*/
|
||||
public int getCurrentIndex()
|
||||
{
|
||||
return this.indexOf(this.getCurrentSong());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this queue will loop after the end of the song.
|
||||
*
|
||||
* @return True if the queue loops, false otherwise.
|
||||
*/
|
||||
public boolean isRepeating()
|
||||
{
|
||||
return this.repeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this queue should loop after the last song plays.
|
||||
*
|
||||
* @param repeat - Whether the queue loops.
|
||||
*/
|
||||
public void setRepeat(boolean repeat)
|
||||
{
|
||||
this.repeat = repeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this queue is in shuffle mode.
|
||||
*
|
||||
* @return True if the queue shuffles, false otherwise.
|
||||
*/
|
||||
public boolean isShuffling()
|
||||
{
|
||||
return this.shuffle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this queue should be shuffled or not.
|
||||
*
|
||||
* @param shuffle - Whether or not the queue shuffles.
|
||||
*/
|
||||
public void setShuffle(boolean shuffle)
|
||||
{
|
||||
this.shuffle = shuffle;
|
||||
this.queueOrder.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the next song.
|
||||
*
|
||||
* @return The new song, or null if we finished the list.
|
||||
*/
|
||||
public Song skipNext()
|
||||
{
|
||||
if (++this.currentIndex >= this.queueOrder.size())
|
||||
{
|
||||
if (this.repeat)
|
||||
{
|
||||
this.currentIndex = 0;
|
||||
/*
|
||||
* Reshuffle the queue as needed.
|
||||
*/
|
||||
this.queueOrder.clear();
|
||||
this.getOrder();
|
||||
this.triggerSongChangeListeners();
|
||||
return this.getCurrentSong();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Make sure we aren't infinitely increasing the current index.
|
||||
*/
|
||||
this.currentIndex = this.queueOrder.size() - 1;
|
||||
this.triggerSongChangeListeners();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
this.triggerSongChangeListeners();
|
||||
return this.getCurrentSong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the previous song.
|
||||
*
|
||||
* @return The previous song, or the first one if we go underboard.
|
||||
*/
|
||||
public Song skipPrev()
|
||||
{
|
||||
if (--this.currentIndex < 0)
|
||||
{
|
||||
if (this.repeat)
|
||||
{
|
||||
this.currentIndex = this.queueOrder.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
this.triggerSongChangeListeners();
|
||||
return this.getCurrentSong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the song at the provided song index.
|
||||
*
|
||||
* @param index - The new index. This is not relational to the queue.
|
||||
* @return The new song, or null if we finished the list.
|
||||
*/
|
||||
public Song skipToSong(int index)
|
||||
{
|
||||
Song song = this.get(index);
|
||||
this.currentIndex = this.queueOrder.indexOf(song);
|
||||
this.triggerSongChangeListeners();
|
||||
return this.getCurrentSong();
|
||||
}
|
||||
|
||||
private ArrayList<Song> getOrder()
|
||||
{
|
||||
LinkedList<Song> indexesRemaining;
|
||||
if (this.queueOrder.isEmpty())
|
||||
{
|
||||
this.queueOrder.addAll(this);
|
||||
if (this.shuffle)
|
||||
{
|
||||
indexesRemaining = new LinkedList<>(this.queueOrder);
|
||||
this.queueOrder.clear();
|
||||
while (indexesRemaining.size() > 0)
|
||||
{
|
||||
this.queueOrder.add(indexesRemaining.remove(random.nextInt(indexesRemaining.size())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.queueOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Song song)
|
||||
{
|
||||
int newIndex;
|
||||
if (super.add(song))
|
||||
{
|
||||
if (this.shuffle)
|
||||
{
|
||||
newIndex = random.nextInt(this.queueOrder.size());
|
||||
if (newIndex <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex++;
|
||||
}
|
||||
this.queueOrder.add(newIndex, song);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.queueOrder.add(song);
|
||||
}
|
||||
this.triggerQueueChangeListeners();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, Song song)
|
||||
{
|
||||
int newIndex;
|
||||
super.add(index, song);
|
||||
if (this.shuffle)
|
||||
{
|
||||
newIndex = this.random.nextInt(this.queueOrder.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
newIndex = index;
|
||||
}
|
||||
if (newIndex <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex++;
|
||||
}
|
||||
this.queueOrder.add(newIndex, song);
|
||||
this.triggerQueueChangeListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Song remove(int index)
|
||||
{
|
||||
Song removed = super.remove(index);
|
||||
if (removed != null)
|
||||
{
|
||||
index = this.queueOrder.indexOf(removed);
|
||||
if (index <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex--;
|
||||
}
|
||||
this.queueOrder.remove(removed);
|
||||
this.triggerQueueChangeListeners();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
int index = this.indexOf(o);
|
||||
if (super.remove(o))
|
||||
{
|
||||
if (index <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex--;
|
||||
}
|
||||
this.queueOrder.remove(o);
|
||||
this.triggerQueueChangeListeners();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
super.clear();
|
||||
this.queueOrder.clear();
|
||||
this.currentIndex = 0;
|
||||
this.triggerQueueChangeListeners();
|
||||
this.triggerSongChangeListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Song> c)
|
||||
{
|
||||
int newIndex;
|
||||
int length = this.size();
|
||||
if (super.addAll(c))
|
||||
{
|
||||
for (Song song : c)
|
||||
{
|
||||
if (this.shuffle)
|
||||
{
|
||||
newIndex = random.nextInt(this.queueOrder.size());
|
||||
if (newIndex <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex++;
|
||||
}
|
||||
this.queueOrder.add(newIndex, song);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.queueOrder.add(song);
|
||||
}
|
||||
}
|
||||
this.triggerQueueChangeListeners();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends Song> c)
|
||||
{
|
||||
int newIndex;
|
||||
int i = 0;
|
||||
if (super.addAll(index, c))
|
||||
{
|
||||
for (Song song : c)
|
||||
{
|
||||
if (this.shuffle)
|
||||
{
|
||||
newIndex = random.nextInt(this.queueOrder.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
newIndex = index + i++;
|
||||
}
|
||||
if (newIndex <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex++;
|
||||
}
|
||||
this.queueOrder.add(newIndex, song);
|
||||
}
|
||||
this.triggerQueueChangeListeners();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeRange(int fromIndex, int toIndex)
|
||||
{
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
{
|
||||
this.queueOrder.remove(this.get(i));
|
||||
if (i <= this.currentIndex)
|
||||
{
|
||||
this.currentIndex--;
|
||||
}
|
||||
}
|
||||
super.removeRange(fromIndex, toIndex);
|
||||
this.triggerQueueChangeListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c)
|
||||
{
|
||||
boolean success;
|
||||
for (int i = 0, removed = 0; i <= this.currentIndex && i < this.size(); i++)
|
||||
{
|
||||
if (c.contains(this.get(i)))
|
||||
{
|
||||
this.currentIndex--;
|
||||
}
|
||||
}
|
||||
this.queueOrder.removeAll(c);
|
||||
success = super.removeAll(c);
|
||||
this.triggerQueueChangeListeners();
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c)
|
||||
{
|
||||
boolean success;
|
||||
for (int i = 0, removed = 0; i <= this.currentIndex && i < this.size(); i++)
|
||||
{
|
||||
if (!c.contains(this.get(i)))
|
||||
{
|
||||
this.currentIndex--;
|
||||
}
|
||||
}
|
||||
this.queueOrder.retainAll(c);
|
||||
success = super.retainAll(c);
|
||||
this.triggerQueueChangeListeners();
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the current song changes.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void addSongChangeListener(SongChangeListener listener)
|
||||
{
|
||||
this.songListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for when the current song changes.
|
||||
*
|
||||
* @param listener - The listener to remove.
|
||||
*/
|
||||
public void removeSongChangeListener(SongChangeListener listener)
|
||||
{
|
||||
this.songListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells all listeners that the song changed.
|
||||
*/
|
||||
protected void triggerSongChangeListeners()
|
||||
{
|
||||
this.songListeners.forEach(listener -> listener.onSongChange(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the queue contents changes.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void addQueueChangeListener(QueueChangeListener listener)
|
||||
{
|
||||
this.queueListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for when the queue contents changes.
|
||||
*
|
||||
* @param listener - The listener to remove.
|
||||
*/
|
||||
public void removeQueueChangeListener(QueueChangeListener listener)
|
||||
{
|
||||
this.queueListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells all listeners that the queue changed.
|
||||
*/
|
||||
protected void triggerQueueChangeListeners()
|
||||
{
|
||||
this.queueListeners.forEach(listener -> listener.onQueueChange(this));
|
||||
}
|
||||
|
||||
public interface SongChangeListener extends EventListener
|
||||
{
|
||||
/**
|
||||
* Called when the song of a queue changes.
|
||||
*
|
||||
* @param queue - The queue whose song changed.
|
||||
*/
|
||||
void onSongChange(Queue queue);
|
||||
}
|
||||
|
||||
public interface QueueChangeListener extends EventListener
|
||||
{
|
||||
/**
|
||||
* Called when the queue contents change.
|
||||
*
|
||||
* @param queue - The queue that changed.
|
||||
*/
|
||||
void onQueueChange(Queue queue);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user