diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java index 3d1fa20f..b410f178 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java @@ -11,10 +11,13 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.stage.FileChooser; +import javafx.stage.Stage; import org.controlsfx.glyphfont.Glyph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.*; import java.util.Optional; public class TextAreaDialog extends Dialog { @@ -22,6 +25,8 @@ public class TextAreaDialog extends Dialog { private final TextArea textArea; private final String defaultValue; + private final String fileName; + private final byte[] fileBytes; public TextAreaDialog() { this(""); @@ -31,7 +36,11 @@ public class TextAreaDialog extends Dialog { this(defaultValue, true); } - public TextAreaDialog(@NamedArg("defaultValue") String defaultValue, @NamedArg("editable") boolean editable) { + public TextAreaDialog(String defaultValue, boolean editable) { + this(defaultValue, editable, null, null); + } + + public TextAreaDialog(@NamedArg("defaultValue") String defaultValue, @NamedArg("editable") boolean editable, @NamedArg("fileName") String fileName, @NamedArg("fileBytes") byte[] fileBytes) { final DialogPane dialogPane = new TextAreaDialogPane(); setDialogPane(dialogPane); @@ -48,6 +57,8 @@ public class TextAreaDialog extends Dialog { HBox.setHgrow(this.textArea, Priority.ALWAYS); this.defaultValue = defaultValue; + this.fileName = fileName; + this.fileBytes = fileBytes; dialogPane.setContent(hbox); dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); @@ -60,6 +71,9 @@ public class TextAreaDialog extends Dialog { final ButtonType scanButtonType = new javafx.scene.control.ButtonType("Scan QR", ButtonBar.ButtonData.LEFT); dialogPane.getButtonTypes().add(scanButtonType); + } else if(fileBytes != null) { + final ButtonType saveButtonType = new javafx.scene.control.ButtonType("Save File...", ButtonBar.ButtonData.LEFT); + dialogPane.getButtonTypes().add(saveButtonType); } Platform.runLater(textArea::requestFocus); @@ -86,7 +100,7 @@ public class TextAreaDialog extends Dialog { @Override protected Node createButton(ButtonType buttonType) { Node button; - if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT) { + if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT && buttonType.getText().equals("Scan QR")) { Button scanButton = new Button(buttonType.getText()); scanButton.setGraphicTextGap(5); scanButton.setGraphic(getGlyph(FontAwesome5.Glyph.CAMERA)); @@ -118,6 +132,38 @@ public class TextAreaDialog extends Dialog { }); button = scanButton; + } else if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT && buttonType.getText().equals("Save File...")) { + Button saveButton = new Button(buttonType.getText()); + + final ButtonBar.ButtonData buttonData = buttonType.getButtonData(); + ButtonBar.setButtonData(saveButton, buttonData); + + saveButton.setOnAction(event -> { + Stage window = new Stage(); + + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save File"); + + if(fileName != null) { + fileChooser.setInitialFileName(fileName); + } + + AppServices.moveToActiveWindowScreen(window, 800, 450); + File file = fileChooser.showSaveDialog(window); + if(file != null) { + try { + try(OutputStream outputStream = new FileOutputStream(file)) { + outputStream.write(fileBytes); + outputStream.flush(); + } + } catch(IOException e) { + log.error("Error saving file", e); + AppServices.showErrorDialog("Error saving file", "Cannot write to " + file.getAbsolutePath()); + } + } + }); + + button = saveButton; } else { button = super.createButton(buttonType); } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java index d846d21a..7b2a6eb4 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java @@ -35,6 +35,7 @@ import tornadofx.control.Field; import javax.smartcardio.CardException; import java.net.URL; +import java.util.Base64; import java.util.Optional; import java.util.ResourceBundle; import java.util.stream.Collectors; @@ -476,7 +477,8 @@ public class KeystoreController extends WalletFormController implements Initiali Service backupService = cardApi.getBackupService(); backupService.setOnSucceeded(event -> { String backup = backupService.getValue(); - TextAreaDialog backupDialog = new TextAreaDialog(backup, false); + String filename = fingerprint.getText() + ".aes"; + TextAreaDialog backupDialog = new TextAreaDialog(backup, false, filename, Base64.getDecoder().decode(backup)); backupDialog.setTitle("Backup Private Key"); backupDialog.getDialogPane().setHeaderText((requiresBackup ? "Please backup first by saving" : "Save") + " the following text in a safe place. It contains an encrypted copy of the card's private key, and can be decrypted using the backup key written on the back of the card."); backupDialog.showAndWait();