Improves error handling (foreground still needs testing).

This commit is contained in:
Markil3
2021-09-14 11:21:02 -06:00
parent 3eab100a08
commit 849771853c
11 changed files with 623 additions and 181 deletions

View File

@@ -125,6 +125,18 @@ var listeners = [function (message, returnValue) {
case "CommandQuit": case "CommandQuit":
returnValue = quit(); returnValue = quit();
break; break;
case "CommandError":
console.error("Error message", message);
if (message.forward)
{
returnValue = queryTab(message);
}
else
{
throw new TypeError("This is a background test");
returnValue = false;
}
break;
} }
} }
else if (message == "quit") else if (message == "quit")
@@ -202,7 +214,19 @@ interfacePort.onMessage.addListener((message) => {
console.log("Sending ", returnValue) console.log("Sending ", returnValue)
interfacePort.postMessage(returnValue); interfacePort.postMessage(returnValue);
}).catch(error => { }).catch(error => {
console.error("Error in evaluating message: ", error) // console.error("Error in evaluating message: ", error);
interfacePort.postMessage({
"messageNum": message.messageNum,
"message": {
"type": "edu.regis.universeplayer.browserCommands.CommandReturn",
"returnValue": null,
"confirmation": {
type: "edu.regis.universeplayer.browserCommands.CommandConfirmation",
message: "Error in executing request",
errorCode: JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)))
}
}
});
}); });
}); });

View File

@@ -30,6 +30,9 @@ function handleMessage(message)
return false; return false;
case "CommandSeek": case "CommandSeek":
return seek(message.time); return seek(message.time);
case "CommandError":
throw new TypeError("This is a foreground test");
return false;
} }
} }
} }

View File

@@ -5,6 +5,8 @@
package edu.regis.universeplayer.addon; package edu.regis.universeplayer.addon;
import com.google.gson.*; import com.google.gson.*;
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
import edu.regis.universeplayer.browserCommands.MessageRunner; import edu.regis.universeplayer.browserCommands.MessageRunner;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -266,4 +268,10 @@ public class BrowserLink extends MessageRunner
} }
return returnValue; return returnValue;
} }
@Override
public Object getErrorObject(Throwable e)
{
return new CommandConfirmation(e);
}
} }

View File

@@ -21,7 +21,7 @@ public class ThrowableSerializer implements JsonSerializer<Throwable>, JsonDeser
{ {
stack.add(context.serialize(trace)); stack.add(context.serialize(trace));
} }
ob.add("trace", stack); ob.add("stack", stack);
JsonArray suppressed = new JsonArray(); JsonArray suppressed = new JsonArray();
for (Throwable throwable: src.getSuppressed()) for (Throwable throwable: src.getSuppressed())
{ {
@@ -35,19 +35,37 @@ public class ThrowableSerializer implements JsonSerializer<Throwable>, JsonDeser
public Throwable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException public Throwable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{ {
JsonObject ob = json.getAsJsonObject(); JsonObject ob = json.getAsJsonObject();
Throwable throwable = new Throwable(ob.get("message").getAsString(), context.deserialize(ob.get("cause"), typeOfT)); Throwable cause = context.deserialize(ob.get("cause"), typeOfT);
throwable.initCause(context.deserialize(ob.get("cause"), Throwable.class)); Throwable throwable;
JsonArray traceJson = ob.getAsJsonArray("trace"); if (cause == null)
StackTraceElement[] trace = new StackTraceElement[traceJson.size()];
for (int i = 0; i < trace.length; i++)
{ {
trace[i] = context.deserialize(traceJson.get(i), StackTraceElement.class); throwable = new Throwable(ob.get("message").getAsString());
} }
throwable.setStackTrace(trace); else
JsonArray suppressed = ob.getAsJsonArray("suppressed");
for (JsonElement el: suppressed)
{ {
throwable.addSuppressed(context.deserialize(el, Throwable.class)); throwable = new Throwable(ob.get("message").getAsString(), context
.deserialize(ob.get("cause"), typeOfT));
throwable.initCause(context.deserialize(ob.get("cause"), Throwable.class));
}
JsonArray traceJson = ob.getAsJsonArray("stack");
if (traceJson != null)
{
StackTraceElement[] trace = new StackTraceElement[traceJson.size()];
for (int i = 0; i < trace.length; i++)
{
trace[i] = context
.deserialize(traceJson.get(i), StackTraceElement.class);
}
throwable.setStackTrace(trace);
}
JsonArray suppressed = ob.getAsJsonArray("suppressed");
if (suppressed != null)
{
for (JsonElement el : suppressed)
{
throwable.addSuppressed(context
.deserialize(el, Throwable.class));
}
} }
return throwable; return throwable;
} }

View File

@@ -0,0 +1,44 @@
package edu.regis.universeplayer.browserCommands;
/**
* A debugging command that instructs the browser to throw an error.
*/
public class CommandError implements BrowserCommand
{
private boolean forward;
/**
* Creates an error command.
*
* @param forward - Determines where the error should be thrown. If true, it
* will be thrown from the website tab. If false, it will be
* thrown by the addon background script.
*/
public CommandError(boolean forward)
{
this.forward = forward;
}
/**
* Checks whether the error will be thrown from the foreground or background
* script.
*
* @return True if the error will be thrown from the foreground script,
* false if it will be thrown from the background.
*/
public boolean isForeground()
{
return this.forward;
}
/**
* Obtains the name of the command.
*
* @return The command name.
*/
@Override
public String getCommandName()
{
return "error";
}
}

View File

@@ -21,17 +21,17 @@ import java.util.concurrent.Future;
public class MessageHandler implements Runnable, MessageSerializer public class MessageHandler implements Runnable, MessageSerializer
{ {
private final Logger logger; private final Logger logger;
public final String name; public final String name;
private final InputStream input; private final InputStream input;
private final OutputStream output; private final OutputStream output;
private final ExecutorService executor; private final ExecutorService executor;
private final LinkedList<MessageListener> listeners = new LinkedList<>(); private final LinkedList<MessageListener> listeners = new LinkedList<>();
protected final HashMap<Integer, Future<Object>> messageResponses = new HashMap<>(); protected final HashMap<Integer, Future<Object>> messageResponses = new HashMap<>();
protected final Queue<Object> updates = new LinkedList<>(); protected final Queue<Object> updates = new LinkedList<>();
/** /**
* Creates a message handler. * Creates a message handler.
* *
@@ -47,13 +47,13 @@ public class MessageHandler implements Runnable, MessageSerializer
this.output = output; this.output = output;
this.executor = Executors.newCachedThreadPool(); this.executor = Executors.newCachedThreadPool();
} }
@Override @Override
public Logger getLogger() public Logger getLogger()
{ {
return logger; return logger;
} }
/** /**
* Called at the beginning of every loop to do extra processing and check to see if we can still run. * Called at the beginning of every loop to do extra processing and check to see if we can still run.
* *
@@ -63,13 +63,13 @@ public class MessageHandler implements Runnable, MessageSerializer
{ {
return false; return false;
} }
@Override @Override
public void run() public void run()
{ {
BufferedInputStream browserIn = null; BufferedInputStream browserIn = null;
BufferedOutputStream browserOut = null; BufferedOutputStream browserOut = null;
byte[][] message; byte[][] message;
byte[] messageByte; byte[] messageByte;
Object messageOb; Object messageOb;
@@ -77,12 +77,12 @@ public class MessageHandler implements Runnable, MessageSerializer
ByteBuffer numBuffer = ByteBuffer.allocate(4); ByteBuffer numBuffer = ByteBuffer.allocate(4);
HashSet<Integer> toRemove = new HashSet<>(); HashSet<Integer> toRemove = new HashSet<>();
boolean running = true; boolean running = true;
try try
{ {
browserIn = new BufferedInputStream(this.input); browserIn = new BufferedInputStream(this.input);
browserOut = new BufferedOutputStream(this.output); browserOut = new BufferedOutputStream(this.output);
while (!this.onRun() && running) while (!this.onRun() && running)
{ {
try try
@@ -99,22 +99,32 @@ public class MessageHandler implements Runnable, MessageSerializer
{ {
numBuffer.clear(); numBuffer.clear();
numBuffer.put(message[0]); numBuffer.put(message[0]);
messageOb = this.deserializeObject(message[1]); try
messageResponse = this.triggerListeners(messageOb); {
messageOb = this.deserializeObject(message[1]);
}
catch (IOException | ClassNotFoundException e)
{
logger.error("Could not read message", e);
messageOb = this.getErrorObject(e);
}
messageResponse = this
.triggerListeners(messageOb);
synchronized (this.messageResponses) synchronized (this.messageResponses)
{ {
numBuffer.clear(); numBuffer.clear();
this.messageResponses.put(numBuffer.getInt(), messageResponse); this.messageResponses.put(numBuffer
.getInt(), messageResponse);
} }
} }
} }
} }
catch (IOException | ClassNotFoundException e) catch (IOException e)
{ {
logger.error("Could not retrieve message", e); logger.error("Could not retrieve message", e);
running = false; running = false;
} }
/* /*
* Returns any finished responses. * Returns any finished responses.
*/ */
@@ -143,7 +153,7 @@ public class MessageHandler implements Runnable, MessageSerializer
toRemove.clear(); toRemove.clear();
} }
} }
/* /*
* Send in any updates * Send in any updates
*/ */
@@ -218,14 +228,14 @@ public class MessageHandler implements Runnable, MessageSerializer
} }
} }
} }
/** /**
* Callback for when closing the application. * Callback for when closing the application.
*/ */
protected void onClose() protected void onClose()
{ {
} }
/** /**
* Triggers the message listeners. * Triggers the message listeners.
* *
@@ -254,7 +264,7 @@ public class MessageHandler implements Runnable, MessageSerializer
}); });
return returnVal; return returnVal;
} }
/** /**
* Adds a message listener * Adds a message listener
* *
@@ -268,7 +278,7 @@ public class MessageHandler implements Runnable, MessageSerializer
this.listeners.add(listener); this.listeners.add(listener);
} }
} }
/** /**
* Sends an object through the handler as an update not associated with any message. * Sends an object through the handler as an update not associated with any message.
* *
@@ -281,7 +291,7 @@ public class MessageHandler implements Runnable, MessageSerializer
this.updates.add(object); this.updates.add(object);
} }
} }
/** /**
* Checks to see if a listener has been added to the list. * Checks to see if a listener has been added to the list.
* *
@@ -295,7 +305,7 @@ public class MessageHandler implements Runnable, MessageSerializer
return this.listeners.contains(listener); return this.listeners.contains(listener);
} }
} }
/** /**
* Removes a message listener from the list. * Removes a message listener from the list.
* *
@@ -308,7 +318,7 @@ public class MessageHandler implements Runnable, MessageSerializer
this.listeners.remove(listener); this.listeners.remove(listener);
} }
} }
/** /**
* A message listener is called when a message is sent from the remote. * A message listener is called when a message is sent from the remote.
* *

View File

@@ -12,7 +12,7 @@ import java.nio.ByteBuffer;
public interface MessageSerializer public interface MessageSerializer
{ {
Logger getLogger(); Logger getLogger();
/** /**
* Converts an object into a form that can be sent. * Converts an object into a form that can be sent.
* *
@@ -31,13 +31,14 @@ public interface MessageSerializer
return byteStream.toByteArray(); return byteStream.toByteArray();
} }
} }
/** /**
* Converts a byte stream into an object. * Converts a byte stream into an object.
* *
* @param message - The message received. * @param message - The message received.
* @return An object. * @return An object.
* @throws IOException If there is an error in parsing the message. * @throws IOException If there is an error in parsing the
* message.
* @throws ClassNotFoundException If the object is not recognized * @throws ClassNotFoundException If the object is not recognized
*/ */
default Object deserializeObject(byte[] message) throws IOException, ClassNotFoundException default Object deserializeObject(byte[] message) throws IOException, ClassNotFoundException
@@ -50,12 +51,13 @@ public interface MessageSerializer
} }
} }
} }
/** /**
* Writes a message to the output stream. * Writes a message to the output stream.
* *
* @param out - The output stream to write to. * @param out - The output stream to write to.
* @param messageNum - The ID of the message being sent. This will help keep track of responses. * @param messageNum - The ID of the message being sent. This will help keep
* track of responses.
* @param message - The actual message contents to write. * @param message - The actual message contents to write.
* @throws IOException Thrown when an exception occures * @throws IOException Thrown when an exception occures
*/ */
@@ -86,7 +88,7 @@ public interface MessageSerializer
out.write(message); out.write(message);
out.flush(); out.flush();
} }
/** /**
* Reads a message from the input stream * Reads a message from the input stream
* *
@@ -119,7 +121,8 @@ public interface MessageSerializer
readLength = in.read(lengthBuffer.array()); readLength = in.read(lengthBuffer.array());
if (readLength == 0) if (readLength == 0)
{ {
getLogger().error("Malformed message, could not get message length."); getLogger()
.error("Malformed message, could not get message length.");
return null; return null;
} }
message = new byte[lengthBuffer.getInt()]; message = new byte[lengthBuffer.getInt()];
@@ -129,9 +132,21 @@ public interface MessageSerializer
readLength = in.read(message); readLength = in.read(message);
if (readLength < message.length) if (readLength < message.length)
{ {
getLogger().warn("Message shorter than reported (expected {} bytes, got {} bytes)", message.length, readLength); getLogger()
.warn("Message shorter than reported (expected {} bytes, got {} bytes)", message.length, readLength);
} }
getLogger().trace("Reading message"); getLogger().trace("Reading message");
return new byte[][] {messageNum, message}; return new byte[][]{messageNum, message};
}
/**
* This method is called when an error in deserialization occurs.
*
* @param e - The error thrown.
* @return An object to return to listeners in the event of a read error.
*/
default Object getErrorObject(Throwable e)
{
return e;
} }
} }

View File

@@ -12,6 +12,7 @@ import edu.regis.universeplayer.browserCommands.*;
import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.InternetSong;
import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.PlaybackEvent;
import edu.regis.universeplayer.data.Song; import edu.regis.universeplayer.data.Song;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -28,14 +29,15 @@ import java.util.concurrent.*;
*/ */
public class BrowserPlayer implements Player<InternetSong>, UpdateListener public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
private static final Logger logger = LoggerFactory.getLogger(BrowserPlayer.class); private static final Logger logger = LoggerFactory
.getLogger(BrowserPlayer.class);
private final LinkedList<PlaybackListener> listeners = new LinkedList<>(); private final LinkedList<PlaybackListener> listeners = new LinkedList<>();
private InternetSong currentSong; private InternetSong currentSong;
private Browser browserRef = null; private Browser browserRef = null;
private Browser getBrowser() private Browser getBrowser()
{ {
if (browserRef == null) if (browserRef == null)
@@ -56,19 +58,20 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
} }
return browserRef; return browserRef;
} }
@Override @Override
public Song getCurrentSong() public Song getCurrentSong()
{ {
return this.currentSong; return this.currentSong;
} }
@Override @Override
public QueryFuture<Void> loadSong(InternetSong song) public QueryFuture<Void> loadSong(InternetSong song)
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandLoadSong(song.location))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandLoadSong(song.location)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -76,7 +79,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
/** /**
* Tells the browser process to shut down. * Tells the browser process to shut down.
*/ */
@@ -85,7 +88,8 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
try try
{ {
QueryFuture<Void> future = new ForwardedFuture(getBrowser().sendObject(new CommandQuit())); QueryFuture<Void> future = new ForwardedFuture(getBrowser()
.sendObject(new CommandQuit()));
return future; return future;
} }
catch (IOException e) catch (IOException e)
@@ -94,7 +98,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
/** /**
* Adds a listener for playback status updates. * Adds a listener for playback status updates.
* *
@@ -105,7 +109,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
this.listeners.add(listener); this.listeners.add(listener);
} }
/** /**
* Checks to see if a listener has been added. * Checks to see if a listener has been added.
* *
@@ -117,7 +121,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
return this.listeners.contains(listener); return this.listeners.contains(listener);
} }
/** /**
* Removes a listener for playback status updates. * Removes a listener for playback status updates.
* *
@@ -128,13 +132,14 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
this.listeners.remove(listener); this.listeners.remove(listener);
} }
@Override @Override
public QueryFuture<Void> play() public QueryFuture<Void> play()
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -142,13 +147,14 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
@Override @Override
public QueryFuture<Void> pause() public QueryFuture<Void> pause()
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -156,13 +162,14 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
@Override @Override
public QueryFuture<Void> togglePlayback() public QueryFuture<Void> togglePlayback()
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -170,7 +177,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
/** /**
* Stops playback of the current song. * Stops playback of the current song.
*/ */
@@ -179,7 +186,8 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandLoadSong((URL) null))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandLoadSong((URL) null)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -187,13 +195,14 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
@Override @Override
public QueryFuture<Void> seek(float time) public QueryFuture<Void> seek(float time)
{ {
try try
{ {
return new ForwardedFuture(getBrowser().sendObject(new CommandSeek(time))); return new ForwardedFuture(getBrowser()
.sendObject(new CommandSeek(time)));
} }
catch (IOException e) catch (IOException e)
{ {
@@ -201,7 +210,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
/** /**
* Obtains the player's current playback status. * Obtains the player's current playback status.
* *
@@ -215,54 +224,54 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
Future future = getBrowser().sendObject(new QueryStatus()); Future future = getBrowser().sendObject(new QueryStatus());
return new QueryFuture<>() return new QueryFuture<>()
{ {
private CommandReturn<String> getVal() throws ExecutionException, InterruptedException private CommandReturn<String> getVal() throws ExecutionException, InterruptedException
{ {
return ((CommandReturn<String>) future.get()); return ((CommandReturn<String>) future.get());
} }
private CommandReturn<String> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException private CommandReturn<String> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
{ {
return ((CommandReturn<String>) future.get(timeout, unit)); return ((CommandReturn<String>) future.get(timeout, unit));
} }
@Override @Override
public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException
{ {
return this.getVal().getConfirmation(); return this.getVal().getConfirmation();
} }
@Override @Override
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
{ {
return this.getVal(timeout, unit).getConfirmation(); return this.getVal(timeout, unit).getConfirmation();
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) public boolean cancel(boolean mayInterruptIfRunning)
{ {
return future.cancel(mayInterruptIfRunning); return future.cancel(mayInterruptIfRunning);
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return future.isCancelled(); return future.isCancelled();
} }
@Override @Override
public boolean isDone() public boolean isDone()
{ {
return future.isDone(); return future.isDone();
} }
@Override @Override
public PlaybackStatus get() throws InterruptedException, ExecutionException public PlaybackStatus get() throws InterruptedException, ExecutionException
{ {
String value = getVal().getReturnValue(); String value = getVal().getReturnValue();
return PlaybackStatus.valueOf(value); return PlaybackStatus.valueOf(value);
} }
@Override @Override
public PlaybackStatus get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException public PlaybackStatus get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{ {
@@ -277,19 +286,40 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
return null; return null;
} }
} }
@Override @Override
public QueryFuture<Float> getCurrentTime() public QueryFuture<Float> getCurrentTime()
{ {
return null; return null;
} }
@Override @Override
public QueryFuture<Float> getLength() public QueryFuture<Float> getLength()
{ {
return null; return null;
} }
/**
* Causes the browser to throw an error
*
* @param forward - Whether the error should be thrown from the foreground
* script or the background script.
* @return
*/
public QueryFuture<Void> throwError(boolean forward)
{
try
{
return new ForwardedFuture(getBrowser()
.sendObject(new CommandError(forward)));
}
catch (IOException e)
{
logger.error("Could not send message", e);
return null;
}
}
@Override @Override
public void onUpdate(Object object, MessageRunner runner) public void onUpdate(Object object, MessageRunner runner)
{ {
@@ -300,62 +330,62 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
this.listeners.forEach(l -> l.onPlaybackChanged(status)); this.listeners.forEach(l -> l.onPlaybackChanged(status));
} }
} }
private class ForwardedFuture<T> implements QueryFuture<T> private class ForwardedFuture<T> implements QueryFuture<T>
{ {
private final Future<T> future; private final Future<T> future;
ForwardedFuture(Future<T> future) ForwardedFuture(Future<T> future)
{ {
this.future = future; this.future = future;
} }
private CommandReturn<T> getVal() throws ExecutionException, InterruptedException private CommandReturn<T> getVal() throws ExecutionException, InterruptedException
{ {
return ((CommandReturn<T>) this.future.get()); return ((CommandReturn<T>) this.future.get());
} }
private CommandReturn<T> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException private CommandReturn<T> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
{ {
return ((CommandReturn<T>) this.future.get(timeout, unit)); return ((CommandReturn<T>) this.future.get(timeout, unit));
} }
@Override @Override
public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException
{ {
return this.getVal().getConfirmation(); return this.getVal().getConfirmation();
} }
@Override @Override
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{ {
return this.getVal(timeout, unit).getConfirmation(); return this.getVal(timeout, unit).getConfirmation();
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) public boolean cancel(boolean mayInterruptIfRunning)
{ {
return this.future.cancel(mayInterruptIfRunning); return this.future.cancel(mayInterruptIfRunning);
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.future.isCancelled(); return this.future.isCancelled();
} }
@Override @Override
public boolean isDone() public boolean isDone()
{ {
return this.future.isDone(); return this.future.isDone();
} }
@Override @Override
public T get() throws InterruptedException, ExecutionException public T get() throws InterruptedException, ExecutionException
{ {
return this.getVal().getReturnValue(); return this.getVal().getReturnValue();
} }
@Override @Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{ {

View File

@@ -7,6 +7,8 @@ package edu.regis.universeplayer.player;
import edu.regis.universeplayer.Player; import edu.regis.universeplayer.Player;
import edu.regis.universeplayer.browser.Browser; import edu.regis.universeplayer.browser.Browser;
import edu.regis.universeplayer.browser.BrowserPlayer; import edu.regis.universeplayer.browser.BrowserPlayer;
import edu.regis.universeplayer.browserCommands.CommandError;
import edu.regis.universeplayer.browserCommands.QueryFuture;
import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.InternetSong;
import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.Queue;
import edu.regis.universeplayer.data.*; import edu.regis.universeplayer.data.*;
@@ -28,9 +30,11 @@ import java.util.Collection;
import java.util.Locale; import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
/** /**
* The Interface class serves as the primary GUI that the player interacts with. * The Interface class serves as the primary GUI that the player interacts
* with.
* *
* @author William Hubbard * @author William Hubbard
* @version 0.1 * @version 0.1
@@ -40,7 +44,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
// static { // static {
// Locale.setDefault(new Locale("es", "ES")); // Locale.setDefault(new Locale("es", "ES"));
// } // }
private static final Logger logger = LoggerFactory.getLogger(Interface.class); private static final Logger logger = LoggerFactory
.getLogger(Interface.class);
private static final ResourceBundle langs = ResourceBundle private static final ResourceBundle langs = ResourceBundle
.getBundle("lang.interface", Locale.getDefault()); .getBundle("lang.interface", Locale.getDefault());
@@ -119,8 +124,10 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
inter.players.add(browserPlayer = new BrowserPlayer()); inter.players.add(browserPlayer = new BrowserPlayer());
logger.debug("Sending ping"); logger.debug("Sending ping");
browser.sendObject("ping"); browser.sendObject("ping");
Runtime.getRuntime().addShutdownHook(new Thread(browserPlayer::close)); Runtime.getRuntime()
Player.REGISTERED_PLAYERS.put(InternetSong.class, browserPlayer); .addShutdownHook(new Thread(browserPlayer::close));
Player.REGISTERED_PLAYERS
.put(InternetSong.class, browserPlayer);
// LinkedList<Future<Object>> pingRequests = new LinkedList<>(); // LinkedList<Future<Object>> pingRequests = new LinkedList<>();
// for (int i = 0; i < 20; i++) // for (int i = 0; i < 20; i++)
@@ -155,7 +162,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
{ {
logger.error("Could not open browser background", e); logger.error("Could not open browser background", e);
JOptionPane JOptionPane
.showMessageDialog(inter != null && inter.isVisible() ? inter : null, e, langs .showMessageDialog(inter != null && inter
.isVisible() ? inter : null, e, langs
.getString("error.generic"), JOptionPane.ERROR_MESSAGE); .getString("error.generic"), JOptionPane.ERROR_MESSAGE);
} }
} }
@@ -221,7 +229,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
{ {
if (!commDir.getParentFile().mkdir()) if (!commDir.getParentFile().mkdir())
{ {
logger.error("Could not create shared directory {}", commDir.getParent()); logger.error("Could not create shared directory {}", commDir
.getParent());
} }
} }
if (!commDir.exists()) if (!commDir.exists())
@@ -262,7 +271,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
{ {
AbstractAction action; AbstractAction action;
this.actions.put("refresh", action = new AbstractAction(langs.getString("actions.refresh")) this.actions.put("refresh", action = new AbstractAction(langs
.getString("actions.refresh"))
{ {
@Override @Override
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
@@ -302,7 +312,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
}); });
action.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_E); action.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_E);
this.actions.put("exit", action = new AbstractAction(langs.getString("actions.exit")) this.actions.put("exit", action = new AbstractAction(langs
.getString("actions.exit"))
{ {
@Override @Override
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
@@ -316,7 +327,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
}); });
action.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X); action.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X);
this.actions.put("logs", action = new AbstractAction(langs.getString("actions.logs")) this.actions.put("logs", action = new AbstractAction(langs
.getString("actions.logs"))
{ {
@Override @Override
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
@@ -330,9 +342,114 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
} }
} }
}); });
action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); action.putValue(Action.ACCELERATOR_KEY, KeyStroke
.getKeyStroke(KeyEvent.VK_F12, 0));
this.actions.put("about", action = new AbstractAction(langs.getString("actions.about")) this.actions.put("debug.error", action =
new AbstractAction(langs.getString("actions.debug.error"))
{
@Override
public void actionPerformed(ActionEvent e)
{
if (this.isEnabled())
{
new SwingWorker<Void, Void>()
{
/**
* Computes
* a
* result,
* or
* throws
* an
* exception
* if
* unable
* to
* do
* so.
*
* <p>
* Note
* that
* this
* method
* is
* executed
* only
* once.
*
* <p>
* Note:
* this
* method
* is
* executed
* in
* a
* background
* thread.
*
* @return the computed result
* @throws Exception if unable to compute a result
*/
@Override
protected Void doInBackground() throws Exception
{
Interface.this.players.stream()
.filter(p -> p instanceof BrowserPlayer)
.map(p -> (BrowserPlayer) p)
.findFirst()
.ifPresentOrElse(p -> {
logger.debug("Throwing " +
"error");
QueryFuture<Void> command = p
.throwError((false));
try
{
if (!command
.getConfirmation()
.wasSuccessful())
{
logger.error("Could not run command",
command.getConfirmation()
.getError());
JOptionPane
.showMessageDialog(Interface.this,
command.getConfirmation()
.getError(),
command.getConfirmation()
.getMessage(),
JOptionPane.ERROR_MESSAGE);
}
}
catch (ExecutionException | InterruptedException executionException)
{
logger.error("Error" +
" creating " +
"debug " +
"message", executionException);
JOptionPane
.showMessageDialog(Interface.this, executionException
.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
}
}, () -> JOptionPane
.showMessageDialog(Interface.this, langs
.getString("error.browser.null"), langs
.getString("error.debug.error"), JOptionPane.ERROR_MESSAGE));
return null;
}
}.execute();
}
}
});
action.putValue(Action.ACCELERATOR_KEY, KeyStroke
.getKeyStroke(KeyEvent.VK_F12, 0));
this.actions.put("about", action = new AbstractAction(langs
.getString("actions.about"))
{ {
@Override @Override
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
@@ -345,7 +462,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
}); });
this.actions this.actions
.put("view.all", action = new AbstractAction(langs.getString("actions.view.all")) .put("view.all", action = new AbstractAction(langs
.getString("actions.view.all"))
{ {
@Override @Override
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
@@ -521,7 +639,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
protected void constructWindow() protected void constructWindow()
{ {
JMenuBar toolbar; JMenuBar toolbar;
JMenu fileMenu, viewMenu, collectionsMenu, playbackMenu, helpMenu; JMenu fileMenu, viewMenu, collectionsMenu, playbackMenu, helpMenu,
debugMenu;
this.setTitle(langs.getString("title")); this.setTitle(langs.getString("title"));
this.getContentPane().setLayout(new BorderLayout()); this.getContentPane().setLayout(new BorderLayout());
@@ -569,6 +688,10 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
helpMenu.setMnemonic(KeyEvent.VK_H); helpMenu.setMnemonic(KeyEvent.VK_H);
toolbar.add(helpMenu); toolbar.add(helpMenu);
debugMenu = new JMenu(langs.getString("menu.debug.title"));
debugMenu.setMnemonic(KeyEvent.VK_D);
helpMenu.add(debugMenu);
fileMenu.add(new JMenuItem(actions.get("refresh"))); fileMenu.add(new JMenuItem(actions.get("refresh")));
fileMenu.add(actions.get("addExternal")); fileMenu.add(actions.get("addExternal"));
@@ -590,6 +713,9 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
helpMenu.add(new JMenuItem(actions.get("logs"))); helpMenu.add(new JMenuItem(actions.get("logs")));
helpMenu.add(debugMenu);
debugMenu.add(new JMenuItem(actions.get("debug.error")));
helpMenu.add(new JMenuItem(actions.get("about"))); helpMenu.add(new JMenuItem(actions.get("about")));
this.setJMenuBar(toolbar); this.setJMenuBar(toolbar);
@@ -606,15 +732,18 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
((SortingFocusTraversalPolicy) this.getFocusTraversalPolicy()) ((SortingFocusTraversalPolicy) this.getFocusTraversalPolicy())
.setImplicitDownCycleTraversal(true); .setImplicitDownCycleTraversal(true);
this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Set this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Set
.of(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_DOWN, 0), AWTKeyStroke .of(AWTKeyStroke
.getAWTKeyStroke(KeyEvent.VK_DOWN, 0), AWTKeyStroke
.getAWTKeyStroke(KeyEvent.VK_RIGHT, 0))); .getAWTKeyStroke(KeyEvent.VK_RIGHT, 0)));
this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Set this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Set
.of(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_UP, 0), AWTKeyStroke .of(AWTKeyStroke
.getAWTKeyStroke(KeyEvent.VK_UP, 0), AWTKeyStroke
.getAWTKeyStroke(KeyEvent.VK_LEFT, 0))); .getAWTKeyStroke(KeyEvent.VK_LEFT, 0)));
this.setFocusTraversalKeys(KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, Set this.setFocusTraversalKeys(KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, Set
.of(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0))); .of(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0)));
this.setFocusTraversalKeys(KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, Set this.setFocusTraversalKeys(KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, Set
.of(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK))); .of(AWTKeyStroke
.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK)));
} }
@Override @Override
@@ -752,7 +881,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
else else
{ {
int index = -1; int index = -1;
Component[] children = ((Container) e.getComponent()).getComponents(); Component[] children = ((Container) e.getComponent())
.getComponents();
for (int i = 0, l = children.length; index == -1 && i < l; i++) for (int i = 0, l = children.length; index == -1 && i < l; i++)
{ {
if (children[i] == e.getOppositeComponent()) if (children[i] == e.getOppositeComponent())

View File

@@ -8,52 +8,63 @@ import edu.regis.universeplayer.PlaybackInfo;
import edu.regis.universeplayer.PlaybackListener; import edu.regis.universeplayer.PlaybackListener;
import edu.regis.universeplayer.PlaybackStatus; import edu.regis.universeplayer.PlaybackStatus;
import edu.regis.universeplayer.Player; import edu.regis.universeplayer.Player;
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
import edu.regis.universeplayer.browserCommands.CommandReturn;
import edu.regis.universeplayer.browserCommands.QueryFuture;
import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.PlaybackEvent;
import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.Queue;
import edu.regis.universeplayer.data.Song; import edu.regis.universeplayer.data.Song;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.FocusAdapter; import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
/** /**
* This panel contains the buttons necessary for controlling the playback of audio. * This panel contains the buttons necessary for controlling the playback of
* audio.
* *
* @author William Hubbard * @author William Hubbard
* @version 0.1 * @version 0.1
*/ */
public class PlayerControls extends JPanel implements Queue.SongChangeListener, PlaybackListener public class PlayerControls extends JPanel implements Queue.SongChangeListener, PlaybackListener
{ {
private static final Logger logger = LoggerFactory.getLogger(PlayerControls.class); private static final Logger logger = LoggerFactory
.getLogger(PlayerControls.class);
private static final ResourceBundle langs = ResourceBundle
.getBundle("lang.interface", Locale.getDefault());
private final ImageIcon PLAY_ICON, PAUSE_ICON; private final ImageIcon PLAY_ICON, PAUSE_ICON;
/** /**
* A reference to the song currently playing. * A reference to the song currently playing.
*/ */
private Song currentSong = null; private Song currentSong = null;
private Player currentPlayer = null; private Player currentPlayer = null;
private final JButton playButton; private final JButton playButton;
private final JButton nextButton; private final JButton nextButton;
private final JButton prevButton; private final JButton prevButton;
private final JProgressBar progress; private final JProgressBar progress;
private final JProgressBar updateProgress; private final JProgressBar updateProgress;
private final ForkJoinPool service = new ForkJoinPool(); private final ForkJoinPool service = new ForkJoinPool();
/** /**
* A list of all things interested in knowing when we trigger a command. * A list of all things interested in knowing when we trigger a command.
*/ */
private final LinkedList<PlaybackCommandListener> listeners = new LinkedList<>(); private final LinkedList<PlaybackCommandListener> listeners = new LinkedList<>();
public PlayerControls() public PlayerControls()
{ {
final Dimension BUTTON_SIZE = new Dimension(32, 32); final Dimension BUTTON_SIZE = new Dimension(32, 32);
@@ -62,71 +73,84 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
JPanel buttonCont, progressCont; JPanel buttonCont, progressCont;
FlowLayout buttonLayout; FlowLayout buttonLayout;
SpringLayout progressLayout; SpringLayout progressLayout;
SpringLayout layout = new SpringLayout(); SpringLayout layout = new SpringLayout();
this.setLayout(layout); this.setLayout(layout);
this.setFocusable(true); this.setFocusable(true);
this.setFocusCycleRoot(false); this.setFocusCycleRoot(false);
buttonLayout = new FlowLayout(); buttonLayout = new FlowLayout();
buttonCont = new JPanel(buttonLayout); buttonCont = new JPanel(buttonLayout);
this.add(buttonCont); this.add(buttonCont);
this.prevButton = new JButton(Interface.getInstance().actions.get("playback.skipPrev")); this.prevButton = new JButton(Interface.getInstance().actions
.get("playback.skipPrev"));
this.prevButton.setText(""); this.prevButton.setText("");
icon = new ImageIcon(this.getClass() icon = new ImageIcon(this.getClass()
.getResource("/gui/icons/skipPrev.png"), "Previous Button"); .getResource("/gui/icons/skipPrev.png"), "Previous Button");
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0)); icon.setImage(icon.getImage()
.getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
this.prevButton.setIcon(icon); this.prevButton.setIcon(icon);
this.prevButton.setPreferredSize(BUTTON_SIZE); this.prevButton.setPreferredSize(BUTTON_SIZE);
buttonCont.add(this.prevButton); buttonCont.add(this.prevButton);
this.playButton = new JButton(Interface.getInstance().actions.get("playback.toggle")); this.playButton = new JButton(Interface.getInstance().actions
.get("playback.toggle"));
this.playButton.setText(""); this.playButton.setText("");
PAUSE_ICON = icon = new ImageIcon(this.getClass().getResource("/gui/icons/pause.png"), "Pause Button"); PAUSE_ICON = icon = new ImageIcon(this.getClass()
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0)); .getResource("/gui/icons/pause.png"), "Pause Button");
PLAY_ICON = icon = new ImageIcon(this.getClass().getResource("/gui/icons/play.png"), "Play Button"); icon.setImage(icon.getImage()
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0)); .getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
PLAY_ICON = icon = new ImageIcon(this.getClass()
.getResource("/gui/icons/play.png"), "Play Button");
icon.setImage(icon.getImage()
.getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
this.playButton.setIcon(icon); this.playButton.setIcon(icon);
this.playButton.setPreferredSize(BUTTON_SIZE); this.playButton.setPreferredSize(BUTTON_SIZE);
buttonCont.add(this.playButton); buttonCont.add(this.playButton);
this.nextButton = new JButton(Interface.getInstance().actions.get("playback.skipNext")); this.nextButton = new JButton(Interface.getInstance().actions
.get("playback.skipNext"));
this.nextButton.setText(""); this.nextButton.setText("");
icon = new ImageIcon(this.getClass().getResource("/gui/icons/skipNext.png"), "Next Button"); icon = new ImageIcon(this.getClass()
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0)); .getResource("/gui/icons/skipNext.png"), "Next Button");
icon.setImage(icon.getImage()
.getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
this.nextButton.setIcon(icon); this.nextButton.setIcon(icon);
this.nextButton.setPreferredSize(BUTTON_SIZE); this.nextButton.setPreferredSize(BUTTON_SIZE);
buttonCont.add(this.nextButton); buttonCont.add(this.nextButton);
progressLayout = new SpringLayout(); progressLayout = new SpringLayout();
progressCont = new JPanel(progressLayout); progressCont = new JPanel(progressLayout);
this.add(progressCont); this.add(progressCont);
this.progress = new JProgressBar(); this.progress = new JProgressBar();
this.progress.addMouseListener(new MouseAdapter() this.progress.addMouseListener(new MouseAdapter()
{ {
@Override @Override
public void mouseClicked(MouseEvent e) public void mouseClicked(MouseEvent e)
{ {
seek((float) e.getX() / (float) e.getComponent().getWidth() * ((JProgressBar) e.getComponent()).getMaximum()); seek((float) e.getX() / (float) e.getComponent()
.getWidth() * ((JProgressBar) e
.getComponent()).getMaximum());
logger.debug("Changing time"); logger.debug("Changing time");
} }
}); });
this.add(this.progress); this.add(this.progress);
this.updateProgress = new JProgressBar(); this.updateProgress = new JProgressBar();
this.updateProgress.setStringPainted(true); this.updateProgress.setStringPainted(true);
this.setUpdateProgress(0, 0, null); this.setUpdateProgress(0, 0, null);
this.add(this.updateProgress); this.add(this.updateProgress);
this.addFocusListener(new FocusAdapter() this.addFocusListener(new FocusAdapter()
{ {
@Override @Override
public void focusGained(FocusEvent e) public void focusGained(FocusEvent e)
{ {
int index = -1; int index = -1;
Component[] children = ((Container) e.getComponent()).getComponents(); Component[] children = ((Container) e.getComponent())
.getComponents();
for (int i = 0, l = children.length; index == -1 && i < l; i++) for (int i = 0, l = children.length; index == -1 && i < l; i++)
{ {
if (children[i] == e.getOppositeComponent()) if (children[i] == e.getOppositeComponent())
@@ -144,7 +168,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
} }
} }
}); });
layout.putConstraint(SpringLayout.NORTH, buttonCont, 0, SpringLayout.NORTH, this); layout.putConstraint(SpringLayout.NORTH, buttonCont, 0, SpringLayout.NORTH, this);
layout.putConstraint(SpringLayout.WEST, buttonCont, 0, SpringLayout.WEST, this); layout.putConstraint(SpringLayout.WEST, buttonCont, 0, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.EAST, buttonCont, 0, SpringLayout.EAST, this); layout.putConstraint(SpringLayout.EAST, buttonCont, 0, SpringLayout.EAST, this);
@@ -153,20 +177,39 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
layout.putConstraint(SpringLayout.EAST, this.progress, 5, SpringLayout.EAST, this); layout.putConstraint(SpringLayout.EAST, this.progress, 5, SpringLayout.EAST, this);
layout.putConstraint(SpringLayout.NORTH, this.updateProgress, 5, SpringLayout.SOUTH, this.progress); layout.putConstraint(SpringLayout.NORTH, this.updateProgress, 5, SpringLayout.SOUTH, this.progress);
layout.putConstraint(SpringLayout.WEST, this.updateProgress, 5, SpringLayout.WEST, this); layout.putConstraint(SpringLayout.WEST, this.updateProgress, 5, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.EAST, this, 5, SpringLayout.EAST, this.progress); layout.putConstraint(SpringLayout.EAST, this, 5, SpringLayout.EAST, this.progress);
layout.putConstraint(SpringLayout.EAST, this, 5, SpringLayout.EAST, this.updateProgress); layout.putConstraint(SpringLayout.EAST, this, 5, SpringLayout.EAST, this.updateProgress);
layout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.SOUTH, this.updateProgress); layout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.SOUTH, this.updateProgress);
Queue.getInstance().addSongChangeListener(this); Queue.getInstance().addSongChangeListener(this);
} }
private void seek(float value) private void seek(float value)
{ {
this.service.execute(() -> { this.service.execute(() -> {
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
this.currentPlayer.seek(value); QueryFuture<Void> command = this.currentPlayer.seek(value);
try
{
if (!command.getConfirmation().wasSuccessful())
{
JOptionPane.showMessageDialog(this,
command.getConfirmation().getError(),
command.getConfirmation().getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
command.getConfirmation().getError());
}
}
catch (ExecutionException | InterruptedException e)
{
logger.error("Could not run command", e);
JOptionPane.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
}
} }
}); });
this.triggerCommandListeners(PlaybackCommand.SEEK, value); this.triggerCommandListeners(PlaybackCommand.SEEK, value);
@@ -179,28 +222,49 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
switch ((PlaybackStatus) this.currentPlayer.getStatus().get()) QueryFuture<PlaybackStatus> status =
this.currentPlayer.getStatus();
CommandConfirmation confirmation = status.getConfirmation();
if (status.getConfirmation().wasSuccessful())
{ {
case PAUSED -> this.currentPlayer.play(); switch (status.get())
case STOPPED, EMPTY -> {
if (Queue.getInstance().size() > 0)
{ {
if (Queue.getInstance().getCurrentSong() == null) case PAUSED -> confirmation = this.currentPlayer.play()
.getConfirmation();
case STOPPED, EMPTY -> {
if (Queue.getInstance().size() > 0)
{ {
Queue.getInstance().skipToSong(0); if (Queue.getInstance()
} .getCurrentSong() == null)
else {
{ Queue.getInstance().skipToSong(0);
this.currentPlayer.play(); }
else
{
confirmation = this.currentPlayer.play()
.getConfirmation();
}
} }
} }
}
} }
if (confirmation != null && !confirmation.wasSuccessful())
{
JOptionPane.showMessageDialog(this,
confirmation.getError(),
confirmation.getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
confirmation.getError());
} }
} }
} }
catch (ExecutionException | InterruptedException e) catch (ExecutionException | InterruptedException e)
{ {
logger.error("Could not get current playback status", e); logger.error("Could not get current playback status", e);
JOptionPane.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
} }
}); });
this.triggerCommandListeners(PlaybackCommand.PLAY, null); this.triggerCommandListeners(PlaybackCommand.PLAY, null);
@@ -213,20 +277,39 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
switch ((PlaybackStatus) this.currentPlayer.getStatus().get()) QueryFuture<PlaybackStatus> status =
this.currentPlayer.getStatus();
CommandConfirmation confirmation = status.getConfirmation();
if (status.getConfirmation().wasSuccessful())
{ {
case PLAYING -> this.currentPlayer.pause(); switch (status.get())
{
case PLAYING -> confirmation =
this.currentPlayer.pause().getConfirmation();
}
}
if (confirmation != null && !confirmation.wasSuccessful())
{
JOptionPane.showMessageDialog(this,
confirmation.getError(),
confirmation.getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
confirmation.getError());
} }
} }
} }
catch (ExecutionException | InterruptedException e) catch (ExecutionException | InterruptedException e)
{ {
logger.error("Could not get current playback status", e); logger.error("Could not get current playback status", e);
JOptionPane.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
} }
}); });
this.triggerCommandListeners(PlaybackCommand.PAUSE, null); this.triggerCommandListeners(PlaybackCommand.PAUSE, null);
} }
/** /**
* Toggles the playback of the current song. * Toggles the playback of the current song.
*/ */
@@ -237,34 +320,56 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
switch ((PlaybackStatus) this.currentPlayer.getStatus().get()) QueryFuture<PlaybackStatus> status =
this.currentPlayer.getStatus();
CommandConfirmation confirmation = status.getConfirmation();
if (status.getConfirmation().wasSuccessful())
{ {
case PAUSED -> this.currentPlayer.play(); switch (status.get())
case PLAYING -> this.currentPlayer.pause();
case STOPPED, EMPTY -> {
if (Queue.getInstance().size() > 0)
{ {
if (Queue.getInstance().getCurrentSong() == null) case PAUSED -> confirmation = this.currentPlayer.play()
.getConfirmation();
case PLAYING -> confirmation = this.currentPlayer.pause()
.getConfirmation();
case STOPPED, EMPTY -> {
if (Queue.getInstance().size() > 0)
{ {
Queue.getInstance().skipToSong(0); if (Queue.getInstance()
} .getCurrentSong() == null)
else {
{ Queue.getInstance().skipToSong(0);
this.currentPlayer.play(); }
else
{
confirmation = this.currentPlayer.play()
.getConfirmation();
}
} }
} }
}
} }
if (confirmation != null && !confirmation.wasSuccessful())
{
JOptionPane.showMessageDialog(this,
confirmation.getError(),
confirmation.getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
confirmation.getError());
} }
} }
} }
catch (ExecutionException | InterruptedException e) catch (ExecutionException | InterruptedException e)
{ {
logger.error("Could not get current playback status", e); logger.error("Could not get current playback status", e);
JOptionPane.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
} }
}); });
this.triggerCommandListeners(PlaybackCommand.PLAY, null); this.triggerCommandListeners(PlaybackCommand.PLAY, null);
} }
/** /**
* Skips to the next song. * Skips to the next song.
*/ */
@@ -273,7 +378,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
Queue.getInstance().skipPrev(); Queue.getInstance().skipPrev();
this.triggerCommandListeners(PlaybackCommand.PREVIOUS, null); this.triggerCommandListeners(PlaybackCommand.PREVIOUS, null);
} }
/** /**
* Skips to the next song. * Skips to the next song.
*/ */
@@ -282,7 +387,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
Queue.getInstance().skipNext(); Queue.getInstance().skipNext();
this.triggerCommandListeners(PlaybackCommand.NEXT, null); this.triggerCommandListeners(PlaybackCommand.NEXT, null);
} }
void setUpdateProgress(int updated, int toUpdate, String updating) void setUpdateProgress(int updated, int toUpdate, String updating)
{ {
this.updateProgress.setString(updating); this.updateProgress.setString(updating);
@@ -307,7 +412,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
} }
} }
} }
/** /**
* Adds a listener for playback commands. * Adds a listener for playback commands.
* *
@@ -317,7 +422,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
this.listeners.add(listener); this.listeners.add(listener);
} }
/** /**
* Removes a playback listener. * Removes a playback listener.
* *
@@ -327,7 +432,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
this.listeners.remove(listener); this.listeners.remove(listener);
} }
/** /**
* Triggers all the command listeners. * Triggers all the command listeners.
* *
@@ -341,7 +446,7 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
listener.onCommand(command, data); listener.onCommand(command, data);
} }
} }
@Override @Override
public void onSongChange(Queue queue) public void onSongChange(Queue queue)
{ {
@@ -349,14 +454,37 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
this.currentPlayer.stopSong(); this.service.execute(() -> {
QueryFuture<Void> command = this.currentPlayer.stopSong();
try
{
if (!command.getConfirmation().wasSuccessful())
{
JOptionPane.showMessageDialog(this,
command.getConfirmation().getError(),
command.getConfirmation().getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
command.getConfirmation().getError());
}
}
catch (ExecutionException | InterruptedException e)
{
logger.error("Could not get current playback status", e);
JOptionPane
.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
}
});
} }
} }
this.currentSong = queue.getCurrentSong(); this.currentSong = queue.getCurrentSong();
this.playButton.setIcon(PLAY_ICON); this.playButton.setIcon(PLAY_ICON);
if (this.currentSong != null) if (this.currentSong != null)
{ {
this.currentPlayer = Player.REGISTERED_PLAYERS.get(this.currentSong.getClass()); this.currentPlayer = Player.REGISTERED_PLAYERS
.get(this.currentSong.getClass());
this.progress.setMaximum((int) (this.currentSong.duration / 1000)); this.progress.setMaximum((int) (this.currentSong.duration / 1000));
if (this.currentPlayer != null) if (this.currentPlayer != null)
{ {
@@ -364,12 +492,37 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
{ {
this.currentPlayer.addPlaybackListener(this); this.currentPlayer.addPlaybackListener(this);
} }
this.currentPlayer.loadSong(this.currentSong); this.service.execute(() -> {
QueryFuture<Void> command =
this.currentPlayer.loadSong(this.currentSong);
try
{
if (!command.getConfirmation().wasSuccessful())
{
JOptionPane.showMessageDialog(this,
command.getConfirmation().getError(),
command.getConfirmation().getMessage(),
JOptionPane.ERROR_MESSAGE);
logger.error("Could not run command",
command.getConfirmation().getError());
}
}
catch (ExecutionException | InterruptedException e)
{
logger.error("Could not get current playback status", e);
JOptionPane
.showMessageDialog(this, e.getMessage(), langs
.getString(
"error.command"), JOptionPane.ERROR_MESSAGE);
}
});
// this.currentPlayer.play(); // this.currentPlayer.play();
} }
else else
{ {
logger.error("No logger found for song {}", this.currentSong.getClass()); logger.error("No logger found for song {}", this.currentSong
.getClass());
} }
} }
else else
@@ -378,11 +531,12 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
this.progress.setMaximum(0); this.progress.setMaximum(0);
} }
} }
@Override @Override
public void onPlaybackChanged(PlaybackEvent status) public void onPlaybackChanged(PlaybackEvent status)
{ {
if (status.getSource() != null && status.getSource() == this.currentPlayer) if (status.getSource() != null && status
.getSource() == this.currentPlayer)
{ {
switch (status.getInfo().getStatus()) switch (status.getInfo().getStatus())
{ {
@@ -391,7 +545,8 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
case PAUSED, STOPPED, EMPTY -> this.playButton.setIcon(PLAY_ICON); case PAUSED, STOPPED, EMPTY -> this.playButton.setIcon(PLAY_ICON);
} }
this.progress.setValue((int) status.getInfo().getPlayTime()); this.progress.setValue((int) status.getInfo().getPlayTime());
this.progress.setMaximum((int) (status.getInfo().getSong().duration / 1000)); this.progress.setMaximum((int) (status.getInfo()
.getSong().duration / 1000));
} }
} }
} }

View File

@@ -14,6 +14,7 @@ menu.view.title=View
menu.view.collections=Collections menu.view.collections=Collections
menu.playback.title=Playback menu.playback.title=Playback
menu.help.title=Help menu.help.title=Help
menu.debug.title=Debugging
albumInfo.album=Album albumInfo.album=Album
albumInfo.artists=Artists albumInfo.artists=Artists
@@ -42,6 +43,7 @@ actions.refresh=Refresh Song Cache
actions.exit=Exit actions.exit=Exit
actions.logs=Show Logs actions.logs=Show Logs
actions.debug.error=Throw Error
actions.about=About actions.about=About
@@ -61,4 +63,7 @@ actions.playback.skipPrev=Skip Previously
actions.playback.skipNext=Skip Next actions.playback.skipNext=Skip Next
error.generic=Error! error.generic=Error!
error.browser.launch=Could not open browser background error.command=Could not run command
error.debug.error=Could not create error
error.browser.launch=Could not open browser background
error.browser.null=Could not find browser background