add config, usb status fixes

This commit is contained in:
Craig Raw 2020-05-21 15:37:13 +02:00
parent fdd8327464
commit 7fc0e9b530
12 changed files with 194 additions and 53 deletions

View file

@ -69,7 +69,7 @@ jlink {
jpackage {
imageName = "Sparrow"
installerName = "Sparrow"
appVersion = "0.51"
appVersion = "0.6"
skipInstaller = true
imageOptions = []
installerOptions = [

View file

@ -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<Device> 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;
UsbStatusButton usbStatus = null;
for(Node node : statusBar.getRightItems()) {
if(node instanceof UsbStatusButton) {
usbStatus = node;
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());
}
}
}

View file

@ -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();
}

View file

@ -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<Device> devices;
public UsbStatusButton(List<Device> 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<Device> 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;
}
}

View file

@ -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<File, Long> 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;
}
}

View file

@ -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');

View file

@ -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<File> {
@Override
public JsonElement serialize(File src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getAbsolutePath());
}
}
private static class FileDeserializer implements JsonDeserializer<File> {
@Override
public File deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new File(json.getAsJsonPrimitive().getAsString());
}
}
}

View file

@ -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<Device> enumerate(String passphrase) throws ImportException {
try {
List<String> command;
@ -86,9 +85,10 @@ public class Hwi {
return CharStreams.toString(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
}
private synchronized File getHwiExecutable() throws IOException {
private synchronized File getHwiExecutable() {
File hwiExecutable = Config.get().getHwi();
if(hwiExecutable == null || !hwiExecutable.exists()) {
try {
if (hwiExecutable == null) {
Platform platform = Platform.getCurrent();
Set<PosixFilePermission> 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,11 +137,13 @@ public class Hwi {
hwiExecutable = tempExec;
}
}
} catch(Exception e) {
e.printStackTrace();
}
Config.get().setHwi(hwiExecutable);
}
return hwiExecutable;
}
@ -199,6 +200,24 @@ public class Hwi {
}
}
public static class ScheduledEnumerateService extends ScheduledService<List<Device>> {
private final String passphrase;
public ScheduledEnumerateService(String passphrase) {
this.passphrase = passphrase;
}
@Override
protected Task<List<Device>> createTask() {
return new Task<>() {
protected List<Device> call() throws ImportException {
Hwi hwi = new Hwi();
return hwi.enumerate(passphrase);
}
};
}
}
public static class PromptPinService extends Service<Boolean> {
private Device device;

View file

@ -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"));
}

View file

@ -19,3 +19,7 @@
.drag-over > .background-text {
-fx-fill: #383a42;
}
.status-bar .right-items {
-fx-padding: 0 0 0 8;
}

View file

@ -41,6 +41,6 @@
<TabPane fx:id="tabs" />
</StackPane>
<StatusBar fx:id="statusBar" text=""/>
<StatusBar fx:id="statusBar" text="" minHeight="36"/>
</children>
</VBox>

View file

@ -16,7 +16,7 @@
<ToggleGroup fx:id="walletMenu" />
</toggleGroup>
<graphic>
<Glyph fontFamily="FontAwesome" icon="BTC" />
<Glyph fontFamily="FontAwesome" icon="BTC" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="TRANSACTIONS"/>
@ -24,7 +24,7 @@
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Send" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity" toggleGroup="$walletMenu">
<graphic>
<Glyph fontFamily="FontAwesome" icon="SEND" />
<Glyph fontFamily="FontAwesome" icon="SEND" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="SEND"/>
@ -32,7 +32,7 @@
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Receive" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity" toggleGroup="$walletMenu">
<graphic>
<Glyph fontFamily="FontAwesome" icon="ARROW_DOWN" />
<Glyph fontFamily="FontAwesome" icon="ARROW_DOWN" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="RECEIVE"/>
@ -40,7 +40,7 @@
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Addresses" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity" toggleGroup="$walletMenu">
<graphic>
<Glyph fontFamily="FontAwesome" icon="TH_LIST" />
<Glyph fontFamily="FontAwesome" icon="TH_LIST" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="ADDRESSES"/>
@ -48,7 +48,7 @@
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Policies" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity" toggleGroup="$walletMenu">
<graphic>
<Glyph fontFamily="FontAwesome" icon="FILE_TEXT" />
<Glyph fontFamily="FontAwesome" icon="FILE_TEXT" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="POLICIES"/>
@ -56,7 +56,7 @@
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Settings" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity" toggleGroup="$walletMenu">
<graphic>
<Glyph fontFamily="FontAwesome" icon="COG" />
<Glyph fontFamily="FontAwesome" icon="COG" fontSize="20" />
</graphic>
<userData>
<Function fx:constant="SETTINGS"/>