From e216152e6b31973823e2b4f92d29a3c89b7870aa Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 27 Jul 2020 12:00:49 +0200 Subject: [PATCH] Added ability to save attachments --- .../client/data/audio/AudioRecorder.java | 9 ++++-- .../envoy/client/ui/controller/ChatScene.java | 11 +++++-- .../client/ui/listcell/MessageControl.java | 28 +++++++++++++++-- .../src/main/java/envoy/data/Attachment.java | 26 ++++++++++------ .../main/java/envoy/server/data/Message.java | 31 ++++++++++++++----- 5 files changed, 81 insertions(+), 24 deletions(-) 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/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index f9ba2d7..a708dd0 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; @@ -240,9 +242,10 @@ public final class ChatScene implements Restorable { this.client = client; this.writeProxy = writeProxy; + MessageControl.setUser(localDB.getUser()); + MessageControl.setSceneContext(sceneContext); chatList.setItems(FXCollections.observableList(localDB.getChats())); contactLabel.setText(localDB.getUser().getName()); - MessageControl.setUser(localDB.getUser()); if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info"); recorder = new AudioRecorder(); @@ -334,7 +337,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))); @@ -385,7 +390,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) 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 a6ecae5..795cd42 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; @@ -16,9 +16,11 @@ import javafx.scene.control.MenuItem; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; import envoy.client.ui.AudioControl; import envoy.client.ui.IconUtil; +import envoy.client.ui.SceneContext; import envoy.data.Message; import envoy.data.Message.MessageStatus; import envoy.data.User; @@ -38,6 +40,8 @@ public class MessageControl extends Label { private static User client; + 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); @@ -116,11 +120,31 @@ 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) { + // Show save file dialog + final var fileChooser = new FileChooser(); + fileChooser.setInitialFileName(message.getAttachment().getName()); + + final File file = fileChooser.showSaveDialog(sceneContext.getStage()); + + // 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."); + } catch (final IOException e) { + logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e); + } + } /** * @param client the user who has logged in * @since Envoy Client v0.1-beta */ public static void setUser(User client) { MessageControl.client = client; } + + /** + * @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/common/src/main/java/envoy/data/Attachment.java b/common/src/main/java/envoy/data/Attachment.java index 177441b..67fca01 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,19 +54,22 @@ 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.type = type; + this.name = name; } /** @@ -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/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