ensure file and uri opening on original app instance

This commit is contained in:
Craig Raw 2021-05-11 12:31:36 +02:00
parent 74c83fc5e1
commit 574209c837
9 changed files with 229 additions and 48 deletions

View file

@ -71,6 +71,9 @@ dependencies {
}
implementation('dev.bwt:bwt-jni:0.1.7')
implementation('net.sourceforge.javacsv:javacsv:2.0')
implementation('tk.pratanumandal:unique4j:1.3') {
exclude group: 'com.google.code.gson'
}
implementation('org.slf4j:jul-to-slf4j:1.7.30') {
exclude group: 'org.slf4j'
}
@ -151,7 +154,8 @@ jlink {
"--add-opens=javafx.graphics/com.sun.glass.ui=com.sparrowwallet.sparrow",
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow",
"--add-reads=com.sparrowwallet.merged.module=java.desktop"]
"--add-reads=com.sparrowwallet.merged.module=java.desktop",
"--add-reads=com.sparrowwallet.merged.module=java.sql"]
if(os.macOsX) {
jvmArgs += "--add-opens=javafx.graphics/com.sun.glass.ui.mac=com.sparrowwallet.merged.module"

View file

@ -54,5 +54,69 @@
</array>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>org.bitcoin.psbt</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>psbt</string>
</array>
</dict>
<key>UTTypeDescription</key>
<string>Partially Signed Bitcoin Transaction</string>
<key>UTTypeIconFile</key>
<string>sparrow.icns</string>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.bitcoin.txn</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>txn</string>
</array>
</dict>
<key>UTTypeDescription</key>
<string>Bitcoin Transaction</string>
<key>UTTypeIconFile</key>
<string>sparrow.icns</string>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleTypeIconFile</key>
<string>sparrow.icns</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>psbt</string>
</array>
<key>CFBundleTypeName</key>
<string>Partially Signed Bitcoin Transaction</string>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleTypeIconFile</key>
<string>sparrow.icns</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>txn</string>
</array>
<key>CFBundleTypeName</key>
<string>Bitcoin Transaction</string>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,3 @@
mime-type=application/bitcoin-transaction
extension=txn
description=Bitcoin Transaction

View file

@ -691,11 +691,6 @@ public class AppController implements Initializable {
}
}
private boolean isWalletFile(File file) {
FileType fileType = IOUtils.getFileType(file);
return FileType.JSON.equals(fileType) || FileType.BINARY.equals(fileType);
}
private void setServerToggleTooltip(Integer currentBlockHeight) {
Tooltip tooltip = new Tooltip(getServerToggleTooltipText(currentBlockHeight));
tooltip.setShowDuration(Duration.seconds(15));
@ -1808,16 +1803,24 @@ public class AppController implements Initializable {
@Subscribe
public void requestWalletOpen(RequestWalletOpenEvent event) {
if(tabs.getScene().getWindow().equals(event.getWindow())) {
if(event.getFile() != null) {
openWalletFile(event.getFile(), true);
} else {
openWallet(true);
}
}
}
@Subscribe
public void requestTransactionOpen(RequestTransactionOpenEvent event) {
if(tabs.getScene().getWindow().equals(event.getWindow())) {
if(event.getFile() != null) {
openTransactionFile(event.getFile());
} else {
openTransactionFromFile(null);
}
}
}
@Subscribe
public void requestQRScan(RequestQRScanEvent event) {

View file

@ -13,10 +13,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.TextUtils;
import com.sparrowwallet.sparrow.control.TrayManager;
import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.io.Device;
import com.sparrowwallet.sparrow.io.Hwi;
import com.sparrowwallet.sparrow.io.Storage;
import com.sparrowwallet.sparrow.io.*;
import com.sparrowwallet.sparrow.net.*;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
@ -45,13 +42,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.*;
import java.awt.desktop.OpenURIEvent;
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.OpenURIHandler;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
@ -104,6 +102,10 @@ public class AppServices {
private static List<Device> devices;
private static final List<File> argFiles = new ArrayList<>();
private static final List<URI> argUris = new ArrayList<>();
private static final Map<Address, BitcoinURI> payjoinURIs = new HashMap<>();
private final ChangeListener<Boolean> onlineServicesListener = new ChangeListener<>() {
@ -124,12 +126,11 @@ public class AppServices {
};
private static final OpenURIHandler openURIHandler = event -> {
URI uri = event.getURI();
if("bitcoin".equals(uri.getScheme())) {
Platform.runLater(() -> openBitcoinUri(uri));
} else if("aopp".equals(uri.getScheme())) {
Platform.runLater(() -> openAddressOwnershipProof(uri));
}
openURI(event.getURI());
};
private static final OpenFilesHandler openFilesHandler = event -> {
openFiles(event.getFiles(), null);
};
public AppServices(MainApp application) {
@ -521,6 +522,11 @@ public class AppServices {
ElectrumServer.clearRetrievedScriptHashes(wallet);
}
public static boolean isWalletFile(File file) {
FileType fileType = IOUtils.getFileType(file);
return FileType.JSON.equals(fileType) || FileType.BINARY.equals(fileType);
}
public static Optional<ButtonType> showWarningDialog(String title, String content, ButtonType... buttons) {
return showAlertDialog(title, content, Alert.AlertType.WARNING, buttons);
}
@ -611,15 +617,72 @@ public class AppServices {
}
}
public static void handleURI(URI uri) {
openURIHandler.openURI(new OpenURIEvent(uri));
static void parseFileUriArguments(List<String> fileUriArguments) {
for(String fileUri : fileUriArguments) {
try {
File file = new File(fileUri.replace("~", System.getProperty("user.home")));
if(file.exists()) {
argFiles.add(file);
continue;
}
URI uri = new URI(fileUri);
argUris.add(uri);
} catch(URISyntaxException e) {
log.warn("Could not parse " + fileUri + " as a valid file or URI");
} catch(Exception e) {
//ignore
}
}
}
public static void openFileUriArguments(Window window) {
openFiles(argFiles, window);
argFiles.clear();
for(URI argUri : argUris) {
openURI(argUri);
}
argUris.clear();
}
private static void openFiles(List<File> files, Window window) {
final List<File> openFiles = new ArrayList<>(files);
Platform.runLater(() -> {
Window openWindow = window;
if(openWindow == null) {
openWindow = getActiveWindow();
}
for(File file : openFiles) {
if(isWalletFile(file)) {
EventManager.get().post(new RequestWalletOpenEvent(openWindow, file));
} else {
EventManager.get().post(new RequestTransactionOpenEvent(openWindow, file));
}
}
});
}
private static void openURI(URI uri) {
Platform.runLater(() -> {
if("bitcoin".equals(uri.getScheme())) {
openBitcoinUri(uri);
} else if("aopp".equals(uri.getScheme())) {
openAddressOwnershipProof(uri);
}
});
}
public static void addURIHandlers() {
try {
if(Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.APP_OPEN_URI)) {
if(Desktop.isDesktopSupported()) {
if(Desktop.getDesktop().isSupported(Desktop.Action.APP_OPEN_FILE)) {
Desktop.getDesktop().setOpenFileHandler(openFilesHandler);
}
if(Desktop.getDesktop().isSupported(Desktop.Action.APP_OPEN_URI)) {
Desktop.getDesktop().setOpenURIHandler(openURIHandler);
}
}
} catch(Exception e) {
log.error("Could not add URI handler", e);
}

View file

@ -23,14 +23,15 @@ import org.controlsfx.tools.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import tk.pratanumandal.unique4j.Unique4jList;
import tk.pratanumandal.unique4j.exception.Unique4jException;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;
public class MainApp extends Application {
public static final String APP_ID = "com.sparrowwallet.sparrow";
public static final String APP_NAME = "Sparrow";
public static final String APP_VERSION = "1.4.0";
public static final String APP_HOME_PROPERTY = "sparrow.home";
@ -38,8 +39,7 @@ public class MainApp extends Application {
private Stage mainStage;
private static final List<File> argFiles = new ArrayList<>();
private static final List<URI> argUris = new ArrayList<>();
private static SparrowUnique sparrowUnique;
@Override
public void init() throws Exception {
@ -113,13 +113,7 @@ public class MainApp extends Application {
}
}
for(File argFile : argFiles) {
appController.openFile(argFile);
}
for(URI argUri : argUris) {
AppServices.handleURI(argUri);
}
AppServices.openFileUriArguments(stage);
AppServices.get().start();
}
@ -128,6 +122,9 @@ public class MainApp extends Application {
public void stop() throws Exception {
AppServices.get().stop();
mainStage.close();
if(sparrowUnique != null) {
sparrowUnique.freeLock();
}
}
public static void main(String[] argv) {
@ -175,22 +172,18 @@ public class MainApp extends Application {
getLogger().info("Using " + Network.get() + " configuration");
}
if(!jCommander.getUnknownOptions().isEmpty()) {
for(String fileUri : jCommander.getUnknownOptions()) {
List<String> fileUriArguments = jCommander.getUnknownOptions();
if(!fileUriArguments.isEmpty()) {
if(args.network == null && args.dir == null) {
try {
File file = new File(fileUri);
if(file.exists()) {
argFiles.add(file);
continue;
}
URI uri = new URI(fileUri);
argUris.add(uri);
} catch(URISyntaxException e) {
getLogger().warn("Could not parse " + fileUri + " as a valid file or URI");
} catch(Exception e) {
//ignore
sparrowUnique = new SparrowUnique(APP_ID, fileUriArguments);
sparrowUnique.acquireLock(); //Will exit app after sending fileUriArguments if lock cannot be acquired
} catch(Unique4jException e) {
getLogger().error("Could not obtain unique lock", e);
}
}
AppServices.parseFileUriArguments(fileUriArguments);
}
SLF4JBridgeHandler.removeHandlersForRootLogger();
@ -201,4 +194,29 @@ public class MainApp extends Application {
private static Logger getLogger() {
return LoggerFactory.getLogger(MainApp.class);
}
private static class SparrowUnique extends Unique4jList {
private final List<String> fileUriArguments;
public SparrowUnique(String APP_ID, List<String> fileUriArguments) {
super(APP_ID + "." + Network.get());
this.fileUriArguments = fileUriArguments;
}
@Override
protected void receiveMessageList(List<String> messageList) {
AppServices.parseFileUriArguments(messageList);
AppServices.openFileUriArguments(null);
}
@Override
protected List<String> sendMessageList() {
return fileUriArguments;
}
@Override
protected void beforeExit() {
getLogger().info("Opening files/URIs in already running instance, exiting...");
}
}
}

View file

@ -2,17 +2,30 @@ package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
import java.io.File;
/**
* Event class used to request the transaction open file dialog
*/
public class RequestTransactionOpenEvent {
private final Window window;
private final File file;
public RequestTransactionOpenEvent(Window window) {
this.window = window;
this.file = null;
}
public RequestTransactionOpenEvent(Window window, File file) {
this.window = window;
this.file = file;
}
public Window getWindow() {
return window;
}
public File getFile() {
return file;
}
}

View file

@ -2,17 +2,30 @@ package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
import java.io.File;
/**
* Event class used to request the wallet open dialog
*/
public class RequestWalletOpenEvent {
private final Window window;
private final File file;
public RequestWalletOpenEvent(Window window) {
this.window = window;
this.file = null;
}
public RequestWalletOpenEvent(Window window, File file) {
this.window = window;
this.file = file;
}
public Window getWindow() {
return window;
}
public File getFile() {
return file;
}
}

View file

@ -11,7 +11,6 @@ open module com.sparrowwallet.sparrow {
requires com.sparrowwallet.drongo;
requires com.google.common;
requires flowless;
requires com.google.gson;
requires com.google.zxing;
requires com.google.zxing.javase;
requires simple.json.rpc.client;
@ -28,6 +27,7 @@ open module com.sparrowwallet.sparrow {
requires bwt.jni;
requires jtorctl;
requires javacsv;
requires unique4j;
requires jul.to.slf4j;
requires bridj;
}