package wallettemplate; import com.aquafx_project.AquaFx; import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.kits.WalletAppKit; import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.RegTestParams; import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.utils.BriefLogFormatter; import com.google.bitcoin.utils.Threading; import com.google.common.base.Throwables; import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import wallettemplate.utils.GuiUtils; import wallettemplate.utils.TextFieldValidator; import java.io.File; import java.io.IOException; import java.net.URL; import static wallettemplate.utils.GuiUtils.*; public class Main extends Application { public static String APP_NAME = "WalletTemplate"; public static NetworkParameters params = MainNetParams.get(); public static WalletAppKit bitcoin; public static Main instance; private StackPane uiStack; private Pane mainUI; @Override public void start(Stage mainWindow) throws Exception { instance = this; // Show the crash dialog for any exceptions that we don't handle and that hit the main loop. GuiUtils.handleCrashesOnThisThread(); try { init(mainWindow); } catch (Throwable t) { // Nicer message for the case where the block store file is locked. if (Throwables.getRootCause(t) instanceof BlockStoreException) { GuiUtils.informationalAlert("Already running", "This application is already running and cannot be started twice."); } else { throw t; } } } private void init(Stage mainWindow) throws IOException { if (System.getProperty("os.name").toLowerCase().contains("mac")) { AquaFx.style(); } // Load the GUI. The Controller class will be automagically created and wired up. URL location = getClass().getResource("main.fxml"); FXMLLoader loader = new FXMLLoader(location); mainUI = loader.load(); Controller controller = loader.getController(); // Configure the window with a StackPane so we can overlay things on top of the main UI. uiStack = new StackPane(mainUI); mainWindow.setTitle(APP_NAME); final Scene scene = new Scene(uiStack); TextFieldValidator.configureScene(scene); // Add CSS that we need. mainWindow.setScene(scene); // Make log output concise. BriefLogFormatter.init(); // Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means // we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener // we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in // a future version. Threading.USER_THREAD = Platform::runLater; // Create the app kit. It won't do any heavyweight initialization until after we start it. bitcoin = new WalletAppKit(params, new File("."), APP_NAME); if (params == RegTestParams.get()) { bitcoin.connectToLocalHost(); // You should run a regtest mode bitcoind locally. } else if (params == MainNetParams.get()) { // Checkpoints are block headers that ship inside our app: for a new user, we pick the last header // in the checkpoints file and then download the rest from the network. It makes things much faster. // Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the // last months worth or more (takes a few seconds). bitcoin.setCheckpoints(getClass().getResourceAsStream("checkpoints")); } // Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen // or progress widget to keep the user engaged whilst we initialise, but we don't. bitcoin.setDownloadListener(controller.progressBarUpdater()) .setBlockingStartup(false) .setUserAgent(APP_NAME, "1.0") .startAndWait(); // Don't make the user wait for confirmations for now, as the intention is they're sending it their own money! bitcoin.wallet().allowSpendingUnconfirmedTransactions(); bitcoin.peerGroup().setMaxConnections(11); System.out.println(bitcoin.wallet()); controller.onBitcoinSetup(); mainWindow.show(); } public class OverlayUI<T> { public Node ui; public T controller; public OverlayUI(Node ui, T controller) { this.ui = ui; this.controller = controller; } public void show() { blurOut(mainUI); uiStack.getChildren().add(ui); fadeIn(ui); } public void done() { checkGuiThread(); fadeOutAndRemove(ui, uiStack); blurIn(mainUI); this.ui = null; this.controller = null; } } public <T> OverlayUI<T> overlayUI(Node node, T controller) { checkGuiThread(); OverlayUI<T> pair = new OverlayUI<T>(node, controller); // Auto-magically set the overlayUi member, if it's there. try { controller.getClass().getDeclaredField("overlayUi").set(controller, pair); } catch (IllegalAccessException | NoSuchFieldException ignored) { } pair.show(); return pair; } /** Loads the FXML file with the given name, blurs out the main UI and puts this one on top. */ public <T> OverlayUI<T> overlayUI(String name) { try { checkGuiThread(); // Load the UI from disk. URL location = getClass().getResource(name); FXMLLoader loader = new FXMLLoader(location); Pane ui = loader.load(); T controller = loader.getController(); OverlayUI<T> pair = new OverlayUI<T>(ui, controller); // Auto-magically set the overlayUi member, if it's there. try { controller.getClass().getDeclaredField("overlayUi").set(controller, pair); } catch (IllegalAccessException | NoSuchFieldException ignored) { } pair.show(); return pair; } catch (IOException e) { throw new RuntimeException(e); // Can't happen. } } @Override public void stop() throws Exception { bitcoin.stopAndWait(); super.stop(); } public static void main(String[] args) { launch(args); } }