diff --git a/build.gradle b/build.gradle index d2d6858c..1bc96e78 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ jlink { jpackage { imageName = "Sparrow" installerName = "Sparrow" - appVersion = "0.51" + appVersion = "0.6" skipInstaller = true imageOptions = [] installerOptions = [ diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 9ae40372..14246546 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -290,6 +290,7 @@ public class AppController implements Initializable { throw new IOException("Unsupported file type"); } } catch(Exception e) { + e.printStackTrace(); showErrorDialog("Error Opening Wallet", e.getMessage()); } } @@ -380,7 +381,8 @@ public class AppController implements Initializable { controller.setWalletForm(walletForm); if(!storage.getWalletFile().exists() || wallet.containsSource(KeystoreSource.HW_USB)) { - Hwi.EnumerateService enumerateService = new Hwi.EnumerateService(null); + Hwi.ScheduledEnumerateService enumerateService = new Hwi.ScheduledEnumerateService(null); + enumerateService.setPeriod(new Duration(30 * 1000)); enumerateService.setOnSucceeded(workerStateEvent -> { List devices = enumerateService.getValue(); EventManager.get().post(new UsbDeviceEvent(devices)); @@ -536,19 +538,26 @@ public class AppController implements Initializable { @Subscribe public void usbDevicesFound(UsbDeviceEvent event) { - if(event.getDevices().isEmpty()) { - Node usbStatus = null; - for(Node node : statusBar.getRightItems()) { - if(node instanceof UsbStatusButton) { - usbStatus = node; - } + UsbStatusButton usbStatus = null; + for(Node node : statusBar.getRightItems()) { + if(node instanceof UsbStatusButton) { + usbStatus = (UsbStatusButton)node; } + } + + if(event.getDevices().isEmpty()) { if(usbStatus != null) { statusBar.getRightItems().removeAll(usbStatus); } } else { - UsbStatusButton usbStatusButton = new UsbStatusButton(event.getDevices()); - statusBar.getRightItems().add(usbStatusButton); + if(usbStatus == null) { + usbStatus = new UsbStatusButton(); + statusBar.getRightItems().add(usbStatus); + } else { + usbStatus.getItems().remove(0, usbStatus.getItems().size()); + } + + usbStatus.setDevices(event.getDevices()); } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/MainApp.java b/src/main/java/com/sparrowwallet/sparrow/MainApp.java index 76802c42..ff3e7f50 100644 --- a/src/main/java/com/sparrowwallet/sparrow/MainApp.java +++ b/src/main/java/com/sparrowwallet/sparrow/MainApp.java @@ -3,7 +3,6 @@ package com.sparrowwallet.sparrow; import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.wallet.Wallet; -import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; import javafx.application.Application; @@ -14,9 +13,6 @@ import javafx.scene.image.Image; import javafx.stage.Stage; import org.controlsfx.glyphfont.GlyphFontRegistry; -import java.nio.file.Path; -import java.nio.file.Paths; - public class MainApp extends Application { @Override @@ -46,9 +42,6 @@ public class MainApp extends Application { // KeystoreImportDialog dlg = new KeystoreImportDialog(wallet); // dlg.showAndWait(); - Path path = Paths.get("").toAbsolutePath(); - System.out.println(path.toFile().getAbsolutePath()); - stage.show(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/UsbStatusButton.java b/src/main/java/com/sparrowwallet/sparrow/control/UsbStatusButton.java index 37db5a41..61cfd153 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/UsbStatusButton.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/UsbStatusButton.java @@ -1,5 +1,6 @@ package com.sparrowwallet.sparrow.control; +import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; import com.sparrowwallet.sparrow.io.Device; import javafx.geometry.Side; @@ -10,33 +11,41 @@ import org.controlsfx.glyphfont.Glyph; import java.util.List; public class UsbStatusButton extends MenuButton { - private final List devices; - - public UsbStatusButton(List devices) { + public UsbStatusButton() { super(""); setGraphic(getIcon()); - this.devices = devices; - this.setPopupSide(Side.TOP); + getStyleClass().add("status-bar-button"); + this.setPopupSide(Side.RIGHT); + } + public void setDevices(List devices) { for(Device device : devices) { MenuItem deviceItem = new MenuItem(device.getModel().toDisplayString()); + if(device.getNeedsPinSent()) { + deviceItem.setGraphic(getLockIcon()); + } else { + deviceItem.setGraphic(getLockOpenIcon()); + } getItems().add(deviceItem); } } private Node getIcon() { Glyph usb = new Glyph(FontAwesome5Brands.FONT_NAME, FontAwesome5Brands.Glyph.USB); - usb.setFontSize(10); + usb.setFontSize(15); return usb; } - private class UsbStatusContextMenu extends ContextMenu { - public UsbStatusContextMenu() { - for(Device device : devices) { - MenuItem deviceItem = new MenuItem(device.getModel().toDisplayString()); - getItems().add(deviceItem); - } - } + private Node getLockIcon() { + Glyph usb = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LOCK); + usb.setFontSize(12); + return usb; + } + + private Node getLockOpenIcon() { + Glyph usb = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LOCK_OPEN); + usb.setFontSize(12); + return usb; } } diff --git a/src/main/java/com/sparrowwallet/sparrow/event/StorageEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/StorageEvent.java index 985981f3..dd113fb4 100644 --- a/src/main/java/com/sparrowwallet/sparrow/event/StorageEvent.java +++ b/src/main/java/com/sparrowwallet/sparrow/event/StorageEvent.java @@ -1,17 +1,23 @@ package com.sparrowwallet.sparrow.event; +import com.sparrowwallet.sparrow.io.Config; + import java.io.File; import java.util.HashMap; import java.util.Map; public class StorageEvent extends TimedEvent { private static boolean firstRunDone = false; - private static int keyDerivationPeriod = -1; private static final Map eventTime = new HashMap<>(); public StorageEvent(File file, Action action, String status) { super(action, status); + Integer keyDerivationPeriod = Config.get().getKeyDerivationPeriod(); + if(keyDerivationPeriod == null) { + keyDerivationPeriod = -1; + } + if(action == Action.START) { eventTime.put(file, System.currentTimeMillis()); timeMills = keyDerivationPeriod; @@ -19,9 +25,9 @@ public class StorageEvent extends TimedEvent { long start = eventTime.get(file); if(firstRunDone) { keyDerivationPeriod = (int)(System.currentTimeMillis() - start); + Config.get().setKeyDerivationPeriod(keyDerivationPeriod); } firstRunDone = true; - System.out.println(keyDerivationPeriod); timeMills = 0; } } diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java index 4e66cca6..684e728f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java +++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java @@ -20,6 +20,8 @@ public class FontAwesome5 extends GlyphFont { EYE('\uf06e'), KEY('\uf084'), LAPTOP('\uf109'), + LOCK('\uf023'), + LOCK_OPEN('\uf3c1'), SD_CARD('\uf7c2'), WALLET('\uf555'); diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Config.java b/src/main/java/com/sparrowwallet/sparrow/io/Config.java new file mode 100644 index 00000000..d1193dd7 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/io/Config.java @@ -0,0 +1,99 @@ +package com.sparrowwallet.sparrow.io; + +import com.google.gson.*; +import com.sparrowwallet.drongo.Utils; + +import java.io.*; +import java.lang.reflect.Type; + +public class Config { + public static final String CONFIG_FILENAME = ".config"; + + private Integer keyDerivationPeriod; + private File hwi; + + private static Config INSTANCE; + + private static Gson getGson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(File.class, new FileSerializer()); + gsonBuilder.registerTypeAdapter(File.class, new FileDeserializer()); + return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create(); + } + + private static File getConfigFile() { + return new File(Storage.getSparrowDir(), CONFIG_FILENAME); + } + + private static Config load() { + File configFile = getConfigFile(); + if(configFile.exists()) { + try { + Reader reader = new FileReader(configFile); + Config config = getGson().fromJson(reader, Config.class); + reader.close(); + + if(config != null) { + return config; + } + } catch (IOException e) { + e.printStackTrace(); + //Ignore and assume no config + } + } + + return new Config(); + } + + public static synchronized Config get() { + if(INSTANCE == null) { + INSTANCE = load(); + } + + return INSTANCE; + } + + public Integer getKeyDerivationPeriod() { + return keyDerivationPeriod; + } + + public void setKeyDerivationPeriod(Integer keyDerivationPeriod) { + this.keyDerivationPeriod = keyDerivationPeriod; + flush(); + } + + public File getHwi() { + return hwi; + } + + public void setHwi(File hwi) { + this.hwi = hwi; + flush(); + } + + private void flush() { + Gson gson = getGson(); + try { + File configFile = getConfigFile(); + Writer writer = new FileWriter(configFile); + gson.toJson(this, writer); + writer.close(); + } catch (IOException e) { + //Ignore + } + } + + private static class FileSerializer implements JsonSerializer { + @Override + public JsonElement serialize(File src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getAbsolutePath()); + } + } + + private static class FileDeserializer implements JsonDeserializer { + @Override + public File deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return new File(json.getAsJsonPrimitive().getAsString()); + } + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java index a8becb04..f3a8c75f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java @@ -4,6 +4,7 @@ import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import com.google.gson.*; import com.sparrowwallet.drongo.wallet.WalletModel; +import javafx.concurrent.ScheduledService; import javafx.concurrent.Service; import javafx.concurrent.Task; import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream; @@ -23,8 +24,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class Hwi { - private static File hwiExecutable; - public List enumerate(String passphrase) throws ImportException { try { List command; @@ -86,9 +85,10 @@ public class Hwi { return CharStreams.toString(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); } - private synchronized File getHwiExecutable() throws IOException { - try { - if (hwiExecutable == null) { + private synchronized File getHwiExecutable() { + File hwiExecutable = Config.get().getHwi(); + if(hwiExecutable == null || !hwiExecutable.exists()) { + try { Platform platform = Platform.getCurrent(); Set ownerExecutableWritable = PosixFilePermissions.fromString("rwxr--r--"); @@ -100,16 +100,15 @@ public class Hwi { InputStream inputStream = Hwi.class.getResourceAsStream("/external/" + platform.getPlatformId().toLowerCase() + "/hwi-1.1.0-mac-amd64-signed.zip"); Path tempHwiDirPath = Files.createTempDirectory("hwi", PosixFilePermissions.asFileAttribute(ownerExecutableWritable)); File tempHwiDir = tempHwiDirPath.toFile(); - tempHwiDir.deleteOnExit(); - - System.out.println(tempHwiDir.getAbsolutePath()); + //tempHwiDir.deleteOnExit(); + //System.out.println(tempHwiDir.getAbsolutePath()); File tempExec = null; ZipInputStream zis = new ZipInputStream(inputStream); ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { File newFile = newFile(tempHwiDir, zipEntry, ownerExecutableWritable); - newFile.deleteOnExit(); + //newFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(newFile); ByteStreams.copy(zis, new FileOutputStream(newFile)); fos.flush(); @@ -129,7 +128,7 @@ public class Hwi { InputStream inputStream = Hwi.class.getResourceAsStream("/external/" + platform.getPlatformId().toLowerCase() + "/hwi"); Path tempExecPath = Files.createTempFile("hwi", null, PosixFilePermissions.asFileAttribute(ownerExecutableWritable)); File tempExec = tempExecPath.toFile(); - tempExec.deleteOnExit(); + //tempExec.deleteOnExit(); OutputStream tempExecStream = new BufferedOutputStream(new FileOutputStream(tempExec)); ByteStreams.copy(new FramedLZ4CompressorInputStream(inputStream), tempExecStream); inputStream.close(); @@ -138,9 +137,11 @@ public class Hwi { hwiExecutable = tempExec; } + } catch(Exception e) { + e.printStackTrace(); } - } catch(Exception e) { - e.printStackTrace(); + + Config.get().setHwi(hwiExecutable); } return hwiExecutable; @@ -199,6 +200,24 @@ public class Hwi { } } + public static class ScheduledEnumerateService extends ScheduledService> { + private final String passphrase; + + public ScheduledEnumerateService(String passphrase) { + this.passphrase = passphrase; + } + + @Override + protected Task> createTask() { + return new Task<>() { + protected List call() throws ImportException { + Hwi hwi = new Hwi(); + return hwi.enumerate(passphrase); + } + }; + } + } + public static class PromptPinService extends Service { private Device device; diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index 973b1c32..581d8896 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -220,11 +220,11 @@ public class Storage { return walletsDir; } - private static File getSparrowDir() { + static File getSparrowDir() { return new File(getHomeDir(), SPARROW_DIR); } - private static File getHomeDir() { + static File getHomeDir() { return new File(System.getProperty("user.home")); } diff --git a/src/main/resources/com/sparrowwallet/sparrow/app.css b/src/main/resources/com/sparrowwallet/sparrow/app.css index f516d79a..d821bff3 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/app.css +++ b/src/main/resources/com/sparrowwallet/sparrow/app.css @@ -19,3 +19,7 @@ .drag-over > .background-text { -fx-fill: #383a42; } + +.status-bar .right-items { + -fx-padding: 0 0 0 8; +} diff --git a/src/main/resources/com/sparrowwallet/sparrow/app.fxml b/src/main/resources/com/sparrowwallet/sparrow/app.fxml index 0803d490..de7b1f4c 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/app.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/app.fxml @@ -41,6 +41,6 @@ - + diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/wallet.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/wallet.fxml index d678f205..f766655c 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/wallet.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/wallet.fxml @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + @@ -56,7 +56,7 @@ - +