diff --git a/src/main/java/envoy/client/data/ClientConfig.java b/src/main/java/envoy/client/data/ClientConfig.java
index a30dffb..49e7892 100644
--- a/src/main/java/envoy/client/data/ClientConfig.java
+++ b/src/main/java/envoy/client/data/ClientConfig.java
@@ -1,7 +1,8 @@
package envoy.client.data;
+import static java.util.function.Function.identity;
+
import java.io.File;
-import java.util.function.Function;
import java.util.logging.Level;
import envoy.client.ui.Startup;
@@ -34,15 +35,15 @@ public class ClientConfig extends Config {
}
private ClientConfig() {
- items.put("server", new ConfigItem<>("server", "s", Function.identity(), null, true));
+ items.put("server", new ConfigItem<>("server", "s", identity(), null, true));
items.put("port", new ConfigItem<>("port", "p", Integer::parseInt, null, true));
items.put("localDB", new ConfigItem<>("localDB", "db", File::new, new File("localDB"), true));
items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", Boolean::parseBoolean, false, false));
items.put("homeDirectory", new ConfigItem<>("homeDirectory", "h", File::new, new File(System.getProperty("user.home"), ".envoy"), true));
items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.CONFIG, true));
items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.FINEST, true));
- items.put("user", new ConfigItem<>("user", "u", Function.identity()));
- items.put("password", new ConfigItem<>("password", "pw", String::toCharArray));
+ items.put("user", new ConfigItem<>("user", "u", identity()));
+ items.put("password", new ConfigItem<>("password", "pw", identity()));
}
/**
@@ -97,7 +98,7 @@ public class ClientConfig extends Config {
* @return the password
* @since Envoy Client v0.3-alpha
*/
- public char[] getPassword() { return (char[]) items.get("password").get(); }
+ public String getPassword() { return (String) items.get("password").get(); }
/**
* @return {@code true} if user name and password are set
diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java
index 36f4a7f..4175f17 100644
--- a/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -2,7 +2,10 @@ package envoy.client.ui.controller;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
+import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -12,10 +15,12 @@ import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
+import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
+import javafx.stage.FileChooser;
import envoy.client.data.*;
import envoy.client.data.audio.AudioRecorder;
@@ -60,6 +65,9 @@ public final class ChatScene implements Restorable {
@FXML
private Button voiceButton;
+ @FXML
+ private Button attachmentButton;
+
@FXML
private Button settingsButton;
@@ -87,12 +95,15 @@ public final class ChatScene implements Restorable {
private AudioRecorder recorder;
private boolean recording;
private Attachment pendingAttachment;
- private boolean postingPermanentlyDisabled = false;
+ private boolean postingPermanentlyDisabled;
- private static final Settings settings = Settings.getInstance();
- private static final EventBus eventBus = EventBus.getInstance();
- private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
- private static final int MAX_MESSAGE_LENGTH = 255;
+ private static final Settings settings = Settings.getInstance();
+ private static final EventBus eventBus = EventBus.getInstance();
+ private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
+
+ private static final Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
+ private static final int MAX_MESSAGE_LENGTH = 255;
+ private static final int DEFAULT_ICON_SIZE = 16;
/**
* Initializes the appearance of certain visual components.
@@ -106,8 +117,10 @@ public final class ChatScene implements Restorable {
messageList.setCellFactory(MessageListCellFactory::new);
userList.setCellFactory(ContactListCellFactory::new);
- settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", 16)));
- voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
+ settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
+ attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
+ attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
// Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> {
@@ -229,7 +242,7 @@ public final class ChatScene implements Restorable {
recording = false;
}
pendingAttachment = null;
- attachmentView.setVisible(false);
+ updateAttachmentView(false);
remainingChars.setVisible(true);
remainingChars
@@ -237,6 +250,7 @@ public final class ChatScene implements Restorable {
}
messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
voiceButton.setDisable(!recorder.isSupported());
+ attachmentButton.setDisable(false);
userList.refresh();
}
@@ -270,18 +284,17 @@ public final class ChatScene implements Restorable {
recording = true;
Platform.runLater(() -> {
voiceButton.setText("Recording");
- voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", 24)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", DEFAULT_ICON_SIZE)));
});
recorder.start();
} else {
pendingAttachment = new Attachment(recorder.finish(), AttachmentType.VOICE);
recording = false;
Platform.runLater(() -> {
- voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
voiceButton.setText(null);
checkPostConditions(false);
- attachmentView.setImage(IconUtil.loadIconThemeSensitive("attachment_present", 20));
- attachmentView.setVisible(true);
+ updateAttachmentView(true);
});
}
} catch (final EnvoyException e) {
@@ -291,6 +304,52 @@ public final class ChatScene implements Restorable {
}).start();
}
+ @FXML
+ private void attachmentButtonClicked() {
+
+ // Display file chooser
+ final var fileChooser = new FileChooser();
+ fileChooser.setTitle("Add Attachment");
+ fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
+ fileChooser.getExtensionFilters()
+ .addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"),
+ new FileChooser.ExtensionFilter("Videos", "*.mp4"),
+ new FileChooser.ExtensionFilter("All Files", "*.*"));
+ final var file = fileChooser.showOpenDialog(sceneContext.getStage());
+
+ if (file != null) {
+
+ // Check max file size
+ if (file.length() > 16E6) {
+ new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 16MB!").showAndWait();
+ return;
+ }
+
+ // Get attachment type (default is document)
+ AttachmentType type = AttachmentType.DOCUMENT;
+ switch (fileChooser.getSelectedExtensionFilter().getDescription()) {
+ case "Pictures":
+ type = AttachmentType.PICTURE;
+ break;
+ case "Videos":
+ type = AttachmentType.VIDEO;
+ break;
+ }
+
+ // Create the pending attachment
+ try {
+ final var fileBytes = Files.readAllBytes(file.toPath());
+ pendingAttachment = new Attachment(fileBytes, type);
+ // Setting the preview image as image of the attachmentView
+ if (type == AttachmentType.PICTURE)
+ attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
+ attachmentView.setVisible(true);
+ } catch (final IOException e) {
+ new Alert(AlertType.ERROR, "The selected file could not be loaded!").showAndWait();
+ }
+ }
+ }
+
/**
* Checks the text length of the {@code messageTextArea}, adjusts the
* {@code remainingChars} label and checks whether to send the message
@@ -377,16 +436,16 @@ public final class ChatScene implements Restorable {
final var text = messageTextArea.getText().strip();
try {
// Creating the message and its metadata
- final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
+ final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(text);
- // Setting an attachment, if present
- if (pendingAttachment != null) {
+ // Setting an attachment, if present
+ if (pendingAttachment != null) {
builder.setAttachment(pendingAttachment);
pendingAttachment = null;
- attachmentView.setVisible(false);
+ updateAttachmentView(false);
}
- // Building the final message
- final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
+ // Building the final message
+ final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
: builder.build();
// Send message
@@ -432,8 +491,22 @@ public final class ChatScene implements Restorable {
infoLabel.setVisible(true);
}
+ /**
+ * Updates the {@code attachmentView} in terms of visibility.
+ * Additionally resets the shown image to
+ * {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image is currently
+ * present.
+ *
+ * @param visible whether the {@code attachmentView} should be displayed
+ * @since Envoy Client v0.1-beta
+ */
+ private void updateAttachmentView(boolean visible) {
+ if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE)) attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
+ attachmentView.setVisible(visible);
+ }
+
// Context menu actions
-
+
@FXML
private void deleteContact() { try {} catch (final NullPointerException e) {} }
diff --git a/src/main/java/envoy/client/ui/controller/LoginScene.java b/src/main/java/envoy/client/ui/controller/LoginScene.java
index b29613a..3f0df5b 100644
--- a/src/main/java/envoy/client/ui/controller/LoginScene.java
+++ b/src/main/java/envoy/client/ui/controller/LoginScene.java
@@ -107,14 +107,13 @@ public final class LoginScene {
} else if (!Bounds.isValidContactName(userTextField.getTextField().getText())) {
new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
userTextField.getTextField().clear();
- } else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText().toCharArray(),
- registerCheckBox.isSelected(), Startup.VERSION));
+ } else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), registerCheckBox.isSelected(),
+ Startup.VERSION));
}
@FXML
private void offlineModeButtonPressed() {
- attemptOfflineMode(
- new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText().toCharArray(), false, Startup.VERSION));
+ attemptOfflineMode(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText(), false, Startup.VERSION));
}
@FXML
diff --git a/src/main/java/envoy/client/ui/listcell/MessageControl.java b/src/main/java/envoy/client/ui/listcell/MessageControl.java
index 55be558..8b7115b 100644
--- a/src/main/java/envoy/client/ui/listcell/MessageControl.java
+++ b/src/main/java/envoy/client/ui/listcell/MessageControl.java
@@ -2,6 +2,7 @@ package envoy.client.ui.listcell;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
+import java.io.ByteArrayInputStream;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.logging.Level;
@@ -67,6 +68,7 @@ public class MessageControl extends Label {
if (message.hasAttachment()) {
switch (message.getAttachment().getType()) {
case PICTURE:
+ vbox.getChildren().add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
break;
case VIDEO:
break;
@@ -90,9 +92,7 @@ public class MessageControl extends Label {
statusIcon.setPreserveRatio(true);
vbox.getChildren().add(statusIcon);
getStyleClass().add("own-message");
- } else {
- getStyleClass().add("received-message");
- }
+ } else getStyleClass().add("received-message");
// Adjusting height and weight of the cell to the corresponding ListView
paddingProperty().setValue(new Insets(5, 20, 5, 20));
setContextMenu(contextMenu);
diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml
index 4a6ac3a..fd48b75 100644
--- a/src/main/resources/fxml/ChatScene.fxml
+++ b/src/main/resources/fxml/ChatScene.fxml
@@ -51,7 +51,7 @@
-
+
@@ -82,24 +82,29 @@
-
-
+
-
+