From ef5399008f0dff5202f6619a49b02eb5a43caf92 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Fri, 8 May 2020 16:08:15 +0200 Subject: [PATCH] hw improvements including arbitrary derivation --- .../sparrow/control/DevicePane.java | 71 ++++++++++++++++++- .../keystoreimport/HwUsbScanController.java | 6 +- .../KeystoreImportController.java | 6 ++ .../sparrow/keystoreimport/hw_usb-none.fxml | 14 ++++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/com/sparrowwallet/sparrow/keystoreimport/hw_usb-none.fxml diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java index c07c1d67..21f73f74 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java @@ -25,6 +25,10 @@ import org.controlsfx.control.textfield.CustomPasswordField; import org.controlsfx.control.textfield.CustomTextField; import org.controlsfx.control.textfield.TextFields; import org.controlsfx.glyphfont.Glyph; +import org.controlsfx.validation.ValidationResult; +import org.controlsfx.validation.ValidationSupport; +import org.controlsfx.validation.Validator; +import org.controlsfx.validation.decoration.StyleClassValidationDecoration; import java.util.List; @@ -35,6 +39,7 @@ public class DevicePane extends TitledPane { private Label mainLabel; private Label statusLabel; + private Hyperlink showHideLink; private CustomPasswordField pinField; private CustomTextField passphraseField; private Button unlockButton; @@ -101,11 +106,34 @@ public class DevicePane extends TitledPane { mainLabel.getStyleClass().add("main-label"); labelsBox.getChildren().add(mainLabel); + HBox statusBox = new HBox(); + statusBox.setSpacing(7); this.statusLabel = new Label(); + statusLabel.getStyleClass().add("status-label"); statusLabel.textProperty().bind(status); + statusBox.getChildren().add(statusLabel); + + showHideLink = new Hyperlink("Show details..."); + showHideLink.managedProperty().bind(showHideLink.visibleProperty()); + showHideLink.setVisible(false); + showHideLink.setOnAction(event -> { + if(this.isExpanded()) { + setExpanded(false); + } else { + setExpanded(true); + } + }); + this.expandedProperty().addListener((observable, oldValue, newValue) -> { + if(newValue) { + showHideLink.setText(showHideLink.getText().replace("Show", "Hide")); + } else { + showHideLink.setText(showHideLink.getText().replace("Hide", "Show")); + } + }); + statusBox.getChildren().add(showHideLink); + + labelsBox.getChildren().add(statusBox); - labelsBox.getChildren().add(statusLabel); - statusLabel.getStyleClass().add("status-label"); listItem.getChildren().add(labelsBox); HBox.setHgrow(labelsBox, Priority.ALWAYS); @@ -388,8 +416,47 @@ public class DevicePane extends TitledPane { private void showOperationButton() { if(deviceAccordion.getDeviceOperation().equals(DeviceAccordion.DeviceOperation.IMPORT)) { importButton.setVisible(true); + showHideLink.setText("Show derivation..."); + showHideLink.setVisible(true); + setContent(getDerivationEntry(wallet.getScriptType().getDefaultDerivation())); } else { //TODO: Support further device operations such as signing } } + + private Node getDerivationEntry(List derivation) { + TextField derivationField = new TextField(); + derivationField.setPromptText("Derivation path"); + derivationField.setText(KeyDerivation.writePath(derivation)); + HBox.setHgrow(derivationField, Priority.ALWAYS); + + ValidationSupport validationSupport = new ValidationSupport(); + validationSupport.registerValidator(derivationField, Validator.combine( + Validator.createEmptyValidator("Derivation is required"), + (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid derivation", !KeyDerivation.isValid(newValue)) + )); + validationSupport.setValidationDecorator(new StyleClassValidationDecoration()); + + Button importDerivationButton = new Button("Import"); + importDerivationButton.setOnAction(event -> { + showHideLink.setVisible(true); + setExpanded(false); + List importDerivation = KeyDerivation.parsePath(derivationField.getText()); + importXpub(importDerivation); + }); + + derivationField.textProperty().addListener((observable, oldValue, newValue) -> { + importDerivationButton.setDisable(newValue.isEmpty() || !KeyDerivation.isValid(newValue)); + }); + + HBox contentBox = new HBox(); + contentBox.setAlignment(Pos.TOP_RIGHT); + contentBox.setSpacing(20); + contentBox.getChildren().add(derivationField); + contentBox.getChildren().add(importDerivationButton); + contentBox.setPadding(new Insets(10, 30, 10, 30)); + contentBox.setPrefHeight(60); + + return contentBox; + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbScanController.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbScanController.java index 157b9387..a24feab5 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbScanController.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbScanController.java @@ -28,7 +28,11 @@ public class HwUsbScanController extends KeystoreImportDetailController { Hwi.EnumerateService enumerateService = new Hwi.EnumerateService(null); enumerateService.setOnSucceeded(workerStateEvent -> { List devices = enumerateService.getValue(); - getMasterController().showUsbDevices(devices); + if(devices.isEmpty()) { + getMasterController().showUsbNone(); + } else { + getMasterController().showUsbDevices(devices); + } }); enumerateService.setOnFailed(workerStateEvent -> { getMasterController().showUsbError(enumerateService.getException().getMessage()); diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportController.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportController.java index 77bb9632..d0d69d67 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportController.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportController.java @@ -57,6 +57,12 @@ public class KeystoreImportController implements Initializable { controller.initializeView(devices); } + void showUsbNone() { + FXMLLoader loader = setImportPane("hw_usb-none"); + HwUsbScanController controller = loader.getController(); + controller.initializeView("No hardware wallets found"); + } + void showUsbError(String message) { FXMLLoader loader = setImportPane("hw_usb-error"); HwUsbScanController controller = loader.getController(); diff --git a/src/main/resources/com/sparrowwallet/sparrow/keystoreimport/hw_usb-none.fxml b/src/main/resources/com/sparrowwallet/sparrow/keystoreimport/hw_usb-none.fxml new file mode 100644 index 00000000..a6eac9dd --- /dev/null +++ b/src/main/resources/com/sparrowwallet/sparrow/keystoreimport/hw_usb-none.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + +