refactor import accordion, add wallet import

This commit is contained in:
Craig Raw 2020-05-13 17:16:52 +02:00
parent bc5690346c
commit d516eaa9d6
21 changed files with 361 additions and 237 deletions

View file

@ -14,6 +14,7 @@ import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTParseException; import com.sparrowwallet.drongo.psbt.PSBTParseException;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.TextAreaDialog; import com.sparrowwallet.sparrow.control.TextAreaDialog;
import com.sparrowwallet.sparrow.control.WalletImportDialog;
import com.sparrowwallet.sparrow.control.WalletNameDialog; import com.sparrowwallet.sparrow.control.WalletNameDialog;
import com.sparrowwallet.sparrow.control.WalletPasswordDialog; import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
import com.sparrowwallet.sparrow.event.TabEvent; import com.sparrowwallet.sparrow.event.TabEvent;
@ -257,6 +258,17 @@ public class AppController implements Initializable {
} }
} }
public void importWallet(ActionEvent event) {
WalletImportDialog dlg = new WalletImportDialog();
Optional<Wallet> optionalWallet = dlg.showAndWait();
if(optionalWallet.isPresent()) {
Wallet wallet = optionalWallet.get();
File walletFile = Storage.getStorage().getWalletFile(wallet.getName());
Tab tab = addWalletTab(walletFile, null, wallet);
tabs.getSelectionModel().select(tab);
}
}
public Tab addWalletTab(File walletFile, ECKey encryptionPubKey, Wallet wallet) { public Tab addWalletTab(File walletFile, ECKey encryptionPubKey, Wallet wallet) {
try { try {
String name = walletFile.getName(); String name = walletFile.getName();

View file

@ -59,7 +59,7 @@ public class DevicePane extends TitledPane {
setPadding(Insets.EMPTY); setPadding(Insets.EMPTY);
setGraphic(getTitle()); setGraphic(getTitle());
getStyleClass().add("devicepane"); getStyleClass().add("titled-description-pane");
setDefaultStatus(); setDefaultStatus();
removeArrow(); removeArrow();

View file

@ -0,0 +1,121 @@
package com.sparrowwallet.sparrow.control;
import com.google.gson.JsonParseException;
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
import com.sparrowwallet.sparrow.io.FileImport;
import com.sparrowwallet.sparrow.io.ImportException;
import com.sparrowwallet.sparrow.io.KeystoreFileImport;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.CustomPasswordField;
import org.controlsfx.control.textfield.TextFields;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public abstract class FileImportPane extends TitledDescriptionPane {
private final FileImport importer;
private Button importButton;
private final SimpleStringProperty password = new SimpleStringProperty("");
public FileImportPane(FileImport importer, String title, String description, String content, String imageUrl) {
super(title, description, content, imageUrl);
this.importer = importer;
}
@Override
protected Control createButton() {
importButton = new Button("Import File...");
importButton.setAlignment(Pos.CENTER_RIGHT);
importButton.setOnAction(event -> {
importFile();
});
return importButton;
}
private void importFile() {
Stage window = new Stage();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All Files", "*.*"),
new FileChooser.ExtensionFilter("JSON", "*.json")
);
File file = fileChooser.showOpenDialog(window);
if(file != null) {
importFile(file, null);
}
}
private void importFile(File file, String password) {
if(file.exists()) {
try {
if(importer.isEncrypted(file) && password == null) {
setDescription("Password Required");
showHideLink.setVisible(false);
setContent(getPasswordEntry(file));
importButton.setDisable(true);
setExpanded(true);
} else {
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
importFile(file.getName(), inputStream, password);
}
} catch (Exception e) {
String errorMessage = e.getMessage();
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
errorMessage = e.getCause().getMessage();
}
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) {
errorMessage = "Invalid wallet password";
}
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
errorMessage = "File was not in JSON format";
}
setError("Import Error", errorMessage);
importButton.setDisable(false);
}
}
}
protected abstract void importFile(String fileName, InputStream inputStream, String password) throws ImportException;
private Node getPasswordEntry(File file) {
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
passwordField.setPromptText("Wallet password");
password.bind(passwordField.textProperty());
HBox.setHgrow(passwordField, Priority.ALWAYS);
Button importEncryptedButton = new Button("Import");
importEncryptedButton.setOnAction(event -> {
showHideLink.setVisible(true);
setExpanded(false);
importFile(file, password.get());
});
HBox contentBox = new HBox();
contentBox.setAlignment(Pos.TOP_RIGHT);
contentBox.setSpacing(20);
contentBox.getChildren().add(passwordField);
contentBox.getChildren().add(importEncryptedButton);
contentBox.setPadding(new Insets(10, 30, 10, 30));
contentBox.setPrefHeight(60);
return contentBox;
}
}

View file

@ -1,121 +1,26 @@
package com.sparrowwallet.sparrow.control; package com.sparrowwallet.sparrow.control;
import com.google.gson.JsonParseException;
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.KeystoreImportEvent; import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
import com.sparrowwallet.sparrow.io.ImportException;
import com.sparrowwallet.sparrow.io.KeystoreFileImport; import com.sparrowwallet.sparrow.io.KeystoreFileImport;
import com.sparrowwallet.sparrow.io.KeystoreImport;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.CustomPasswordField;
import org.controlsfx.control.textfield.TextFields;
import java.io.*; import java.io.*;
public class FileKeystoreImportPane extends KeystoreImportPane { public class FileKeystoreImportPane extends FileImportPane {
protected final Wallet wallet;
private final KeystoreFileImport importer; private final KeystoreFileImport importer;
private Button importButton;
private final SimpleStringProperty password = new SimpleStringProperty("");
public FileKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) { public FileKeystoreImportPane(Wallet wallet, KeystoreFileImport importer) {
super(importAccordion, wallet, importer); super(importer, importer.getName(), "Keystore file import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
this.wallet = wallet;
this.importer = importer; this.importer = importer;
} }
@Override protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
protected Node getTitle(KeystoreImport importer) { Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password);
Node title = super.getTitle(importer); EventManager.get().post(new KeystoreImportEvent(keystore));
setDescription("Keystore file import");
importButton = new Button("Import File...");
importButton.setAlignment(Pos.CENTER_RIGHT);
importButton.setOnAction(event -> {
importFile();
});
buttonBox.getChildren().add(importButton);
return title;
}
private void importFile() {
Stage window = new Stage();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All Files", "*.*"),
new FileChooser.ExtensionFilter("JSON", "*.json")
);
File file = fileChooser.showOpenDialog(window);
if(file != null) {
importFile(file, null);
}
}
private void importFile(File file, String password) {
if(file.exists()) {
try {
if(importer.isEncrypted(file) && password == null) {
setDescription("Password Required");
showHideLink.setVisible(false);
setContent(getPasswordEntry(file));
importButton.setDisable(true);
setExpanded(true);
} else {
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password);
EventManager.get().post(new KeystoreImportEvent(keystore));
}
} catch (Exception e) {
String errorMessage = e.getMessage();
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
errorMessage = e.getCause().getMessage();
}
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) {
errorMessage = "Invalid wallet password";
}
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
errorMessage = "File was not in JSON format";
}
setError("Import Error", errorMessage);
importButton.setDisable(false);
}
}
}
private Node getPasswordEntry(File file) {
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
passwordField.setPromptText("Wallet password");
password.bind(passwordField.textProperty());
HBox.setHgrow(passwordField, Priority.ALWAYS);
Button importEncryptedButton = new Button("Import");
importEncryptedButton.setOnAction(event -> {
showHideLink.setVisible(true);
setExpanded(false);
importFile(file, password.get());
});
HBox contentBox = new HBox();
contentBox.setAlignment(Pos.TOP_RIGHT);
contentBox.setSpacing(20);
contentBox.getChildren().add(passwordField);
contentBox.getChildren().add(importEncryptedButton);
contentBox.setPadding(new Insets(10, 30, 10, 30));
contentBox.setPrefHeight(60);
return contentBox;
} }
} }

View file

@ -0,0 +1,27 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletImportEvent;
import com.sparrowwallet.sparrow.io.ImportException;
import com.sparrowwallet.sparrow.io.WalletImport;
import java.io.InputStream;
public class FileWalletImportPane extends FileImportPane {
private final WalletImport importer;
public FileWalletImportPane(WalletImport importer) {
super(importer, importer.getName(), "Wallet file import", importer.getWalletImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
this.importer = importer;
}
@Override
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
Wallet wallet = importer.importWallet(inputStream, password);
if(wallet.getName() == null) {
wallet.setName(fileName);
}
EventManager.get().post(new WalletImportEvent(wallet));
}
}

View file

@ -16,12 +16,12 @@ public class KeystoreImportAccordion extends Accordion {
this.importers = importers; this.importers = importers;
for(KeystoreImport importer : importers) { for(KeystoreImport importer : importers) {
KeystoreImportPane importPane = null; TitledDescriptionPane importPane = null;
if(importer instanceof KeystoreFileImport) { if(importer instanceof KeystoreFileImport) {
importPane = new FileKeystoreImportPane(this, wallet, (KeystoreFileImport)importer); importPane = new FileKeystoreImportPane(wallet, (KeystoreFileImport)importer);
} else if(importer instanceof KeystoreMnemonicImport) { } else if(importer instanceof KeystoreMnemonicImport) {
importPane = new MnemonicKeystoreImportPane(this, wallet, (KeystoreMnemonicImport)importer); importPane = new MnemonicKeystoreImportPane(wallet, (KeystoreMnemonicImport)importer);
} else { } else {
throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass()); throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass());
} }

View file

@ -34,7 +34,8 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
public class MnemonicKeystoreImportPane extends KeystoreImportPane { public class MnemonicKeystoreImportPane extends TitledDescriptionPane {
protected final Wallet wallet;
private final KeystoreMnemonicImport importer; private final KeystoreMnemonicImport importer;
private SplitMenuButton enterMnemonicButton; private SplitMenuButton enterMnemonicButton;
@ -48,21 +49,19 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
private SimpleListProperty<String> wordEntriesProperty; private SimpleListProperty<String> wordEntriesProperty;
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty(); private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
public MnemonicKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreMnemonicImport importer) { public MnemonicKeystoreImportPane(Wallet wallet, KeystoreMnemonicImport importer) {
super(importAccordion, wallet, importer); super(importer.getName(), "Mnemonic import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
this.wallet = wallet;
this.importer = importer; this.importer = importer;
createImportButton();
buttonBox.getChildren().add(importButton);
} }
@Override @Override
protected Node getTitle(KeystoreImport importer) { protected Control createButton() {
Node title = super.getTitle(importer);
setDescription("Keystore file import");
createEnterMnemonicButton(); createEnterMnemonicButton();
createImportButton(); return enterMnemonicButton;
buttonBox.getChildren().addAll(enterMnemonicButton, importButton);
return title;
} }
private void createEnterMnemonicButton() { private void createEnterMnemonicButton() {
@ -103,8 +102,6 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
importButton.getItems().add(item); importButton.getItems().add(item);
} }
importButton.managedProperty().bind(importButton.visibleProperty()); importButton.managedProperty().bind(importButton.visibleProperty());
importButton.setVisible(false); importButton.setVisible(false);
} }

View file

@ -1,53 +1,33 @@
package com.sparrowwallet.sparrow.control; package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppController;
import com.sparrowwallet.sparrow.io.KeystoreImport;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Hyperlink; import javafx.scene.control.*;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority; import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
public abstract class KeystoreImportPane extends TitledPane { public class TitledDescriptionPane extends TitledPane {
protected final KeystoreImportAccordion importAccordion;
protected final Wallet wallet;
private Label mainLabel;
private Label descriptionLabel; private Label descriptionLabel;
protected Hyperlink showHideLink; protected Hyperlink showHideLink;
protected HBox buttonBox; protected HBox buttonBox;
public KeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreImport importer) { public TitledDescriptionPane(String title, String description, String content, String imageUrl) {
this.importAccordion = importAccordion; getStylesheets().add(AppController.class.getResource("general.css").toExternalForm());
this.wallet = wallet; getStyleClass().add("titled-description-pane");
setPadding(Insets.EMPTY); setPadding(Insets.EMPTY);
setGraphic(getTitle(importer)); setGraphic(getTitle(title, description, imageUrl));
getStyleClass().add("importpane"); setContent(getContentBox(content));
setContent(getContentBox(importer.getKeystoreImportDescription()));
removeArrow(); removeArrow();
} }
private void removeArrow() { protected Node getTitle(String title, String description, String imageUrl) {
Platform.runLater(() -> {
Node arrow = this.lookup(".arrow");
if (arrow != null) {
arrow.setVisible(false);
arrow.setManaged(false);
} else {
removeArrow();
}
});
}
protected Node getTitle(KeystoreImport importer) {
HBox listItem = new HBox(); HBox listItem = new HBox();
listItem.setPadding(new Insets(10, 20, 10, 10)); listItem.setPadding(new Insets(10, 20, 10, 10));
listItem.setSpacing(10); listItem.setSpacing(10);
@ -57,7 +37,7 @@ public abstract class KeystoreImportPane extends TitledPane {
imageBox.setMinHeight(50); imageBox.setMinHeight(50);
listItem.getChildren().add(imageBox); listItem.getChildren().add(imageBox);
Image image = new Image("image/" + importer.getWalletModel().getType() + ".png", 50, 50, true, true); Image image = new Image(imageUrl, 50, 50, true, true);
if (!image.isError()) { if (!image.isError()) {
ImageView imageView = new ImageView(); ImageView imageView = new ImageView();
imageView.setImage(image); imageView.setImage(image);
@ -67,8 +47,8 @@ public abstract class KeystoreImportPane extends TitledPane {
VBox labelsBox = new VBox(); VBox labelsBox = new VBox();
labelsBox.setSpacing(5); labelsBox.setSpacing(5);
labelsBox.setAlignment(Pos.CENTER_LEFT); labelsBox.setAlignment(Pos.CENTER_LEFT);
this.mainLabel = new Label(); Label mainLabel = new Label();
mainLabel.setText(importer.getName()); mainLabel.setText(title);
mainLabel.getStyleClass().add("main-label"); mainLabel.getStyleClass().add("main-label");
labelsBox.getChildren().add(mainLabel); labelsBox.getChildren().add(mainLabel);
@ -76,16 +56,12 @@ public abstract class KeystoreImportPane extends TitledPane {
descriptionBox.setSpacing(7); descriptionBox.setSpacing(7);
labelsBox.getChildren().add(descriptionBox); labelsBox.getChildren().add(descriptionBox);
descriptionLabel = new Label("Keystore Import"); descriptionLabel = new Label(description);
descriptionLabel.getStyleClass().add("description-label"); descriptionLabel.getStyleClass().add("description-label");
showHideLink = new Hyperlink("Show Details..."); showHideLink = new Hyperlink("Show Details...");
showHideLink.managedProperty().bind(showHideLink.visibleProperty()); showHideLink.managedProperty().bind(showHideLink.visibleProperty());
showHideLink.setOnAction(event -> { showHideLink.setOnAction(event -> {
if(this.isExpanded()) { setExpanded(!this.isExpanded());
setExpanded(false);
} else {
setExpanded(true);
}
}); });
this.expandedProperty().addListener((observable, oldValue, newValue) -> { this.expandedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue) { if(newValue) {
@ -101,6 +77,10 @@ public abstract class KeystoreImportPane extends TitledPane {
buttonBox = new HBox(); buttonBox = new HBox();
buttonBox.setAlignment(Pos.CENTER_RIGHT); buttonBox.setAlignment(Pos.CENTER_RIGHT);
Control button = createButton();
if(button != null) {
buttonBox.getChildren().add(button);
}
listItem.getChildren().add(buttonBox); listItem.getChildren().add(buttonBox);
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> { this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
@ -111,6 +91,11 @@ public abstract class KeystoreImportPane extends TitledPane {
return listItem; return listItem;
} }
protected Control createButton() {
//No buttons by default
return null;
}
protected void setDescription(String text) { protected void setDescription(String text) {
descriptionLabel.getStyleClass().remove("description-error"); descriptionLabel.getStyleClass().remove("description-error");
descriptionLabel.getStyleClass().add("description-label"); descriptionLabel.getStyleClass().add("description-label");
@ -141,4 +126,16 @@ public abstract class KeystoreImportPane extends TitledPane {
return contentBox; return contentBox;
} }
private void removeArrow() {
Platform.runLater(() -> {
Node arrow = this.lookup(".arrow");
if (arrow != null) {
arrow.setVisible(false);
arrow.setManaged(false);
} else {
removeArrow();
}
});
}
} }

View file

@ -0,0 +1,59 @@
package com.sparrowwallet.sparrow.control;
import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletImportEvent;
import com.sparrowwallet.sparrow.io.*;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import java.util.List;
public class WalletImportDialog extends Dialog<Wallet> {
private Wallet wallet;
public WalletImportDialog() {
EventManager.get().register(this);
final DialogPane dialogPane = getDialogPane();
StackPane stackPane = new StackPane();
dialogPane.setContent(stackPane);
AnchorPane anchorPane = new AnchorPane();
stackPane.getChildren().add(anchorPane);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setPrefWidth(500);
scrollPane.setPrefHeight(500);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
anchorPane.getChildren().add(scrollPane);
scrollPane.setFitToWidth(true);
AnchorPane.setLeftAnchor(scrollPane, 0.0);
AnchorPane.setRightAnchor(scrollPane, 0.0);
List<WalletImport> importers = List.of(new ColdcardMultisig(), new Electrum());
Accordion importAccordion = new Accordion();
for(WalletImport importer : importers) {
FileWalletImportPane importPane = new FileWalletImportPane(importer);
importAccordion.getPanes().add(importPane);
}
scrollPane.setContent(importAccordion);
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
dialogPane.getButtonTypes().addAll(cancelButtonType);
dialogPane.setPrefWidth(650);
dialogPane.setPrefHeight(600);
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null);
}
@Subscribe
public void walletImported(WalletImportEvent event) {
wallet = event.getWallet();
setResult(wallet);
this.close();
}
}

View file

@ -0,0 +1,15 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
public class WalletImportEvent {
private Wallet wallet;
public WalletImportEvent(Wallet wallet) {
this.wallet = wallet;
}
public Wallet getWallet() {
return wallet;
}
}

View file

@ -18,7 +18,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImport, WalletExport { public class ColdcardMultisig implements WalletImport, KeystoreFileImport, WalletExport {
private final Gson gson = new Gson(); private final Gson gson = new Gson();
@Override @Override
@ -125,6 +125,10 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
wallet.setDefaultPolicy(policy); wallet.setDefaultPolicy(policy);
wallet.setScriptType(scriptType); wallet.setScriptType(scriptType);
if(!wallet.isValid()) {
throw new IllegalStateException("This file does not describe a valid wallet. Please use the Settings > Multisig Wallets > Export XPUB feature on your Coldcard.");
}
return wallet; return wallet;
} catch(Exception e) { } catch(Exception e) {
throw new ImportException(e); throw new ImportException(e);

View file

@ -22,7 +22,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
public class Electrum implements KeystoreFileImport, SinglesigWalletImport, MultisigWalletImport, WalletExport { public class Electrum implements KeystoreFileImport, WalletImport, WalletExport {
@Override @Override
public String getName() { public String getName() {
return "Electrum"; return "Electrum";
@ -131,7 +131,7 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
} }
if(!wallet.isValid()) { if(!wallet.isValid()) {
throw new IllegalStateException("Electrum wallet is in an inconsistent state"); throw new IllegalStateException("Electrum wallet is in an inconsistent state.");
} }
return wallet; return wallet;
@ -145,15 +145,6 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
return "Import an Electrum wallet"; return "Import an Electrum wallet";
} }
@Override
public Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
Wallet wallet = importWallet(inputStream, password);
wallet.setScriptType(scriptType);
//TODO: Check this usage results in a valid wallet
return wallet;
}
@Override @Override
public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException { public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException {
try { try {

View file

@ -0,0 +1,7 @@
package com.sparrowwallet.sparrow.io;
import java.io.File;
public interface FileImport extends Import {
boolean isEncrypted(File file);
}

View file

@ -1,5 +1,8 @@
package com.sparrowwallet.sparrow.io; package com.sparrowwallet.sparrow.io;
import com.sparrowwallet.drongo.wallet.WalletModel;
public interface Import { public interface Import {
String getName(); String getName();
WalletModel getWalletModel();
} }

View file

@ -6,7 +6,6 @@ import com.sparrowwallet.drongo.wallet.Keystore;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
public interface KeystoreFileImport extends KeystoreImport { public interface KeystoreFileImport extends KeystoreImport, FileImport {
boolean isEncrypted(File file);
Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException; Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException;
} }

View file

@ -4,6 +4,5 @@ import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.wallet.WalletModel; import com.sparrowwallet.drongo.wallet.WalletModel;
public interface KeystoreImport extends Import { public interface KeystoreImport extends Import {
WalletModel getWalletModel();
String getKeystoreImportDescription(); String getKeystoreImportDescription();
} }

View file

@ -1,13 +0,0 @@
package com.sparrowwallet.sparrow.io;
import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.wallet.Wallet;
import java.io.File;
import java.io.InputStream;
public interface SinglesigWalletImport extends Import {
String getWalletImportDescription();
Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException;
boolean isEncrypted(File file);
}

View file

@ -5,8 +5,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
public interface MultisigWalletImport extends Import { public interface WalletImport extends Import, FileImport {
String getWalletImportDescription(); String getWalletImportDescription();
Wallet importWallet(InputStream inputStream, String password) throws ImportException; Wallet importWallet(InputStream inputStream, String password) throws ImportException;
boolean isEncrypted(File file);
} }

View file

@ -13,7 +13,7 @@
<Menu mnemonicParsing="false" text="File"> <Menu mnemonicParsing="false" text="File">
<items> <items>
<MenuItem mnemonicParsing="false" text="New Wallet" onAction="#newWallet"/> <MenuItem mnemonicParsing="false" text="New Wallet" onAction="#newWallet"/>
<MenuItem mnemonicParsing="false" text="Open Wallet" onAction="#openWallet"/> <MenuItem mnemonicParsing="false" text="Open Wallet..." onAction="#openWallet"/>
<Menu mnemonicParsing="false" text="Open Transaction"> <Menu mnemonicParsing="false" text="Open Transaction">
<items> <items>
<MenuItem text="File..." onAction="#openFromFile"/> <MenuItem text="File..." onAction="#openFromFile"/>
@ -21,6 +21,7 @@
<MenuItem text="Examples" onAction="#openExamples"/> <MenuItem text="Examples" onAction="#openExamples"/>
</items> </items>
</Menu> </Menu>
<MenuItem mnemonicParsing="false" text="Import Wallet..." onAction="#importWallet"/>
<MenuItem mnemonicParsing="false" text="Close" onAction="#closeTab"/> <MenuItem mnemonicParsing="false" text="Close" onAction="#closeTab"/>
</items> </items>
</Menu> </Menu>

View file

@ -50,3 +50,51 @@
-fx-padding: 20; -fx-padding: 20;
} }
.titled-description-pane > .title {
-fx-background-color: white;
-fx-padding: 0;
-fx-border-color: #e5e5e6;
/*-fx-border-width: 1;*/
}
.titled-description-pane > .title > .arrow-button {
-fx-padding: 0;
}
.titled-description-pane > .title > .arrow-button > .arrow {
visibility: hidden;
-fx-translate-x: -1000;
-fx-padding: 0;
}
.titled-description-pane .main-label .text {
}
.titled-description-pane .status-label .text, .titled-description-pane .description-label .text {
-fx-fill: #a0a1a7;
}
.titled-description-pane .status-error .text, .titled-description-pane .description-error .text {
-fx-fill: #ca1243;
}
.titled-description-pane .description-label, .titled-description-pane .description-error {
-fx-border-width: 0px;
-fx-border-color: transparent;
}
.titled-description-pane .hyperlink {
-fx-padding: 0;
-fx-border-width: 0;
-fx-fill: #1e88cf;
}
.titled-description-pane .hyperlink:visited {
-fx-text-fill: #1e88cf;
-fx-underline: false;
}
.titled-description-pane .hyperlink:hover:visited {
-fx-underline: true;
}

View file

@ -33,52 +33,5 @@
-fx-background-color: transparent; -fx-background-color: transparent;
} }
.titled-pane > .title {
-fx-background-color: white;
-fx-padding: 0;
-fx-border-color: #e5e5e6;
/*-fx-border-width: 1;*/
}
.titled-pane > .title > .arrow-button {
-fx-padding: 0;
}
.titled-pane > .title > .arrow-button > .arrow {
visibility: hidden;
-fx-translate-x: -1000;
-fx-padding: 0;
}
.main-label .text {
}
.status-label .text, .description-label .text {
-fx-fill: #a0a1a7;
}
.status-error .text, .description-error .text {
-fx-fill: #ca1243;
}
.description-label, .description-error {
-fx-border-width: 0px;
-fx-border-color: transparent;
}
.hyperlink {
-fx-padding: 0;
-fx-border-width: 0;
-fx-fill: #1e88cf;
}
.hyperlink:visited {
-fx-text-fill: #1e88cf;
-fx-underline: false;
}
.hyperlink:hover:visited {
-fx-underline: true;
}