) items.get("hideOnClose")).set(hideOnClose); }
/**
* @return the items
diff --git a/client/src/main/java/envoy/client/data/audio/AudioRecorder.java b/client/src/main/java/envoy/client/data/audio/AudioRecorder.java
index 85dafae..251041a 100644
--- a/client/src/main/java/envoy/client/data/audio/AudioRecorder.java
+++ b/client/src/main/java/envoy/client/data/audio/AudioRecorder.java
@@ -27,6 +27,11 @@ public final class AudioRecorder {
*/
public static final AudioFormat DEFAULT_AUDIO_FORMAT = new AudioFormat(16000, 16, 1, true, false);
+ /**
+ * The format in which audio files will be saved.
+ */
+ public static final String FILE_FORMAT = "wav";
+
private final AudioFormat format;
private final DataLine.Info info;
@@ -78,7 +83,7 @@ public final class AudioRecorder {
line.start();
// Prepare temp file
- tempFile = Files.createTempFile("recording", "wav");
+ tempFile = Files.createTempFile("recording", FILE_FORMAT);
// Start the recording
final var ais = new AudioInputStream(line);
@@ -117,6 +122,6 @@ public final class AudioRecorder {
line.close();
try {
Files.deleteIfExists(tempFile);
- } catch (IOException e) {}
+ } catch (final IOException e) {}
}
}
diff --git a/client/src/main/java/envoy/client/data/commands/OnCall.java b/client/src/main/java/envoy/client/data/commands/OnCall.java
new file mode 100644
index 0000000..dc9c015
--- /dev/null
+++ b/client/src/main/java/envoy/client/data/commands/OnCall.java
@@ -0,0 +1,34 @@
+package envoy.client.data.commands;
+
+import java.util.function.Supplier;
+
+/**
+ * This interface defines an action that should be performed when a system
+ * command gets called.
+ *
+ * Project: envoy-client
+ * File: OnCall.java
+ * Created: 23.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public interface OnCall {
+
+ /**
+ * Performs class specific actions when a {@link SystemCommand} has been called.
+ *
+ * @since Envoy Client v0.2-beta
+ */
+ void onCall();
+
+ /**
+ * Performs actions that can only be performed by classes that are not
+ * {@link SystemCommand}s when a SystemCommand has been called.
+ *
+ * @param consumer the action to perform when this {@link SystemCommand} has
+ * been called
+ * @since Envoy Client v0.2-beta
+ */
+ void onCall(Supplier consumer);
+}
diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommand.java b/client/src/main/java/envoy/client/data/commands/SystemCommand.java
new file mode 100644
index 0000000..3f1a9a5
--- /dev/null
+++ b/client/src/main/java/envoy/client/data/commands/SystemCommand.java
@@ -0,0 +1,135 @@
+package envoy.client.data.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * This class is the base class of all {@code SystemCommands} and contains an
+ * action and a number of arguments that should be used as input for this
+ * function.
+ * No {@code SystemCommand} can return anything.
+ * Every {@code SystemCommand} must have as argument type {@code List} so
+ * that the words following the indicator String can be used as input of the
+ * function. This approach has one limitation:
+ * Order matters! Changing the order of arguments will likely result in
+ * unexpected behavior.
+ *
+ * Project: envoy-client
+ * File: SystemCommand.java
+ * Created: 16.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public class SystemCommand implements OnCall {
+
+ protected int relevance;
+
+ /**
+ * The argument count of the command.
+ */
+ protected final int numberOfArguments;
+
+ /**
+ * This function takes a {@code List} as argument because automatically
+ * {@code SystemCommand#numberOfArguments} words following the necessary command
+ * will be put into this list.
+ *
+ * @see String#split(String)
+ */
+ protected final Consumer> action;
+
+ protected final String description;
+
+ protected final List defaults;
+
+ /**
+ * Constructs a new {@code SystemCommand}.
+ *
+ * @param action the action performed by the command
+ * @param numberOfArguments the argument count accepted by the action
+ * @param defaults the default values for the corresponding arguments
+ * @param description the description of this {@code SystemCommand}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommand(Consumer> action, int numberOfArguments, List defaults, String description) {
+ this.numberOfArguments = numberOfArguments;
+ this.action = action;
+ this.defaults = defaults == null ? new ArrayList<>() : defaults;
+ this.description = description;
+ }
+
+ /**
+ * @return the action that should be performed
+ * @since Envoy Client v0.2-beta
+ */
+ public Consumer> getAction() { return action; }
+
+ /**
+ * @return the argument count of the command
+ * @since Envoy Client v0.2-beta
+ */
+ public int getNumberOfArguments() { return numberOfArguments; }
+
+ /**
+ * @return the description
+ * @since Envoy Client v0.2-beta
+ */
+ public String getDescription() { return description; }
+
+ /**
+ * @return the relevance
+ * @since Envoy Client v0.2-beta
+ */
+ public int getRelevance() { return relevance; }
+
+ /**
+ * @param relevance the relevance to set
+ * @since Envoy Client v0.2-beta
+ */
+ public void setRelevance(int relevance) { this.relevance = relevance; }
+
+ /**
+ * Increments the relevance of this {@code SystemCommand}.
+ */
+ @Override
+ public void onCall() { relevance++; }
+
+ /**
+ * Increments the relevance of this {@code SystemCommand} and executes the
+ * supplier.
+ */
+ @Override
+ public void onCall(Supplier consumer) {
+ onCall();
+ consumer.get();
+ }
+
+ /**
+ * @return the defaults
+ * @since Envoy Client v0.2-beta
+ */
+ public List getDefaults() { return defaults; }
+
+ @Override
+ public int hashCode() { return Objects.hash(action); }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final SystemCommand other = (SystemCommand) obj;
+ return Objects.equals(action, other.action);
+ }
+
+ @Override
+ public String toString() {
+ return "SystemCommand [relevance=" + relevance + ", numberOfArguments=" + numberOfArguments + ", "
+ + (action != null ? "action=" + action + ", " : "") + (description != null ? "description=" + description + ", " : "")
+ + (defaults != null ? "defaults=" + defaults : "") + "]";
+ }
+}
diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java b/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java
new file mode 100644
index 0000000..fde7ba2
--- /dev/null
+++ b/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java
@@ -0,0 +1,144 @@
+package envoy.client.data.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * This class acts as a builder for {@link SystemCommand}s.
+ *
+ * Project: envoy-client
+ * File: SystemCommandBuilder.java
+ * Created: 23.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public class SystemCommandBuilder {
+
+ private int numberOfArguments;
+ private Consumer> action;
+ private List defaults;
+ private String description;
+ private int relevance;
+
+ /**
+ * @param numberOfArguments the numberOfArguments to set
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder setNumberOfArguments(int numberOfArguments) {
+ this.numberOfArguments = numberOfArguments;
+ return this;
+ }
+
+ /**
+ * @param action the action to set
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder setAction(Consumer> action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * @param description the description to set
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @param relevance the relevance to set
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder setRelevance(int relevance) {
+ this.relevance = relevance;
+ return this;
+ }
+
+ /**
+ * @param defaults the defaults to set
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder setDefaults(String... defaults) {
+ this.defaults = List.of(defaults);
+ return this;
+ }
+
+ /**
+ * Resets all values stored.
+ *
+ * @return this {@code SystemCommandBuilder}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommandBuilder reset() {
+ numberOfArguments = 0;
+ action = null;
+ defaults = new ArrayList<>();
+ description = "";
+ relevance = 0;
+ return this;
+ }
+
+ /**
+ * Builds a {@code SystemCommand} based upon the previously entered data.
+ *
+ * @return the built {@code SystemCommand}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommand build() { return build(true); }
+
+ /**
+ * Builds a {@code SystemCommand} based upon the previously entered data.
+ * {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the
+ * previous value.
+ * At the end, this {@code SystemCommandBuilder} will be reset.
+ *
+ * @return the built {@code SystemCommand}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommand buildNoArg() {
+ numberOfArguments = 0;
+ return build(true);
+ }
+
+ /**
+ * Builds a {@code SystemCommand} based upon the previously entered data.
+ * {@code SystemCommand#numberOfArguments} will be set to use the rest of the
+ * string as argument, regardless of the previous value.
+ * At the end, this {@code SystemCommandBuilder} will be reset.
+ *
+ * @return the built {@code SystemCommand}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommand buildRemainingArg() {
+ numberOfArguments = -1;
+ return build(true);
+ }
+
+ /**
+ * Builds a {@code SystemCommand} based upon the previously entered data.
+ * At the end, this {@code SystemCommandBuilder} can be reset but must
+ * not be.
+ *
+ * @param reset whether this {@code SystemCommandBuilder} should be reset
+ * afterwards.
+ * This can be useful if another command wants to execute something
+ * similar
+ * @return the built {@code SystemCommand}
+ * @since Envoy Client v0.2-beta
+ */
+ public SystemCommand build(boolean reset) {
+ final var sc = new SystemCommand(action, numberOfArguments, defaults, description);
+ sc.setRelevance(relevance);
+ if (reset) reset();
+ return sc;
+ }
+}
diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java
new file mode 100644
index 0000000..cfcb0a4
--- /dev/null
+++ b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java
@@ -0,0 +1,263 @@
+package envoy.client.data.commands;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import envoy.util.EnvoyLog;
+
+/**
+ * This class stores all {@link SystemCommand}s used.
+ *
+ * Project: envoy-client
+ * File: SystemCommandsMap.java
+ * Created: 17.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public final class SystemCommandsMap {
+
+ private final Map systemCommands = new HashMap<>();
+
+ private final Pattern commandPattern = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$");
+
+ private static final Logger logger = EnvoyLog.getLogger(SystemCommandsMap.class);
+
+ /**
+ * Adds a new command to the map if the command name is valid.
+ *
+ * @param command the input string to execute the
+ * given action
+ * @param systemCommand the command to add - can be built using
+ * {@link SystemCommandBuilder}
+ * @see SystemCommandsMap#isValidKey(String)
+ * @since Envoy Client v0.2-beta
+ */
+ public void add(String command, SystemCommand systemCommand) { if (isValidKey(command)) systemCommands.put(command, systemCommand); }
+
+ /**
+ * This method checks if the input String is a key in the map and returns the
+ * wrapped System command if present.
+ * It will return an empty optional if the value after the slash is not a key in
+ * the map, which is a valid case (i.e. input="3/4" and "4" is not a key in the
+ * map).
+ *
+ * Usage example:
+ * {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
+ * {@code Button button = new Button();}
+ * {@code systemCommands.add("example", text -> button.setText(text.get(0), 1);}
+ * {@code ....}
+ * user input: {@code "/example xyz ..."}
+ * {@code systemCommands.get("example xyz ...")} or
+ * {@code systemCommands.get("/example xyz ...")}
+ * result: {@code Optional}
+ *
+ * @param input the input string given by the user
+ * @return the wrapped system command, if present
+ * @since Envoy Client v0.2-beta
+ */
+ public Optional get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input))); }
+
+ /**
+ * This method ensures that the "/" of a {@link SystemCommand} is stripped.
+ * It returns the command as (most likely) entered as key in the map for the
+ * first word of the text.
+ * It should only be called on strings that contain a "/" at position 0/-1.
+ *
+ * @param raw the input
+ * @return the command as entered in the map
+ * @since Envoy Client v0.2-beta
+ * @apiNote this method will (most likely) not return anything useful if
+ * whatever is entered after the slash is not a system command. Only
+ * exception: for recommendation purposes.
+ */
+ public String getCommand(String raw) {
+ final var trimmed = raw.stripLeading();
+ final var index = trimmed.indexOf(' ');
+ return trimmed.substring(trimmed.charAt(0) == '/' ? 1 : 0, index < 1 ? trimmed.length() : index);
+ }
+
+ /**
+ * Examines whether a key can be put in the map and logs it with
+ * {@code Level.WARNING} if that key violates API constrictions.
+ * (allowed chars are a-zA-Z0-9_:!()?.,;-)
+ *
+ * The approach to not throw an exception was taken so that an ugly try-catch
+ * block for every addition to the system commands map could be avoided, an
+ * error that should only occur during implementation and not in production.
+ *
+ * @param command the key to examine
+ * @return whether this key can be used in the map
+ * @since Envoy Client v0.2-beta
+ */
+ public boolean isValidKey(String command) {
+ final boolean valid = commandPattern.matcher(command).matches();
+ if (!valid) logger.log(Level.WARNING,
+ "The command \"" + command
+ + "\" is not valid. As it will cause problems in execution, it will not be entered into the map. Only the characters "
+ + commandPattern + "are allowed");
+ return valid;
+ }
+
+ /**
+ * Takes a 'raw' string (the whole input) and checks if "/" is the first visible
+ * character and then checks if a command is present after that "/". If that is
+ * the case, it will be executed.
+ *
+ *
+ * @param raw the raw input string
+ * @return whether a command could be found
+ * @since Envoy Client v0.2-beta
+ */
+ public boolean executeIfAnyPresent(String raw) {
+ // possibly a command was detected and could be executed
+ final var raw2 = raw.stripLeading();
+ final var commandFound = raw2.startsWith("/") ? executeIfPresent(raw2) : false;
+ // the command was executed successfully - no further checking needed
+ if (commandFound) logger.log(Level.FINE, "executed system command " + getCommand(raw2));
+ return commandFound;
+ }
+
+ /**
+ * This method checks if the input String is a key in the map and executes the
+ * wrapped System command if present.
+ * Its intended usage is after a "/" has been detected in the input String.
+ * It will do nothing if the value after the slash is not a key in
+ * the map, which is a valid case (i.e. input="3/4" and "4" is not a key in the
+ * map).
+ *
+ * Usage example:
+ * {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
+ * {@code Button button = new Button();}
+ * {@code systemCommands.add("example", (words)-> button.setText(words.get(0), 1);}
+ * {@code ....}
+ * user input: {@code "/example xyz ..."}
+ * {@code systemCommands.executeIfPresent("example xyz ...")}
+ * result: {@code button.getText()=="xyz"}
+ *
+ * @param input the input string given by the user
+ * @return whether a command could be found
+ * @since Envoy Client v0.2-beta
+ */
+ public boolean executeIfPresent(String input) {
+ final var command = getCommand(input);
+ final var value = get(command);
+ value.ifPresent(systemCommand -> {
+ // Splitting the String so that the leading command including the first " " is
+ // removed and only as many following words as allowed by the system command
+ // persist
+ final var arguments = extractArguments(input, systemCommand);
+ // Executing the function
+ try {
+ systemCommand.getAction().accept(arguments);
+ systemCommand.onCall();
+ } catch (final Exception e) {
+ logger.log(Level.WARNING, "The system command " + command + " threw an exception: ", e);
+ }
+ });
+ return value.isPresent();
+ }
+
+ /**
+ * Supplies missing values with default values.
+ *
+ * @param input the input String
+ * @param systemCommand the command that is expected
+ * @return the list of arguments that can be used to parse the systemCommand
+ * @since Envoy Client v0.2-beta
+ */
+ private List extractArguments(String input, SystemCommand systemCommand) {
+ // no more arguments follow after the command (e.g. text = "/DABR")
+ final var indexOfSpace = input.indexOf(" ");
+ if (indexOfSpace < 0) return supplementDefaults(new String[] {}, systemCommand);
+ // the arguments behind a system command
+ final var remainingString = input.substring(indexOfSpace + 1);
+ final var numberOfArguments = systemCommand.getNumberOfArguments();
+ // splitting those arguments and supplying default values
+ final var textArguments = remainingString.split(" ", -1);
+ final var originalArguments = numberOfArguments >= 0 ? Arrays.copyOfRange(textArguments, 0, numberOfArguments) : textArguments;
+ final var arguments = supplementDefaults(originalArguments, systemCommand);
+ return arguments;
+ }
+
+ /**
+ * Retrieves the recommendations based on the current input entered.
+ * The first word is used for the recommendations and
+ * it does not matter if the "/" is at its beginning or not.
+ * If none are present, nothing will be done.
+ * Otherwise the given function will be executed on the recommendations.
+ *
+ * @param input the input string
+ * @param action the action that should be taken for the recommendations, if any
+ * are present
+ * @since Envoy Client v0.2-beta
+ */
+ public void requestRecommendations(String input, Function, Void> action) {
+ final var partialCommand = getCommand(input);
+ // Get the expected commands
+ final var recommendations = recommendCommands(partialCommand);
+ if (recommendations.isEmpty()) return;
+ // Execute the given action
+ else action.apply(recommendations);
+ }
+
+ /**
+ * Recommends commands based upon the currently entered input.
+ * In the current implementation, all we check is whether a key contains this
+ * input. This might be updated later on.
+ *
+ * @param partialCommand the partially entered command
+ * @return a set of all commands that match this input
+ * @since Envoy Client v0.2-beta
+ */
+ private Set recommendCommands(String partialCommand) {
+ // current implementation only looks if input is contained within a command,
+ // might be updated
+ return systemCommands.keySet()
+ .stream()
+ .filter(command -> command.contains(partialCommand))
+ .sorted((command1, command2) -> Integer.compare(systemCommands.get(command1).getRelevance(), systemCommands.get(command2).getRelevance()))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ *
+ * Supplies the default values for arguments if none are present in the text for
+ * any argument.
+ * Will only work for {@code SystemCommand}s whose argument counter is bigger
+ * than 1.
+ *
+ * @param textArguments the arguments that were parsed from the text
+ * @param toEvaluate the system command whose default values should be used
+ * @return the final argument list
+ * @since Envoy Client v0.2-beta
+ * @apiNote this method will insert an empty String if the size of the list
+ * given to the {@code SystemCommand} is smaller than its argument
+ * counter and no more text arguments could be found.
+ */
+ private List supplementDefaults(String[] textArguments, SystemCommand toEvaluate) {
+ final var defaults = toEvaluate.getDefaults();
+ final var numberOfArguments = toEvaluate.getNumberOfArguments();
+ final List result = new ArrayList<>();
+
+ if (toEvaluate.getNumberOfArguments() > 0) for (int index = 0; index < numberOfArguments; index++) {
+ String textArg = null;
+ if (index < textArguments.length) textArg = textArguments[index];
+ // Set the argument at position index to the current argument of the text, if it
+ // is present. Otherwise the default for that argument will be taken if present.
+ // In the worst case, an empty String will be used.
+ result.add(!(textArg == null) && !textArg.isBlank() ? textArg : index < defaults.size() ? defaults.get(index) : "");
+ }
+ return result;
+ }
+
+ /**
+ * @return all {@link SystemCommand}s used with the underlying command as key
+ * @since Envoy Client v0.2-beta
+ */
+ public Map getSystemCommands() { return systemCommands; }
+}
diff --git a/client/src/main/java/envoy/client/data/commands/package-info.java b/client/src/main/java/envoy/client/data/commands/package-info.java
new file mode 100644
index 0000000..b3f2b4f
--- /dev/null
+++ b/client/src/main/java/envoy/client/data/commands/package-info.java
@@ -0,0 +1,12 @@
+/**
+ * This package contains all classes that can be used as system commands.
+ * Every system command can be called using a specific syntax:"/<command>"
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 16.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+package envoy.client.data.commands;
diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java
index a01468f..b642141 100644
--- a/client/src/main/java/envoy/client/net/Client.java
+++ b/client/src/main/java/envoy/client/net/Client.java
@@ -155,6 +155,9 @@ public class Client implements Closeable {
// Process group size changes
receiver.registerProcessor(GroupResize.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); });
+ // Process IsTyping events
+ receiver.registerProcessor(IsTyping.class, eventBus::dispatch);
+
// Send event
eventBus.register(SendEvent.class, evt -> {
try {
diff --git a/client/src/main/java/envoy/client/net/Receiver.java b/client/src/main/java/envoy/client/net/Receiver.java
index e325c4f..fdfa1e8 100644
--- a/client/src/main/java/envoy/client/net/Receiver.java
+++ b/client/src/main/java/envoy/client/net/Receiver.java
@@ -1,8 +1,6 @@
package envoy.client.net;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
+import java.io.*;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
@@ -82,8 +80,9 @@ public class Receiver extends Thread {
logger.log(Level.WARNING, String.format("The received object has the %s for which no processor is defined.", obj.getClass()));
else processor.accept(obj);
}
- } catch (final SocketException e) {
+ } catch (final SocketException | EOFException e) {
// Connection probably closed by client.
+ logger.log(Level.FINER, "Exiting receiver...");
return;
} catch (final Exception e) {
logger.log(Level.SEVERE, "Error on receiver thread", e);
diff --git a/client/src/main/java/envoy/client/ui/IconUtil.java b/client/src/main/java/envoy/client/ui/IconUtil.java
index 5324619..f95d1a6 100644
--- a/client/src/main/java/envoy/client/ui/IconUtil.java
+++ b/client/src/main/java/envoy/client/ui/IconUtil.java
@@ -1,9 +1,13 @@
package envoy.client.ui;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.logging.Level;
+import javax.imageio.ImageIO;
+
import javafx.scene.image.Image;
import envoy.client.data.Settings;
@@ -145,6 +149,23 @@ public class IconUtil {
return icons;
}
+ /**
+ * Loads a buffered image from the resource folder which is compatible with AWT.
+ *
+ * @param path the path to the icon inside the resource folder
+ * @return the loaded image
+ * @since Envoy Client v0.2-beta
+ */
+ public static BufferedImage loadAWTCompatible(String path) {
+ BufferedImage image = null;
+ try {
+ image = ImageIO.read(IconUtil.class.getResource(path));
+ } catch (IOException e) {
+ EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
+ }
+ return image;
+ }
+
/**
* This method should be called if the display of an image depends upon the
* currently active theme.
@@ -154,7 +175,7 @@ public class IconUtil {
* @return the theme specific folder
* @since Envoy Client v0.1-beta
*/
- public static String themeSpecificSubFolder() {
+ private static String themeSpecificSubFolder() {
return Settings.getInstance().isUsingDefaultTheme() ? Settings.getInstance().getCurrentTheme() + "/" : "";
}
}
diff --git a/client/src/main/java/envoy/client/ui/StatusTrayIcon.java b/client/src/main/java/envoy/client/ui/StatusTrayIcon.java
index eb0178d..10c5300 100644
--- a/client/src/main/java/envoy/client/ui/StatusTrayIcon.java
+++ b/client/src/main/java/envoy/client/ui/StatusTrayIcon.java
@@ -2,15 +2,13 @@ package envoy.client.ui;
import java.awt.*;
import java.awt.TrayIcon.MessageType;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.logging.Level;
+
+import javafx.application.Platform;
+import javafx.stage.Stage;
import envoy.client.event.MessageCreationEvent;
import envoy.data.Message;
import envoy.event.EventBus;
-import envoy.exception.EnvoyException;
-import envoy.util.EnvoyLog;
/**
* Project: envoy-client
@@ -35,66 +33,65 @@ public class StatusTrayIcon {
*/
private boolean displayMessages = false;
+ /**
+ * @return {@code true} if the status tray icon is supported on this platform
+ * @since Envoy Client v0.2-beta
+ */
+ public static boolean isSupported() { return SystemTray.isSupported(); }
+
/**
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up
* menu.
*
- * @param focusTarget the {@link Window} which focus determines if message
- * notifications are displayed
- * @throws EnvoyException if the currently used OS does not support the System
- * Tray API
- * @since Envoy Client v0.2-alpha
+ * @param stage the stage whose focus determines if message
+ * notifications are displayed
+ * @since Envoy Client v0.2-beta
*/
- public StatusTrayIcon(Window focusTarget) throws EnvoyException {
- if (!SystemTray.isSupported()) throw new EnvoyException("The Envoy tray icon is not supported.");
-
- final ClassLoader loader = Thread.currentThread().getContextClassLoader();
- final Image img = Toolkit.getDefaultToolkit().createImage(loader.getResource("envoy_logo.png"));
- trayIcon = new TrayIcon(img, "Envoy Client");
+ public StatusTrayIcon(Stage stage) {
+ trayIcon = new TrayIcon(IconUtil.loadAWTCompatible("/icons/envoy_logo.png"), "Envoy");
trayIcon.setImageAutoSize(true);
trayIcon.setToolTip("You are notified if you have unread messages.");
final PopupMenu popup = new PopupMenu();
final MenuItem exitMenuItem = new MenuItem("Exit");
- exitMenuItem.addActionListener(evt -> System.exit(0));
+ exitMenuItem.addActionListener(evt -> { Platform.exit(); System.exit(0); });
popup.add(exitMenuItem);
trayIcon.setPopupMenu(popup);
- // Only display messages if the chat window is not focused
- focusTarget.addWindowFocusListener(new WindowAdapter() {
-
- @Override
- public void windowGainedFocus(WindowEvent e) { displayMessages = false; }
-
- @Override
- public void windowLostFocus(WindowEvent e) { displayMessages = true; }
- });
+ // Only display messages if the stage is not focused
+ stage.focusedProperty().addListener((ov, onHidden, onShown) -> displayMessages = !ov.getValue());
// Show the window if the user clicks on the icon
- trayIcon.addActionListener(evt -> { focusTarget.setVisible(true); focusTarget.requestFocus(); });
+ trayIcon.addActionListener(evt -> Platform.runLater(() -> { stage.setIconified(false); stage.toFront(); stage.requestFocus(); }));
// Start processing message events
- // TODO: Handle other message types
- EventBus.getInstance()
- .register(MessageCreationEvent.class,
- evt -> { if (displayMessages) trayIcon.displayMessage("New message received", evt.get().getText(), MessageType.INFO); });
+ EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
+ if (displayMessages) trayIcon
+ .displayMessage(
+ evt.get().hasAttachment() ? "New " + evt.get().getAttachment().getType().toString().toLowerCase() + " message received"
+ : "New message received",
+ evt.get().getText(),
+ MessageType.INFO);
+ });
}
/**
- * Makes this {@link StatusTrayIcon} appear in the system tray.
+ * Makes the icon appear in the system tray.
*
- * @throws EnvoyException if the status icon could not be attaches to the system
- * tray for system-internal reasons
* @since Envoy Client v0.2-alpha
*/
- public void show() throws EnvoyException {
+ public void show() {
try {
SystemTray.getSystemTray().add(trayIcon);
- } catch (final AWTException e) {
- EnvoyLog.getLogger(StatusTrayIcon.class).log(Level.INFO, "Could not display StatusTrayIcon: ", e);
- throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e);
- }
+ } catch (AWTException e) {}
}
+
+ /**
+ * Removes the icon from the system tray.
+ *
+ * @since Envoy Client v0.2-beta
+ */
+ public void hide() { SystemTray.getSystemTray().remove(trayIcon); }
}
diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java
index 94984a8..d8f096e 100644
--- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -8,6 +8,8 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -33,7 +35,10 @@ import javafx.util.Duration;
import envoy.client.data.*;
import envoy.client.data.audio.AudioRecorder;
+import envoy.client.data.commands.SystemCommandBuilder;
+import envoy.client.data.commands.SystemCommandsMap;
import envoy.client.event.MessageCreationEvent;
+import envoy.client.event.SendEvent;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.*;
@@ -126,6 +131,8 @@ public final class ChatScene implements Restorable {
private Attachment pendingAttachment;
private boolean postingPermanentlyDisabled;
+ private final SystemCommandsMap messageTextAreaCommands = new SystemCommandsMap();
+
private static final Settings settings = Settings.getInstance();
private static final EventBus eventBus = EventBus.getInstance();
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
@@ -221,7 +228,7 @@ public final class ChatScene implements Restorable {
switch (e.getOperationType()) {
case ADD:
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
- Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
+ final Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
Platform.runLater(() -> chatList.getItems().add(chat));
break;
case REMOVE:
@@ -231,6 +238,22 @@ public final class ChatScene implements Restorable {
});
}
+ /**
+ * Initializes all {@code SystemCommands} used in {@code ChatScene}.
+ *
+ * @since Envoy Client v0.2-beta
+ */
+ private void initializeSystemCommandsMap() {
+ final var builder = new SystemCommandBuilder();
+ // Do A Barrel roll initialization
+ final var random = new Random();
+ builder.setAction(text -> doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1))))
+ .setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1))
+ .setDescription("See for yourself :)")
+ .setNumberOfArguments(2);
+ messageTextAreaCommands.add("DABR", builder.build());
+ }
+
/**
* Initializes all necessary data via dependency injection-
*
@@ -251,9 +274,12 @@ public final class ChatScene implements Restorable {
chatList.setItems(chats);
contactLabel.setText(localDB.getUser().getName());
MessageControl.setLocalDB(localDB);
+ MessageControl.setSceneContext(sceneContext);
+
if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info");
recorder = new AudioRecorder();
+ initializeSystemCommandsMap();
}
@Override
@@ -362,7 +388,9 @@ public final class ChatScene implements Restorable {
});
recorder.start();
} else {
- pendingAttachment = new Attachment(recorder.finish(), AttachmentType.VOICE);
+ pendingAttachment = new Attachment(recorder.finish(), "Voice_recording_"
+ + DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss").format(LocalDateTime.now()) + "." + AudioRecorder.FILE_FORMAT,
+ AttachmentType.VOICE);
recording = false;
Platform.runLater(() -> {
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
@@ -413,7 +441,7 @@ public final class ChatScene implements Restorable {
// Create the pending attachment
try {
final var fileBytes = Files.readAllBytes(file.toPath());
- pendingAttachment = new Attachment(fileBytes, type);
+ pendingAttachment = new Attachment(fileBytes, file.getName(), type);
checkPostConditions(false);
// Setting the preview image as image of the attachmentView
if (type == AttachmentType.PICTURE)
@@ -426,20 +454,34 @@ public final class ChatScene implements Restorable {
}
/**
- * Rotates every element in our application by 360° in at most 2.75s.
+ * Rotates every element in our application by (at most 4 *) 360° in at most
+ * 2.75s.
*
* @since Envoy Client v0.1-beta
*/
@FXML
private void doABarrelRoll() {
+ final var random = new Random();
+ doABarrelRoll(random.nextInt(3) + 1, random.nextDouble() * 3 + 1);
+ }
+
+ /**
+ * Rotates every element in our application by {@code rotations}*360° in
+ * {@code an}.
+ *
+ * @param rotations the amount of times the scene is rotated by 360°
+ * @param animationTime the time in seconds that this animation lasts
+ * @since Envoy Client v0.1-beta
+ */
+ private void doABarrelRoll(int rotations, double animationTime) {
// contains all Node objects in ChatScene in alphabetical order
- final var rotatableNodes = new Node[] { attachmentButton, attachmentView, contactLabel, infoLabel, messageList, messageTextArea,
- postButton, remainingChars, rotateButton, scene, settingsButton, chatList, voiceButton };
- final var random = new Random();
+ final var rotatableNodes = new Node[] { attachmentButton, attachmentView, contactLabel, infoLabel, messageList, messageTextArea, postButton,
+ remainingChars, rotateButton, scene, settingsButton, chatList, voiceButton };
for (final var node : rotatableNodes) {
- // Defines at most four whole rotation in at most 4s
- final var rotateTransition = new RotateTransition(Duration.seconds(random.nextDouble() * 3 + 1), node);
- rotateTransition.setByAngle((random.nextInt(3) + 1) * 360);
+ // Sets the animation duration to {animationTime}
+ final var rotateTransition = new RotateTransition(Duration.seconds(animationTime), node);
+ // rotates every element {rotations} times
+ rotateTransition.setByAngle(rotations * 360);
rotateTransition.play();
// This is needed as for some strange reason objects could stop before being
// rotated back to 0°
@@ -459,10 +501,28 @@ public final class ChatScene implements Restorable {
private void checkKeyCombination(KeyEvent e) {
// Checks whether the text is too long
messageTextUpdated();
+ // Sending an IsTyping event if none has been sent for
+ // IsTyping#millisecondsActive
+ if (client.isOnline() && currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) {
+ eventBus.dispatch(new SendEvent(new IsTyping(getChatID(), currentChat.getRecipient().getID())));
+ currentChat.lastWritingEventWasNow();
+ }
// Automatic sending of messages via (ctrl +) enter
checkPostConditions(e);
}
+ /**
+ * Returns the id that should be used to send things to the server:
+ * the id of 'our' {@link User} if the recipient of that object is another User,
+ * else the id of the {@link Group} 'our' user is sending to.
+ *
+ * @return an id that can be sent to the server
+ * @since Envoy Client v0.2-beta
+ */
+ private long getChatID() {
+ return currentChat.getRecipient() instanceof User ? client.getSender().getID() : currentChat.getRecipient().getID();
+ }
+
/**
* @param e the keys that have been pressed
* @since Envoy Client v0.1-beta
@@ -531,7 +591,7 @@ public final class ChatScene implements Restorable {
return;
}
final var text = messageTextArea.getText().strip();
- try {
+ if (!messageTextAreaCommands.executeIfAnyPresent(text)) try {
// Creating the message and its metadata
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(text);
@@ -619,10 +679,15 @@ public final class ChatScene implements Restorable {
private void copyAndPostMessage() {
final var messageText = messageTextArea.getText();
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
+ final var image = attachmentView.getImage();
+ final var messageAttachment = pendingAttachment;
postMessage();
messageTextArea.setText(messageText);
updateRemainingCharsLabel();
postButton.setDisable(messageText.isBlank());
+ attachmentView.setImage(image);
+ if (attachmentView.getImage() != null) attachmentView.setVisible(true);
+ pendingAttachment = messageAttachment;
}
@FXML
diff --git a/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java b/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
index e21c646..3e6071a 100644
--- a/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/ContactSearchScene.java
@@ -1,5 +1,6 @@
package envoy.client.ui.controller;
+import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -53,8 +54,20 @@ public class ContactSearchScene {
private LocalDB localDB;
- private static EventBus eventBus = EventBus.getInstance();
- private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
+ private Alert alert = new Alert(AlertType.CONFIRMATION);
+
+ private User currentlySelectedUser;
+
+ private final Consumer handler = e -> {
+ final var contact = e.get();
+ if (e.getOperationType() == ElementOperation.ADD) Platform.runLater(() -> {
+ userList.getItems().remove(contact);
+ if (currentlySelectedUser != null && currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close();
+ });
+ };
+
+ private static final EventBus eventBus = EventBus.getInstance();
+ private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
/**
* @param sceneContext enables the user to return to the chat scene
@@ -72,6 +85,7 @@ public class ContactSearchScene {
searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); userList.getItems().clear(); });
eventBus.register(UserSearchResult.class,
response -> Platform.runLater(() -> { userList.getItems().clear(); userList.getItems().addAll(response.get()); }));
+ eventBus.register(ContactOperation.class, handler);
}
/**
@@ -108,20 +122,21 @@ public class ContactSearchScene {
private void userListClicked() {
final var user = userList.getSelectionModel().getSelectedItem();
if (user != null) {
- final var alert = new Alert(AlertType.CONFIRMATION);
- alert.setTitle("Add Contact to Contact List");
- alert.setHeaderText("Add the user " + user.getName() + " to your contact list?");
+ currentlySelectedUser = user;
+ alert = new Alert(AlertType.CONFIRMATION);
+ alert.setTitle("Add User to Contact List");
+ alert.setHeaderText("Add the user " + currentlySelectedUser.getName() + " to your contact list?");
// Normally, this would be total BS (we are already on the FX Thread), however
// it could be proven that the creation of this dialog wrapped in
// Platform.runLater is less error-prone than without it
Platform.runLater(() -> alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
- final var event = new ContactOperation(user, ElementOperation.ADD);
+ final var event = new ContactOperation(currentlySelectedUser, ElementOperation.ADD);
// Sends the event to the server
eventBus.dispatch(new SendEvent(event));
// Removes the chosen user and updates the UI
- userList.getItems().remove(user);
+ userList.getItems().remove(currentlySelectedUser);
eventBus.dispatch(event);
- logger.log(Level.INFO, "Added user " + user);
+ logger.log(Level.INFO, "Added user " + currentlySelectedUser);
}));
}
}
diff --git a/client/src/main/java/envoy/client/ui/controller/LoginScene.java b/client/src/main/java/envoy/client/ui/controller/LoginScene.java
index 2d12891..d3f1956 100644
--- a/client/src/main/java/envoy/client/ui/controller/LoginScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/LoginScene.java
@@ -17,9 +17,7 @@ import javafx.scene.image.ImageView;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
-import envoy.client.ui.IconUtil;
-import envoy.client.ui.SceneContext;
-import envoy.client.ui.Startup;
+import envoy.client.ui.*;
import envoy.data.LoginCredentials;
import envoy.data.User;
import envoy.data.User.UserStatus;
@@ -77,6 +75,7 @@ public final class LoginScene {
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
private static final EventBus eventBus = EventBus.getInstance();
private static final ClientConfig config = ClientConfig.getInstance();
+ private static final Settings settings = Settings.getInstance();
@FXML
private void initialize() {
@@ -239,5 +238,23 @@ public final class LoginScene {
sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE);
sceneContext.getController().initializeData(sceneContext, localDB, client, writeProxy);
sceneContext.getStage().centerOnScreen();
+
+ if (StatusTrayIcon.isSupported()) {
+
+ // Configure hide on close
+ sceneContext.getStage().setOnCloseRequest(e -> {
+ if (settings.isHideOnClose()) {
+ sceneContext.getStage().setIconified(true);
+ e.consume();
+ }
+ });
+
+ // Initialize status tray icon
+ final var trayIcon = new StatusTrayIcon(sceneContext.getStage());
+ settings.getItems().get("hideOnClose").setChangeHandler(c -> {
+ if ((Boolean) c) trayIcon.show();
+ else trayIcon.hide();
+ });
+ }
}
}
diff --git a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java
index b7cc49d..f3358a9 100644
--- a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java
+++ b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java
@@ -4,6 +4,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.*;
import envoy.client.ui.SceneContext;
+import envoy.client.ui.settings.DownloadSettingsPane;
import envoy.client.ui.settings.GeneralSettingsPane;
import envoy.client.ui.settings.SettingsPane;
@@ -29,7 +30,10 @@ public class SettingsScene {
* @param sceneContext enables the user to return to the chat scene
* @since Envoy Client v0.1-beta
*/
- public void initializeData(SceneContext sceneContext) { this.sceneContext = sceneContext; }
+ public void initializeData(SceneContext sceneContext) {
+ this.sceneContext = sceneContext;
+ settingsList.getItems().add(new DownloadSettingsPane(sceneContext));
+ }
@FXML
private void initialize() {
diff --git a/client/src/main/java/envoy/client/ui/listcell/ChatControl.java b/client/src/main/java/envoy/client/ui/listcell/ChatControl.java
index 39086ca..8e2a698 100644
--- a/client/src/main/java/envoy/client/ui/listcell/ChatControl.java
+++ b/client/src/main/java/envoy/client/ui/listcell/ChatControl.java
@@ -53,12 +53,12 @@ public class ChatControl extends HBox {
getChildren().add(new ContactControl(chat.getRecipient()));
// Unread messages
if (chat.getUnreadAmount() != 0) {
- Region spacing = new Region();
+ final var spacing = new Region();
setHgrow(spacing, Priority.ALWAYS);
getChildren().add(spacing);
final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount()));
unreadMessagesLabel.setMinSize(15, 15);
- var vBox2 = new VBox();
+ final var vBox2 = new VBox();
vBox2.setAlignment(Pos.CENTER_RIGHT);
unreadMessagesLabel.setAlignment(Pos.CENTER);
unreadMessagesLabel.getStyleClass().add("unreadMessagesAmount");
diff --git a/client/src/main/java/envoy/client/ui/listcell/MessageControl.java b/client/src/main/java/envoy/client/ui/listcell/MessageControl.java
index 9d2d32a..5a0c640 100644
--- a/client/src/main/java/envoy/client/ui/listcell/MessageControl.java
+++ b/client/src/main/java/envoy/client/ui/listcell/MessageControl.java
@@ -2,7 +2,7 @@ package envoy.client.ui.listcell;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
-import java.io.ByteArrayInputStream;
+import java.io.*;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
@@ -17,10 +17,14 @@ import javafx.scene.control.MenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
+import javafx.stage.FileChooser;
import envoy.client.data.LocalDB;
+import envoy.client.data.Settings;
import envoy.client.ui.AudioControl;
import envoy.client.ui.IconUtil;
+import envoy.client.ui.SceneContext;
+
import envoy.data.GroupMessage;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
@@ -44,9 +48,12 @@ public class MessageControl extends Label {
private static LocalDB localDB;
+ private static SceneContext sceneContext;
+
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
+ private static final Settings settings = Settings.getInstance();
private static final Logger logger = EnvoyLog.getLogger(MessageControl.class);
/**
@@ -149,7 +156,26 @@ public class MessageControl extends Label {
private void loadMessageInfoScene(Message message) { logger.log(Level.FINEST, "message info scene was requested for " + message); }
- private void saveAttachment(Message message) { logger.log(Level.FINEST, "attachment saving was requested for " + message); }
+ private void saveAttachment(Message message) {
+ File file;
+ final var fileName = message.getAttachment().getName();
+ final var downloadLocation = settings.getDownloadLocation();
+ // Show save file dialog, if the user did not opt-out
+ if (!settings.isDownloadSavedWithoutAsking()) {
+ final var fileChooser = new FileChooser();
+ fileChooser.setInitialFileName(fileName);
+ fileChooser.setInitialDirectory(downloadLocation);
+ file = fileChooser.showSaveDialog(sceneContext.getStage());
+ } else file = new File(downloadLocation, fileName);
+
+ // A file was selected
+ if (file != null) try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(message.getAttachment().getData());
+ logger.log(Level.FINE, "Attachment of message was saved at " + file.getAbsolutePath());
+ } catch (final IOException e) {
+ logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e);
+ }
+ }
/**
* @param localDB the localDB
@@ -163,4 +189,10 @@ public class MessageControl extends Label {
* @since Envoy Client v0.1-beta
*/
public boolean isOwnMessage() { return ownMessage; }
+
+ /**
+ * @param sceneContext the scene context storing the stage used in Envoy
+ * @since Envoy Client v0.1-beta
+ */
+ public static void setSceneContext(SceneContext sceneContext) { MessageControl.sceneContext = sceneContext; }
}
diff --git a/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java
new file mode 100644
index 0000000..bdc1352
--- /dev/null
+++ b/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java
@@ -0,0 +1,65 @@
+package envoy.client.ui.settings;
+
+import javafx.geometry.Insets;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.DirectoryChooser;
+
+import envoy.client.ui.SceneContext;
+
+/**
+ * Displays options for downloading {@link envoy.data.Attachment}s.
+ *
+ * Project: envoy-client
+ * File: DownloadSettingsPane.java
+ * Created: 27.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public class DownloadSettingsPane extends SettingsPane {
+
+ /**
+ * Constructs a new {@code DownloadSettingsPane}.
+ *
+ * @param sceneContext the {@code SceneContext} used to block input to the
+ * {@link javafx.stage.Stage} used in Envoy
+ * @since Envoy Client v0.2-beta
+ */
+ public DownloadSettingsPane(SceneContext sceneContext) {
+ super("Download");
+ final var vbox = new VBox(15);
+ vbox.setPadding(new Insets(15));
+ // checkbox to disable asking
+ final var checkBox = new CheckBox(settings.getItems().get("autoSaveDownloads").getUserFriendlyName());
+ checkBox.setSelected(settings.isDownloadSavedWithoutAsking());
+ checkBox.setOnAction(e -> settings.setDownloadSavedWithoutAsking(checkBox.isSelected()));
+ vbox.getChildren().add(checkBox);
+
+ // Displaying the default path to save to
+ vbox.getChildren().add(new Label(settings.getItems().get("downloadLocation").getDescription() + ":"));
+ final var hbox = new HBox(20);
+ final var currentPath = new Label(settings.getDownloadLocation().getAbsolutePath());
+ hbox.getChildren().add(currentPath);
+
+ // Setting the default path
+ final var button = new Button("Select");
+ button.setOnAction(e -> {
+ final var directoryChooser = new DirectoryChooser();
+ directoryChooser.setTitle("Select the directory where attachments should be saved to");
+ directoryChooser.setInitialDirectory(settings.getDownloadLocation());
+ final var selectedDirectory = directoryChooser.showDialog(sceneContext.getStage());
+
+ if (selectedDirectory != null) {
+ currentPath.setText(selectedDirectory.getAbsolutePath());
+ settings.setDownloadLocation(selectedDirectory);
+ }
+ });
+ hbox.getChildren().add(button);
+ vbox.getChildren().add(hbox);
+ getChildren().add(vbox);
+ }
+}
diff --git a/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java
index e6d5761..af46fbb 100644
--- a/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java
+++ b/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java
@@ -5,7 +5,6 @@ import java.util.List;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
-import envoy.client.data.Settings;
import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent;
import envoy.data.User.UserStatus;
@@ -21,8 +20,6 @@ import envoy.event.EventBus;
*/
public class GeneralSettingsPane extends SettingsPane {
- private static final Settings settings = Settings.getInstance();
-
/**
* @since Envoy Client v0.1-beta
*/
@@ -31,7 +28,7 @@ public class GeneralSettingsPane extends SettingsPane {
final var vbox = new VBox();
// TODO: Support other value types
- List.of("onCloseMode", "enterToSend")
+ List.of("hideOnClose", "enterToSend")
.stream()
.map(settings.getItems()::get)
.map(i -> new SettingsCheckbox((SettingsItem) i))
diff --git a/client/src/main/java/envoy/client/ui/settings/SettingsPane.java b/client/src/main/java/envoy/client/ui/settings/SettingsPane.java
index 7c84221..49f8abd 100644
--- a/client/src/main/java/envoy/client/ui/settings/SettingsPane.java
+++ b/client/src/main/java/envoy/client/ui/settings/SettingsPane.java
@@ -2,11 +2,13 @@ package envoy.client.ui.settings;
import javafx.scene.layout.Pane;
+import envoy.client.data.Settings;
+
/**
* Project: envoy-client
* File: SettingsPane.java
* Created: 18.04.2020
- *
+ *
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
@@ -14,6 +16,8 @@ public abstract class SettingsPane extends Pane {
protected String title;
+ protected static final Settings settings = Settings.getInstance();
+
protected SettingsPane(String title) { this.title = title; }
/**
diff --git a/common/.settings/org.eclipse.jdt.ui.prefs b/common/.settings/org.eclipse.jdt.ui.prefs
index 1d718a1..3110cf7 100644
--- a/common/.settings/org.eclipse.jdt.ui.prefs
+++ b/common/.settings/org.eclipse.jdt.ui.prefs
@@ -6,4 +6,4 @@ org.eclipse.jdt.ui.importorder=java;javax;javafx;org;com;envoy;
org.eclipse.jdt.ui.javadoc=true
org.eclipse.jdt.ui.ondemandthreshold=4
org.eclipse.jdt.ui.staticondemandthreshold=2
-org.eclipse.jdt.ui.text.custom_code_templates=/**\r\n * @return the ${bare_field_name}\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * @param ${param} the ${bare_field_name} to set\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * ${tags}\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * Project\: <strong>${project_name}</strong><br>\r\n * File\: <strong>${file_name}</strong><br>\r\n * Created\: <strong>${date}</strong><br>\r\n *\r\n * @author ${user}\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * ${tags}\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n * @since Envoy Client v0.1-beta\r\n *//**\r\n * {@inheritDoc}\r\n *//**\r\n * ${tags}\r\n * ${see_to_target}\r\n * @since Envoy Client v0.1-beta\r\n */${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}\r\n\r\n\r\n\r\n${exception_var}.printStackTrace();${body_statement}${body_statement}return ${field};${field} \= ${param};
+org.eclipse.jdt.ui.text.custom_code_templates=/**\r\n * @return the ${bare_field_name}\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * @param ${param} the ${bare_field_name} to set\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * ${tags}\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * Project\: <strong>${project_name}</strong><br>\r\n * File\: <strong>${file_name}</strong><br>\r\n * Created\: <strong>${date}</strong><br>\r\n *\r\n * @author ${user}\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * ${tags}\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n * @since Envoy Common v0.2-beta\r\n *//**\r\n * ${tags}\r\n * ${see_to_target}\r\n * @since Envoy Common v0.2-beta\r\n */${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}\r\n\r\n\r\n\r\n${exception_var}.printStackTrace();${body_statement}${body_statement}return ${field};${field} \= ${param};
diff --git a/common/pom.xml b/common/pom.xml
index e7b41b8..190ddec 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -24,46 +24,5 @@
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.1
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 3.2.1
-
-
- attach-sources
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.1.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
- none
-
-
-
diff --git a/common/src/main/java/envoy/data/Attachment.java b/common/src/main/java/envoy/data/Attachment.java
index 177441b..e231a76 100644
--- a/common/src/main/java/envoy/data/Attachment.java
+++ b/common/src/main/java/envoy/data/Attachment.java
@@ -18,29 +18,28 @@ public class Attachment implements Serializable {
/**
* Defines the type of the attachment.
- *
+ *
* @since Envoy Common v0.1-beta
*/
public enum AttachmentType {
/**
* This attachment type denotes a picture.
- *
+ *
* @since Envoy Common v0.1-beta
*/
PICTURE,
-
/**
* This attachment type denotes a video.
- *
+ *
* @since Envoy Common v0.1-beta
*/
VIDEO,
-
+
/**
* This attachment type denotes a voice message.
- *
+ *
* @since Envoy Common v0.1-beta
*/
VOICE,
@@ -55,18 +54,21 @@ public class Attachment implements Serializable {
private final byte[] data;
private final AttachmentType type;
+ private final String name;
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
/**
* Constructs an attachment.
- *
+ *
* @param data the data of the attachment
+ * @param name the name of the attachment
* @param type the type of the attachment
* @since Envoy Common v0.1-beta
*/
- public Attachment(byte[] data, AttachmentType type) {
+ public Attachment(byte[] data, String name, AttachmentType type) {
this.data = data;
+ this.name = name;
this.type = type;
}
@@ -81,4 +83,10 @@ public class Attachment implements Serializable {
* @since Envoy Common v0.1-beta
*/
public AttachmentType getType() { return type; }
+
+ /**
+ * @return the name
+ * @since Envoy Common v0.2-beta
+ */
+ public String getName() { return name; }
}
diff --git a/common/src/main/java/envoy/event/IsTyping.java b/common/src/main/java/envoy/event/IsTyping.java
new file mode 100644
index 0000000..f720459
--- /dev/null
+++ b/common/src/main/java/envoy/event/IsTyping.java
@@ -0,0 +1,45 @@
+package envoy.event;
+
+/**
+ * This event should be sent when a user is currently typing something in a
+ * chat.
+ *
+ * Project: envoy-client
+ * File: IsTyping.java
+ * Created: 24.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Client v0.2-beta
+ */
+public class IsTyping extends Event {
+
+ private final long destinationID;
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The number of milliseconds that this event will be active.
+ * Currently set to 3.5 seconds.
+ *
+ * @since Envoy Common v0.2-beta
+ */
+ public static final int millisecondsActive = 3500;
+
+ /**
+ * Creates a new {@code IsTyping} event with originator and recipient.
+ *
+ * @param sourceID the id of the originator
+ * @param destinationID the id of the contact the user wrote to
+ * @since Envoy Common v0.2-beta
+ */
+ public IsTyping(Long sourceID, long destinationID) {
+ super(sourceID);
+ this.destinationID = destinationID;
+ }
+
+ /**
+ * @return the id of the contact in whose chat the user typed something
+ * @since Envoy Common v0.2-beta
+ */
+ public long getDestinationID() { return destinationID; }
+}
diff --git a/pom.xml b/pom.xml
index d4d20e3..cf7d21c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,18 @@
11
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+
+
+
common
client
diff --git a/server/.settings/org.eclipse.jdt.ui.prefs b/server/.settings/org.eclipse.jdt.ui.prefs
index cb37178..d951389 100755
--- a/server/.settings/org.eclipse.jdt.ui.prefs
+++ b/server/.settings/org.eclipse.jdt.ui.prefs
@@ -2,4 +2,4 @@ eclipse.preferences.version=1
formatter_profile=_KSKE
formatter_settings_version=18
org.eclipse.jdt.ui.javadoc=true
-org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * @param ${param} the ${bare_field_name} to set\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * Creates an instance of @link{${enclosing_type}}.\n *\n * ${tags}\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * Project\: <strong>${project_name}</strong><br>\n * File\: <strong>${file_name}</strong><br>\n * Created\: <strong>${date}</strong><br>\n * \n * @author ${user}\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * ${tags}\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * @author ${user}\n *\n * ${tags}\n * @since Envoy Server Standalone v0.1-beta\n *//**\n * {@inheritDoc}\n *//**\n * ${tags}\n * ${see_to_target}\n * @since Envoy Server Standalone v0.1-beta\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n${exception_var}.printStackTrace();${body_statement}\n${body_statement}\nreturn ${field};${field} \= ${param};
+org.eclipse.jdt.ui.text.custom_code_templates=/**\r\n * @return the ${bare_field_name}\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * @param ${param} the ${bare_field_name} to set\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * Creates an instance of @link{${enclosing_type}}.\r\n *\r\n * ${tags}\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * Project\: <strong>${project_name}</strong><br>\r\n * File\: <strong>${file_name}</strong><br>\r\n * Created\: <strong>${date}</strong><br>\r\n * \r\n * @author ${user}\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * ${tags}\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n * @since Envoy Server v0.2-beta\r\n *//**\r\n * ${tags}\r\n * ${see_to_target}\r\n * @since Envoy Server v0.2-beta\r\n */${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}\r\n\r\n\r\n\r\n${exception_var}.printStackTrace();${body_statement}\r\n${body_statement}\r\nreturn ${field};${field} \= ${param};
diff --git a/server/pom.xml b/server/pom.xml
index 8b4c4c9..488cfab 100755
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -49,15 +49,6 @@
src/main/resources
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.1
-
-
-
org.apache.maven.plugins
diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java
index 489a060..ac24c40 100755
--- a/server/src/main/java/envoy/server/Startup.java
+++ b/server/src/main/java/envoy/server/Startup.java
@@ -69,7 +69,8 @@ public class Startup {
new UserStatusChangeProcessor(),
new IDGeneratorRequestProcessor(),
new UserSearchProcessor(),
- new ContactOperationProcessor())));
+ new ContactOperationProcessor(),
+ new IsTypingProcessor())));
// Initialize the current message ID
final PersistenceManager persistenceManager = PersistenceManager.getInstance();
diff --git a/server/src/main/java/envoy/server/data/Message.java b/server/src/main/java/envoy/server/data/Message.java
index fc564a2..ead0fde 100755
--- a/server/src/main/java/envoy/server/data/Message.java
+++ b/server/src/main/java/envoy/server/data/Message.java
@@ -47,7 +47,7 @@ public class Message {
/**
* Named query retrieving pending messages for a user (parameter {@code :user})
* which was last seen after a specific date (parameter {@code :lastSeen}).
- *
+ *
* @since Envoy Server Standalone v0.1-beta
*/
public static final String getPending = "Message.getPending";
@@ -76,6 +76,7 @@ public class Message {
protected envoy.data.Message.MessageStatus status;
protected AttachmentType attachmentType;
protected byte[] attachment;
+ protected String attachmentName;
protected boolean forwarded;
/**
@@ -93,7 +94,7 @@ public class Message {
* @since Envoy Server Standalone v0.1-alpha
*/
public Message(envoy.data.Message message) {
- PersistenceManager persistenceManager = PersistenceManager.getInstance();
+ final var persistenceManager = PersistenceManager.getInstance();
id = message.getID();
status = message.getStatus();
text = message.getText();
@@ -104,8 +105,10 @@ public class Message {
recipient = persistenceManager.getContactByID(message.getRecipientID());
forwarded = message.isForwarded();
if (message.hasAttachment()) {
- attachment = message.getAttachment().getData();
- attachmentType = message.getAttachment().getType();
+ final var messageAttachment = message.getAttachment();
+ attachment = messageAttachment.getData();
+ attachmentName = messageAttachment.getName();
+ attachmentType = messageAttachment.getType();
}
}
@@ -123,20 +126,20 @@ public class Message {
* @since Envoy Server Standalone v0.1-beta
*/
MessageBuilder prepareBuilder() {
- var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText(text)
+ final var builder = new MessageBuilder(sender.getID(), recipient.getID(), id).setText(text)
.setCreationDate(creationDate)
.setReceivedDate(receivedDate)
.setReadDate(readDate)
.setStatus(status)
.setForwarded(forwarded);
- if (attachment != null) builder.setAttachment(new Attachment(attachment, attachmentType));
+ if (attachment != null) builder.setAttachment(new Attachment(attachment, attachmentName, attachmentType));
return builder;
}
/**
* Sets the message status to {@link MessageStatus#RECEIVED} and sets the
* current time stamp as the received date.
- *
+ *
* @since Envoy Server Standalone v0.1-beta
*/
public void received() {
@@ -147,7 +150,7 @@ public class Message {
/**
* Sets the message status to {@link MessageStatus#READ} and sets the
* current time stamp as the read date.
- *
+ *
* @since Envoy Server Standalone v0.1-beta
*/
public void read() {
@@ -282,6 +285,18 @@ public class Message {
*/
public void setAttachmentType(AttachmentType attachmentType) { this.attachmentType = attachmentType; }
+ /**
+ * @return the attachmentName
+ * @since Envoy Server v0.2-beta
+ */
+ public String getAttachmentName() { return attachmentName; }
+
+ /**
+ * @param attachmentName the attachmentName to set
+ * @since Envoy Server v0.2-beta
+ */
+ public void setAttachmentName(String attachmentName) { this.attachmentName = attachmentName; }
+
/**
* @return whether this message is a forwarded message
* @since Envoy Server Standalone v0.1-alpha
diff --git a/server/src/main/java/envoy/server/net/ObjectMessageProcessor.java b/server/src/main/java/envoy/server/net/ObjectMessageProcessor.java
index ec8488a..d75aa75 100755
--- a/server/src/main/java/envoy/server/net/ObjectMessageProcessor.java
+++ b/server/src/main/java/envoy/server/net/ObjectMessageProcessor.java
@@ -3,6 +3,7 @@ package envoy.server.net;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.lang.reflect.ParameterizedType;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -50,14 +51,19 @@ public class ObjectMessageProcessor implements IMessageProcessor {
logger.fine("Received " + obj);
- // Process object
- processors.stream().filter(p -> p.getInputClass().equals(obj.getClass())).forEach((@SuppressWarnings("rawtypes") ObjectProcessor p) -> {
- try {
- p.process(p.getInputClass().cast(obj), message.socketId, new ObjectWriteProxy(writeProxy));
- } catch (IOException e) {
- logger.log(Level.SEVERE, "Exception during processor execution: ", e);
+ // Get processor and input class and process object
+ for (@SuppressWarnings("rawtypes")
+ ObjectProcessor p : processors) {
+ Class> c = (Class>) ((ParameterizedType) p.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
+ if (c.equals(obj.getClass())) {
+ try {
+ p.process(c.cast(obj), message.socketId, new ObjectWriteProxy(writeProxy));
+ break;
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Exception during processor execution: ", e);
+ }
}
- });
+ }
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
diff --git a/server/src/main/java/envoy/server/processors/ContactOperationProcessor.java b/server/src/main/java/envoy/server/processors/ContactOperationProcessor.java
index 4363f60..60952d3 100755
--- a/server/src/main/java/envoy/server/processors/ContactOperationProcessor.java
+++ b/server/src/main/java/envoy/server/processors/ContactOperationProcessor.java
@@ -41,7 +41,4 @@ public class ContactOperationProcessor implements ObjectProcessor getInputClass() { return ContactOperation.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/GroupCreationProcessor.java b/server/src/main/java/envoy/server/processors/GroupCreationProcessor.java
index 78b1596..06a85a0 100644
--- a/server/src/main/java/envoy/server/processors/GroupCreationProcessor.java
+++ b/server/src/main/java/envoy/server/processors/GroupCreationProcessor.java
@@ -40,7 +40,4 @@ public class GroupCreationProcessor implements ObjectProcessor {
.map(connectionManager::getSocketID)
.forEach(memberSocketID -> writeProxy.write(memberSocketID, new ContactOperation(group.toCommon(), ElementOperation.ADD)));
}
-
- @Override
- public Class getInputClass() { return GroupCreation.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java b/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java
index 8be1a57..2099b76 100644
--- a/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java
+++ b/server/src/main/java/envoy/server/processors/GroupMessageProcessor.java
@@ -61,7 +61,4 @@ public class GroupMessageProcessor implements ObjectProcessor {
logger.warning("Received a groupMessage with an ID that already exists");
}
}
-
- @Override
- public Class getInputClass() { return GroupMessage.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java
index 9e1077b..e24902e 100644
--- a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java
+++ b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java
@@ -63,7 +63,4 @@ public class GroupMessageStatusChangeProcessor implements ObjectProcessor getInputClass() { return GroupMessageStatusChange.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java
index a571811..fede288 100644
--- a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java
+++ b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java
@@ -47,7 +47,4 @@ public class GroupResizeProcessor implements ObjectProcessor {
.map(connectionManager::getSocketID)
.forEach(memberSocketID -> writeProxy.write(memberSocketID, commonGroup));
}
-
- @Override
- public Class getInputClass() { return GroupResize.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/IDGeneratorRequestProcessor.java b/server/src/main/java/envoy/server/processors/IDGeneratorRequestProcessor.java
index c2f423a..73c7f11 100755
--- a/server/src/main/java/envoy/server/processors/IDGeneratorRequestProcessor.java
+++ b/server/src/main/java/envoy/server/processors/IDGeneratorRequestProcessor.java
@@ -21,9 +21,6 @@ public class IDGeneratorRequestProcessor implements ObjectProcessor getInputClass() { return IDGeneratorRequest.class; }
-
@Override
public void process(IDGeneratorRequest input, long socketID, ObjectWriteProxy writeProxy) throws IOException {
writeProxy.write(socketID, createIDGenerator());
diff --git a/server/src/main/java/envoy/server/processors/IsTypingProcessor.java b/server/src/main/java/envoy/server/processors/IsTypingProcessor.java
new file mode 100644
index 0000000..bda22ee
--- /dev/null
+++ b/server/src/main/java/envoy/server/processors/IsTypingProcessor.java
@@ -0,0 +1,34 @@
+package envoy.server.processors;
+
+import java.io.IOException;
+
+import envoy.event.IsTyping;
+import envoy.server.data.PersistenceManager;
+import envoy.server.data.User;
+import envoy.server.net.ConnectionManager;
+import envoy.server.net.ObjectWriteProxy;
+
+/**
+ * This processor handles incoming {@link IsTyping} events.
+ *
+ * Project: envoy-server-standalone
+ * File: IsTypingProcessor.java
+ * Created: 24.07.2020
+ *
+ * @author Leon Hofmeister
+ * @since Envoy Server v0.2-beta
+ */
+public class IsTypingProcessor implements ObjectProcessor {
+
+ private static final ConnectionManager connectionManager = ConnectionManager.getInstance();
+ private static final PersistenceManager persistenceManager = PersistenceManager.getInstance();
+
+ @Override
+ public void process(IsTyping event, long socketID, ObjectWriteProxy writeProxy) throws IOException {
+ final var contact = persistenceManager.getContactByID(event.get());
+ if (contact instanceof User) {
+ final var destinationID = event.getDestinationID();
+ if (connectionManager.isOnline(destinationID)) writeProxy.write(connectionManager.getSocketID(destinationID), event);
+ } else writeProxy.writeToOnlineContacts(contact.getContacts(), event);
+ }
+}
diff --git a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java
index 598d2d4..87da681 100755
--- a/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java
+++ b/server/src/main/java/envoy/server/processors/LoginCredentialProcessor.java
@@ -191,7 +191,4 @@ public final class LoginCredentialProcessor implements ObjectProcessor getInputClass() { return LoginCredentials.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/MessageProcessor.java b/server/src/main/java/envoy/server/processors/MessageProcessor.java
index e4058e2..ae34581 100755
--- a/server/src/main/java/envoy/server/processors/MessageProcessor.java
+++ b/server/src/main/java/envoy/server/processors/MessageProcessor.java
@@ -58,7 +58,4 @@ public class MessageProcessor implements ObjectProcessor {
logger.log(Level.WARNING, "Received " + message + " with an ID that already exists!");
}
}
-
- @Override
- public Class getInputClass() { return Message.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java
index 1c76f77..507a83f 100755
--- a/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java
+++ b/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java
@@ -42,7 +42,4 @@ public class MessageStatusChangeProcessor implements ObjectProcessor getInputClass() { return MessageStatusChange.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/NameChangeProcessor.java b/server/src/main/java/envoy/server/processors/NameChangeProcessor.java
index 75f713c..7e782f7 100644
--- a/server/src/main/java/envoy/server/processors/NameChangeProcessor.java
+++ b/server/src/main/java/envoy/server/processors/NameChangeProcessor.java
@@ -28,7 +28,4 @@ public class NameChangeProcessor implements ObjectProcessor {
// Notify online contacts of the name change
writeProxy.writeToOnlineContacts(toUpdate.getContacts(), nameChange);
}
-
- @Override
- public Class getInputClass() { return NameChange.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/ObjectProcessor.java b/server/src/main/java/envoy/server/processors/ObjectProcessor.java
index a542d8f..ab6666e 100755
--- a/server/src/main/java/envoy/server/processors/ObjectProcessor.java
+++ b/server/src/main/java/envoy/server/processors/ObjectProcessor.java
@@ -18,12 +18,6 @@ import envoy.server.net.ObjectWriteProxy;
*/
public interface ObjectProcessor {
- /**
- * @return the class of the request object
- * @since Envoy Server Standalone v0.1-alpha
- */
- Class getInputClass();
-
/**
* @param input the request object
* @param socketID the ID of the socket from which the object was received
diff --git a/server/src/main/java/envoy/server/processors/UserSearchProcessor.java b/server/src/main/java/envoy/server/processors/UserSearchProcessor.java
index 73d9558..65679fb 100755
--- a/server/src/main/java/envoy/server/processors/UserSearchProcessor.java
+++ b/server/src/main/java/envoy/server/processors/UserSearchProcessor.java
@@ -38,7 +38,4 @@ public class UserSearchProcessor implements ObjectProcessor {
.map(User::toCommon)
.collect(Collectors.toList())));
}
-
- @Override
- public Class getInputClass() { return UserSearchRequest.class; }
}
diff --git a/server/src/main/java/envoy/server/processors/UserStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/UserStatusChangeProcessor.java
index 87dd179..264f263 100755
--- a/server/src/main/java/envoy/server/processors/UserStatusChangeProcessor.java
+++ b/server/src/main/java/envoy/server/processors/UserStatusChangeProcessor.java
@@ -26,9 +26,6 @@ public class UserStatusChangeProcessor implements ObjectProcessor getInputClass() { return UserStatusChange.class; }
-
@Override
public void process(UserStatusChange input, long socketID, ObjectWriteProxy writeProxy) {
// new status should not equal old status