diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6f55b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target/ +/backup/ +/staging/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f6807bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,246 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 NEM + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================= +nem2-sdk-java Subcomponents: + +The nem2-sdk-java contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + +======================================================================== +For vertx: +======================================================================== + +See license/LICENSE-vertx.txt + + +======================================================================== +MIT licenses +======================================================================== + +The following components are provided under the MIT License. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + (MIT License) bcprov-jdk15on (org.bouncycastle:bcprov-jdk15on:1.58 - https://www.bouncycastle.org/java.html) + + +======================================================================== +Apache licenses +======================================================================== + +The following components are provided under the Apache License. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + (APACHE-2.0 License) rxjava2 (io.reactivex.rxjava2:rxjava:2.1.7 - http://reactivex.io) + (APACHE-2.0 License) log4j (org.apache.logging.log4j:log4j-core:2.5 - https://logging.apache.org/log4j/1.2) + (APACHE-2.0 License) commons-codec (commons-codec:commons-codec:1.11 - https://commons.apache.org/proper/commons-codec) + (APACHE-2.0 License) commons-lang (commons-lang:commons-lang:2.6 - https://commons.apache.org/proper/commons-lang) + (APACHE-2.0 License) commons-io (commons-io:commons-io:2.4 - https://commons.apache.org/proper/commons-io) + (APACHE-2.0 License) commons-math3 (org.apache.commons:commons-math3:3.2 - https://commons.apache.org/proper/commons-math) + (APACHE-2.0 License) httpasyncclient (org.apache.httpcomponents:httpasyncclient:4.1.1 - https://hc.apache.org/httpcomponents-asyncclient-4.1.x/index.html) + (APACHE-2.0 License) JavaEWAH (com.googlecode.javaewah:JavaEWAH:1.0.0 - https://github.com/lemire/javaewah) + (APACHE-2.0 License) flatbuffers (com.google.flatbuffers:flatbuffers-java:1.8.0 - https://google.github.io/flatbuffers/) + (APACHE-2.0 License) gson (com.google.code.gson:gson:2.8.2 - https://github.com/google/gson) + (APACHE-2.0 License) swagger-parser (io.swagger:swagger-parser:1.0.34 - http://swagger.io) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..07cc2fa --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# File IT + +- Sharing any files using ProximaX SDK +- Upload any files using ProximaX SDK + +## Requirements +- OS Window, Mac OS, Linux +- Java 1.8 + +## Technology/Libraries +- Java SDK 1.8 and javafx (GUI) +- ProximaX Storage Java SDK: java-chain-xipfs-sdk +- ProximaX Java Chain SDK: java-xpx-chain-sdk + +## Installation +Checkout the repository and build + +``` +# mvn clean install +``` + +## Run + +``` +# cd staging +# java -jar file-it-.jar io.proximax.main.FileIt +``` + +## Demo + + + +Proximax Limited Copyright (c) 2019 diff --git a/libs/repository/com/qoppa/jPDFViewerFX/v2017R1.03/jPDFViewerFX-v2017R1.03.jar b/libs/repository/com/qoppa/jPDFViewerFX/v2017R1.03/jPDFViewerFX-v2017R1.03.jar new file mode 100644 index 0000000..9e7adfd Binary files /dev/null and b/libs/repository/com/qoppa/jPDFViewerFX/v2017R1.03/jPDFViewerFX-v2017R1.03.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9d2b871 --- /dev/null +++ b/pom.xml @@ -0,0 +1,168 @@ + + 4.0.0 + io.proximax.app + file-it + 0.5.1 + + yyyy-MM-dd HH:mm:ss + + + + local-maven-repo + file:///${project.basedir}/libs + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + central + bintray + http://jcenter.bintray.com + + + jitpack.io + https://jitpack.io + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.2 + + 8 + 8 + + + + maven-assembly-plugin + + + package + + single + + + + + + jar-with-dependencies + + + + io.proximax.app.main.FileIt + true + + + ${maven.build.timestamp} + + + + + + maven-antrun-plugin + + + moveFiles + install + + + + + + + + + run + + + + + + + + + io.swagger + swagger-annotations + 1.5.15 + + + io.vertx + vertx-web-client + 3.5.0 + compile + + + io.vertx + vertx-rx-java2 + 3.5.0 + compile + + + + io.proximax + java-xpx-chain-sdk + 0.5.1 + + + io.proximax + java-chain-xipfs-sdk + 0.5.0 + + + io.proximax + java-xpx-ipfs-api + 0.4.1 + + + org.apache.httpcomponents + httpmime + 4.5.9 + + + com.jfoenix + jfoenix + 8.0.7 + + + commons-validator + commons-validator + 1.6 + + + commons-io + commons-io + 2.5 + + + de.jensd + fontawesomefx + 8.1 + + + com.google.code.gson + gson + 2.8.5 + + + org.xerial + sqlite-jdbc + 3.23.1 + + + org.fxmisc.richtext + richtextfx + 0.9.3 + + + com.qoppa + jPDFViewerFX + v2017R1.03 + + + + diff --git a/scripts/run.cmd b/scripts/run.cmd new file mode 100644 index 0000000..4aa5484 --- /dev/null +++ b/scripts/run.cmd @@ -0,0 +1 @@ +java -Xmx2G -Djava.net.preferIPv4Stack=true -jar file-it-0.5.1.jar \ No newline at end of file diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100644 index 0000000..4aa5484 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1 @@ +java -Xmx2G -Djava.net.preferIPv4Stack=true -jar file-it-0.5.1.jar \ No newline at end of file diff --git a/src/assembly/assembly.xml b/src/assembly/assembly.xml new file mode 100644 index 0000000..8559074 --- /dev/null +++ b/src/assembly/assembly.xml @@ -0,0 +1,29 @@ + + + zip + + + + src/assembly + + + **/*.jar + **/*.bat + + + + target + + + **/*-fat.jar + + + + target/lib + + + bcprov-jdk15on-1.58.jar + + + + \ No newline at end of file diff --git a/src/assembly/runProxibox.bat b/src/assembly/runProxibox.bat new file mode 100644 index 0000000..fd1efd2 --- /dev/null +++ b/src/assembly/runProxibox.bat @@ -0,0 +1 @@ +java -cp bcprov-jdk15on-1.58.jar;proxibox-2.0.1-SNAPSHOT-fat.jar io.proximax.app.main.ProxiBox %1 \ No newline at end of file diff --git a/src/main/java/io/proximax/app/controller/AboutUsDialog.java b/src/main/java/io/proximax/app/controller/AboutUsDialog.java new file mode 100644 index 0000000..9421259 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/AboutUsDialog.java @@ -0,0 +1,113 @@ +package io.proximax.app.controller; + +import io.proximax.app.core.ui.IApp; +import io.proximax.app.utils.CONST; +import java.awt.Desktop; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Date; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class AboutUsDialog extends AbstractController { + + @FXML + private Label titleLbl; + @FXML + private Label msgLbl; + + private String appVer = ""; + + public AboutUsDialog() { + super(true); + } + + @Override + protected void initialize() { + String version = "0.5.1"; + String buildTime = CONST.SDF.format(new Date()); + String jdk = "1.8.0_191"; + + Manifest manifest = IApp.getManifest(); + if (manifest != null) { + Attributes mainAttributes = manifest.getMainAttributes(); + version = mainAttributes.getValue("Implementation-Version"); + buildTime = mainAttributes.getValue("Build-Time"); + jdk = mainAttributes.getValue("Build-Jdk"); + + } + String msg = "Product Version\n"; + appVer = CONST.APP_NAME + "-" + version; + msg += " " + CONST.APP_NAME + " " + version + "\n\n"; + msg += "Build Information \n"; + msg += " Version " + version + "\n"; + msg += " Date: " + buildTime + "\n"; + msg += " Java Version: " + jdk + "\n\n\n"; + msg += "Copyright (c) 2019, Proximax"; + msgLbl.setText(msg); + String title = "ABOUT " + CONST.APP_NAME; + titleLbl.setText(title.toUpperCase()); + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.ABOUTDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.ABOUTDLG_FXML; + } + + public static void showAbout(AbstractController parent) { + try { + AboutUsDialog dlg = new AboutUsDialog(); + dlg.setParent(parent); + dlg.openWindow(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public static void showAboutSafe(AbstractController parent) { + IApp.runSafe(() -> { + try { + AboutUsDialog dlg = new AboutUsDialog(); + dlg.setParent(parent); + dlg.openWindow(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + + } + + @FXML + protected void feedbackBtn(ActionEvent event) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.MAIL)) { + try { + String url = "mailto:support@proximax.io?subject=" + URLEncoder.encode(appVer + "-Feedback", "UTF-8"); + System.out.println("Url: " + url); + URI mailto = new URI(url); + desktop.mail(mailto); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + } + +} diff --git a/src/main/java/io/proximax/app/controller/AbstractController.java b/src/main/java/io/proximax/app/controller/AbstractController.java new file mode 100644 index 0000000..d330a78 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/AbstractController.java @@ -0,0 +1,313 @@ +package io.proximax.app.controller; + +import io.proximax.app.core.ui.IApp; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.StringUtils; +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.geometry.Rectangle2D; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.ButtonType; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.stage.Modality; +import javafx.stage.Screen; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.WindowEvent; + +public abstract class AbstractController implements Initializable, EventHandler { + + @FXML + protected Pane shadowPane; //shadow dialog + @FXML + protected Pane mainPane; //main pane + protected Stage primaryStage = null; + protected Scene scene = null; + protected AbstractController parentWindow = null; + protected boolean resizable = true; + protected boolean modal = false; + protected IApp mainApp = CONST.IAPP; + + private double xOffset = 0; + private double yOffset = 0; + + private ButtonType buttonType; + + protected abstract void dispose(); + + protected void onClosing(Event event) { + dispose(); + } + + public IApp getMainApp() { + return mainApp; + } + + public AbstractController(boolean modal) { + this.modal = modal; + + } + + public void setParent(AbstractController parentWindow) { + this.parentWindow = parentWindow; + } + + public void setResizable(boolean resizable) { + this.resizable = resizable; + } + + public AbstractController getParent() { + return parentWindow; + } + + public void setModal(boolean modal) { + this.modal = modal; + } + + public void openWindow(AbstractController parent) throws IOException { + setParent(parent); + openWindow(); + } + + public void openWindow() throws IOException { + if (getParent() != null && modal) { + openWindow(getParent().getStage(), getTitle(), getFXML()); + } else { + openWindow(null, getTitle(), getFXML()); + } + } + + public Stage getStage() { + return primaryStage; + } + + private void openWindow(Stage stage, String title, String fxml) throws IOException { + if (stage == null) { + stage = mainApp.getPrimaryStage(); + } + if (modal) { + primaryStage = new Stage(); + primaryStage.initModality(Modality.WINDOW_MODAL); + primaryStage.initOwner(stage); + } else { + primaryStage = new Stage(); + } + scene = createScene(fxml); + String themeUrl = getStylesheet(); + if (!scene.getStylesheets().contains(themeUrl)) { + scene.getStylesheets().clear(); + scene.getStylesheets().add(themeUrl); + } + primaryStage.setTitle(title); + primaryStage.setScene(scene); + primaryStage.setResizable(resizable); + primaryStage.setOnCloseRequest(this); + setIcon(); + + getMainPane().setOnMousePressed(new EventHandler() { + @Override + public void handle(MouseEvent event) { + xOffset = event.getSceneX(); + yOffset = event.getSceneY(); + } + }); + getMainPane().setOnMouseDragged(new EventHandler() { + @Override + public void handle(MouseEvent event) { + primaryStage.setX(event.getScreenX() - xOffset); + primaryStage.setY(event.getScreenY() - yOffset); + } + }); + if (shadowPane != null) { + scene.setFill(Color.TRANSPARENT); + primaryStage.initStyle(StageStyle.TRANSPARENT); + } + initialize(); + //keyboard + scene.getAccelerators().put(new KeyCodeCombination( + KeyCode.F1), (Runnable) () -> { + aboutUs(null); + }); + if (modal) { + primaryStage.showAndWait(); + } else { + primaryStage.show(); + } + + } + + public void reloadTheme() { + String themeUrl = getStylesheet(); + if (!scene.getStylesheets().contains(themeUrl)) { + scene.getStylesheets().clear(); + scene.getStylesheets().add(themeUrl); + } + } + + protected String getStylesheet() { + return mainApp.getCurrentThemeUrl(); + } + + private void setIcon() { + if (getMainApp() != null && getStage() != null) { + getStage().getIcons().add(getMainApp().getIcon()); + } + } + + public abstract String getTitle(); + + public abstract String getFXML(); + + protected void initialize() { + //init private data + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + } + + protected Scene createScene(String fxml) throws IOException { + FXMLLoader loader = loadXML(fxml); + loader.setController(this); + Parent root = loader.load(); + return new Scene(root); + } + + protected FXMLLoader loadXML(String fxml) throws IOException { + URL url = null; + if (fxml.startsWith(CONST.FXML_PATH)) { + url = getClass().getResource(fxml); + } else { + String os = IApp.getOS(); + if (!StringUtils.isEmpty(os)) { + url = getClass().getResource(CONST.FXML_PATH + os + "/" + fxml); + } + } + if (url == null) { //by default + url = getClass().getResource(CONST.FXML_PATH + fxml); + } + return new FXMLLoader(url); + + } + + protected void hide() { + primaryStage.hide(); + } + + protected void show() { + primaryStage.show(); + } + + protected void close() { + if (canExit()) { + dispose(); + primaryStage.close(); + } + } + + @Override + public void handle(Event event) { + if (event.getEventType() == WindowEvent.WINDOW_CLOSE_REQUEST) { + if (canExit()) { + buttonType = ButtonType.CLOSE; + onClosing(event); + } else { + event.consume(); + } + } + } + + protected boolean canExit() { + return true; + } + + public void showParent() { + if (parentWindow != null) { + parentWindow.show(); + } + } + + public void setMinimize() { + primaryStage.setIconified(true); + } + + public void setMaximize() { + if (primaryStage.isMaximized()) { + primaryStage.setMaximized(false); + } else { + Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds(); + primaryStage.setX(primaryScreenBounds.getMinX()); + primaryStage.setY(primaryScreenBounds.getMinY()); + primaryStage.setWidth(primaryScreenBounds.getWidth()); + primaryStage.setHeight(primaryScreenBounds.getHeight()); + primaryStage.setMaximized(true); + System.out.println("Screen: " + primaryScreenBounds); + } + } + + private Pane getMainPane() { + if (shadowPane != null) { + return shadowPane; + } + return mainPane; + } + + @FXML + protected void closeBtn(ActionEvent event) { + if (canExit()) { + buttonType = ButtonType.CLOSE; + onClosing(event); + close(); + } + } + + @FXML + protected void maximizeBtn(ActionEvent event) { + setMaximize(); + } + + @FXML + protected void minimizeBtn(ActionEvent event) { + setMinimize(); + } + + @FXML + protected void aboutUs(ActionEvent event) { + AboutUsDialog.showAbout(this); + } + + @FXML + protected void themeBtn(ActionEvent event) { + if (mainApp.getCurrentTheme().equals("Light")) { + mainApp.setTheme(1); + } else { + mainApp.setTheme(0); + } + reloadTheme(); + } + + public ButtonType getResultType() { + return buttonType; + } + + public void setButtonType(ButtonType buttonType) { + this.buttonType = buttonType; + } + + public void setResultType(ButtonType buttonType) { + this.buttonType = buttonType; + } + +} diff --git a/src/main/java/io/proximax/app/controller/ErrorDialog.java b/src/main/java/io/proximax/app/controller/ErrorDialog.java new file mode 100644 index 0000000..8cb0d71 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/ErrorDialog.java @@ -0,0 +1,82 @@ +package io.proximax.app.controller; + +import io.proximax.app.core.ui.IApp; +import io.proximax.app.utils.CONST; +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class ErrorDialog extends AbstractController { + + @FXML + private Label titleLbl; + @FXML + private Label msgLbl; + + private String title; + private StringProperty msgProperty = new SimpleStringProperty(); + + public ErrorDialog() { + super(true); + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + msgLbl.textProperty().bind(msgProperty); + } + + public void setTitle(String title) { + this.title = title; + } + + public void setContentText(String msg) { + msgProperty.set(msg); + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.ERRORDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.ERRORDLG_FXML; + } + + public static void showError(AbstractController parent, String msg) { + try { + ErrorDialog dlg = new ErrorDialog(); + dlg.setContentText(msg); + dlg.setParent(parent); + dlg.openWindow(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public static void showErrorSafe(AbstractController parent, String msg) { + IApp.runSafe(() -> { + try { + ErrorDialog dlg = new ErrorDialog(); + dlg.setContentText(msg); + dlg.setParent(parent); + dlg.openWindow(); + } catch (IOException ex) { + ex.printStackTrace(); + } + }); + } + +} diff --git a/src/main/java/io/proximax/app/controller/FilePropertiesDialog.java b/src/main/java/io/proximax/app/controller/FilePropertiesDialog.java new file mode 100644 index 0000000..386fa68 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/FilePropertiesDialog.java @@ -0,0 +1,90 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.app.utils.StringUtils; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; + +/** + * + * @author thcao + */ +public class FilePropertiesDialog extends AbstractController { + + private LocalFile localFile = null; + private LocalAccount localAccount = null; + + @FXML + JFXTextField nameField; + + @FXML + JFXTextField hashField; + + @FXML + JFXTextField nemField; + + @FXML + JFXTextField sizeField; + + @FXML + JFXTextField dateField; + + public FilePropertiesDialog(LocalAccount localAccount, LocalFile localFile) { + super(true); + this.localFile = localFile; + this.localAccount = localAccount; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + nameField.setText(localFile.reName); + hashField.setText(localFile.hash); + nemField.setText(localFile.nemHash); + sizeField.setText(StringUtils.getFileSize(localFile.fileSize)); + dateField.setText(localFile.getModified()); + } + + @Override + protected void dispose() { + } + + @FXML + protected void renameBtn(ActionEvent event) { + try { + String reName = nameField.getText(); + if (StringUtils.isNotEmpty(reName) && !localFile.reName.equals(reName)) { + int ret = LocalFileHelpers.checkNameExisted(localAccount, localFile.category, reName); + if (ret == 0) { + localFile.reName = reName; + LocalFileHelpers.renameLocalFile(localAccount, localFile, reName); + } else if (ret == 1) { + throw new Exception(reName + " already exists"); + } + setResultType(ButtonType.OK); + } else { + setResultType(ButtonType.FINISH); + } + close(); + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + @Override + public String getTitle() { + return CONST.FILEPROPDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.FILEPROPDLG_FXML; + } + +} diff --git a/src/main/java/io/proximax/app/controller/FileViewDialog.java b/src/main/java/io/proximax/app/controller/FileViewDialog.java new file mode 100644 index 0000000..2ab40ac --- /dev/null +++ b/src/main/java/io/proximax/app/controller/FileViewDialog.java @@ -0,0 +1,239 @@ +package io.proximax.app.controller; + +import com.qoppa.pdfViewerFX.PDFViewer; +import io.proximax.app.core.ui.IApp; +import io.proximax.download.DownloadParameter; +import io.proximax.download.DownloadResult; +import io.proximax.download.Downloader; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.app.utils.MimeTypes; +import io.proximax.app.utils.StringUtils; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.concurrent.Task; +import javafx.concurrent.Worker; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.layout.BorderPane; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.scene.media.MediaView; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import javafx.util.Duration; +import org.apache.commons.io.FileUtils; + +/** + * + * @author thcao + */ +public class FileViewDialog extends AbstractController { + + @FXML + WebView webView; + @FXML + MediaView mediaView; + private LocalAccount localAccount; + private LocalFile localFile; + private boolean isMediaViewer = false; + + public FileViewDialog(LocalAccount account, LocalFile localFile) { + super(true); + this.localAccount = account; + this.localFile = localFile; + } + + @Override + protected void initialize() { + String mimeType = MimeTypes.getMimeType(localFile.fileName); + if (mimeType.contains("video") || mimeType.contains("audio")) { + if (!localFile.isSecure()) { + String fileURL = localAccount.getNetworkConfiguration().getDownloadUrl() + localFile.hash; //.toExternalForm(); + playVideo(fileURL); + } else { + reloadContent(); + } + } else { + reloadContent(); + } + } + + private void playVideo(String url) { + try { + Media media = new Media(url); + MediaPlayer player = new MediaPlayer(media); + player.setOnEndOfMedia(() -> playBtn.setVisible(true)); + mediaView.setMediaPlayer(player); + } catch (Exception ex) { + ex.printStackTrace(); + } + + } + + @FXML + private Button playBtn; + + @FXML + public void playAndHide(ActionEvent event) { + playBtn.setVisible(false); + mediaView.getMediaPlayer().seek(Duration.ZERO); + mediaView.getMediaPlayer().play(); + } + + @Override + protected void dispose() { + + } + + @Override + public String getTitle() { + return CONST.FILEVIEWER_TITLE + localFile.fileName; + } + + @Override + public String getFXML() { + String mimeType = MimeTypes.getMimeType(localFile.fileName); + String resource = CONST.FILEVIEWER_FXML; + isMediaViewer = false; + if (mimeType.contains("video") || mimeType.contains("audio")) { + resource = CONST.MEDIAVIEWER_FXML; + isMediaViewer = true; + } + return resource; + } + + public boolean isMediaViewer() { + return isMediaViewer; + } + + AtomicInteger taskExecution = new AtomicInteger(0); + + private void reloadContent() { + if (localFile != null) { + Alert alert = new Alert( + Alert.AlertType.INFORMATION, + "Operation in progress", + ButtonType.CANCEL + ); + alert.setTitle("Load File"); + alert.setHeaderText("Please wait... "); + ProgressIndicator progressIndicator = new ProgressIndicator(); + alert.setGraphic(progressIndicator); + + Task task = new Task() { + final int N_ITERATIONS = 999; + + { + setOnFailed(a -> { + alert.close(); + updateMessage("Failed"); + close(); + showParent(); + }); + setOnSucceeded(a -> { + alert.close(); + updateMessage("Succeeded"); + }); + setOnCancelled(a -> { + alert.close(); + updateMessage("Cancelled"); + close(); + showParent(); + }); + } + + @Override + protected Void call() throws Exception { + updateMessage("Processing"); + int progress = 100; + updateProgress(progress, N_ITERATIONS); + File fileCache = LocalFileHelpers.getSourceFile(localAccount, localFile); + if (fileCache == null) { + try { + fileCache = LocalFileHelpers.createFileCache(localAccount, localFile); + //need download + DownloadParameter parameter = LocalFileHelpers.createDownloadParameter(localFile); + Downloader download = new Downloader(localAccount.connectionConfig); + DownloadResult downloadResult = download.download(parameter); + InputStream byteStream = downloadResult.getData().getByteStream(); + FileOutputStream fouts = new FileOutputStream(fileCache); + byte[] buffer = new byte[1024]; + int read = 0; + long sum = 0; + while ((read = byteStream.read(buffer)) >= 0) { + fouts.write(buffer, 0, read); + sum += read; + updateProgress(progress + 800 * sum / localFile.fileSize, 999); + } + } catch (Exception ex) { + ex.printStackTrace(); + failed(); + } + } + String mimeType = MimeTypes.getMimeType(localFile.fileName); + if (mimeType.contains("video") || mimeType.contains("audio")) { + if (localFile.isSecure()) { + String fileURL = fileCache.toURI().toURL().toString(); //.toExternalForm(); + updateProgress(N_ITERATIONS, N_ITERATIONS); + playVideo(fileURL); + + } + } else if (localFile.fileName.endsWith(".pdf")) { + try { + updateProgress(N_ITERATIONS, N_ITERATIONS); + PDFViewer pdfViewer = new PDFViewer(); + pdfViewer.getToolBar().getOpenDoc().setVisible(false); + pdfViewer.setSplitVisible(false); + pdfViewer.loadPDF(fileCache.getAbsolutePath()); + ((BorderPane) mainPane).setCenter(pdfViewer); + } catch (Exception ex) { + ex.printStackTrace(); + failed(); + } + } else { + String fileURL = fileCache.toURI().toURL().toExternalForm(); + System.out.println("Open URL: " + fileURL); + updateProgress(N_ITERATIONS, N_ITERATIONS); + IApp.runSafe(() -> { + WebEngine webEngine = webView.getEngine(); + webEngine.load(fileURL); + }); + } + + if (!isCancelled()) { + updateProgress(0, N_ITERATIONS); + } + + return null; + } + }; + progressIndicator.progressProperty() + .bind(task.progressProperty()); + Thread taskThread = new Thread( + task, + "view-thread-" + taskExecution.getAndIncrement() + ); + + taskThread.start(); + + alert.initOwner(getStage()); + Optional result = alert.showAndWait(); + + if (result.isPresent() + && result.get() == ButtonType.CANCEL && task.isRunning()) { + task.cancel(); + } + } + } +} diff --git a/src/main/java/io/proximax/app/controller/FolderDialog.java b/src/main/java/io/proximax/app/controller/FolderDialog.java new file mode 100644 index 0000000..13adff4 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/FolderDialog.java @@ -0,0 +1,165 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.app.utils.StringUtils; +import java.net.URL; +import java.util.List; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class FolderDialog extends AbstractController { + + @FXML + private JFXTextField folderField; + @FXML + private Label titleLbl; + @FXML + private Label folderLbl; + @FXML + private Button folderBtn; + @FXML + private JFXComboBox folderCbx; + + private LocalAccount localAccount; + private LocalFile localFile = null; + private boolean bNew = false; + + public FolderDialog(LocalAccount localAccount, boolean bNew, LocalFile localFile) { + super(true); + this.localFile = localFile; + this.localAccount = localAccount; + this.bNew = bNew; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + if (bNew) { + if (localFile != null) { + folderLbl.setText("Parent folder: " + localFile.filePath); + } + titleLbl.setText("CREATE A FOLDER"); + folderBtn.setText("CREATE"); + folderField.setVisible(true); + folderCbx.setVisible(false); + } else { + titleLbl.setText("MOVE TO FOLDER"); + folderBtn.setText("MOVE"); + folderField.setVisible(false); + folderCbx.setVisible(true); + ObservableList obList = FXCollections.observableList(LocalFileHelpers.getAllFolder(localAccount)); + obList.add(0, "/"); + folderCbx.setItems(obList); + folderCbx.setValue(obList.get(0)); + } + } + + public static boolean isValidName(String text) { + Pattern pattern = Pattern.compile( + "# Match a valid Windows filename (unspecified file system). \n" + + "^ # Anchor to start of string. \n" + + "(?! # Assert filename is not: CON, PRN, \n" + + " (?: # AUX, NUL, COM1, COM2, COM3, COM4, \n" + + " CON|PRN|AUX|NUL| # COM5, COM6, COM7, COM8, COM9, \n" + + " COM[1-9]|LPT[1-9] # LPT1, LPT2, LPT3, LPT4, LPT5, \n" + + " ) # LPT6, LPT7, LPT8, and LPT9... \n" + + " (?:\\.[^.]*)? # followed by optional extension \n" + + " $ # and end of string \n" + + ") # End negative lookahead assertion. \n" + + "[^<>:\"/\\\\|?*\\x00-\\x1F]* # Zero or more valid filename chars.\n" + + "[^<>:\"/\\\\|?*\\x00-\\x1F\\ .] # Last char is not a space or dot. \n" + + "$ # Anchor to end of string. ", + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS); + Matcher matcher = pattern.matcher(text); + boolean isMatch = matcher.matches(); + return isMatch; + } + + @FXML + private void folderBtn(ActionEvent event) { + try { + if (bNew) { + String nFolder = folderField.getText(); + if (StringUtils.isEmpty(nFolder)) { + throw new Exception("Folder name cannot be empty."); + } + if (!isValidName(nFolder)) { + throw new Exception("Folder cannot have special character"); + } + List folders = LocalFileHelpers.getAllFolder(localAccount); + + String filePath = CONST.HOME + nFolder; + if (localFile != null) { + filePath = localFile.filePath + "/" + nFolder; + } + if (folders.contains(filePath)) { + throw new Exception("Folder existed"); + } + LocalFileHelpers.addFolder(localAccount, nFolder, localFile); + } else { + String sFolder = folderCbx.getValue(); + if (localFile.isFolder) { + if (!sFolder.equals(localFile.filePath)) { + String filePath = CONST.HOME + localFile.category; + if (!CONST.HOME.equals(sFolder)) { + filePath = sFolder + "/" + localFile.category; + } + int ret = LocalFileHelpers.checkFolderExisted(localAccount, filePath); + if (ret == 0) { + LocalFileHelpers.moveFolderToFolder(localAccount, localFile, sFolder); + } else if (ret == 1) { + throw new Exception("Could not move \"" + localFile.category + "\", target existed"); + } + } + } else { + if (!sFolder.equals(localFile.category)) { + int ret = LocalFileHelpers.checkNameExisted(localAccount, sFolder, localFile.reName); + if (ret == 0) { + LocalFileHelpers.moveFileFolder(localAccount, localFile.id, sFolder); + } else if (ret == 1) { + throw new Exception("Could not move \"" + localFile.reName + "\", target existed"); + } + } + } + } + IApp.runSafe(() -> { + close(); + showParent(); + }); + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + @Override + protected void dispose() { + + } + + @Override + public String getTitle() { + return CONST.FOLDERDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.FOLDERDLG_FXML; + } + +} diff --git a/src/main/java/io/proximax/app/controller/HomeDialog.java b/src/main/java/io/proximax/app/controller/HomeDialog.java new file mode 100644 index 0000000..326623e --- /dev/null +++ b/src/main/java/io/proximax/app/controller/HomeDialog.java @@ -0,0 +1,1436 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXSpinner; +import com.jfoenix.controls.JFXTextField; +import com.jfoenix.controls.JFXToggleButton; +import io.proximax.sdk.infrastructure.Listener; +import io.proximax.sdk.model.account.Address; +import io.proximax.sdk.model.transaction.Transaction; +import io.proximax.sdk.model.transaction.TransferTransaction; +import io.proximax.download.DownloadParameter; +import io.proximax.download.DownloadResult; +import io.proximax.download.Downloader; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import java.io.File; +import java.io.IOException; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.stage.FileChooser; +import io.proximax.app.db.LocalFile; +import io.proximax.app.fx.control.ProxiBoxStatusBar; +import io.proximax.app.fx.control.text.RichTextEditor; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.DBHelpers; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.app.utils.MimeTypes; +import io.proximax.app.utils.StringUtils; +import io.proximax.async.AsyncCallbacks; +import io.proximax.async.AsyncTask; +import io.proximax.model.ProximaxMessagePayloadModel; +import io.proximax.service.RetrieveProximaxMessagePayloadService; +import io.proximax.upload.UploadParameter; +import io.proximax.upload.UploadResult; +import io.proximax.upload.Uploader; +import io.reactivex.disposables.Disposable; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.concurrent.ScheduledService; +import javafx.concurrent.Task; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.geometry.Pos; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.control.cell.TextFieldTableCell; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; +import javafx.util.Duration; +import javafx.util.StringConverter; + +/** + * FXML Controller class + * + * @author thcao + */ +public class HomeDialog extends AbstractController { + + @FXML + private ProxiBoxStatusBar statusBar; + @FXML + private Label statusLbl; + @FXML + private Label titleLbl; + @FXML + private Label userLbl; + @FXML + private JFXToggleButton nightModeBtn; + @FXML + private ToggleButton homeBtn; + @FXML + private JFXButton uploadBtn; + @FXML + private AnchorPane profilePane; + @FXML + private JFXTextField searchField; + @FXML + private TableView fileTable; + @FXML + private TableColumn nameCol; + @FXML + private TableColumn modifiedCol; + @FXML + private TableColumn typeCol; + @FXML + private TableColumn actionsCol; + //Local account + private LocalAccount localAccount; + //Download file + private File fileSave; + //bind status text property + private final StringProperty statusProperty; + // The table's data + private ObservableList localFiles = FXCollections.observableArrayList(); + // The table's data + private FilteredList filteredData = null; + // TableView type: 0 normal, 1: delete, 2: history + private int tableType = 0; + + //private String curFolder = CONST.HOME; + private LocalFile curFolder = null; + + private BooleanProperty bConnected = new SimpleBooleanProperty(false); + + /** + * + * @param account + */ + public HomeDialog(LocalAccount account) { + super(false); + this.statusProperty = new SimpleStringProperty(); + this.localAccount = account; + } + + final ContextMenu fileMenu = new ContextMenu(); + + private void viewFileAction() { + viewFileAction(null); + } + + private void viewFileAction(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + FileViewDialog viewer = new FileViewDialog(localAccount, localFile); + viewer.openWindow(this); + } catch (IOException ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + private void downloadFileAction() { + downloadFileAction(null); + } + + private void downloadFileAction(LocalFile localFile) { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save File"); + fileChooser.setInitialFileName(localFile.fileName); + fileChooser.setInitialDirectory(mainApp.getCurrentFolder()); + fileSave = fileChooser.showSaveDialog(primaryStage); + if (fileSave != null) { + mainApp.saveCurrentDir(fileSave.getAbsoluteFile().getParent()); + IntegerProperty pendingTasks = new SimpleIntegerProperty(0); + Task task = createDownloadTask(localFile, fileSave); + pendingTasks.set(pendingTasks.get() + 1); + + task.setOnSucceeded(taskEvent -> { + statusBar.progressProperty().unbind(); + statusBar.textProperty().unbind(); + pendingTasks.set(pendingTasks.get() - 1); + }); + // run task in single-thread executor (will queue if another task is running): + execJob.submit(task); + } + } + + private void shareFileAction() { + shareFileAction(null); + } + + private void shareFileAction(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + SharingDialog upload = new SharingDialog(localAccount, localFile); + upload.openWindow(this); + } catch (IOException ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + private void editFileAction() { + editFileAction(null); + } + + private void editFileAction(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + if (MimeTypes.isPlainText(localFile.fileName)) { + RichTextEditor dlg = new RichTextEditor(localAccount, localFile); + dlg.openWindow(this); + } else { + throw new Exception("Don't support this format"); + } + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + + } + + private void deleteFileAction() { + deleteFileAction(null); + } + + private void deleteFileAction(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + String quest = "Do you want to delete a file ?"; + if (localFile.isFolder) { + quest = "Do you want to delete all sub folders and files ?"; + } + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, quest, ButtonType.YES, ButtonType.NO); + ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().add(getMainApp().getIcon()); + alert.showAndWait(); + if (alert.getResult() == ButtonType.YES) { + if (localFile.isFolder) { + LocalFileHelpers.deleteFolder(localAccount, localFile); + } else { + LocalFileHelpers.deleteFile(localAccount, localFile); + } + updateFileTable(); + } + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + + } + + private void renameFileAction() { + renameFileAction(null); + } + + private void renameFileAction(LocalFile localFile) { + try { + int selectedRowIndex = fileTable.getSelectionModel().getSelectedIndex(); + fileTable.edit(selectedRowIndex, fileTable.getColumns().get(0)); + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + + } + + private void historyFileAction() { + historyFileAction(null); + } + + private void historyFileAction(LocalFile localFile) { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + showHistory(localFile); + + } + + private void moveFileAction() { + moveFileAction(null); + } + + private void moveFileAction(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + FolderDialog dlg = new FolderDialog(localAccount, false, localFile); + dlg.openWindow(this); + } catch (IOException ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize() { + try { + userLbl.setText(localAccount.fullName); + profilePane.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + showProfile(); + }); + SeparatorMenuItem separatorM = new SeparatorMenuItem(); + fileMenu.getItems().add(createMenuItem("Copy Hash", "copyhash", this::copyHashToClipboard, true)); + fileMenu.getItems().add(createMenuItem("Copy Url", "copyurl", this::copyUrlToClipboard, true)); + fileMenu.getItems().add(createMenuItem("Copy Information", "copyinfo", this::copyFullInfoToClipboard, true)); + fileMenu.getItems().add(createMenuItem("Properties", "", this::showFileProperties, true)); + fileMenu.getItems().add(separatorM); + fileMenu.getItems().add(createMenuItem("View", "view-img", this::viewFileAction, true)); + fileMenu.getItems().add(createMenuItem("Download", "download-img", this::downloadFileAction, true)); + fileMenu.getItems().add(createMenuItem("Sharing", "sharing-img-d", this::shareFileAction, true)); + fileMenu.getItems().add(createMenuItem("Edit", "edit-img", this::editFileAction, true)); + fileMenu.getItems().add(createMenuItem("Delete", "", this::deleteFileAction, false)); + fileMenu.getItems().add(createMenuItem("Rename", "", this::renameFileAction, false)); + fileMenu.getItems().add(createMenuItem("Move To Folder", "folder-img", this::moveFileAction, false)); + fileMenu.getItems().add(createMenuItem("History", "history-img", this::historyFileAction, true)); + fileTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + fileTable.setEditable(true); + fileTable.setPlaceholder(null); + nameCol = new TableColumn<>("FILE NAME"); + nameCol.setCellValueFactory(new PropertyValueFactory<>("name")); +// nameCol.setOnEditCommit((TableColumn.CellEditEvent t) -> { +// LocalFile localFile = ((LocalFile) t.getTableView().getItems().get( +// t.getTablePosition().getRow())); +// System.out.println("Edit: " + localFile); +// updateFileTable(tableType); +// +// +//// ((LocalFile) t.getTableView().getItems().get( +//// t.getTablePosition().getRow())).setName(t.getNewValue()); +// }); + modifiedCol = new TableColumn<>("MODIFIED"); + modifiedCol.setCellValueFactory(new PropertyValueFactory<>("modified")); + typeCol = new TableColumn<>("TYPE"); + typeCol.setCellValueFactory(new PropertyValueFactory<>("member")); + actionsCol = new TableColumn<>("ACTIONS"); + HomeDialog fileController = this; + actionsCol.setCellFactory(col -> new TableCell() { + private final HBox container; + JFXButton historyBtn, folderBtn, viewBtn, downloadBtn, shareBtn, editBtn; + + { + viewBtn = createImageButtonCSS("view-img", null, "Quick View"); + downloadBtn = createImageButtonCSS("download-img", null, "Download File"); + shareBtn = createImageButtonCSS("sharing-img-d", null, "Share File"); + editBtn = createImageButtonCSS("edit-img", null, "Edit File"); + folderBtn = createImageButtonCSS("folder-img", null, "Move to Folder"); + historyBtn = createImageButtonCSS("history-img", null, "View History"); +// viewBtn.disableProperty().bind(Bindings.not(bConnected)); +// editBtn.disableProperty().bind(Bindings.not(bConnected)); + downloadBtn.disableProperty().bind(Bindings.not(bConnected)); + shareBtn.disableProperty().bind(Bindings.not(bConnected)); + downloadBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + downloadFileAction(localFile); + }); + shareBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + shareFileAction(localFile); + }); + viewBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + viewFileAction(localFile); + }); + editBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + editFileAction(localFile); + }); + historyBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + historyFileAction(localFile); + }); + folderBtn.setOnAction(evt -> { + LocalFile localFile = filteredData.get(getIndex()); + moveFileAction(localFile); + }); + //container = new HBox(5, viewBtn, downloadBtn, shareBtn, editBtn, folderBtn, historyBtn); + container = new HBox(5, viewBtn, downloadBtn, shareBtn, editBtn, historyBtn); + container.setStyle("-fx-alignment: CENTER;"); + } + + @Override + public void updateItem(Void item, boolean empty) { + super.updateItem(item, empty); + int idx = getIndex(); + if (idx >= 0 && idx < filteredData.size()) { + LocalFile localFile = filteredData.get(idx); + if (localFile.isFolder) { + setGraphic(null); + } else { + if (localFile.status == CONST.FILE_STATUS_NOR + || localFile.status == CONST.FILE_STATUS_DEL) { + setGraphic(empty ? null : container); + if (getTableType() == CONST.TABLEVIEW_HIS) { + historyBtn.setVisible(false); + folderBtn.setVisible(false); + } else { + historyBtn.setVisible(true); + folderBtn.setVisible(true); + } + BooleanProperty bDisable2 = new SimpleBooleanProperty(LocalFileHelpers.isViewSupport(localFile) == false); + viewBtn.disableProperty().bind(Bindings.or(bDisable2, Bindings.not(bConnected))); + BooleanProperty bDisable3 = new SimpleBooleanProperty(LocalFileHelpers.isEditSupport(localFile) == false); + editBtn.disableProperty().bind(Bindings.or(bDisable3, Bindings.not(bConnected))); + } else { + //Label lbl = new Label("Waiting..."); + JFXSpinner progress = new JFXSpinner(); + progress.setPrefSize(1.0, 1.0); + //progress.setId("upload-progress"); + setGraphic(empty ? null : progress); + } + } + } else { + setGraphic(empty ? null : container); + } + } + }); + fileTable.setRowFactory(tv -> { + TableRow row = new TableRow<>(); + row.setOnMouseClicked(event -> { + if (!row.isEmpty()) { + LocalFile rowFile = row.getItem(); + if (event.getButton() == MouseButton.PRIMARY) { + if (event.getClickCount() == 2) { + if (rowFile.isFolder) { + curFolder = rowFile; + updateFileTable(); + } + } + } else if (event.getButton() == MouseButton.SECONDARY) { + BooleanProperty bDisable = new SimpleBooleanProperty(rowFile.isFolder); + for (MenuItem item : fileMenu.getItems()) { + String mnuText = item.getText(); + if ("History".equals(mnuText)) { + BooleanProperty bDisable1 = new SimpleBooleanProperty(getTableType() == CONST.TABLEVIEW_HIS); + item.disableProperty().bind(Bindings.or(bDisable, bDisable1)); + } else { + if ("Sharing".equals(mnuText) || "Download".equals(mnuText)) { + item.disableProperty().bind(Bindings.or(bDisable, Bindings.not(bConnected))); + } else if ("Edit".equals(mnuText)) { + BooleanProperty bDisable2 = new SimpleBooleanProperty(LocalFileHelpers.isEditSupport(rowFile) == false); + item.disableProperty().bind(Bindings.or(bDisable2, Bindings.or(bDisable, Bindings.not(bConnected)))); + } else if ("View".equals(mnuText)) { + BooleanProperty bDisable2 = new SimpleBooleanProperty(LocalFileHelpers.isViewSupport(rowFile) == false); + item.disableProperty().bind(Bindings.or(bDisable2, Bindings.or(bDisable, Bindings.not(bConnected)))); + } else if ("Delete".equals(mnuText) || "Rename".equals(mnuText) || "Move To Folder".equals(mnuText)) { + BooleanProperty bDisable1 = new SimpleBooleanProperty(getTableType() == CONST.TABLEVIEW_HIS); + item.disableProperty().bind(bDisable1); + } else { + item.disableProperty().bind(bDisable); + } + } + } + } + } else { + if (event.getButton() == MouseButton.SECONDARY) { + goBack(); + } + } + }); + row.setOnDragDropped(new EventHandler() { + @Override + public void handle(DragEvent event) { + Dragboard db = event.getDragboard(); + boolean success = false; + if (db.hasString()) { + int dropIndex = -1; + if (!row.isEmpty()) { + dropIndex = row.getIndex(); + LocalFile category = filteredData.get(dropIndex); + if (category.isFolder) { + String text = db.getString(); + String[] selected = text.split(";"); + for (String i : selected) { + LocalFile localFile = filteredData.get(Integer.parseInt(i)); + if (localFile.isFolder) { + if (category.id != localFile.id) { + LocalFileHelpers.moveFolderToFolder(localAccount, localFile, category); + } + } else { + LocalFileHelpers.moveFileFolder(localAccount, localFile.id, category.filePath); + } + } + updateFileTable(); + } + } + success = true; + } + event.setDropCompleted(success); + event.consume(); + } + }); + + // only display context menu for non-null items: + row.contextMenuProperty().bind( + Bindings.when(Bindings.isNotNull(row.itemProperty())) + .then(fileMenu) + .otherwise((ContextMenu) null)); + + return row; + }); + + nameCol.setCellFactory(col -> { + TextFieldTableCell cell = new TextFieldTableCell(new StringConverter() { + @Override + public String toString(String object) { + return object.toString(); + } + + @Override + public String fromString(String string) { + return string; + } + }) { + @Override + public void cancelEdit() { + super.cancelEdit(); + fileTable.refresh(); + } + + @Override + public void commitEdit(String newValue) { + if (localFile != null) { + if (StringUtils.isNotEmpty(newValue)) { + if (localFile.isFolder) { + if (!newValue.equals(localFile.category)) { + int idx = localFile.filePath.lastIndexOf(localFile.category); + String filePath = localFile.filePath.substring(0, idx) + newValue; + int ret = LocalFileHelpers.checkFolderExisted(localAccount, filePath); + if (ret == 0) { + LocalFileHelpers.renameFolder(localAccount, localFile, newValue); + super.commitEdit(newValue); + return; + } else if (ret == 1) { + setStatus(newValue + " already exists"); + } else { + setStatus("something wrong, cannot change new name " + newValue); + } + } + } else { + if (!newValue.equals(localFile.reName)) { + int ret = LocalFileHelpers.checkNameExisted(localAccount, localFile.category, newValue); + if (ret == 0) { + LocalFileHelpers.renameLocalFile(localAccount, localFile, newValue); + super.commitEdit(newValue); + return; + } else if (ret == 1) { + setStatus(newValue + " already exists"); + } else { + setStatus("something wrong, cannot change new name " + newValue); + } + } + } + } + cancelEdit(); + } + } + + private LocalFile localFile = null; + + @Override + public void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setText(null); + setGraphic(null); + } else { + int idx = getIndex(); + localFile = filteredData.get(idx); + if (localFile.isFolder) { + HBox box = new HBox(); + box.setSpacing(10); + Label lbl = new Label(localFile.getName()); + ImageView imageView = new ImageView(); + imageView.setFitWidth(24.0); + imageView.setFitHeight(24.0); + imageView.setId("folder-img"); + lbl.setStyle("-fx-font-weight:bold;"); + box.getChildren().addAll(imageView, lbl); + setGraphic(empty ? null : box); + setText(null); + } + } + } + }; + return cell; + + }); + fileTable.getColumns().add(nameCol); + fileTable.getColumns().add(modifiedCol); + fileTable.getColumns().add(typeCol); + fileTable.getColumns().add(actionsCol); + modifiedCol.setStyle("-fx-alignment: CENTER;"); + typeCol.setStyle("-fx-alignment: CENTER;"); + actionsCol.setStyle("-fx-alignment: CENTER;"); + filteredData = new FilteredList<>(localFiles, p -> true); + fileTable.setItems(filteredData); + searchField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(theFile -> { + if (newValue == null || newValue.isEmpty()) { + return true; + } + String lowerCaseFilter = newValue.toLowerCase(); + if (String.valueOf(theFile.getName()).toLowerCase().contains(lowerCaseFilter)) { + return true; + } + return false; // Does not match. + }); + }); + updateFileTable(CONST.TABLEVIEW_HOME); + fileTable.setOnDragDetected(new EventHandler() { //drag + @Override + public void handle(MouseEvent event) { + // drag was detected, start drag-and-drop gesture + List selected = fileTable.getSelectionModel().getSelectedIndices(); + if (selected != null) { + String str = ""; + for (Integer i : selected) { + str += i + ";"; + } + if (!str.isEmpty()) { + Dragboard db = fileTable.startDragAndDrop(TransferMode.ANY); + ClipboardContent content = new ClipboardContent(); + content.putString(str); + db.setContent(content); + } + event.consume(); + } + } + }); + fileTable.setOnDragOver(new EventHandler() { + @Override + public void handle(DragEvent event) { + // data is dragged over the target + Dragboard db = event.getDragboard(); + if (event.getDragboard().hasString()) { + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } + event.consume(); + } + }); + fileTable.setOnMouseClicked(event -> { + if (event.getButton() == MouseButton.SECONDARY) { + if (fileTable.getItems().isEmpty()) { + goBack(); + } + } + }); + initializeStatusBar(); + //check server is alive + initializeCheckingConnection(); + //monitor transaction + initializeMonitorTransaction(); + // monitor job + initializeJob(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + private void goBack() { + if (curFolder != null && getTableType() == CONST.TABLEVIEW_HOME) { + if (curFolder.fileId > 0) { + curFolder = LocalFileHelpers.getFolder(localAccount, curFolder.fileId); + } else { + curFolder = null; + } + updateFileTable(); + } + } + + public int getTableType() { + return tableType; + } + + public void setTableType(int tableType) { + this.tableType = tableType; + } + + private JFXButton createImageButtonCSS(String imgUrl, Runnable action, String toolTip) { + ImageView iv = new ImageView(); + iv.setFitWidth(16); + iv.setFitHeight(16); + iv.setId(imgUrl); + JFXButton button = new JFXButton("", iv); + button.setGraphicTextGap(0.0); + if (action != null) { + button.setOnAction(evt -> { + action.run(); + }); + } + if (toolTip != null) { + button.setTooltip(new Tooltip(toolTip)); + } + return button; + } + + private MenuItem createMenuItem(String title, String styleClass, Runnable action, boolean disable) { + ImageView iv = new ImageView(); + iv.setId(styleClass); + MenuItem menuItem = new MenuItem(title, iv); + //menuItem.getStyleClass().add(styleClass); + if (action != null) { + menuItem.setOnAction(evt -> { + action.run(); + }); + } + menuItem.setDisable(disable); + return menuItem; + } + + private JFXButton createImageButton(String imgUrl, Runnable action, String toolTip) { + Image image = mainApp.getImageFromResource(imgUrl + ".png"); + ImageView iv = new ImageView(image); + iv.setFitWidth(16); + iv.setFitHeight(16); + JFXButton button = new JFXButton("", iv); + button.setGraphicTextGap(0.0); + if (action != null) { + button.setOnAction(evt -> { + action.run(); + }); + } + if (toolTip != null) { + button.setTooltip(new Tooltip(toolTip)); + } + return button; + } + + private void copyUrlToClipboard() { + LocalFile localFile = fileTable.getSelectionModel().getSelectedItem(); + if (localFile != null) { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(localAccount.getNetworkConfiguration().getDownloadUrl() + localFile.hash); + clipboard.setContent(content); + } + } + + private void copyHashToClipboard() { + LocalFile localFile = fileTable.getSelectionModel().getSelectedItem(); + if (localFile != null) { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(localFile.hash); + clipboard.setContent(content); + } + } + + private void copyFullInfoToClipboard() { + List files = fileTable.getSelectionModel().getSelectedItems(); + String str = ""; + for (LocalFile localFile : files) { + if (!localFile.isFolder) { + str += localFile.fileName + "(" + localFile.fileSize + ")" + " - Link: " + localAccount.getNetworkConfiguration().getDownloadUrl() + localFile.hash + "\n"; + } + } + if (!StringUtils.isEmpty(str)) { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(str); + clipboard.setContent(content); + } + } + + /** + * Initialize status bar + */ + private void initializeStatusBar() { + statusBar = new ProxiBoxStatusBar(); + statusBar.setImageSuccess(getMainApp().getImageFromResource(CONST.IMAGE_GREEN, 14.0, 14.0)); + statusBar.setImageFailed(getMainApp().getImageFromResource(CONST.IMAGE_RED, 14.0, 14.0)); + statusBar.setEventHandler(this); + ((BorderPane) mainPane).setBottom(statusBar); + BorderPane.setAlignment(statusBar, Pos.BOTTOM_CENTER); + // text in status bar + statusBar.textProperty().bind(statusProperty); + List list = localAccount.getNodes(); + ObservableList obList = FXCollections.observableList(list); + if (localAccount.getCurrentNodeIndex() == -1) { + localAccount.setConnectionIndex(0); + } + statusBar.setNodeItems(obList, localAccount.getCurrentNodeIndex()); + // connection status + setConnection(localAccount.isConnected()); + } + + /** + * Set connection status in status bar + * + * @param connected + */ + public void setConnection(Boolean connected) { + this.statusBar.setImageStatus(connected); + bConnected.set(connected); + if (connected) { + if (this.listener == null) { + initializeMonitorTransaction(); + } + } else { + if (this.listener != null) { + this.listener.close(); + this.listener = null; + } + } + } + + /** + * Set connection status: image, text in status bar + * + * @param connected + */ + public void setConnectionStatus(Boolean connected) { + if (connected) { + setStatus(CONST.STR_CONNECTED); + } else { + setStatus(CONST.STR_DISCONNECTED); + } + this.statusBar.setImageStatus(connected); + } + + /** + * Set status text in status bar + * + * @param status + */ + public void setStatus(String status) { + this.statusProperty.set(CONST.STR_STATUS + status); + } + + @FXML + private void navHome(ActionEvent event) { + tableType = CONST.TABLEVIEW_HOME; + curFolder = null; + updateFileTable(tableType); + } + + private void showProfile() { + try { + UserProfileDialog dlg = new UserProfileDialog(localAccount); + dlg.openWindow(this); + } catch (IOException ex) { + } + } + + private void showFileProperties() { + showFileProperties(null); + } + + private void showFileProperties(LocalFile localFile) { + try { + if (localFile == null) { + localFile = filteredData.get(fileTable.getSelectionModel().getSelectedIndex()); + } + FilePropertiesDialog dlg = new FilePropertiesDialog(localAccount, localFile); + dlg.openWindow(this); + if (dlg.getResultType() == ButtonType.OK) { + updateFileTable(); + } + } catch (IOException ex) { + } + } + + @FXML + private void allFilesNav(ActionEvent event) { + tableType = CONST.TABLEVIEW_ALL; + updateFileTable(tableType); + } + + @FXML + private void sharingNav(ActionEvent event) { + tableType = CONST.TABLEVIEW_SHAR; + updateFileTable(tableType); + } + + @FXML + private void logoutBtn(ActionEvent event) { + resetDefaultFocus(); + try { + close(); + showParent(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @FXML + private void uploadBtn(ActionEvent event) { + try { + UploaderDialog dialog = new UploaderDialog(localAccount, (curFolder != null) ? curFolder.filePath : CONST.HOME); + dialog.setParent(this); + dialog.openWindow(); + } catch (Exception ex) { + ex.printStackTrace(); + } + resetDefaultFocus(); + } + + @FXML + private void nightModeBtn1(ActionEvent event) { + if (nightModeBtn.isSelected()) { + nightModeBtn.setSelected(false); + } else { + nightModeBtn.setSelected(true); + } + nightModeBtn(event); + } + + @FXML + private void nightModeBtn(ActionEvent event) { + if (nightModeBtn.isSelected()) { + mainApp.setTheme(1); + reloadTheme(); + } else { + mainApp.setTheme(0); + reloadTheme(); + } + } + + @FXML + private void netCfgBtn(ActionEvent event) { + try { + NetworkDialog dlg = new NetworkDialog(); + dlg.setParent(this); + dlg.openWindow(); + if (dlg.getResultType() == ButtonType.OK) { + List list = localAccount.getNodes(); + ObservableList obList = FXCollections.observableList(list); + statusBar.setNodeItems(obList, localAccount.getCurrentNodeIndex()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + resetDefaultFocus(); + } + + @FXML + private void newFolderBtn(ActionEvent event) { + try { + FolderDialog dialog = new FolderDialog(localAccount, true, curFolder); + dialog.setParent(this); + dialog.openWindow(); + } catch (Exception ex) { + ex.printStackTrace(); + } + resetDefaultFocus(); + } + + private void resetDefaultFocus() { + homeBtn.requestFocus(); + } + + @FXML + private void showDeleted(ActionEvent event) { + tableType = CONST.TABLEVIEW_DEL; + updateFileTable(tableType); + } + + private void showHistory(LocalFile localFile) { + tableType = CONST.TABLEVIEW_HIS; + updateFileTable(tableType, localFile.fileId); + } + + private void updateFileTable() { + updateFileTable(tableType); + } + + private void updateFileTable(int status) { + updateFileTable(status, 0); + } + + private void updateFileTable(int status, int fileId) { + List listFiles = null; + List listFolders = null; + tableType = status; + switch (status) { + case CONST.TABLEVIEW_DEL: + titleLbl.setText("Deleted Files"); + listFiles = LocalFileHelpers.getDelFiles(localAccount.fullName, localAccount.network); + if (listFiles != null) { + fileTable.getColumns().get(3).setVisible(false); + } + break; + case CONST.TABLEVIEW_HIS: + titleLbl.setText("Version History"); + listFiles = LocalFileHelpers.getHistoryFile(localAccount.fullName, localAccount.network, fileId); + if (listFiles != null) { + fileTable.getColumns().get(3).setVisible(true); + } + break; + case CONST.TABLEVIEW_ALL: + titleLbl.setText("ALL FILES"); + listFiles = LocalFileHelpers.getFiles(localAccount.fullName, localAccount.network); + fileTable.getColumns().get(3).setVisible(true); + break; + case CONST.TABLEVIEW_HOME: + if (curFolder == null) { + titleLbl.setText("HOME"); + listFolders = LocalFileHelpers.getFolders(localAccount.fullName, localAccount.network, curFolder); + listFiles = LocalFileHelpers.getFilesFolder(localAccount.fullName, localAccount.network, CONST.HOME); + } else { + String title = "HOME" + curFolder.filePath; + titleLbl.setText(title.replace("/", ">")); + listFolders = LocalFileHelpers.getFolders(localAccount.fullName, localAccount.network, curFolder); + listFiles = LocalFileHelpers.getFilesFolder(localAccount.fullName, localAccount.network, curFolder.filePath); + } + fileTable.getColumns().get(3).setVisible(true); + break; + case CONST.TABLEVIEW_SHAR: + titleLbl.setText("SHARING"); + listFiles = LocalFileHelpers.getSharingFiles(localAccount.fullName, localAccount.network, CONST.HOME); + fileTable.getColumns().get(3).setVisible(true); + break; + } + localFiles.clear(); + if (listFolders != null) { + localFiles.addAll(listFolders); + } + if (listFiles != null) { + localFiles.addAll(listFiles); + } + } + + private ScheduledService serverStatus = null; + private ScheduledService jobStatus = null; + + /** + * Initialize service checking connection + */ + private void initializeCheckingConnection() { + serverStatus = new ScheduledService() { + @Override + protected Task createTask() { + Task aliveTask = new Task() { + @Override + protected Boolean call() throws Exception { + if (localAccount.testNode()) { + //sendXPXMessage(netconf.getNetworkType(), localAccount.getApiUrl(), netconf.enc2, localAccount.address, XPX_10); + //NetworkUtils.send10XPX() + return true; + } + return false; + } + + @Override + protected void succeeded() { + serverStatus.setPeriod(Duration.minutes(1)); + if (getValue()) { // alive + setConnection(Boolean.TRUE); + } else { + setConnection(Boolean.FALSE); + } + } + }; + return aliveTask; + } + }; + serverStatus.setPeriod(Duration.seconds(1)); + serverStatus.start(); + } + +// /** +// * Initialize service checking connection +// */ + private void initializeJob() { + jobStatus = new ScheduledService() { + @Override + protected Task createTask() { + Task aliveTask = new Task() { + @Override + protected Boolean call() throws Exception { + if (localAccount.testNode()) { + if (uploadTask != null) { + return true; + } + LocalFile localFile = DBHelpers.getJob(localAccount.fullName, localAccount.network); + if (localFile != null) { + try { + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_QUEUE); + IntegerProperty pendingTasks = new SimpleIntegerProperty(0); + File fileSave = new File(localFile.filePath); + uploadTask = createUploadTask(localFile, fileSave); + pendingTasks.set(pendingTasks.get() + 1); + uploadTask.setOnSucceeded(taskEvent -> { + IApp.runSafe(() -> { + statusBar.progressProperty().unbind(); + statusBar.textProperty().unbind(); + }); + pendingTasks.set(pendingTasks.get() - 1); + uploadTask = null; + }); + uploadTask.setOnCancelled(taskEvent -> { + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_FAILED); + pendingTasks.set(pendingTasks.get() - 1); + uploadTask = null; + }); + // run task in single-thread executor (will queue if another task is running): + execJob.submit(uploadTask); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + return true; + } + + }; + return aliveTask; + } + }; + jobStatus.setPeriod(Duration.seconds(10)); + jobStatus.start(); + } + + /** + * Dispose when close application + */ + @Override + protected void dispose() { + execJob.shutdownNow(); + try { + if (!execJob.awaitTermination(800, TimeUnit.MILLISECONDS)) { + execJob.shutdownNow(); + } + } catch (InterruptedException e) { + execJob.shutdownNow(); + } + if (localAccount == null) { + localAccount.disconnect(); + localAccount = null; + } + if (serverStatus != null) { + serverStatus.cancel(); + } + if (jobStatus != null) { + jobStatus.cancel(); + } + } + + private AtomicInteger taskCount = new AtomicInteger(0); + private ExecutorService execJob = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); // allows app to exit if tasks are running + return t; + }); + + /** + * Create download task when user click download + * + * @param localFile + * @param fileSave + * @return + */ + private Task createDownloadTask(LocalFile localFile, File fileSave) { + final int taskNumber = taskCount.incrementAndGet(); + Task task = new Task() { + @Override + protected Void call() throws Exception { + IApp.runSafe(() -> { + statusBar.progressProperty().bind(progressProperty()); + statusBar.textProperty().bind(messageProperty()); + }); + updateMessage("Status: downloading " + fileSave.getName() + "..."); + DownloadParameter parameter = LocalFileHelpers.createDownloadParameter(localFile); + Downloader download = new Downloader(localAccount.connectionConfig); + DownloadResult downloadResult = download.download(parameter); + updateProgress(100, 999); + InputStream byteStream = downloadResult.getData().getByteStream(); + FileOutputStream fouts = new FileOutputStream(fileSave); + byte[] buffer = new byte[1024]; + int read = 0; + long sum = 0; + while ((read = byteStream.read(buffer)) >= 0) { + fouts.write(buffer, 0, read); + sum += read; + updateProgress(100 + 800 * sum / localFile.fileSize, 999); + } + fouts.close(); + byteStream.close(); + updateProgress(999, 999); + updateMessage("Status: download " + fileSave.getName() + " completed."); + return null; + } + }; + return task; + } + + private AsyncTask asyncTask = null; + private Task uploadTask = null; + + private Task createUploadTask(LocalFile localFile, File file) { + final int taskNumber = taskCount.incrementAndGet(); + Task task = new Task() { + @Override + protected Void call() throws Exception { + if (localAccount.testNode()) { + IApp.runSafe(() -> { + statusBar.progressProperty().bind(progressProperty()); + statusBar.textProperty().bind(messageProperty()); + updateMessage("Status: uploading " + localFile.filePath + " ..."); + updateProgress(100, 999); + }); + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_UPLOAD); + UploadParameter uploadParameter = LocalFileHelpers.createUploadFileParameter(localAccount, localFile, file); + Uploader upload = new Uploader(localAccount.connectionConfig); + asyncTask = upload.uploadAsync(uploadParameter, + AsyncCallbacks.create( + (UploadResult uploadResult) -> { + localFile.uploadDate = System.currentTimeMillis(); + localFile.hash = uploadResult.getData().getDataHash(); + localFile.nemHash = uploadResult.getTransactionHash(); + localFile.status = CONST.FILE_STATUS_TXN; + LocalFileHelpers.updateLocalFile(localAccount.fullName, localAccount.network, localFile.id, localFile.hash, localFile.nemHash, localFile.uploadDate, localFile.status); + IApp.runSafe(() -> { + updateMessage("Status: upload " + localFile.filePath + " completed."); + updateProgress(999, 999); + }); + }, + (Throwable ex) -> { + ex.printStackTrace(); + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_FAILED); + IApp.runSafe(() -> { + updateMessage("Status: issue upload " + localFile.filePath); + updateProgress(0, 999); + }); + if (isCancelled()) { + return; + } + })); + int progress = 100; + while (!asyncTask.isDone()) { + if (progress < 900) { + progress += 10; + updateProgress(progress, 999); + } + if (asyncTask.isCancelled()) { + IApp.runSafe(() -> { + updateMessage("Status: cancel upload " + localFile.filePath); + updateProgress(0, 999); + }); + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_FAILED); + break; + } + Thread.sleep(100); + } + } else { + LocalFileHelpers.updateLocalFileStatus(localAccount.fullName, localAccount.network, localFile.id, CONST.FILE_STATUS_FAILED); + } + return null; + } + }; + return task; + } + + private void disconnectNode() { + localAccount.disconnect(); + if (this.listener != null) { + this.listener.close(); + this.listener = null; + } + } + + @Override + public void handle(Event event) { + if (event.getSource() instanceof ImageView) { + if (event instanceof MouseEvent && ((MouseEvent) event).getClickCount() == 2) { + ImageView imageView = (ImageView) event.getSource(); + if (imageView.getId().equals("status-image")) { + if (localAccount.isConnected()) { + localAccount.disconnect(); + setConnection(false); + } else { + + } + } + } + } else if (event.getSource() instanceof ComboBox) { + ComboBox comboBox = (ComboBox) event.getSource(); + if (comboBox.getId().equals("status-nodes")) { + //setConnectionStatus(localAccount.connectToNode(comboBox.getSelectionModel().getSelectedIndex())); + localAccount.disconnect(); + setConnection(false); + localAccount.setConnectionIndex(comboBox.getSelectionModel().getSelectedIndex()); + if (serverStatus != null) { + serverStatus.cancel(); + serverStatus.setPeriod(Duration.seconds(1)); + serverStatus.restart(); + } + } + } + super.handle(event); + } + + @Override + protected void onClosing(Event event) { + super.onClosing(event); + IApp.exit(0); + } + + @Override + protected void show() { + primaryStage.show(); + updateFileTable(); + } + + @Override + public String getTitle() { + return CONST.HOME_TITLE + " [" + localAccount.fullName + "]"; + } + + @Override + public String getFXML() { + return CONST.HOME_FXML; + } + + private Listener listener = null; + + private void initializeMonitorTransaction() { + try { + if (!localAccount.testNode()) { + return; + } + listener = (Listener)localAccount.connectionConfig.getBlockchainNetworkConnection().getBlockchainApi().createListener(); + Address address = Address.createFromRawAddress(localAccount.address); + listener.open().get(); + //listener.status(address); + // wait for transaction to be confirmed + Disposable subscribe = listener.confirmed(address).subscribe((Transaction txn) -> { + if (txn instanceof TransferTransaction) { + TransferTransaction transferTransaction = (TransferTransaction) txn; + String sender = transferTransaction.getSigner().get().getAddress().plain(); + String recipient = transferTransaction.getRecipient().getAddress().get().plain(); + String nemHash = transferTransaction.getTransactionInfo().get().getHash().get(); + if (localAccount.address.equals(recipient)) { + if (localAccount.address.equals(sender)) { + LocalFileHelpers.updateFileFromTransaction(localAccount.fullName, localAccount.network, nemHash, CONST.FILE_STATUS_NOR); + IApp.runSafe(() -> { + updateFileTable(); + }); + } else { + try { + RetrieveProximaxMessagePayloadService retrieveProximaxMessagePayloadService = new RetrieveProximaxMessagePayloadService(localAccount.connectionConfig.getBlockchainNetworkConnection()); + ProximaxMessagePayloadModel result = retrieveProximaxMessagePayloadService.getMessagePayload(transferTransaction, localAccount.privateKey); + if (result != null && result.getData() != null) { + if (result.getData().getDescription().contains(CONST.APP_NAME)) { + LocalFile newFile = new LocalFile(); + newFile.fileName = result.getData().getName(); + newFile.hash = result.getData().getDataHash(); + Map metaData = result.getData().getMetadata(); + newFile.shared = metaData.get("user"); + newFile.uType = StringUtils.parseInt(metaData.get("utype"), 0); + newFile.fileSize = StringUtils.parseLong(metaData.get("size"), 0); + newFile.nemHash = nemHash; + newFile.modified = result.getData().getTimestamp(); + newFile.uploadDate = result.getData().getTimestamp(); + newFile.privateKey = localAccount.privateKey; + newFile.publicKey = AccountHelpers.getPublicKeyFromAddress(localAccount.getApiHost(), localAccount.getApiPort(), sender); + newFile.address = recipient; + newFile.metadata = metaData.toString(); + newFile.status = CONST.FILE_STATUS_NOR; + LocalFileHelpers.addSharedFile(localAccount, newFile); + //LocalFileHelpers.updateFileFromTransaction(localAccount.fullName, localAccount.network, nemHash, CONST.FILE_STATUS_NOR); + IApp.runSafe(() -> { + updateFileTable(); + }); + } + } + } catch (Exception ex) { + } + } + } + } + }); + } catch (Exception ex) { + ErrorDialog.showError(this, ex.getMessage()); + } + } + + @Override + protected boolean canExit() { + if (uploadTask != null) { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Do you quit application and cancel upload file ?", ButtonType.YES, ButtonType.NO); + ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().add(getMainApp().getIcon()); + alert.showAndWait(); + if (alert.getResult() == ButtonType.YES) { + jobStatus.cancel(); + if (asyncTask != null) { + while (!asyncTask.isDone()) { + asyncTask.cancel(); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + if (asyncTask.isCancelled()) { + break; + } + } + } + while (!uploadTask.isDone()) { + uploadTask.cancel(true); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + if (uploadTask.isCancelled()) { + break; + } + } + uploadTask.cancel(true); + } else { + return false; + } + } + return true; + } + +} diff --git a/src/main/java/io/proximax/app/controller/LoginDialog.java b/src/main/java/io/proximax/app/controller/LoginDialog.java new file mode 100644 index 0000000..ba6038a --- /dev/null +++ b/src/main/java/io/proximax/app/controller/LoginDialog.java @@ -0,0 +1,162 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXPasswordField; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.StringUtils; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.ResourceBundle; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ToggleButton; +import javafx.scene.input.KeyCode; +import javafx.stage.Stage; + +/** + * + * @author Marvin + */ +public class LoginDialog extends AbstractController { + + @FXML + private JFXPasswordField passwordField; + @FXML + private JFXComboBox usernameCbx; + @FXML + private JFXTextField passField; + @FXML + private ToggleButton viewBtn; + + public LoginDialog() { + super(false); + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + refreshUsers(); + + passField.setManaged(false); + passField.setVisible(false); + passField.managedProperty().bind(viewBtn.selectedProperty()); + passField.visibleProperty().bind(viewBtn.selectedProperty()); + passwordField.managedProperty().bind(viewBtn.selectedProperty().not()); + passwordField.visibleProperty().bind(viewBtn.selectedProperty().not()); + passField.textProperty().bindBidirectional(passwordField.textProperty()); + + usernameCbx.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (viewBtn.isSelected()) { + passField.requestFocus(); + } else { + passwordField.requestFocus(); + } + }); + passwordField.setOnKeyPressed((event) -> { + if (event.getCode() == KeyCode.ENTER) { + loginBtn(null); + } + }); + passField.setOnKeyPressed((event) -> { + if (event.getCode() == KeyCode.ENTER) { + loginBtn(null); + } + }); + } + + @FXML + void loginBtn(ActionEvent event) { + try { + String password = passwordField.getText(); + String fullName = usernameCbx.getValue(); + if (!StringUtils.isEmpty(fullName)) { + String[] usernet = fullName.split("/"); + LocalAccount account = AccountHelpers.login(usernet[1], usernet[0], password); + if (account != null) { + //account.connectNextNode(); + passwordField.setText(""); + hide(); + HomeDialog fileController = new HomeDialog(account); + fileController.setParent(this); + fileController.openWindow(); + + } else { + ErrorDialog.showError(this, "Account issue: invalid password"); + } + } else { + throw new Exception("Please select your account"); + } + } catch (Exception e) { + ErrorDialog.showError(this, e.getMessage()); + } + + } + + @FXML + void networkBtn(ActionEvent event) { + try { + NetworkDialog dlg = new NetworkDialog(); + dlg.setParent(this); + dlg.openWindow(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @FXML + void registerBtn(ActionEvent event) { + try { + // load + hide(); + RegistrationDialog register = new RegistrationDialog(); + register.openWindow(this); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + protected void dispose() { + IApp.exit(0); + } + + public static void showDialog(Stage stage, IApp app) { + try { + LoginDialog dialog = new LoginDialog(); + dialog.setResizable(false); + dialog.openWindow(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + public void show() { + refreshUsers(); + reloadTheme(); + super.show(); + } + + private void refreshUsers() { + usernameCbx.getItems().clear(); + List list = AccountHelpers.getAccounts(); + ObservableList obList = FXCollections.observableList(list); + usernameCbx.setItems(obList); + } + + @Override + public String getTitle() { + return CONST.LOGIN_TITLE; + } + + @Override + public String getFXML() { + return CONST.LOGIN_FXML; + } +} diff --git a/src/main/java/io/proximax/app/controller/NetworkDialog.java b/src/main/java/io/proximax/app/controller/NetworkDialog.java new file mode 100644 index 0000000..1d8007e --- /dev/null +++ b/src/main/java/io/proximax/app/controller/NetworkDialog.java @@ -0,0 +1,234 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXListView; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.db.NetworkConfiguration; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.DBHelpers; +import io.proximax.app.utils.NetworkUtils; +import io.proximax.app.utils.StringUtils; +import java.util.Map; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ListView; + +/** + * + * @author thcao + */ +public class NetworkDialog extends AbstractController { + + @FXML + private JFXComboBox networkCbx; + + @FXML + private JFXTextField nodeField; + + @FXML + private JFXTextField ipfsField; + + @FXML + private ListView nodesLv; + + @FXML + private JFXButton addNodeBtn; + + @FXML + private JFXButton addIpfsBtn; + + @FXML + private JFXListView ipfsLv; + + private String network = NetworkUtils.NETWORK_DEFAULT; + + private Map configs; + + private boolean modified = false; + + public NetworkDialog() { + super(true); + } + + @Override + protected void initialize() { + try { + configs = DBHelpers.getNetworkConfiguration(-1); + } catch (Exception ex) { + ex.printStackTrace(); + } + ObservableList obList = FXCollections.observableList(NetworkUtils.NETWORK_SUPPORT); + networkCbx.setItems(obList); + networkCbx.setValue(network); + onSelectedNetwork(network); + + networkCbx.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (!network.equals(newValue)) { + onSelectedNetwork(newValue); + } + }); + + addNodeBtn.disableProperty().bind(nodeField.textProperty().isEmpty()); + addIpfsBtn.disableProperty().bind(ipfsField.textProperty().isEmpty()); + + } + + @Override + protected void dispose() { + + } + + @Override + public String getTitle() { + return CONST.NETWORKDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.NETWORKDLG_FXML; + } + + @FXML + void saveBtn(ActionEvent event) { + for (String net : NetworkUtils.NETWORK_SUPPORT) { + NetworkConfiguration netconf = configs.get(net); + if (netconf.modified) { + try { + DBHelpers.updateNetworkConfiguration(netconf); + netconf.modified = false; + } catch (Exception ex) { + } + } + } + if (modified) { + NetworkUtils.loadNetworkConfig(); + modified = false; + setButtonType(ButtonType.OK); + } else { + setButtonType(ButtonType.CLOSE); + } + close(); + } + + @FXML + void addNodeBtn(ActionEvent event) { + try { + String str = nodeField.getText().trim(); + if (!StringUtils.isEmpty(str)) { + if (!nodesLv.getItems().contains(str)) { + if (NetworkUtils.testNode(network, str)) { + NetworkConfiguration netconf = configs.get(network); + netconf.addNode(str); + nodesLv.getItems().add(str); + netconf.modified = true; + modified = true; + } else { + ErrorDialog.showError(this, "Invalid catapult server"); + } + } + } + } catch (Exception ex) { + ErrorDialog.showError(this, "Invalid catapult server"); + } + } + + @FXML + void removeNodeBtn(ActionEvent event) { + int idx = nodesLv.getSelectionModel().getSelectedIndex(); + if (idx != -1) { + nodesLv.getItems().remove(idx); + + NetworkConfiguration netconf = configs.get(network); + netconf.removeNode(idx); + netconf.modified = true; + modified = true; + } + } + + @FXML + void upNodeBtn(ActionEvent event) { + int idx = nodesLv.getSelectionModel().getSelectedIndex(); + if (idx > 0) { + String item1 = nodesLv.getSelectionModel().getSelectedItem(); + String item2 = nodesLv.getItems().get(idx - 1); + nodesLv.getItems().set(idx - 1, item1); + nodesLv.getItems().set(idx, item2); + nodesLv.getSelectionModel().select(idx - 1); + + NetworkConfiguration netconf = configs.get(network); + netconf.moveUpNode(idx); + netconf.modified = true; + modified = true; + } + } + + @FXML + void addIpfsBtn(ActionEvent event) { + try { + String str = ipfsField.getText().trim(); + if (!StringUtils.isEmpty(str)) { + if (!ipfsLv.getItems().contains(str)) { + NetworkConfiguration netconf = configs.get(network); + if (netconf.addIpfs(str) != null) { + ipfsLv.getItems().add(str); + netconf.modified = true; + modified = true; + } else { + ErrorDialog.showError(this, "Invalid ipfs server"); + } + } + } + } catch (Exception ex) { + ErrorDialog.showError(this, "Invalid ipfs server"); + } + } + + @FXML + void removeIpfsBtn(ActionEvent event) { + int idx = ipfsLv.getSelectionModel().getSelectedIndex(); + if (idx != -1) { + ipfsLv.getItems().remove(idx); + NetworkConfiguration netconf = configs.get(network); + netconf.removeIpfs(idx); + netconf.modified = true; + modified = true; + } + } + + @FXML + void upIpfsBtn(ActionEvent event) { + int idx = ipfsLv.getSelectionModel().getSelectedIndex(); + if (idx > 0) { + String item1 = ipfsLv.getSelectionModel().getSelectedItem(); + String item2 = ipfsLv.getItems().get(idx - 1); + ipfsLv.getItems().set(idx - 1, item1); + ipfsLv.getItems().set(idx, item2); + ipfsLv.getSelectionModel().select(idx - 1); + + NetworkConfiguration netconf = configs.get(network); + netconf.moveUpIpfs(idx); + netconf.modified = true; + modified = true; + } + } + + private void onSelectedNetwork(String newNetwork) { + try { + network = newNetwork; + NetworkConfiguration netconf = configs.get(newNetwork); + ObservableList nodeList = FXCollections.observableArrayList(); + nodeList.addAll(netconf.getNodes()); + nodesLv.setItems(nodeList); + ObservableList ipfsList = FXCollections.observableArrayList(); + ipfsList.addAll(netconf.getIpfses()); + ipfsLv.setItems(ipfsList); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/io/proximax/app/controller/ProxiDialog.java b/src/main/java/io/proximax/app/controller/ProxiDialog.java new file mode 100644 index 0000000..4735233 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/ProxiDialog.java @@ -0,0 +1,70 @@ +package io.proximax.app.controller; + +import io.proximax.app.utils.CONST; +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class ProxiDialog extends AbstractController { + + @FXML + private Label titleLbl; + @FXML + private Label msgLbl; + + private String title; + private int type; + private StringProperty msgProperty = new SimpleStringProperty(); + + public ProxiDialog(int type) { + super(true); + this.type = type; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + msgLbl.textProperty().bind(msgProperty); + } + + public void setTitle(String title) { + this.title = title; + } + + public void setContentText(String msg) { + msgProperty.set(msg); + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.PROXIDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.PROXIDLG_FXML; + } + + public static void showError(AbstractController parent, String msg) { + try { + ProxiDialog dlg = new ProxiDialog(0); + dlg.setContentText(msg); + dlg.setParent(parent); + dlg.openWindow(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/io/proximax/app/controller/RecoveryDialog.java b/src/main/java/io/proximax/app/controller/RecoveryDialog.java new file mode 100644 index 0000000..840783f --- /dev/null +++ b/src/main/java/io/proximax/app/controller/RecoveryDialog.java @@ -0,0 +1,177 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.recovery.AccountInfo; +import io.proximax.app.recovery.ProxiLicenseHttp; +import io.proximax.app.utils.CONST; +import java.net.URL; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class RecoveryDialog extends AbstractController { + + @FXML + JFXComboBox question1Cbx; + @FXML + JFXComboBox question2Cbx; + @FXML + JFXComboBox question3Cbx; + @FXML + JFXTextField emailField; + @FXML + JFXTextField answer1Field; + @FXML + JFXTextField answer2Field; + @FXML + JFXTextField answer3Field; + @FXML + private Label errorLbl; + @FXML + private JFXButton saveBtn; + + private ProxiLicenseHttp proxiHttp = null; + + public RecoveryDialog() { + super(true); + } + + private AccountInfo accountInfo = null; + + @Override + public void initialize(URL location, ResourceBundle resources) { + saveBtn.setText("RECOVERY"); + question1Cbx.setItems(FXCollections.observableArrayList()); + question1Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + question2Cbx.setItems(FXCollections.observableArrayList()); + question2Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + question3Cbx.setItems(FXCollections.observableArrayList()); + question3Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + + try { + proxiHttp = new ProxiLicenseHttp("http://dev-proxi-service.proximax.io:8080/LicenseServer"); + } catch (Exception ex) { + proxiHttp = null; + } + emailField.focusedProperty().addListener((obs, oldVal, newVal) -> { + if (!newVal && proxiHttp != null) { + try { + String email = emailField.getText(); + if (validEmail(email)) { + errorLbl.setText(""); + accountInfo = proxiHttp.getAccountInfo(email).toFuture().get(); + } else { + errorLbl.setText("Error: invalid email"); + emailField.requestFocus(); + } + } catch (Exception ex) { + //ex.printStackTrace(); + } + } + }); + } + + @FXML + protected void saveBtn(ActionEvent event) { + if (!validEmail(emailField.getText())) { + errorLbl.setText("Error: invalid email"); + emailField.requestFocus(); + return; + } + if (question1Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 1 is required"); + question1Cbx.requestFocus(); + return; + } + if (question2Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 2 is required"); + question2Cbx.requestFocus(); + return; + } + if (question3Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 3 is required"); + question3Cbx.requestFocus(); + return; + } + if (question1Cbx.getSelectionModel().getSelectedIndex() == question2Cbx.getSelectionModel().getSelectedIndex() + || question1Cbx.getSelectionModel().getSelectedIndex() == question3Cbx.getSelectionModel().getSelectedIndex() + || question2Cbx.getSelectionModel().getSelectedIndex() == question3Cbx.getSelectionModel().getSelectedIndex()) { + errorLbl.setText("Error: questions are the same"); + return; + } + if (answer1Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer1Field.requestFocus(); + return; + } + if (answer2Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer2Field.requestFocus(); + return; + } + if (answer3Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer3Field.requestFocus(); + return; + } + if (answer1Field.getText().equals(answer2Field.getText()) + || answer1Field.getText().equals(answer3Field.getText()) + || answer3Field.getText().equals(answer2Field.getText())) { + errorLbl.setText("Error: answers are the same"); + return; + } + if (accountInfo == null) { + errorLbl.setText("Error: cannot find your key in ProximaX Central Data"); + return; + } + if (accountInfo.getQuestion1().equals(question1Cbx.getSelectionModel().getSelectedItem()) + && accountInfo.getQuestion2().equals(question2Cbx.getSelectionModel().getSelectedItem()) + && accountInfo.getQuestion3().equals(question3Cbx.getSelectionModel().getSelectedItem()) + && accountInfo.getAnswer1().equals(answer1Field.getText()) + && accountInfo.getAnswer2().equals(answer2Field.getText()) + && accountInfo.getAnswer3().equals(answer3Field.getText())) { + setButtonType(ButtonType.OK); + close(); + } else { + errorLbl.setText("Error: your questions and answers not match"); + } + + } + + private boolean validEmail(String email) { + String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(email); + return matcher.matches(); + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.SIGNUP2_TITLE; + } + + @Override + public String getFXML() { + return CONST.SIGNUP2_FXML; + } + + public AccountInfo getAccountInfo() { + return accountInfo; + } + +} diff --git a/src/main/java/io/proximax/app/controller/RegistrationDialog.java b/src/main/java/io/proximax/app/controller/RegistrationDialog.java new file mode 100644 index 0000000..0ca0968 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/RegistrationDialog.java @@ -0,0 +1,208 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXPasswordField; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.recovery.AccountInfo; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.NetworkUtils; +import io.proximax.app.utils.StringUtils; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Control; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; + +/** + * FXML Controller class + * + * @author thcao + */ +public class RegistrationDialog extends AbstractController { + + @FXML + private JFXTextField usernameField; + + @FXML + private JFXPasswordField pwordField; + + @FXML + private JFXPasswordField confirmPwordField; + + @FXML + private JFXComboBox networkCbx; + + @FXML + private JFXTextField privateField; + + @FXML + private CheckBox termCheckBx; + + @FXML + private Label errorLbl; + + @FXML + private Button btnRegister; + + @FXML + private Button btnRecovery; + + private AccountInfo accountInfo = null; + + public RegistrationDialog() { + super(false); + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + ObservableList obList = FXCollections.observableArrayList(); + obList.addAll(NetworkUtils.NETWORK_SUPPORT); + networkCbx.setItems(obList); + networkCbx.setValue(NetworkUtils.NETWORK_DEFAULT); //by default privatetest + if (NetworkUtils.NETWORKS.size() <= 1) { + networkCbx.setDisable(true); + } + btnRegister.disableProperty().bind( + pwordField.textProperty().isEqualTo(confirmPwordField.textProperty()).not() + .or( + usernameField.textProperty().length().lessThan(5) + ) + .or( + pwordField.textProperty().length().lessThan(8) + ) + .or( + termCheckBx.selectedProperty().not() + ) + ); + usernameField.setTooltip(new Tooltip("Username")); + usernameField.focusedProperty().addListener((obs, oldVal, newVal) -> { + //System.out.println(newVal ? "Focused" : "Unfocused"); + if (!newVal) { + if (usernameField.textProperty().length().lessThan(5).get()) { + if (!btnRecovery.isFocused()) { + showError(usernameField, "username at least 5 characters"); + } + } else if (AccountHelpers.isExistAccount(usernameField.getText(), networkCbx.getValue())) { + showError(usernameField, "Account existed"); + } else { + errorLbl.setText(""); + } + } + }); + pwordField.setTooltip(new Tooltip("Password")); + pwordField.focusedProperty().addListener((obs, oldVal, newVal) -> { + if (!newVal) { + if (pwordField.textProperty().length().lessThan(8).get()) { + showError(pwordField, "password at least 8 characters"); + } else { + errorLbl.setText(""); + } + } + }); + confirmPwordField.setTooltip(new Tooltip("Confirm Password")); + confirmPwordField.focusedProperty().addListener((obs, oldVal, newVal) -> { + if (!newVal) { + if (pwordField.textProperty().isEqualTo(confirmPwordField.textProperty()).not().get()) { + showError(confirmPwordField, "password and confirm password not match"); + } else { + errorLbl.setText(""); + } + } + }); + } + + private void showError(Control control, String errMsg) { + if (StringUtils.isEmpty(errMsg)) { + errorLbl.setText(""); + } else { + errorLbl.setText("Error: " + errMsg); + if (control != null) { + control.requestFocus(); + } + } + } + + @FXML + void submitBtn(ActionEvent event) { + // validate + try { + String fullName = usernameField.getText(); + String network = networkCbx.getValue(); + String password = pwordField.getText(); + if (AccountHelpers.isExistAccount(fullName, network)) { + showError(usernameField, "Account existed"); + return; + } + String privateKey = privateField.getText().trim(); + if (!StringUtils.isEmpty(privateKey)) { //create new account + if (!AccountHelpers.isPrivateKeyValid(privateKey)) { + showError(privateField, "Invalid private key"); + return; + } + } + LocalAccount localAccount = AccountHelpers.createAccount(fullName, network, password, privateKey); + if (accountInfo != null) { + AccountHelpers.updateAccountInfo(localAccount, accountInfo); + } + UserProfileDialog dlg = new UserProfileDialog(localAccount); + dlg.openWindow(this); + close(); // hide + showParent(); + } catch (Exception e) { + ErrorDialog.showError(this, e.getMessage()); + } + + } + + @FXML + void recoveryBtn(ActionEvent event) { + try { + errorLbl.setText(""); + RecoveryDialog dlg = new RecoveryDialog(); + dlg.setParent(this); + dlg.openWindow(); + accountInfo = null; + if (dlg.getResultType() == ButtonType.OK) { + accountInfo = dlg.getAccountInfo(); + privateField.setText(dlg.getAccountInfo().getPrivateKey()); + if (StringUtils.isEmpty(usernameField.getText())) { + usernameField.setText(dlg.getAccountInfo().getUserName()); + } + privateField.requestFocus(); + } + } catch (Exception ex) { + + } + + } + + @Override + protected void dispose() { + showParent(); + } + + @Override + public String getTitle() { + return CONST.SIGNUP_TITLE; + } + + @Override + public String getFXML() { + return CONST.SIGNUP_FXML; + } + + @Override + protected void closeBtn(ActionEvent event) { + close(); + showParent(); + } +} diff --git a/src/main/java/io/proximax/app/controller/SharingDialog.java b/src/main/java/io/proximax/app/controller/SharingDialog.java new file mode 100644 index 0000000..3c9c322 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/SharingDialog.java @@ -0,0 +1,345 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXProgressBar; +import com.jfoenix.controls.JFXTextField; +import io.proximax.async.AsyncCallbacks; +import io.proximax.async.AsyncTask; +import io.proximax.download.DownloadParameter; +import io.proximax.download.DownloadResult; +import io.proximax.download.Downloader; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.db.ShareFile; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.app.utils.StringUtils; +import io.proximax.upload.UploadParameter; +import io.proximax.upload.UploadResult; +import io.proximax.upload.Uploader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +/** + * FXML Controller class + * + * @author thcao + */ +public class SharingDialog extends AbstractController { + + @FXML + private Label passwdLbl; + @FXML + private JFXTextField fileField; + @FXML + private JFXProgressBar progressBar; + @FXML + private JFXComboBox uptypeCbx; + @FXML + private JFXTextField passwdField; + @FXML + private JFXTextField nameField; + @FXML + private JFXTextField addressField; + @FXML + private JFXButton shareBtn; + @FXML + private JFXButton cancelBtn; + + private LocalAccount localAccount; + + private LocalFile localFile = null; + Task uploadTask = null; + AsyncTask uploadAsync = null; + private boolean bUploading = false; + + public SharingDialog(LocalAccount account, LocalFile localFile) { + super(true); + this.localAccount = account; + this.localFile = localFile; + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + List list = Arrays.asList(CONST.UPLOAD_TYPES); + ObservableList obList = FXCollections.observableList(list); + uptypeCbx.setItems(obList); + uptypeCbx.setValue(obList.get(localFile.uType)); + setPasswordVisible(localFile.uType == CONST.UTYPE_SECURE_PASSWORD); + uptypeCbx.valueProperty().addListener((ObservableValue observable, String oldValue, String newValue) -> { + if (newValue.equals(CONST.UPLOAD_TYPES[CONST.UTYPE_SECURE_PASSWORD])) { + setPasswordVisible(true); + } else { + setPasswordVisible(false); + } + }); + nameField.focusedProperty().addListener((obs, oldVal, newVal) -> { + //System.out.println(newVal ? "Focused" : "Unfocused"); + if (!newVal) { + String address = AccountHelpers.findAddressByName(localAccount, nameField.getText()); + if (AccountHelpers.isAddressValid(address)) { + addressField.setText(AccountHelpers.formatAddressPretty(address)); + } + } + }); + fileField.setText(localFile.fileName); + passwdField.setText(localFile.password); + progressBar.setProgress(0); + } + + @FXML + private void cancelBtn(ActionEvent event) { + hide(); + } + + @FXML + private void shareFile(ActionEvent event) { + try { + if (shareBtn.getText().equals("CANCEL")) { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, + "Do you want to cancel upload ?", + ButtonType.YES, + ButtonType.NO); + ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().add(getMainApp().getIcon()); + alert.setTitle(CONST.APP_NAME); + Optional result = alert.showAndWait(); + if (result.get() == ButtonType.YES) { + cancelJob(); + bUploading = false; + shareBtn.setText("SHARE"); + } + return; + } + String sPublicKey = ""; + final String sAddress = addressField.getText().trim().replace("-", ""); + if (AccountHelpers.isAddressValid(sAddress)) { + try { + sPublicKey = AccountHelpers.getPublicKeyFromAddress(localAccount.connectionConfig, sAddress); + } catch (MalformedURLException ex) { + } + } + if (StringUtils.isEmpty(sPublicKey)) { + ErrorDialog.showError(this, "Invalid address: " + sAddress); + addressField.requestFocus(); + return; + } + if (LocalFileHelpers.isShared(localAccount.fullName, localAccount.network, localFile.id, sAddress)) { + ErrorDialog.showError(this, "This file is already shared with your friend."); + addressField.requestFocus(); + return; + } + int upType = uptypeCbx.getSelectionModel().getSelectedIndex(); + if (upType == CONST.UTYPE_SECURE_PASSWORD) { + if (passwdField.getText().trim().length() < 10) { + ErrorDialog.showError(this, "Minimum length for password is 10"); + passwdField.requestFocus(); + return; + } + } + final String friendName = nameField.getText(); + if (friendName.isEmpty()) { + ErrorDialog.showError(this, "Please input recipient's name field"); + nameField.requestFocus(); + return; + } + + if (!AccountHelpers.isFriendExisted(localAccount, friendName, sAddress)) { + AccountHelpers.addFriend(localAccount, friendName, sAddress, sPublicKey); + } + String password = passwdField.getText(); + shareBtn.setText("CANCEL"); + String fPublicKey = sPublicKey; + LocalFile sharedFile = new LocalFile(); + sharedFile.publicKey = fPublicKey; + sharedFile.uType = upType; + sharedFile.password = password; + sharedFile.address = sAddress; + sharedFile.shared = friendName; + uploadTask = createUploadTask(sharedFile); + progressBar.progressProperty().bind(uploadTask.progressProperty()); + uploadTask.setOnSucceeded(taskEvent -> { + progressBar.progressProperty().unbind(); + }); + // run task in single-thread executor (will queue if another task is running): + execJob.submit(uploadTask); + } catch (Exception ex) { + ex.printStackTrace(); + ErrorDialog.showError(this, ex.getMessage()); + } + } + + private void setPasswordVisible(boolean bVisible) { + passwdLbl.setVisible(bVisible); + passwdField.setVisible(bVisible); + } + + @Override + public String getTitle() { + return CONST.SHARING_TITLE; + } + + @Override + public String getFXML() { + return CONST.SHARING_FXML; + } + + /** + * Create download task when user click download + * + * @param localFile + * @return + */ + private Task createUploadTask(LocalFile sharedFile) { + final SharingDialog dlg = this; + final int taskNumber = taskCount.incrementAndGet(); + Task task = new Task() { + @Override + protected Void call() throws Exception { + if (localAccount.testNode()) { + int progress = 100; + bUploading = true; + updateMessage("Status: Sharing " + localFile.fileName + " to " + sharedFile.address); + updateProgress(progress, 999); + File fileCache = LocalFileHelpers.getSourceFile(localAccount, localFile); + if (fileCache == null) { + fileCache = LocalFileHelpers.createFileCache(localAccount, localFile); + //need download + DownloadParameter parameter = LocalFileHelpers.createDownloadParameter(localFile); + Downloader download = new Downloader(localAccount.connectionConfig); + DownloadResult downloadResult = download.download(parameter); + InputStream byteStream = downloadResult.getData().getByteStream(); + FileOutputStream fouts = new FileOutputStream(fileCache); + byte[] buffer = new byte[1024]; + int read = 0; + long sum = 0; + while ((read = byteStream.read(buffer)) >= 0) { + fouts.write(buffer, 0, read); + sum += read; + updateProgress(progress + 400 * sum / localFile.fileSize, 999); + } + progress = 500; + } else { + progress = 200; + } + try { + updateProgress(progress, 999); + LocalFile uploadFile = new LocalFile(localFile); + uploadFile.uType = sharedFile.uType; + uploadFile.password = sharedFile.password; + uploadFile.publicKey = sharedFile.publicKey; + uploadFile.address = sharedFile.address; + final String friendName = sharedFile.shared; + final String sAddress = sharedFile.address; + final int upType = sharedFile.uType; + final String password = sharedFile.password; + UploadParameter uploadParameter = LocalFileHelpers.createUploadFileParameter(localAccount, uploadFile, fileCache); + Uploader upload = new Uploader(localAccount.connectionConfig); + uploadAsync = upload.uploadAsync(uploadParameter, + AsyncCallbacks.create( + (UploadResult uploadResult) -> { + bUploading = false; + ShareFile sharedFile = new ShareFile(uploadFile.id, friendName, sAddress, System.currentTimeMillis(), uploadResult.getData().getDataHash(), uploadResult.getTransactionHash(), upType, password, CONST.FILE_STATUS_NOR); + LocalFileHelpers.shareLocalFile(localAccount, sharedFile); + IApp.runSafe(() -> { + updateProgress(999, 999); + close(); + showParent(); + }); + }, + (Throwable ex) -> { + bUploading = false; + ex.printStackTrace(); + updateProgress(0, 999); + if (isCancelled()) { + return; + } + ErrorDialog.showErrorSafe(dlg, ex.getMessage()); + })); + while (!uploadAsync.isDone()) { + if (progress < 900) { + progress += 10; + updateProgress(progress, 999); + } + if (uploadAsync.isCancelled()) { + break; + } + Thread.sleep(100); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return null; + } + }; + return task; + } + + private AtomicInteger taskCount = new AtomicInteger(0); + private ExecutorService execJob = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); // allows app to exit if tasks are running + return t; + }); + + private void cancelJob() { + if (uploadAsync != null) { + if (!uploadAsync.isDone()) { + uploadAsync.cancel(); + } + uploadAsync = null; + } + if (uploadTask != null) { + uploadTask.cancel(true); + uploadTask = null; + } + } + + @Override + protected void dispose() { + cancelJob(); + execJob.shutdownNow(); + try { + if (!execJob.awaitTermination(800, TimeUnit.MILLISECONDS)) { + execJob.shutdownNow(); + } + } catch (InterruptedException e) { + execJob.shutdownNow(); + } + } + + @Override + protected boolean canExit() { + if (bUploading || (uploadAsync != null && !uploadAsync.isDone())) { + return false; + } + return true; + } +} diff --git a/src/main/java/io/proximax/app/controller/Signup2Dialog.java b/src/main/java/io/proximax/app/controller/Signup2Dialog.java new file mode 100644 index 0000000..6177270 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/Signup2Dialog.java @@ -0,0 +1,193 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.recovery.AccountInfo; +import io.proximax.app.recovery.AccountStatus; +import io.proximax.app.recovery.ProxiLicenseHttp; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import java.net.URL; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; + +/** + * + * @author thcao + */ +public class Signup2Dialog extends AbstractController { + + private LocalAccount localAccount = null; + + @FXML + JFXComboBox question1Cbx; + @FXML + JFXComboBox question2Cbx; + @FXML + JFXComboBox question3Cbx; + @FXML + JFXTextField emailField; + @FXML + JFXTextField answer1Field; + @FXML + JFXTextField answer2Field; + @FXML + JFXTextField answer3Field; + @FXML + private Label errorLbl; + + private ProxiLicenseHttp proxiHttp = null; + + public Signup2Dialog(LocalAccount localAccount) { + super(true); + this.localAccount = localAccount; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + question1Cbx.setItems(FXCollections.observableArrayList()); + question1Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + question2Cbx.setItems(FXCollections.observableArrayList()); + question2Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + question3Cbx.setItems(FXCollections.observableArrayList()); + question3Cbx.getItems().addAll(CONST.SECURE_QUESTIONS); + + AccountInfo accountInfo = AccountHelpers.getAccountInfo(localAccount); + if (accountInfo != null) { + question1Cbx.setValue(accountInfo.getQuestion1()); + question2Cbx.setValue(accountInfo.getQuestion2()); + question3Cbx.setValue(accountInfo.getQuestion3()); + answer1Field.setText(accountInfo.getAnswer1()); + answer2Field.setText(accountInfo.getAnswer2()); + answer3Field.setText(accountInfo.getAnswer3()); + emailField.setText(accountInfo.getEmail()); + } + try { + proxiHttp = new ProxiLicenseHttp("http://dev-proxi-service.proximax.io:8080/LicenseServer"); + } catch (Exception ex) { + proxiHttp = null; + } + emailField.focusedProperty().addListener((obs, oldVal, newVal) -> { + if (!newVal && proxiHttp != null) { + try { + String email = emailField.getText(); + if (validEmail(email)) { + errorLbl.setText(""); + AccountInfo accountInfo1 = proxiHttp.getAccountInfo(email).toFuture().get(); + if (accountInfo1 != null) { + if (!accountInfo1.compare(email, localAccount.privateKey, localAccount.publicKey, localAccount.address)) { + errorLbl.setText("Error: email existed"); + emailField.requestFocus(); + } + } + } else { + errorLbl.setText("Error: invalid email"); + emailField.requestFocus(); + } + } catch (Exception ex) { + //ex.printStackTrace(); + } + + } + } + ); + } + + @FXML + protected void saveBtn(ActionEvent event) { + if (!validEmail(emailField.getText())) { + errorLbl.setText("Error: invalid email"); + emailField.requestFocus(); + return; + } + if (question1Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 1 is required"); + question1Cbx.requestFocus(); + return; + } + if (question2Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 2 is required"); + question2Cbx.requestFocus(); + return; + } + if (question3Cbx.getSelectionModel().getSelectedIndex() == -1) { + errorLbl.setText("Error: question 3 is required"); + question3Cbx.requestFocus(); + return; + } + if (question1Cbx.getSelectionModel().getSelectedIndex() == question2Cbx.getSelectionModel().getSelectedIndex() + || question1Cbx.getSelectionModel().getSelectedIndex() == question3Cbx.getSelectionModel().getSelectedIndex() + || question2Cbx.getSelectionModel().getSelectedIndex() == question3Cbx.getSelectionModel().getSelectedIndex()) { + errorLbl.setText("Error: questions are the same"); + return; + } + if (answer1Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer1Field.requestFocus(); + return; + } + if (answer2Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer2Field.requestFocus(); + return; + } + if (answer3Field.getText().isEmpty()) { + errorLbl.setText("Error: answer cannot empty"); + answer3Field.requestFocus(); + return; + } + if (answer1Field.getText().equals(answer2Field.getText()) + || answer1Field.getText().equals(answer3Field.getText()) + || answer3Field.getText().equals(answer2Field.getText())) { + errorLbl.setText("Error: answers are the same"); + return; + } + + AccountInfo accountInfo = new AccountInfo(localAccount.fullName, emailField.getText(), + question1Cbx.getSelectionModel().getSelectedItem(), answer1Field.getText(), + question2Cbx.getSelectionModel().getSelectedItem(), answer2Field.getText(), + question3Cbx.getSelectionModel().getSelectedItem(), answer3Field.getText(), + localAccount.privateKey, localAccount.publicKey, localAccount.address); + AccountHelpers.updateAccountInfo(localAccount, accountInfo); + //update to server + try { + AccountStatus status = proxiHttp.saveAccountInfo(accountInfo).toFuture().get(); + if ("Success".equals(status.getStatus())) { + close(); + } else { + errorLbl.setText("Error: " + status.getMessage()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + } + + private boolean validEmail(String email) { + String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(email); + return matcher.matches(); + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.SIGNUP2_TITLE; + } + + @Override + public String getFXML() { + return CONST.SIGNUP2_FXML; + } + +} diff --git a/src/main/java/io/proximax/app/controller/UploaderDialog.java b/src/main/java/io/proximax/app/controller/UploaderDialog.java new file mode 100644 index 0000000..d486d02 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/UploaderDialog.java @@ -0,0 +1,333 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXProgressBar; +import com.jfoenix.controls.JFXTextField; +import io.proximax.async.AsyncCallbacks; +import io.proximax.async.AsyncTask; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.upload.UploadParameter; +import io.proximax.upload.UploadResult; +import io.proximax.upload.Uploader; +import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Label; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +/** + * FXML Controller class + * + * @author thcao + */ +public class UploaderDialog extends AbstractController { + + @FXML + private Label fileLbl; + @FXML + private JFXTextField fileField; + @FXML + private Label titleLbl; + @FXML + private Label passwdLbl; + @FXML + private Label addressLbl; + @FXML + private JFXProgressBar progressBar; + @FXML + private JFXComboBox uptypeCbx; + @FXML + private JFXTextField passwdField; + @FXML + private JFXTextField addressField; + @FXML + private JFXButton uploadBtn; + @FXML + private JFXButton cancelBtn; + @FXML + private JFXButton browserBtn; + + private LocalAccount localAccount; + + private File file = null; + + private boolean bUploading = false; + + private String curFolder = CONST.HOME; + + public UploaderDialog(LocalAccount account, String curFolder) { + super(true); + this.localAccount = account; + this.curFolder = curFolder; + this.file = null; + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + List list = Arrays.asList(CONST.UPLOAD_TYPES); + ObservableList obList = FXCollections.observableList(list); + uptypeCbx.setItems(obList); + uptypeCbx.setValue(obList.get(0)); + setPasswordVisible(false); + uptypeCbx.valueProperty().addListener((ObservableValue observable, String oldValue, String newValue) -> { + if (newValue.equals(CONST.UPLOAD_TYPES[CONST.UTYPE_SECURE_PASSWORD])) { + setPasswordVisible(true); + } else { + setPasswordVisible(false); + } + }); + addressField.setPromptText(localAccount.address); + progressBar.setProgress(0); + } + + private void setPasswordVisible(boolean bVisible) { + passwdLbl.setVisible(bVisible); + passwdField.setVisible(bVisible); + } + + public boolean isSelectFile() { + return (this.file != null); + } + + @FXML + private void browseBtn(ActionEvent event) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select File"); + fileChooser.setInitialDirectory(mainApp.getCurrentFolder()); + this.file = fileChooser.showOpenDialog(primaryStage); + if (this.file != null) { + mainApp.saveCurrentDir(file.getAbsoluteFile().getParent()); + fileField.setText(file.getName()); + } + } + + @FXML + private void uploadFile(ActionEvent event) { + try { + if (uploadBtn.getText().equalsIgnoreCase("cancel")) { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, + "Do you want to cancel upload ?", + ButtonType.YES, + ButtonType.NO); + ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().add(getMainApp().getIcon()); + alert.setTitle(CONST.APP_NAME); + Optional result = alert.showAndWait(); + if (result.get() == ButtonType.YES) { + cancelJob(); + bUploading = false; + uploadBtn.setText("UPLOAD"); + } + return; + } + if (this.file == null) { + ErrorDialog.showError(this, "Please choose file to upload"); + return; + } + int upType = uptypeCbx.getSelectionModel().getSelectedIndex(); + if (upType == CONST.UTYPE_PUBLIC || upType == CONST.UTYPE_SECURE_NEMKEYS) { //public key + if (LocalFileHelpers.isExisted(localAccount, file, upType)) { + ErrorDialog.showError(this, "File " + file.getName() + " already existed"); + return; + } + } + if (upType == CONST.UTYPE_SECURE_PASSWORD) { + if (passwdField.getText().trim().length() < 10) { + ErrorDialog.showError(this, "Minimum length for password is 10"); + passwdField.requestFocus(); + return; + } + } + LocalFile localFile = new LocalFile(); + final String sAddress = addressField.getText().trim().replace("-", ""); + if (sAddress.isEmpty()) { + localFile.address = localAccount.address; + localFile.publicKey = localAccount.publicKey; + } else { + if (!AccountHelpers.isAddressValid(sAddress)) { + ErrorDialog.showError(this, "Invalid address: " + sAddress); + addressField.requestFocus(); + return; + } + localFile.address = sAddress; + localFile.publicKey = AccountHelpers.getPublicKeyFromAddress(localAccount.getApiHost(), localAccount.getApiPort(), sAddress); + } + localFile.password = passwdField.getText(); + if (localFile.password.trim().isEmpty()) { + localFile.password = ""; + } + uploadBtn.setText("CANCEL"); + localFile.privateKey = localAccount.privateKey; + localFile.uType = upType; + localFile.fileName = file.getName(); + localFile.filePath = file.getAbsolutePath(); + localFile.modified = file.lastModified(); + localFile.fileSize = file.length(); + localFile.category = curFolder; + if (localAccount.isConnected()) { + uploadTask = createUploadTask(localFile); + progressBar.progressProperty().bind(uploadTask.progressProperty()); + uploadTask.setOnSucceeded(taskEvent -> { + progressBar.progressProperty().unbind(); + }); + execJob.submit(uploadTask); + } else { + try { + localFile.status = CONST.FILE_STATUS_FAILED; + LocalFileHelpers.addFile(localAccount, localFile); + IApp.runSafe(() -> { + close(); + showParent(); + }); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + } catch (Exception ex) { + ex.printStackTrace(); + ErrorDialog.showError(this, ex.getMessage()); + } + } + Task uploadTask = null; + AsyncTask asyncTask = null; + + /** + * Create download task when user click download + * + * @param localFile + * @return + */ + private Task createUploadTask(LocalFile localFile) { + final UploaderDialog dlg = this; + final int taskNumber = taskCount.incrementAndGet(); + Task task = new Task() { + @Override + protected Void call() throws Exception { + if (localAccount.testNode()) { + bUploading = true; + updateMessage("Status: uploading ..."); + updateProgress(100, 999); + UploadParameter uploadParameter = LocalFileHelpers.createUploadFileParameter(localAccount, localFile, file); + Uploader upload = new Uploader(localAccount.connectionConfig); + asyncTask = upload.uploadAsync(uploadParameter, + AsyncCallbacks.create( + (UploadResult uploadResult) -> { + bUploading = false; + localFile.uploadDate = System.currentTimeMillis(); + localFile.hash = uploadResult.getData().getDataHash(); + localFile.nemHash = uploadResult.getTransactionHash(); + localFile.status = CONST.FILE_STATUS_TXN; + LocalFileHelpers.addFile(localAccount, localFile); + file = null; + IApp.runSafe(() -> { + updateProgress(999, 999); + close(); + showParent(); + }); + + }, + (Throwable ex) -> { + ex.printStackTrace(); + bUploading = false; + IApp.runSafe(() -> { + updateProgress(0, 999); + ErrorDialog.showError(dlg, ex.getMessage()); + }); + if (isCancelled()) { + return; + } + })); + int progress = 100; + while (!asyncTask.isDone()) { + if (progress < 900) { + progress += 10; + updateProgress(progress, 999); + } + if (asyncTask.isCancelled()) { + break; + } + Thread.sleep(100); + } + } + return null; + } + }; + return task; + } + + private AtomicInteger taskCount = new AtomicInteger(0); + private ExecutorService execJob = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); // allows app to exit if tasks are running + return t; + }); + + private void cancelJob() { + if (asyncTask != null) { + if (!asyncTask.isDone()) { + asyncTask.cancel(); + } + asyncTask = null; + } + if (uploadTask != null) { + uploadTask.cancel(true); + uploadTask = null; + } + } + + @Override + protected void dispose() { + cancelJob(); + execJob.shutdownNow(); + try { + if (!execJob.awaitTermination(800, TimeUnit.MILLISECONDS)) { + execJob.shutdownNow(); + } + } catch (InterruptedException e) { + execJob.shutdownNow(); + } + } + + @Override + protected boolean canExit() { + if (bUploading || (asyncTask != null && !asyncTask.isDone())) { + return false; + } + return true; + } + + @Override + public String getTitle() { + return CONST.UPLOAD_TITLE; + } + + @Override + public String getFXML() { + return CONST.UPLOAD_FXML; + } +} diff --git a/src/main/java/io/proximax/app/controller/UserProfileDialog.java b/src/main/java/io/proximax/app/controller/UserProfileDialog.java new file mode 100644 index 0000000..ee698c4 --- /dev/null +++ b/src/main/java/io/proximax/app/controller/UserProfileDialog.java @@ -0,0 +1,133 @@ +package io.proximax.app.controller; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXTextField; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.NetworkUtils; +import io.proximax.app.utils.StringUtils; +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.stage.Stage; + +/** + * + * @author thcao + */ +public class UserProfileDialog extends AbstractController { + + private LocalAccount localAccount = null; + + @FXML + JFXTextField nameField; + + @FXML + JFXTextField addressField; + + @FXML + JFXTextField publicField; + + @FXML + JFXTextField privateField; + + @FXML + JFXTextField peerField; + + @FXML + JFXTextField storageField; + + @FXML + JFXTextField xpxField; + + @FXML + JFXButton xpxBtn; + + public UserProfileDialog(LocalAccount localAccount) { + super(true); + this.localAccount = localAccount; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + nameField.setText(localAccount.fullName); + addressField.setText(localAccount.getAddressPretty()); + publicField.setText(localAccount.publicKey); + privateField.setText(localAccount.privateKey); + String peerId = localAccount.getPeerId(); + String quest = "Account doesn't have any balance, do you want to get xpx?"; + xpxBtn.setText("GET XPX"); + if ("MAIN_NET".equals(localAccount.getNetwork())) { + quest = "Account doesn't have any balance, do you want to buy xpx?"; + xpxBtn.setText("BUY XPX"); + } + if (!StringUtils.isEmpty(peerId)) { + int idx = peerId.indexOf("{PeerID="); + if (idx != -1 && peerId.length() > 8) { + peerField.setText(peerId.substring(idx + 8, peerId.lastIndexOf("}"))); + } else { + peerField.setText(peerId); + } + } + double GB = 1024 * 1024 * 1024; + storageField.setText(String.format("%.2f GB of %.2f GB used", localAccount.used / GB, localAccount.capacity / GB)); + long xpx = NetworkUtils.getXPX(localAccount); + xpxField.setText(String.format("%d xpx", xpx)); + if (xpx == 0) { + Alert alert = new Alert(Alert.AlertType.WARNING, quest, ButtonType.YES, ButtonType.NO); + ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().add(getMainApp().getIcon()); + alert.showAndWait(); + if (alert.getResult() == ButtonType.YES) { + xpxBtn(null); + } + } + } + + @FXML + protected void saveBtn(ActionEvent event) { + try { + Signup2Dialog dlg = new Signup2Dialog(localAccount); + dlg.openWindow(this); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @FXML + protected void xpxBtn(ActionEvent event) { + String url = "http://bctestnetfaucet.xpxsirius.io/#/"; + if ("MAIN_NET".equals(localAccount.getNetwork())) { + url = "https://www.proximax.io/xpx"; + } + final String xpxUrl = url; + if (Desktop.isDesktopSupported()) { + new Thread(() -> { + try { + Desktop.getDesktop().browse(new URI(xpxUrl)); + } catch (Exception ex) { + } + }).start(); + } + } + + @Override + protected void dispose() { + } + + @Override + public String getTitle() { + return CONST.USERDLG_TITLE; + } + + @Override + public String getFXML() { + return CONST.USERDLG_FXML; + } + +} diff --git a/src/main/java/io/proximax/app/core/cipher/BinaryPBKDF2CipherEncryption.java b/src/main/java/io/proximax/app/core/cipher/BinaryPBKDF2CipherEncryption.java new file mode 100644 index 0000000..e34b87d --- /dev/null +++ b/src/main/java/io/proximax/app/core/cipher/BinaryPBKDF2CipherEncryption.java @@ -0,0 +1,229 @@ +package io.proximax.app.core.cipher; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Base64; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * The Class BinaryPBKDF2Cipher. + */ +public class BinaryPBKDF2CipherEncryption extends SymmetricKeyEncryption { + + /** The Constant CONST_ALGO_PBKDF2. */ + private static final String CONST_ALGO_PBKDF2 = "PBKDF2WithHmacSHA256"; + + /** The Constant SALT. */ + private static final byte[] SALT = { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, + (byte) 0xE3, (byte) 0x03 }; + + /** The Constant FIXED_NONCE. */ + private static final byte[] FIXED_NONCE = { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, + (byte) 0x35, (byte) 0xE3, (byte) 0x03 }; + + + /** + * Encrypt. + * + * @param binary the binary + * @param password the password + * @return the byte[] + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + */ + public byte[] encrypt(byte[] binary, char[] password) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + // ENCRYPTION + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.ENCRYPT_MODE, key, spec); + + byte[] byteCipher = cipher.doFinal(binary); + return byteCipher; + } + + /** + * Encrypt to base 64 string. + * + * @param binary the binary + * @param password the password + * @return the string + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + */ + public String encryptToBase64String(byte[] binary, char[] password) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + // ENCRYPTION + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.ENCRYPT_MODE, key, spec); + + byte[] byteCipher = cipher.doFinal(binary); + String cipherText = new String(Base64.getEncoder().encode(byteCipher)); + return cipherText; + } + + /** + * Decrypt. + * + * @param encodedCipherText the encoded cipher text + * @param password the password + * @return the byte[] + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public byte[] decrypt(String encodedCipherText, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + byte[] decryptedCipher = cipher.doFinal(Base64.getDecoder().decode(encodedCipherText)); + return decryptedCipher; + } + + /** + * Decrypt to base 64 string. + * + * @param encodedCipherText the encoded cipher text + * @param password the password + * @return the string + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public String decryptToBase64String(String encodedCipherText, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + byte[] decryptedCipher = cipher.doFinal(Base64.getDecoder().decode(encodedCipherText)); + String decryptedCipherText = new String(decryptedCipher); + return decryptedCipherText; + } + + /** + * Decrypt. + * + * @param binary the binary + * @param password the password + * @return the byte[] + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public byte[] decrypt(byte[] binary, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + byte[] decryptedCipher = cipher.doFinal(binary); + return decryptedCipher; + } + + /** + * Decrypt to base 64 string. + * + * @param binary the binary + * @param password the password + * @return the string + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public String decryptToBase64String(byte[] binary, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + + // DERIVE key (from password and salt) + SecretKeyFactory factory = SecretKeyFactory.getInstance(CONST_ALGO_PBKDF2); + KeySpec keyspec = new PBEKeySpec(password, SALT, 65536, 128); + SecretKey tmp = factory.generateSecret(keyspec); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(16 * 8, FIXED_NONCE); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + byte[] decryptedCipher = cipher.doFinal(binary); + String decryptedCipherText = new String(decryptedCipher); + return decryptedCipherText; + } + +} diff --git a/src/main/java/io/proximax/app/core/cipher/CustomEncryption.java b/src/main/java/io/proximax/app/core/cipher/CustomEncryption.java new file mode 100644 index 0000000..2d3ad51 --- /dev/null +++ b/src/main/java/io/proximax/app/core/cipher/CustomEncryption.java @@ -0,0 +1,7 @@ +package io.proximax.app.core.cipher; + +/** + * The Interface CustomEncryption. + */ +public interface CustomEncryption { +} diff --git a/src/main/java/io/proximax/app/core/cipher/SymmetricKeyEncryption.java b/src/main/java/io/proximax/app/core/cipher/SymmetricKeyEncryption.java new file mode 100644 index 0000000..9b6a637 --- /dev/null +++ b/src/main/java/io/proximax/app/core/cipher/SymmetricKeyEncryption.java @@ -0,0 +1,49 @@ +package io.proximax.app.core.cipher; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +/** + * The Class SymmetricKeyEncryption. + */ +public abstract class SymmetricKeyEncryption implements CustomEncryption { + + /** + * Encrypt. + * + * @param data the data + * @param key the key + * @return the byte[] + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + */ + public abstract byte[] encrypt(byte[] data, char[] key) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException; + + /** + * Decrypt. + * + * @param data the data + * @param key the key + * @return the byte[] + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public abstract byte[] decrypt(byte[] data, char[] key) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException; + +} diff --git a/src/main/java/io/proximax/app/core/ui/IApp.java b/src/main/java/io/proximax/app/core/ui/IApp.java new file mode 100644 index 0000000..d4f01bc --- /dev/null +++ b/src/main/java/io/proximax/app/core/ui/IApp.java @@ -0,0 +1,171 @@ +package io.proximax.app.core.ui; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Objects; +import java.util.jar.Manifest; +import javafx.application.Platform; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.stage.Stage; + +/** + * + * @author thcao + */ +public interface IApp { + + public static String OS = System.getProperty("os.name").toLowerCase(); + + public Stage getPrimaryStage(); + + public Image getIcon(); + + public void setTheme(int theme); + + public Image getImageFromResource(String resUrl); + + public Image getImageFromResource(String resUrl, double w, double h); + + public void dispose(); + + public String getCurrentTheme(); + + public String getCurrentThemeUrl(); + + public String getThemeUrl(int i); + + public String getCurrentDir(); + + public File getCurrentFolder(); + + public void saveCurrentDir(String sDir); + + public static void runSafe(final Runnable runnable) { + Objects.requireNonNull(runnable, "runnable"); + if (Platform.isFxApplicationThread()) { + runnable.run(); + } else { + Platform.runLater(runnable); + } + } + + public static void exit(int status) { + if (Platform.isFxApplicationThread()) { + Platform.exit(); + System.exit(0); + } + } + + public static boolean isWindow() { + String fileSeparator = System.getProperty("file.separator"); + boolean windows; + if (OS.matches(".*linux.*")) { + windows = false; + } else if (OS.matches(".*win.*")) { + windows = true; + } else if (fileSeparator.equals("\\")) { + windows = true; + } else { + windows = false; + } + return windows; + } + + public static boolean isMac() { + return (OS.indexOf("mac") >= 0); + } + + public static boolean isUnix() { + return (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0); + } + + public static boolean isSolaris() { + return (OS.indexOf("sunos") >= 0); + } + + public static String getOS() { + if (isMac()) { + return "mac"; + } + return ""; + } + + public static ImageView createImageViewFromIconData(byte[] data, ImageView iv) { + return createImageViewFromIconData(data, 20, 20, iv); + } + + public static ImageView createImageViewFromIconData(byte[] data, int w, int h, ImageView iv) { + if (data == null) { + return iv; + } + return createImageViewFromIconData(new ByteArrayInputStream(data), w, h); + } + + public static Image createImageFromIconData(byte[] data, int w, int h) { + return createImageFromIconData(new ByteArrayInputStream(data), w, h); + } + + public static Image createImageFromIconData(InputStream data, int w, int h) { + return new Image(data, w, h, true, true); + } + + public static ImageView createImageViewFromIconData(InputStream data, int w, int h) { + return new ImageView(new Image(data, w, h, true, true)); + } + + public static ImageView createImageViewFromIconData(InputStream data) { + return new ImageView(new Image(data)); + } + + public static boolean isLocalIPFS() { + try { + URL nodeUrl = IApp.class.getResource("/node"); + if (new File(nodeUrl.toURI()).exists()) { + return true; + } + } catch (Exception ex) { + } + return false; + } + + public static String getJarPath() { + Class clazz = IApp.class; + String className = clazz.getSimpleName() + ".class"; + String classPath = clazz.getResource(className).toString(); + if (classPath.startsWith("jar")) { + return classPath; + } + return ""; + } + + public static String getManifestPath() { + Class clazz = IApp.class; + String className = clazz.getSimpleName() + ".class"; + String classPath = clazz.getResource(className).toString(); + if (classPath.startsWith("jar")) { + return classPath.substring(0, classPath.lastIndexOf("!") + 1) + + "/META-INF/MANIFEST.MF"; + } + return ""; + } + + public static Manifest getManifest() { + try { + Class clazz = IApp.class; + String className = clazz.getSimpleName() + ".class"; + String classPath = clazz.getResource(className).toString(); + if (classPath.startsWith("jar")) { + String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + + "/META-INF/MANIFEST.MF"; + URL manifestUrl = new URL(manifestPath); + return new Manifest(manifestUrl.openStream()); + } + } catch (Exception ex) { + } + return null; + + } +} diff --git a/src/main/java/io/proximax/app/db/Category.java b/src/main/java/io/proximax/app/db/Category.java new file mode 100644 index 0000000..b1c0721 --- /dev/null +++ b/src/main/java/io/proximax/app/db/Category.java @@ -0,0 +1,28 @@ +package io.proximax.app.db; + +/** + * + * @author thcao + */ +public class Category { + + public int id; + public String category; + public long createdDate; + public String parent; + + public Category(String folder) { + id = 0; + category = folder; + createdDate = 0; + parent = ""; + } + + public Category(Category folder) { + id = folder.id; + category = folder.category; + createdDate = folder.createdDate; + parent = folder.parent; + } + +} diff --git a/src/main/java/io/proximax/app/db/IpfsConnection.java b/src/main/java/io/proximax/app/db/IpfsConnection.java new file mode 100644 index 0000000..3a72c02 --- /dev/null +++ b/src/main/java/io/proximax/app/db/IpfsConnection.java @@ -0,0 +1,34 @@ +package io.proximax.app.db; + +import io.proximax.app.utils.NetworkUtils; + +/** + * + * @author thcao + */ +public class IpfsConnection extends io.proximax.connection.IpfsConnection { + + private final String schema; + + public IpfsConnection(String apiHost, int apiPort) { + super(apiHost, apiPort); + this.schema = "http"; + } + + public IpfsConnection(String schema, String apiHost, int apiPort) { + super(apiHost, apiPort); + this.schema = schema; + } + + public String getDownloadUrl() { + if (getApiHost().equals(NetworkUtils.LOCALHOST)) { + return NetworkUtils.IPFS_LOCALHOST_URL; + } else { + return String.format("%s://%s/ipfs/", schema, getApiHost()); + } + } + + public boolean isLocalHost() { + return (getApiHost().equalsIgnoreCase("127.0.0.1") || getApiHost().equalsIgnoreCase("localhost")); + } +} diff --git a/src/main/java/io/proximax/app/db/LocalAccount.java b/src/main/java/io/proximax/app/db/LocalAccount.java new file mode 100644 index 0000000..9cefa6b --- /dev/null +++ b/src/main/java/io/proximax/app/db/LocalAccount.java @@ -0,0 +1,197 @@ +package io.proximax.app.db; + +import io.proximax.connection.ConnectionConfig; +import io.proximax.connection.IpfsConnection; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.NetworkUtils; +import io.proximax.connection.BlockchainNetworkConnection; +import java.util.List; + +public final class LocalAccount { + + public String fullName; + public String password; + public String network; + public String privateKey; + public String publicKey; + public String address; + public String licenseKey; + public int status; + public int type; + public long xpx; + public double used; + public double capacity; + //public Account nemAccount = null; + public String version; + public ConnectionConfig connectionConfig = null; + private int nodeIndex = -1; + + public LocalAccount(String fullName, String password, String network, String privateKey, String publicKey, String address, String version) { + this.fullName = fullName; + this.password = password; + this.network = network; + this.privateKey = privateKey; + this.publicKey = publicKey; + this.address = address; + this.version = version; +// NetworkConfiguration netconf = getNetworkConfiguration(); +// if (netconf != null) { +// nemAccount = new NemUtils(netconf.getNetworkType()).getAccount(privateKey); +// } + } + + public LocalAccount(String fullName, String password, String network, String privateKey, String publicKey, String address, String version, int type, int status) { + this.fullName = fullName; + this.password = password; + this.network = network; + this.privateKey = privateKey; + this.publicKey = publicKey; + this.address = address; + this.version = version; + this.type = type; + this.status = status; +// NetworkConfiguration netconf = getNetworkConfiguration(); +// if (netconf != null) { +// //nemAccount = new NemUtils(netconf.getNetworkType()).getAccount(privateKey); +// } + } + + public boolean createConnection() { + if (isConnected()) { + return true; + } + boolean bDone = false; + do { + try { + int ret = connectNextNode(); + if (ret == -1) { + break; + } else if (ret == 1) { + bDone = true; + } + } catch (Exception ex) { + bDone = false; + } + } while (!bDone); + return bDone; + } + + public void setConnectionIndex(int idx) { + disconnect(); + nodeIndex = idx; + } + + public boolean connectToNode(int idx) { + try { + disconnect(); + NetworkConfiguration netconf = getNetworkConfiguration(); + if (NetworkUtils.testNode(netconf.name, netconf.getApiUrl(idx))) { + nodeIndex = idx; + connectionConfig = netconf.createWithIpfsConnection(nodeIndex); + return isConnected(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + return false; + } + + public int connectNextNode() { + int idx = nodeIndex; + NetworkConfiguration netconf = getNetworkConfiguration(); + if (netconf == null) { + netconf = NetworkUtils.getNetworkConfiguration(network); + } + if (idx < netconf.nodeList.size() - 1) { + idx++; + } else { + return -1; + } + if (connectToNode(idx)) { + return 1; + } + nodeIndex = idx; + return 0; + } + + public boolean testNode() { + NetworkConfiguration netconf = getNetworkConfiguration(); + if (NetworkUtils.testNode(netconf.name, netconf.getApiUrl(nodeIndex))) { + if (!isConnected()) { + connectionConfig = netconf.createWithIpfsConnection(nodeIndex); + } + return isConnected(); + } + return false; + } + + public int getNodeIndex() { + return nodeIndex; + } + + public String getCurrentNode() { + NetworkConfiguration netconf = getNetworkConfiguration(); + return netconf.getNodes().get(nodeIndex); + } + + public String getApiHost() { + return connectionConfig.getBlockchainNetworkConnection().getApiHost(); + } + + public String getApiUrl() { + return connectionConfig.getBlockchainNetworkConnection().getApiUrl(); + } + + public int getApiPort() { + return connectionConfig.getBlockchainNetworkConnection().getApiPort(); + } + + public BlockchainNetworkConnection getBlockchainNetworkConnection() { + return connectionConfig.getBlockchainNetworkConnection(); + } + + public String getNetwork() { + return network.toUpperCase(); + } + + public void disconnect() { + connectionConfig = null; + } + + public List getNodes() { + NetworkConfiguration netconf = getNetworkConfiguration(); + return netconf.getNodes(); + } + + public int getCurrentNodeIndex() { + return nodeIndex; + } + + public boolean isConnected() { + return (connectionConfig != null); + } + + public String getPeerId() { + try { + return ((IpfsConnection) connectionConfig.getFileStorageConnection()).getIpfs().config.show().get("Identity").toString(); + } catch (Exception ex) { + } + return "No Connection"; + } + + public String toString() { + return new String().format("usr: %s-pass: %s-net: %s-pri: %s-pub: %s-add: %s", fullName, password, network, privateKey, publicKey, address); + } + + public String getAddressPretty() { + return AccountHelpers.formatAddressPretty(address); + } + + public NetworkConfiguration getNetworkConfiguration() { + return NetworkUtils.getNetworkConfiguration(network); + } + + public String getLicenseKey() { + return licenseKey; + } +} diff --git a/src/main/java/io/proximax/app/db/LocalFile.java b/src/main/java/io/proximax/app/db/LocalFile.java new file mode 100644 index 0000000..b425c44 --- /dev/null +++ b/src/main/java/io/proximax/app/db/LocalFile.java @@ -0,0 +1,139 @@ +package io.proximax.app.db; + +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.StringUtils; + +/** + * + * @author thcao + */ +public class LocalFile { + + public int id; + public String fileName; + public String filePath; + public String reName; + public long modified; + public long fileSize; + public long uploadDate; + public String hash; + public String nemHash; + public String publicKey; + public String address; + public String privateKey; + public String password; + public String category; + public int uType; + public String shared; + public String metadata; + public int rev; + public int fileId; + public int status; + public long createdDate; + public long updatedDate; + + public boolean isFolder; + + public LocalFile() { + this.id = 0; + this.fileName = ""; + this.modified = 0; + this.fileSize = 0; + this.uploadDate = 0; + this.hash = ""; + this.nemHash = ""; + this.publicKey = ""; + this.category = ""; + this.password = ""; + this.address = ""; + this.privateKey = ""; + this.uType = 0; + this.shared = ""; + this.metadata = ""; + this.fileId = id; + this.rev = 0; + this.status = 0; + this.isFolder = false; + } + + public LocalFile(boolean isFolder) { + this.id = 0; + this.fileName = ""; + this.modified = 0; + this.fileSize = 0; + this.uploadDate = 0; + this.hash = ""; + this.nemHash = ""; + this.publicKey = ""; + this.category = ""; + this.password = ""; + this.privateKey = ""; + this.address = ""; + this.uType = 0; + this.shared = ""; + this.metadata = ""; + this.fileId = id; + this.rev = 0; + this.status = 0; + this.isFolder = isFolder; + } + + public LocalFile(LocalFile localFile) { + this.id = localFile.id; + this.fileName = localFile.fileName; + this.filePath = localFile.filePath; + this.reName = localFile.reName; + this.modified = localFile.modified; + this.fileSize = localFile.fileSize; + this.uploadDate = localFile.uploadDate; + this.hash = localFile.hash; + this.nemHash = localFile.nemHash; + this.publicKey = localFile.publicKey; + this.category = localFile.category; + this.password = localFile.password; + this.privateKey = localFile.privateKey; + this.uType = localFile.uType; + this.shared = localFile.shared; + this.metadata = localFile.metadata; + this.fileId = localFile.fileId; + this.rev = localFile.rev; + this.status = localFile.status; + this.isFolder = localFile.isFolder; + this.address = localFile.address; + } + + public boolean isSecure() { + return (uType != CONST.UTYPE_PUBLIC); + } + + public String toString() { + return new StringBuffer(id).append("/f[").append(fileName).append("]/s[").append(uType).append("]/d[").append(modified).append("]/h[").append(hash).append("]/n[").append(nemHash).append("]").toString(); + } + + public String getModified() { + if (isFolder) { + return ""; + } else { + return CONST.SDF.format(modified); + } + } + + public String getName() { + if (isFolder) { + return category; + } else { + if (StringUtils.isEmpty(reName)) { + reName = fileName; + } + return reName; + } + } + + public String getMember() { + if (isFolder) { + return ""; + } else { + return (uType == CONST.UTYPE_PUBLIC ? "Public" : "Private"); + } + } +} diff --git a/src/main/java/io/proximax/app/db/LocalJob.java b/src/main/java/io/proximax/app/db/LocalJob.java new file mode 100644 index 0000000..3fd34d0 --- /dev/null +++ b/src/main/java/io/proximax/app/db/LocalJob.java @@ -0,0 +1,31 @@ +package io.proximax.app.db; + +/** + * + * @author thcao + */ +public final class LocalJob { + public static final int JOB_ACCOUNT = 1; + public static final int JOB_UPLOAD = 2; + public static final int JOB_DOWNLOAD = 3; + public static final int JOB_SHARE = 4; + public static final int JOB_EDIT = 5; + public static final int JOB_VIEW = 6; + + public static final int JOB_STATUS_DONE = 0; + public static final int JOB_STATUS_NEW = 1; + public static final int JOB_STATUS_QUEUE = 2; + public static final int JOB_STATUS_RUNNING = 3; + public static final int JOB_STATUS_FAILED = 4; + public static final int JOB_STATUS_DELETE = 0; + + + public int id; + public String filePath; + public String publicKey; + public String address; + public int jType; + public int fileId; + public int status; + public long createdDate; +} diff --git a/src/main/java/io/proximax/app/db/NetworkConfiguration.java b/src/main/java/io/proximax/app/db/NetworkConfiguration.java new file mode 100644 index 0000000..e21924c --- /dev/null +++ b/src/main/java/io/proximax/app/db/NetworkConfiguration.java @@ -0,0 +1,250 @@ +package io.proximax.app.db; + +import io.proximax.sdk.model.blockchain.NetworkType; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.utils.NetworkUtils; +import io.proximax.app.utils.StringUtils; +import io.proximax.connection.BlockchainNetworkConnection; +import io.proximax.connection.ConnectionConfig; +import io.proximax.connection.HttpProtocol; +import io.proximax.model.BlockchainNetworkType; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author thcao + */ +public class NetworkConfiguration { + + public int id; + public int value; + public List nodeList = new ArrayList<>(); + public List ipfsList = new ArrayList<>(); + public int status; + public String name; + public String enc1; + public String enc2; + public String enc3; + public boolean modified; + private String remoteUrl; + + public NetworkConfiguration(int id, String name, int value, int status, String nodes, String ipfs, String enc1) { + this.id = id; + this.value = value; + this.name = name; + this.status = status; + this.enc1 = enc1; + //only parse network active + if (status == 1) { + parseNodes(nodes); + parseIpfs(ipfs); + } + } + + private void parseNodes(String str) { + if (!StringUtils.isEmpty(str)) { + String[] nodeArr = str.split(","); + for (String n : nodeArr) { + addNode(n); + } + } + } + + public boolean addNode(String url) { + try { + String host; + int port = NetworkUtils.NODE_DEFAULT_PORT; + if (!url.startsWith("http")) { + url = "http://" + url; + } + URI u = new URI(url); + host = u.getHost(); + port = u.getPort(); + if (port == -1) { + port = NetworkUtils.NODE_DEFAULT_PORT; + } + BlockchainNetworkConnection blNode = new BlockchainNetworkConnection( + getBlockchainNetworkType(), + host, + port, + HttpProtocol.HTTP); + return nodeList.add(blNode); + } catch (URISyntaxException ex) { + ex.printStackTrace(); + } + return false; + } + + public BlockchainNetworkConnection removeNode(int idx) { + return nodeList.remove(idx); + } + + public IpfsConnection removeIpfs(int idx) { + return ipfsList.remove(idx); + } + + public void moveUpNode(int idx) { + if (idx > 0) { + BlockchainNetworkConnection curItem = nodeList.get(idx); + BlockchainNetworkConnection prevItem = nodeList.set(idx - 1, curItem); + nodeList.set(idx, prevItem); + } + } + + public void moveUpIpfs(int idx) { + if (idx > 0) { + IpfsConnection curItem = ipfsList.get(idx); + IpfsConnection prevItem = ipfsList.set(idx - 1, curItem); + ipfsList.set(idx, prevItem); + } + } + + private void parseIpfs(String str) { + boolean bLocal = false; + if (!StringUtils.isEmpty(str)) { + String[] ipfsArr = str.split(","); + for (String n : ipfsArr) { + IpfsConnection conn = addIpfs(n); + if (conn.isLocalHost()) { + bLocal = true; + } + } + } else { + for (String n : NetworkUtils.IPFS_SERVER) { + IpfsConnection conn = addIpfs(n); + if (conn.isLocalHost()) { + bLocal = true; + } + } + } + if (IApp.isLocalIPFS() && !bLocal) { + ipfsList.add(0, new IpfsConnection(NetworkUtils.LOCALHOST, NetworkUtils.IPFS_PORT)); + } + } + + public IpfsConnection addIpfs(String url) { + try { + String host; + int port = NetworkUtils.IPFS_PORT; + if (!url.startsWith("http")) { + url = "http://" + url; + } + URI u = new URI(url); + host = u.getHost(); + port = u.getPort(); + if (port == -1) { + port = NetworkUtils.IPFS_PORT; + } + IpfsConnection conn = new IpfsConnection(u.getScheme(), host, port); + ipfsList.add(conn); + return conn; + } catch (URISyntaxException ex) { + ex.printStackTrace(); + } + return null; + + } + + public List getNodes() { + List arrNode = new ArrayList<>(); + for (BlockchainNetworkConnection blNode : nodeList) { + arrNode.add(blNode.getApiHost()); + } + return arrNode; + } + + public List getIpfses() { + List arrIpfs = new ArrayList<>(); + for (IpfsConnection ipfs : ipfsList) { + arrIpfs.add(ipfs.getApiHost()); + } + return arrIpfs; + } + + public BlockchainNetworkConnection getNode(int idx) { + return nodeList.get(idx); + } + + public IpfsConnection getIpfs(int idx) { + return ipfsList.get(idx); + } + + public String getApiUrl(int idx) { + return nodeList.get(idx).getApiUrl(); + } + + public String getApiHost(int idx) { + return nodeList.get(idx).getApiHost(); + } + + public int getApiPort(int idx) { + return nodeList.get(idx).getApiPort(); + } + + public String getIpfsHost(int idx) { + String url; + if (ipfsList.isEmpty()) { + url = NetworkUtils.getIpfs(idx); + } else { + return ipfsList.get(idx).getApiHost(); + } + try { + URI a = new URI(url); + return a.getHost(); + } catch (Exception ex) { + } + return url; + } + + public int getIpfsPort(int idx) { + String url; + if (ipfsList.isEmpty()) { + url = NetworkUtils.getIpfs(idx); + } else { + return ipfsList.get(idx).getApiPort(); + } + try { + URI a = new URI(url); + return a.getPort(); + } catch (Exception ex) { + } + return NetworkUtils.IPFS_PORT; + } + + public NetworkType getNetworkType() { + return NetworkType.valueOf(name); + } + + public BlockchainNetworkType getBlockchainNetworkType() { + return BlockchainNetworkType.fromString(name); + } + + public ConnectionConfig createWithIpfsConnection(int idx) { + return ConnectionConfig.createWithLocalIpfsConnection( + getNode(idx), + getIpfs(0)); + } + + public String getDownloadUrl(int idx) { + return getIpfs(idx).getDownloadUrl(); + } + + public String getDownloadUrl() { + if (StringUtils.isEmpty(remoteUrl)) { + for (IpfsConnection conn : ipfsList) { + if (!conn.isLocalHost()) { + remoteUrl = conn.getDownloadUrl(); + return remoteUrl; + } + } + } + if (StringUtils.isEmpty(remoteUrl)) { + return NetworkUtils.IPFS_LOCALHOST_URL; + } else { + return remoteUrl; + } + } +} diff --git a/src/main/java/io/proximax/app/db/ShareFile.java b/src/main/java/io/proximax/app/db/ShareFile.java new file mode 100644 index 0000000..4ff34eb --- /dev/null +++ b/src/main/java/io/proximax/app/db/ShareFile.java @@ -0,0 +1,57 @@ +package io.proximax.app.db; + +/** + * + * @author thcao + * TABLE PROXIBOX_SHARES + ID INTEGER PRIMARY KEY AUTOINCREMENT, + FILE_ID INTEGER, + USERNAME CHAR(255) NOT NULL, + ADDRESS CHAR(255), + SHARE_DATE INTEGER, + HASH CHAR(255) NOT NULL, + NEM_HASH CHAR(255) NOT NULL, + PASSWORD CHAR(255), + SHARE_TYPE INTEGER + */ +public class ShareFile { + public int id; + public int fileId; + public String userName; + public String address; + public long shareDate; + public String hash; //ipfsHash + public String nemHash; //nemHash + public String password; //secure password + public int shareType; //share type + public int status; + + public ShareFile() { + id = 0; + fileId = 0; + userName = ""; + address = ""; + shareDate = 0; + hash = ""; + nemHash = ""; + password = ""; + shareType = 0; + status = 0; + } + + public ShareFile(int fileId, String userName, String address, long shareDate, String hash, String nemHash, int shareType, String password, int status) { + this.fileId = fileId; + this.userName = userName; + this.address = address; + this.shareDate = shareDate; + this.hash = hash; + this.nemHash = nemHash; + this.password = password; + this.shareType = shareType; + this.status = status; + } + + public String toString() { + return new StringBuffer(id).append("/f[").append(fileId).append("]/s[").append(shareType).append("]/d[").append(shareDate).append("]/h[").append(hash).append("]/n[").append(nemHash).append("]").toString(); + } +} diff --git a/src/main/java/io/proximax/app/fx/control/ProxiBoxStatusBar.java b/src/main/java/io/proximax/app/fx/control/ProxiBoxStatusBar.java new file mode 100644 index 0000000..6508cad --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/ProxiBoxStatusBar.java @@ -0,0 +1,287 @@ +package io.proximax.app.fx.control; + +/** + * + * @author thcao + */ +import io.proximax.app.fx.control.skin.ProxiBoxStatusBarSkin; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.scene.Node; +import javafx.scene.control.Control; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.Skin; +import javafx.scene.image.Image; + +/** + * The status bar control is normally placed at the bottom of a window. It is + * used to display various types of application status information. This can be + * a text message, the progress of a task, or any other kind of status (e.g. red + * / green / yellow lights). By default the status bar contains a label for + * displaying plain text and a progress bar (see {@link ProgressBar}) for long + * running tasks. Additional controls / nodes can be placed on the left and + * right sides (see {@link #getLeftItems()} and {@link #getRightItems()}). + * + *

Screenshots

The picture below shows the default appearance of the + * statusbar.

+ * The following picture shows the status bar reporting progress of a task. + *

+ * The last picture shows the status bar with a couple of extra items added to + * the left and right.
+ * + *

Code Sample

+ * + *
+ ProxiBoxStatusBar statusBar = new ProxiBoxStatusBar();
+ statusBar.getLeftItems().add(new Button("Info"));
+ * statusBar.setProgress(.5);
+ * 
+ */ +public class ProxiBoxStatusBar extends Control { + + private ProxiBoxStatusBarSkin proxiBoxStatusBarSkin = null; + private EventHandler eventHandler; + + /** + * Constructs a new status bar control. + */ + public ProxiBoxStatusBar() { + getStyleClass().add("status-bar"); + } + + /** + * Set event handler + * @param handler + */ + + public void setEventHandler(EventHandler handler) { + this.eventHandler = handler; + } + + /** + * Get event handler + * @return event handler + */ + public EventHandler getEventHandler() { + return eventHandler; + } + + @Override + protected Skin createDefaultSkin() { + proxiBoxStatusBarSkin = new ProxiBoxStatusBarSkin(this); + return proxiBoxStatusBarSkin; + } + + private final StringProperty text = new SimpleStringProperty(this, "text", + "Status"); + + /** + * The property used for storing the text message shown by the status bar. + * + * @return the text message property + */ + public final StringProperty textProperty() { + return text; + } + + /** + * Sets the value of the {@link #textProperty()}. + * + * @param text the text shown by the label control inside the status bar + */ + public final void setText(String text) { + textProperty().set(text); + } + + /** + * Returns the value of the {@link #textProperty()}. + * + * @return the text currently shown by the status bar + */ + public final String getText() { + return textProperty().get(); + } + + private final ObjectProperty graphic = new SimpleObjectProperty<>( + this, "graphic"); + + /** + * The property used to store a graphic node that can be displayed by the + * status label inside the status bar control. + * + * @return the property used for storing a graphic node + */ + public final ObjectProperty graphicProperty() { + return graphic; + } + + /** + * Returns the value of the {@link #graphicProperty()}. + * + * @return the graphic node shown by the label inside the status bar + */ + public final Node getGraphic() { + return graphicProperty().get(); + } + + /** + * Sets the value of {@link #graphicProperty()}. + * + * @param node the graphic node shown by the label inside the status bar + */ + public final void setGraphic(Node node) { + graphicProperty().set(node); + } + + private final ObservableList leftItems = FXCollections + .observableArrayList(); + + /** + * Returns the list of items / nodes that will be shown to the left of the status label. + * + * @return the items on the left-hand side of the status bar + */ + public final ObservableList getLeftItems() { + return leftItems; + } + + private final ObservableList rightItems = FXCollections + .observableArrayList(); + + /** + * Returns the list of items / nodes that will be shown to the right of the status label. + * + * @return the items on the left-hand side of the status bar + */ + public final ObservableList getRightItems() { + return rightItems; + } + + private final DoubleProperty progress = new SimpleDoubleProperty(this, + "progress"); + + /** + * The property used to store the progress, a value between 0 and 1. A negative + * value causes the progress bar to show an indeterminate state. + * + * @return the property used to store the progress of a task + */ + public final DoubleProperty progressProperty() { + return progress; + } + + /** + * Sets the value of the {@link #progressProperty()}. + * + * @param progress the new progress value + */ + public final void setProgress(double progress) { + progressProperty().set(progress); + } + + /** + * Returns the value of {@link #progressProperty()}. + * + * @return the current progress value + */ + public final double getProgress() { + return progressProperty().get(); + } + + private final ObjectProperty> nodeItems = new SimpleObjectProperty>(); + + /** + * Returns the list of items / nodes that will be shown to the right of the status label. + * + * @return the items on the left-hand side of the status bar + */ + public final ObjectProperty > nodeItemsProperty() { + return nodeItems; + } + + /** + * Sets the list of nodes. + * + * @param nodes, index + */ + public final void setNodeItems(ObservableList nodes, int idx) { + nodeItems.set(nodes); + if (idx!=-1) + nodeSelectedItem.set(nodes.get(idx)); + } + + private final StringProperty nodeSelectedItem = new SimpleStringProperty(this, "text", + "Status"); + + /** + * The property used for storing the node text shown by the status bar. + * + * @return the node text property + */ + public final StringProperty nodeSelectedProperty() { + return nodeSelectedItem; + } + + /** + * Sets the value list of node. + * + * @param index + */ + public final void setNodeSelected(int idx) { + nodeSelectedItem.set(nodeItems.get().get(idx)); + } + + /** + * Sets the value list of node. + * + * @param newValue + */ + public final void setNodeSelected(String newValue) { + nodeSelectedItem.setValue(newValue); + } + + private final BooleanProperty imageStatusBoolean = new SimpleBooleanProperty(false); + + /** + * The property used for storing the image status by the status bar. + * + * @return the image status property + */ + public final BooleanProperty imageStatusBooleanProperty() { + return imageStatusBoolean; + } + + /** + * Set image status + * @param status: if true image is green, otherwise red + */ + public final void setImageStatus(Boolean status) { + imageStatusBoolean.set(status); + } + + private ObjectProperty CONNECTION_IMAGE_SUCCESS = null; + private ObjectProperty CONNECTION_IMAGE_FAILED = null; + public void setImageSuccess(Image img) { + CONNECTION_IMAGE_SUCCESS = new SimpleObjectProperty<>(img); + } + public ObjectProperty getImageSuccess() { + return CONNECTION_IMAGE_SUCCESS; + } + public ObjectProperty getImageFailed() { + return CONNECTION_IMAGE_FAILED; + } + public void setImageFailed(Image img) { + CONNECTION_IMAGE_FAILED = new SimpleObjectProperty<>(img); + } + + +} \ No newline at end of file diff --git a/src/main/java/io/proximax/app/fx/control/skin/ProxiBoxStatusBarSkin.java b/src/main/java/io/proximax/app/fx/control/skin/ProxiBoxStatusBarSkin.java new file mode 100644 index 0000000..fded5c0 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/skin/ProxiBoxStatusBarSkin.java @@ -0,0 +1,107 @@ +package io.proximax.app.fx.control.skin; + +/** + * + * @author thcao + */ +import com.jfoenix.controls.JFXComboBox; +import javafx.beans.Observable; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.SkinBase; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; + +import io.proximax.app.fx.control.ProxiBoxStatusBar; +import javafx.beans.binding.When; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; + +public class ProxiBoxStatusBarSkin extends SkinBase { + + private HBox leftBox; + private HBox rightBox; + private Label statusLbl; + private JFXComboBox nodeCbx; + private ProgressBar progressBar; + private Label nodeLbl; + private ImageView imageStatus; + + public ProxiBoxStatusBarSkin(ProxiBoxStatusBar statusBar) { + super(statusBar); + leftBox = new HBox(); + leftBox.getStyleClass().add("left-items"); + + rightBox = new HBox(); + rightBox.getStyleClass().add("right-items"); + + progressBar = new ProgressBar(); + progressBar.setPrefHeight(20.0); + progressBar.progressProperty().bind(statusBar.progressProperty()); + + statusLbl = new Label(); + statusLbl.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); + statusLbl.textProperty().bind(statusBar.textProperty()); + statusLbl.graphicProperty().bind(statusBar.graphicProperty()); + statusLbl.getStyleClass().add("status-label"); + + imageStatus = new ImageView(); + imageStatus.setId("status-image"); + imageStatus.imageProperty().bind( + new When(statusBar.imageStatusBooleanProperty()). + then(statusBar.getImageSuccess()). + otherwise(statusBar.getImageFailed())); + imageStatus.addEventHandler(MouseEvent.MOUSE_CLICKED, statusBar.getEventHandler()); + + nodeLbl = new Label(" Node: "); + nodeLbl.getStyleClass().add("status-label"); + + nodeCbx = new JFXComboBox(); + nodeCbx.setId("status-nodes"); + nodeCbx.getStyleClass().add("status-combox"); + nodeCbx.itemsProperty().bind(statusBar.nodeItemsProperty()); + nodeCbx.valueProperty().bind(statusBar.nodeSelectedProperty()); + nodeCbx.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + statusBar.setNodeSelected((String) newValue); + statusBar.getEventHandler().handle(new Event(nodeCbx, nodeCbx, ActionEvent.ACTION)); + }); + leftBox.getChildren().setAll(getSkinnable().getLeftItems()); + rightBox.getChildren().setAll(getSkinnable().getRightItems()); + statusBar.getLeftItems().addListener( + (Observable evt) -> leftBox.getChildren().setAll( + getSkinnable().getLeftItems())); + statusBar.getRightItems().addListener( + (Observable evt) -> rightBox.getChildren().setAll( + getSkinnable().getRightItems())); + + GridPane gridPane = new GridPane(); + gridPane.setHgap(4); + GridPane.setFillHeight(leftBox, true); + GridPane.setFillHeight(rightBox, true); + GridPane.setFillHeight(nodeLbl, true); + GridPane.setFillHeight(nodeCbx, true); + GridPane.setFillHeight(statusLbl, true); + GridPane.setFillHeight(progressBar, true); + + GridPane.setVgrow(leftBox, Priority.ALWAYS); + GridPane.setVgrow(rightBox, Priority.ALWAYS); + GridPane.setVgrow(nodeLbl, Priority.ALWAYS); + GridPane.setVgrow(nodeCbx, Priority.ALWAYS); + GridPane.setVgrow(statusLbl, Priority.ALWAYS); + GridPane.setVgrow(progressBar, Priority.ALWAYS); + + GridPane.setHgrow(statusLbl, Priority.ALWAYS); + + gridPane.add(leftBox, 0, 0); + gridPane.add(statusLbl, 1, 0); + gridPane.add(imageStatus, 2, 0); + gridPane.add(nodeLbl, 3, 0); + gridPane.add(nodeCbx, 4, 0); + gridPane.add(progressBar, 5, 0); + gridPane.add(rightBox, 7, 0); + getChildren().add(gridPane); + } +} diff --git a/src/main/java/io/proximax/app/fx/control/text/EmptyLinkedImage.java b/src/main/java/io/proximax/app/fx/control/text/EmptyLinkedImage.java new file mode 100644 index 0000000..4497e64 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/EmptyLinkedImage.java @@ -0,0 +1,25 @@ +package io.proximax.app.fx.control.text; + +import javafx.scene.Node; + +/** + * + * @author thcao + */ +public class EmptyLinkedImage implements LinkedImage { + + @Override + public boolean isReal() { + return false; + } + + @Override + public String getImagePath() { + return ""; + } + + @Override + public Node createNode() { + throw new AssertionError("Unreachable code"); + } +} \ No newline at end of file diff --git a/src/main/java/io/proximax/app/fx/control/text/LinkedImage.java b/src/main/java/io/proximax/app/fx/control/text/LinkedImage.java new file mode 100644 index 0000000..2b2cfa1 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/LinkedImage.java @@ -0,0 +1,54 @@ +package io.proximax.app.fx.control.text; + +/** + * + * @author thcao + */ +import javafx.scene.Node; +import org.fxmisc.richtext.model.Codec; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public interface LinkedImage { + static Codec codec() { + return new Codec() { + @Override + public String getName() { + return "LinkedImage"; + } + + @Override + public void encode(DataOutputStream os, LinkedImage linkedImage) throws IOException { + if (linkedImage.isReal()) { + os.writeBoolean(true); + String externalPath = linkedImage.getImagePath().replace("\\", "/"); + Codec.STRING_CODEC.encode(os, externalPath); + } else { + os.writeBoolean(false); + } + } + + @Override + public LinkedImage decode(DataInputStream is) throws IOException { + if (is.readBoolean()) { + String imagePath = Codec.STRING_CODEC.decode(is); + imagePath = imagePath.replace("\\", "/"); + return new RealLinkedImage(imagePath); + } else { + return new EmptyLinkedImage(); + } + } + }; + } + + boolean isReal(); + + /** + * @return The path of the image to render. + */ + String getImagePath(); + + Node createNode(); +} \ No newline at end of file diff --git a/src/main/java/io/proximax/app/fx/control/text/LinkedImageOps.java b/src/main/java/io/proximax/app/fx/control/text/LinkedImageOps.java new file mode 100644 index 0000000..9a4bcac --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/LinkedImageOps.java @@ -0,0 +1,20 @@ +package io.proximax.app.fx.control.text; + +import org.fxmisc.richtext.model.NodeSegmentOpsBase; + +/** + * + * @author thcao + */ +public class LinkedImageOps extends NodeSegmentOpsBase { + + public LinkedImageOps() { + super(new EmptyLinkedImage()); + } + + @Override + public int length(LinkedImage linkedImage) { + return linkedImage.isReal() ? 1 : 0; + } + +} \ No newline at end of file diff --git a/src/main/java/io/proximax/app/fx/control/text/ParStyle.java b/src/main/java/io/proximax/app/fx/control/text/ParStyle.java new file mode 100644 index 0000000..c224776 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/ParStyle.java @@ -0,0 +1,150 @@ +package io.proximax.app.fx.control.text; + +/** + * + * @author thcao + */ +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; + +import javafx.scene.paint.Color; +import javafx.scene.text.TextAlignment; + +import org.fxmisc.richtext.model.Codec; + +/** + * Holds information about the style of a paragraph. + */ +class ParStyle { + + public static final ParStyle EMPTY = new ParStyle(); + + public static final Codec CODEC = new Codec() { + + private final Codec> OPT_ALIGNMENT_CODEC + = Codec.optionalCodec(Codec.enumCodec(TextAlignment.class)); + private final Codec> OPT_COLOR_CODEC + = Codec.optionalCodec(Codec.COLOR_CODEC); + + @Override + public String getName() { + return "par-style"; + } + + @Override + public void encode(DataOutputStream os, ParStyle t) throws IOException { + OPT_ALIGNMENT_CODEC.encode(os, t.alignment); + OPT_COLOR_CODEC.encode(os, t.backgroundColor); + } + + @Override + public ParStyle decode(DataInputStream is) throws IOException { + return new ParStyle( + OPT_ALIGNMENT_CODEC.decode(is), + OPT_COLOR_CODEC.decode(is)); + } + + }; + + public static ParStyle alignLeft() { + return EMPTY.updateAlignment(TextAlignment.LEFT); + } + + public static ParStyle alignCenter() { + return EMPTY.updateAlignment(TextAlignment.CENTER); + } + + public static ParStyle alignRight() { + return EMPTY.updateAlignment(TextAlignment.RIGHT); + } + + public static ParStyle alignJustify() { + return EMPTY.updateAlignment(TextAlignment.JUSTIFY); + } + + public static ParStyle backgroundColor(Color color) { + return EMPTY.updateBackgroundColor(color); + } + + final Optional alignment; + final Optional backgroundColor; + + public ParStyle() { + this(Optional.empty(), Optional.empty()); + } + + public ParStyle(Optional alignment, Optional backgroundColor) { + this.alignment = alignment; + this.backgroundColor = backgroundColor; + } + + @Override + public int hashCode() { + return Objects.hash(alignment, backgroundColor); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ParStyle) { + ParStyle that = (ParStyle) other; + return Objects.equals(this.alignment, that.alignment) + && Objects.equals(this.backgroundColor, that.backgroundColor); + } else { + return false; + } + } + + @Override + public String toString() { + return toCss(); + } + + public String toCss() { + StringBuilder sb = new StringBuilder(); + + alignment.ifPresent(al -> { + String cssAlignment; + switch (al) { + case LEFT: + cssAlignment = "left"; + break; + case CENTER: + cssAlignment = "center"; + break; + case RIGHT: + cssAlignment = "right"; + break; + case JUSTIFY: + cssAlignment = "justify"; + break; + default: + throw new AssertionError("unreachable code"); + } + sb.append("-fx-text-alignment: " + cssAlignment + ";"); + }); + + backgroundColor.ifPresent(color -> { + sb.append("-fx-background-color: " + TextStyle.cssColor(color) + ";"); + }); + + return sb.toString(); + } + + public ParStyle updateWith(ParStyle mixin) { + return new ParStyle( + mixin.alignment.isPresent() ? mixin.alignment : alignment, + mixin.backgroundColor.isPresent() ? mixin.backgroundColor : backgroundColor); + } + + public ParStyle updateAlignment(TextAlignment alignment) { + return new ParStyle(Optional.of(alignment), backgroundColor); + } + + public ParStyle updateBackgroundColor(Color backgroundColor) { + return new ParStyle(alignment, Optional.of(backgroundColor)); + } + +} diff --git a/src/main/java/io/proximax/app/fx/control/text/RealLinkedImage.java b/src/main/java/io/proximax/app/fx/control/text/RealLinkedImage.java new file mode 100644 index 0000000..2ca27a9 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/RealLinkedImage.java @@ -0,0 +1,56 @@ +package io.proximax.app.fx.control.text; + +import java.io.File; + +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +/** + * A custom object which contains a file path to an image file. When rendered in + * the rich text editor, the image is loaded from the specified file. + */ +public class RealLinkedImage implements LinkedImage { + + private final String imagePath; + + /** + * Creates a new linked image object. + * + * @param imagePath The path to the image file. + */ + public RealLinkedImage(String imagePath) { + + // if the image is below the current working directory, + // then store as relative path name. + String currentDir = System.getProperty("user.dir") + File.separatorChar; + if (imagePath.startsWith(currentDir)) { + imagePath = imagePath.substring(currentDir.length()); + } + + this.imagePath = imagePath; + } + + @Override + public boolean isReal() { + return true; + } + + @Override + public String getImagePath() { + return imagePath; + } + + @Override + public String toString() { + return String.format("RealLinkedImage[path=%s]", imagePath); + } + + @Override + public Node createNode() { + Image image = new Image("file:" + imagePath); + // could be cached in the model layer + ImageView result = new ImageView(image); + return result; + } +} diff --git a/src/main/java/io/proximax/app/fx/control/text/RichTextEditor.java b/src/main/java/io/proximax/app/fx/control/text/RichTextEditor.java new file mode 100644 index 0000000..fb3f5de --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/RichTextEditor.java @@ -0,0 +1,1017 @@ +package io.proximax.app.fx.control.text; + +import io.proximax.download.DownloadParameter; +import io.proximax.download.DownloadResult; +import io.proximax.download.Downloader; +import io.proximax.app.controller.AbstractController; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.LocalFile; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.LocalFileHelpers; +import io.proximax.upload.UploadParameter; +import io.proximax.upload.UploadResult; +import io.proximax.upload.Uploader; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import javafx.beans.binding.BooleanBinding; +import javafx.collections.FXCollections; +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import javafx.geometry.Orientation; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ColorPicker; +import javafx.scene.control.ComboBox; +import javafx.scene.control.IndexRange; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.Separator; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.ToolBar; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.TextAlignment; +import javafx.stage.FileChooser; + +import org.fxmisc.flowless.VirtualizedScrollPane; +import org.fxmisc.richtext.GenericStyledArea; +import org.fxmisc.richtext.StyledTextArea; +import org.fxmisc.richtext.TextExt; +import org.fxmisc.richtext.model.Codec; +import org.fxmisc.richtext.model.Paragraph; +import org.fxmisc.richtext.model.ReadOnlyStyledDocument; +import org.fxmisc.richtext.model.SegmentOps; +import org.fxmisc.richtext.model.StyleSpans; +import org.fxmisc.richtext.model.StyledDocument; +import org.fxmisc.richtext.model.StyledSegment; +import org.fxmisc.richtext.model.TextOps; +import org.fxmisc.richtext.model.TwoDimensional; +import org.reactfx.SuspendableNo; +import org.reactfx.util.Either; +import org.reactfx.util.Tuple2; + +public class RichTextEditor extends AbstractController { + + @FXML + VBox vbox; + // the saved/loaded files and their format are arbitrary and may change across versions + private static final String RTFX_FILE_EXTENSION = ".rtfx"; + private static final String TXT_FILE_EXTENSION = ".txt"; + + private final TextOps styledTextOps = SegmentOps.styledTextOps(); + private final LinkedImageOps linkedImageOps = new LinkedImageOps<>(); + + private final GenericStyledArea, TextStyle> area + = new GenericStyledArea<>( + ParStyle.EMPTY, // default paragraph style + (paragraph, style) -> paragraph.setStyle(style.toCss()), // paragraph style setter + + TextStyle.EMPTY.updateFontSize(12).updateFontFamily("Serif").updateTextColor(Color.BLACK), // default segment style + styledTextOps._or(linkedImageOps, (s1, s2) -> Optional.empty()), // segment operations + seg -> createNode(seg, (text, style) -> text.setStyle(style.toCss()))); // Node creator and segment style setter + + { + area.setWrapText(true); + area.setStyleCodecs( + ParStyle.CODEC, + Codec.styledSegmentCodec(Codec.eitherCodec(Codec.STRING_CODEC, LinkedImage.codec()), TextStyle.CODEC)); + } + + private boolean isRichText = false; + + private final SuspendableNo updatingToolbar = new SuspendableNo(); + + private LocalAccount localAccount = null; + private LocalFile localFile = null; + + public RichTextEditor(LocalAccount localAccount, LocalFile localFile) { + super(true); + this.localAccount = localAccount; + this.localFile = localFile; + } + + public RichTextEditor(LocalAccount localAccount) { + super(true); + this.localAccount = localAccount; + } + + public RichTextEditor() { + super(true); + } + + @Override + protected void initialize() { + reloadContent(); + area.requestFocus(); + } + + protected Scene createSceneEx(String fxml) throws IOException { + Button loadBtn = createButton("new", this::loadDocument, + "Load document."); + Button saveBtn = createButton("savefile", this::saveDocument, + "Save document"); + CheckBox wrapToggle = new CheckBox("Wrap"); + wrapToggle.setSelected(true); + area.wrapTextProperty().bind(wrapToggle.selectedProperty()); + Button undoBtn = createButton("undo", area::undo, "Undo"); + Button redoBtn = createButton("redo", area::redo, "Redo"); + Button cutBtn = createButton("cut", area::cut, "Cut"); + Button copyBtn = createButton("copy", area::copy, "Copy"); + Button pasteBtn = createButton("paste", area::paste, "Paste"); + Button boldBtn = createButton("bold", this::toggleBold, "Bold"); + Button italicBtn = createButton("italic", this::toggleItalic, "Italic"); + Button underlineBtn = createButton("underline", this::toggleUnderline, "Underline"); + Button strikeBtn = createButton("strikethrough", this::toggleStrikethrough, "Strike Trough"); + Button insertImageBtn = createButton("insertimage", this::insertImage, "Insert Image"); + ToggleGroup alignmentGrp = new ToggleGroup(); + ToggleButton alignLeftBtn = createToggleButton(alignmentGrp, "align-left", this::alignLeft, "Align left"); + ToggleButton alignCenterBtn = createToggleButton(alignmentGrp, "align-center", this::alignCenter, "Align center"); + ToggleButton alignRightBtn = createToggleButton(alignmentGrp, "align-right", this::alignRight, "Align right"); + ToggleButton alignJustifyBtn = createToggleButton(alignmentGrp, "align-justify", this::alignJustify, "Justify"); + ColorPicker paragraphBackgroundPicker = new ColorPicker(); + ComboBox sizeCombo = new ComboBox<>(FXCollections.observableArrayList(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 48, 56, 64, 72)); + sizeCombo.getSelectionModel().select(Integer.valueOf(12)); + sizeCombo.setTooltip(new Tooltip("Font size")); + ComboBox familyCombo = new ComboBox<>(FXCollections.observableList(Font.getFamilies())); + familyCombo.getSelectionModel().select("Serif"); + familyCombo.setTooltip(new Tooltip("Font family")); + ColorPicker textColorPicker = new ColorPicker(Color.BLACK); + ColorPicker backgroundColorPicker = new ColorPicker(); + + paragraphBackgroundPicker.setTooltip(new Tooltip("Paragraph background")); + textColorPicker.setTooltip(new Tooltip("Text color")); + backgroundColorPicker.setTooltip(new Tooltip("Text background")); + + paragraphBackgroundPicker.valueProperty().addListener((o, old, color) -> updateParagraphBackground(color)); + sizeCombo.setOnAction(evt -> updateFontSize(sizeCombo.getValue())); + familyCombo.setOnAction(evt -> updateFontFamily(familyCombo.getValue())); + textColorPicker.valueProperty().addListener((o, old, color) -> updateTextColor(color)); + backgroundColorPicker.valueProperty().addListener((o, old, color) -> updateBackgroundColor(color)); + + undoBtn.disableProperty().bind(area.undoAvailableProperty().map(x -> !x)); + redoBtn.disableProperty().bind(area.redoAvailableProperty().map(x -> !x)); + + BooleanBinding selectionEmpty = new BooleanBinding() { + { + bind(area.selectionProperty()); + } + + @Override + protected boolean computeValue() { + return area.getSelection().getLength() == 0; + } + }; + + cutBtn.disableProperty().bind(selectionEmpty); + copyBtn.disableProperty().bind(selectionEmpty); + + area.beingUpdatedProperty().addListener((o, old, beingUpdated) -> { + if (!beingUpdated) { + boolean bold, italic, underline, strike; + Integer fontSize; + String fontFamily; + Color textColor; + Color backgroundColor; + + IndexRange selection = area.getSelection(); + if (selection.getLength() != 0) { + StyleSpans styles = area.getStyleSpans(selection); + bold = styles.styleStream().anyMatch(s -> s.bold.orElse(false)); + italic = styles.styleStream().anyMatch(s -> s.italic.orElse(false)); + underline = styles.styleStream().anyMatch(s -> s.underline.orElse(false)); + strike = styles.styleStream().anyMatch(s -> s.strikethrough.orElse(false)); + int[] sizes = styles.styleStream().mapToInt(s -> s.fontSize.orElse(-1)).distinct().toArray(); + fontSize = sizes.length == 1 ? sizes[0] : -1; + String[] families = styles.styleStream().map(s -> s.fontFamily.orElse(null)).distinct().toArray(String[]::new); + fontFamily = families.length == 1 ? families[0] : null; + Color[] colors = styles.styleStream().map(s -> s.textColor.orElse(null)).distinct().toArray(Color[]::new); + textColor = colors.length == 1 ? colors[0] : null; + Color[] backgrounds = styles.styleStream().map(s -> s.backgroundColor.orElse(null)).distinct().toArray(i -> new Color[i]); + backgroundColor = backgrounds.length == 1 ? backgrounds[0] : null; + } else { + int p = area.getCurrentParagraph(); + int col = area.getCaretColumn(); + TextStyle style = area.getStyleAtPosition(p, col); + bold = style.bold.orElse(false); + italic = style.italic.orElse(false); + underline = style.underline.orElse(false); + strike = style.strikethrough.orElse(false); + fontSize = style.fontSize.orElse(-1); + fontFamily = style.fontFamily.orElse(null); + textColor = style.textColor.orElse(null); + backgroundColor = style.backgroundColor.orElse(null); + } + + int startPar = area.offsetToPosition(selection.getStart(), TwoDimensional.Bias.Forward).getMajor(); + int endPar = area.offsetToPosition(selection.getEnd(), TwoDimensional.Bias.Backward).getMajor(); + List, TextStyle>> pars = area.getParagraphs().subList(startPar, endPar + 1); + + @SuppressWarnings("unchecked") + Optional[] alignments = pars.stream().map(p -> p.getParagraphStyle().alignment).distinct().toArray(Optional[]::new); + Optional alignment = alignments.length == 1 ? alignments[0] : Optional.empty(); + + @SuppressWarnings("unchecked") + Optional[] paragraphBackgrounds = pars.stream().map(p -> p.getParagraphStyle().backgroundColor).distinct().toArray(Optional[]::new); + Optional paragraphBackground = paragraphBackgrounds.length == 1 ? paragraphBackgrounds[0] : Optional.empty(); + + updatingToolbar.suspendWhile(() -> { + if (bold) { + if (!boldBtn.getStyleClass().contains("pressed")) { + boldBtn.getStyleClass().add("pressed"); + } + } else { + boldBtn.getStyleClass().remove("pressed"); + } + + if (italic) { + if (!italicBtn.getStyleClass().contains("pressed")) { + italicBtn.getStyleClass().add("pressed"); + } + } else { + italicBtn.getStyleClass().remove("pressed"); + } + + if (underline) { + if (!underlineBtn.getStyleClass().contains("pressed")) { + underlineBtn.getStyleClass().add("pressed"); + } + } else { + underlineBtn.getStyleClass().remove("pressed"); + } + + if (strike) { + if (!strikeBtn.getStyleClass().contains("pressed")) { + strikeBtn.getStyleClass().add("pressed"); + } + } else { + strikeBtn.getStyleClass().remove("pressed"); + } + + if (alignment.isPresent()) { + TextAlignment al = alignment.get(); + switch (al) { + case LEFT: + alignmentGrp.selectToggle(alignLeftBtn); + break; + case CENTER: + alignmentGrp.selectToggle(alignCenterBtn); + break; + case RIGHT: + alignmentGrp.selectToggle(alignRightBtn); + break; + case JUSTIFY: + alignmentGrp.selectToggle(alignJustifyBtn); + break; + } + } else { + alignmentGrp.selectToggle(null); + } + + paragraphBackgroundPicker.setValue(paragraphBackground.orElse(null)); + + if (fontSize != -1) { + sizeCombo.getSelectionModel().select(fontSize); + } else { + sizeCombo.getSelectionModel().clearSelection(); + } + + if (fontFamily != null) { + familyCombo.getSelectionModel().select(fontFamily); + } else { + familyCombo.getSelectionModel().clearSelection(); + } + + if (textColor != null) { + textColorPicker.setValue(textColor); + } + + backgroundColorPicker.setValue(backgroundColor); + }); + } + }); + VBox vbox = new VBox(); + VirtualizedScrollPane, TextStyle>> vsPane = new VirtualizedScrollPane<>(area); + VBox.setVgrow(vsPane, Priority.ALWAYS); + if (isSupportRichText()) { + ToolBar toolBar1 = new ToolBar( + loadBtn, saveBtn, new Separator(Orientation.VERTICAL), + wrapToggle, new Separator(Orientation.VERTICAL), + undoBtn, redoBtn, new Separator(Orientation.VERTICAL), + cutBtn, copyBtn, pasteBtn, new Separator(Orientation.VERTICAL), + boldBtn, italicBtn, underlineBtn, strikeBtn, new Separator(Orientation.VERTICAL), + alignLeftBtn, alignCenterBtn, alignRightBtn, alignJustifyBtn, new Separator(Orientation.VERTICAL), + insertImageBtn, new Separator(Orientation.VERTICAL), + paragraphBackgroundPicker); + ToolBar toolBar2 = new ToolBar(sizeCombo, familyCombo, textColorPicker, backgroundColorPicker); + vbox.getChildren().addAll(toolBar1, toolBar2, vsPane); + } else { + loadBtn.setDisable(true); +// saveBtn.setDisable(true); + ToolBar toolBar1 = new ToolBar( + loadBtn, saveBtn, new Separator(Orientation.VERTICAL), + wrapToggle, new Separator(Orientation.VERTICAL), + undoBtn, redoBtn, new Separator(Orientation.VERTICAL), + cutBtn, copyBtn, pasteBtn, new Separator(Orientation.VERTICAL)); + vbox.getChildren().addAll(toolBar1, vsPane); + } + Scene scene = new Scene(vbox, 800, 600); + mainPane = vbox; + shadowPane = null; + return scene; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + Button loadBtn = createButton("new", this::loadDocument, + "Load document."); + Button saveBtn = createButton("savefile", this::saveDocument, + "Save document"); + ToggleButton wrapToggle = createToggleButton(null, "wrap", null, "Wrap"); + wrapToggle.setSelected(true); + area.wrapTextProperty().bind(wrapToggle.selectedProperty()); + Button undoBtn = createButton("undo", area::undo, "Undo"); + Button redoBtn = createButton("redo", area::redo, "Redo"); + Button cutBtn = createButton("cut", area::cut, "Cut"); + Button copyBtn = createButton("copy", area::copy, "Copy"); + Button pasteBtn = createButton("paste", area::paste, "Paste"); + Button boldBtn = createButton("bold", this::toggleBold, "Bold"); + Button italicBtn = createButton("italic", this::toggleItalic, "Italic"); + Button underlineBtn = createButton("underline", this::toggleUnderline, "Underline"); + Button strikeBtn = createButton("strikethrough", this::toggleStrikethrough, "Strike Trough"); + Button insertImageBtn = createButton("insertimage", this::insertImage, "Insert Image"); + ToggleGroup alignmentGrp = new ToggleGroup(); + ToggleButton alignLeftBtn = createToggleButton(alignmentGrp, "align-left", this::alignLeft, "Align left"); + ToggleButton alignCenterBtn = createToggleButton(alignmentGrp, "align-center", this::alignCenter, "Align center"); + ToggleButton alignRightBtn = createToggleButton(alignmentGrp, "align-right", this::alignRight, "Align right"); + ToggleButton alignJustifyBtn = createToggleButton(alignmentGrp, "align-justify", this::alignJustify, "Justify"); + ColorPicker paragraphBackgroundPicker = new ColorPicker(); + ComboBox sizeCombo = new ComboBox<>(FXCollections.observableArrayList(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 48, 56, 64, 72)); + sizeCombo.getSelectionModel().select(Integer.valueOf(12)); + sizeCombo.setTooltip(new Tooltip("Font size")); + ComboBox familyCombo = new ComboBox<>(FXCollections.observableList(Font.getFamilies())); + familyCombo.getSelectionModel().select("Serif"); + familyCombo.setTooltip(new Tooltip("Font family")); + ColorPicker textColorPicker = new ColorPicker(Color.BLACK); + ColorPicker backgroundColorPicker = new ColorPicker(); + + paragraphBackgroundPicker.setTooltip(new Tooltip("Paragraph background")); + textColorPicker.setTooltip(new Tooltip("Text color")); + backgroundColorPicker.setTooltip(new Tooltip("Text background")); + + paragraphBackgroundPicker.valueProperty().addListener((o, old, color) -> updateParagraphBackground(color)); + sizeCombo.setOnAction(evt -> updateFontSize(sizeCombo.getValue())); + familyCombo.setOnAction(evt -> updateFontFamily(familyCombo.getValue())); + textColorPicker.valueProperty().addListener((o, old, color) -> updateTextColor(color)); + backgroundColorPicker.valueProperty().addListener((o, old, color) -> updateBackgroundColor(color)); + + undoBtn.disableProperty().bind(area.undoAvailableProperty().map(x -> !x)); + redoBtn.disableProperty().bind(area.redoAvailableProperty().map(x -> !x)); + + BooleanBinding selectionEmpty = new BooleanBinding() { + { + bind(area.selectionProperty()); + } + + @Override + protected boolean computeValue() { + return area.getSelection().getLength() == 0; + } + }; + + cutBtn.disableProperty().bind(selectionEmpty); + copyBtn.disableProperty().bind(selectionEmpty); + + area.beingUpdatedProperty().addListener((o, old, beingUpdated) -> { + if (!beingUpdated) { + boolean bold, italic, underline, strike; + Integer fontSize; + String fontFamily; + Color textColor; + Color backgroundColor; + + IndexRange selection = area.getSelection(); + if (selection.getLength() != 0) { + StyleSpans styles = area.getStyleSpans(selection); + bold = styles.styleStream().anyMatch(s -> s.bold.orElse(false)); + italic = styles.styleStream().anyMatch(s -> s.italic.orElse(false)); + underline = styles.styleStream().anyMatch(s -> s.underline.orElse(false)); + strike = styles.styleStream().anyMatch(s -> s.strikethrough.orElse(false)); + int[] sizes = styles.styleStream().mapToInt(s -> s.fontSize.orElse(-1)).distinct().toArray(); + fontSize = sizes.length == 1 ? sizes[0] : -1; + String[] families = styles.styleStream().map(s -> s.fontFamily.orElse(null)).distinct().toArray(String[]::new); + fontFamily = families.length == 1 ? families[0] : null; + Color[] colors = styles.styleStream().map(s -> s.textColor.orElse(null)).distinct().toArray(Color[]::new); + textColor = colors.length == 1 ? colors[0] : null; + Color[] backgrounds = styles.styleStream().map(s -> s.backgroundColor.orElse(null)).distinct().toArray(i -> new Color[i]); + backgroundColor = backgrounds.length == 1 ? backgrounds[0] : null; + } else { + int p = area.getCurrentParagraph(); + int col = area.getCaretColumn(); + TextStyle style = area.getStyleAtPosition(p, col); + bold = style.bold.orElse(false); + italic = style.italic.orElse(false); + underline = style.underline.orElse(false); + strike = style.strikethrough.orElse(false); + fontSize = style.fontSize.orElse(-1); + fontFamily = style.fontFamily.orElse(null); + textColor = style.textColor.orElse(null); + backgroundColor = style.backgroundColor.orElse(null); + } + + int startPar = area.offsetToPosition(selection.getStart(), TwoDimensional.Bias.Forward).getMajor(); + int endPar = area.offsetToPosition(selection.getEnd(), TwoDimensional.Bias.Backward).getMajor(); + List, TextStyle>> pars = area.getParagraphs().subList(startPar, endPar + 1); + + @SuppressWarnings("unchecked") + Optional[] alignments = pars.stream().map(p -> p.getParagraphStyle().alignment).distinct().toArray(Optional[]::new); + Optional alignment = alignments.length == 1 ? alignments[0] : Optional.empty(); + + @SuppressWarnings("unchecked") + Optional[] paragraphBackgrounds = pars.stream().map(p -> p.getParagraphStyle().backgroundColor).distinct().toArray(Optional[]::new); + Optional paragraphBackground = paragraphBackgrounds.length == 1 ? paragraphBackgrounds[0] : Optional.empty(); + + updatingToolbar.suspendWhile(() -> { + if (bold) { + if (!boldBtn.getStyleClass().contains("pressed")) { + boldBtn.getStyleClass().add("pressed"); + } + } else { + boldBtn.getStyleClass().remove("pressed"); + } + + if (italic) { + if (!italicBtn.getStyleClass().contains("pressed")) { + italicBtn.getStyleClass().add("pressed"); + } + } else { + italicBtn.getStyleClass().remove("pressed"); + } + + if (underline) { + if (!underlineBtn.getStyleClass().contains("pressed")) { + underlineBtn.getStyleClass().add("pressed"); + } + } else { + underlineBtn.getStyleClass().remove("pressed"); + } + + if (strike) { + if (!strikeBtn.getStyleClass().contains("pressed")) { + strikeBtn.getStyleClass().add("pressed"); + } + } else { + strikeBtn.getStyleClass().remove("pressed"); + } + + if (alignment.isPresent()) { + TextAlignment al = alignment.get(); + switch (al) { + case LEFT: + alignmentGrp.selectToggle(alignLeftBtn); + break; + case CENTER: + alignmentGrp.selectToggle(alignCenterBtn); + break; + case RIGHT: + alignmentGrp.selectToggle(alignRightBtn); + break; + case JUSTIFY: + alignmentGrp.selectToggle(alignJustifyBtn); + break; + } + } else { + alignmentGrp.selectToggle(null); + } + + paragraphBackgroundPicker.setValue(paragraphBackground.orElse(null)); + + if (fontSize != -1) { + sizeCombo.getSelectionModel().select(fontSize); + } else { + sizeCombo.getSelectionModel().clearSelection(); + } + + if (fontFamily != null) { + familyCombo.getSelectionModel().select(fontFamily); + } else { + familyCombo.getSelectionModel().clearSelection(); + } + + if (textColor != null) { + textColorPicker.setValue(textColor); + } + + backgroundColorPicker.setValue(backgroundColor); + }); + } + }); + VirtualizedScrollPane, TextStyle>> vsPane = new VirtualizedScrollPane<>(area); + VBox.setVgrow(vsPane, Priority.ALWAYS); + if (isSupportRichText()) { + ToolBar toolBar1 = new ToolBar( + loadBtn, saveBtn, new Separator(Orientation.VERTICAL), + wrapToggle, new Separator(Orientation.VERTICAL), + undoBtn, redoBtn, new Separator(Orientation.VERTICAL), + cutBtn, copyBtn, pasteBtn, new Separator(Orientation.VERTICAL), + boldBtn, italicBtn, underlineBtn, strikeBtn, new Separator(Orientation.VERTICAL), + alignLeftBtn, alignCenterBtn, alignRightBtn, alignJustifyBtn, new Separator(Orientation.VERTICAL), + insertImageBtn, new Separator(Orientation.VERTICAL), + paragraphBackgroundPicker); + ToolBar toolBar2 = new ToolBar(sizeCombo, familyCombo, textColorPicker, backgroundColorPicker); + vbox.getChildren().addAll(toolBar1, toolBar2, vsPane); + } else { + loadBtn.setDisable(true); +// saveBtn.setDisable(true); + ToolBar toolBar1 = new ToolBar( + loadBtn, saveBtn, new Separator(Orientation.VERTICAL), + wrapToggle, new Separator(Orientation.VERTICAL), + undoBtn, redoBtn, new Separator(Orientation.VERTICAL), + cutBtn, copyBtn, pasteBtn, new Separator(Orientation.VERTICAL)); + vbox.getChildren().addAll(toolBar1, vsPane); + } + } + + private void reloadContent() { + if (localFile != null) { + Alert alert = new Alert( + Alert.AlertType.INFORMATION, + "Operation in progress", + ButtonType.CANCEL + ); + alert.setTitle("Load File"); + alert.setHeaderText("Please wait... "); + ProgressIndicator progressIndicator = new ProgressIndicator(); + alert.setGraphic(progressIndicator); + + Task task = new Task() { + final int N_ITERATIONS = 999; + + { + setOnFailed(a -> { + alert.close(); + updateMessage("Failed"); + close(); + showParent(); + }); + setOnSucceeded(a -> { + alert.close(); + updateMessage("Succeeded"); + }); + setOnCancelled(a -> { + alert.close(); + updateMessage("Cancelled"); + close(); + showParent(); + }); + } + + @Override + protected Void call() throws Exception { + updateMessage("Processing"); + updateProgress(100, N_ITERATIONS); + File fileCache = LocalFileHelpers.getSourceFile(localAccount, localFile); + if (fileCache == null) { + try { + fileCache = LocalFileHelpers.createFileCache(localAccount, localFile); + //need download + DownloadParameter parameter = LocalFileHelpers.createDownloadParameter(localFile); + Downloader download = new Downloader(localAccount.connectionConfig); + DownloadResult downloadResult = download.download(parameter); + InputStream byteStream = downloadResult.getData().getByteStream(); + FileOutputStream fouts = new FileOutputStream(fileCache); + byte[] buffer = new byte[1024]; + int read = 0; + long sum = 0; + while ((read = byteStream.read(buffer)) >= 0) { + fouts.write(buffer, 0, read); + sum += read; + updateProgress(100 + 800 * sum / localFile.fileSize, N_ITERATIONS); + } + } catch (Exception ex) { + ex.printStackTrace(); + failed(); + } + } + final File file = fileCache; + IApp.runSafe(() -> { + try { + updateProgress(N_ITERATIONS, N_ITERATIONS); + load(file); + } catch (Exception ex) { + } + }); + if (!isCancelled()) { + updateProgress(0, N_ITERATIONS); + } + + return null; + } + }; + progressIndicator.progressProperty().bind(task.progressProperty()); + Thread taskThread = new Thread( + task, + "proxipad-thread-" + taskExecution.getAndIncrement() + ); + taskThread.start(); + alert.initOwner(primaryStage); + Optional result = alert.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.CANCEL && task.isRunning()) { + task.cancel(); + } + } + } + + AtomicInteger taskExecution = new AtomicInteger(0); + + private void saveContent() { + Alert alert = new Alert( + Alert.AlertType.INFORMATION, + "Operation in progress", + ButtonType.CANCEL + ); + alert.setTitle("Save File"); + alert.setHeaderText("Please wait... "); + ProgressIndicator progressIndicator = new ProgressIndicator(); + alert.setGraphic(progressIndicator); + + Task task = new Task() { + final int N_ITERATIONS = 6; + + { + setOnFailed(a -> { + alert.close(); + updateMessage("Failed"); + }); + setOnSucceeded(a -> { + alert.close(); + updateMessage("Succeeded"); + close(); + showParent(); + }); + setOnCancelled(a -> { + alert.close(); + updateMessage("Cancelled"); + }); + } + + @Override + protected Void call() throws Exception { + updateMessage("Processing"); + updateProgress(1, N_ITERATIONS); + File file = new File(System.getProperty("java.io.tmpdir") + "/" + localFile.fileName); + save(file); + updateProgress(2, N_ITERATIONS); + LocalFile saveFile = new LocalFile(localFile); + saveFile.modified = file.lastModified(); + try { + updateProgress(3, N_ITERATIONS); + Uploader upload = new Uploader(localAccount.connectionConfig); + UploadParameter parameter = LocalFileHelpers.createUploadFileParameter(localAccount, saveFile, file); + UploadResult uploadResult = upload.upload(parameter); + updateProgress(4, N_ITERATIONS); + saveFile.uploadDate = System.currentTimeMillis(); + saveFile.hash = uploadResult.getData().getDataHash(); + saveFile.nemHash = uploadResult.getTransactionHash(); + saveFile.fileSize = file.length(); + saveFile.modified = file.lastModified(); + LocalFileHelpers.updateFile(localAccount.fullName, localAccount.network, localFile, saveFile); + updateProgress(5, N_ITERATIONS); + } catch (Exception ex) { + failed(); + } + if (!isCancelled()) { + updateProgress(0, N_ITERATIONS); + } + return null; + } + }; + progressIndicator.progressProperty().bind(task.progressProperty()); + Thread taskThread = new Thread( + task, + "task-thread-" + taskExecution.getAndIncrement() + ); + taskThread.start(); + alert.initOwner(primaryStage); + Optional result = alert.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.CANCEL && task.isRunning()) { + task.cancel(); + } + } + + private Node createNode(StyledSegment, TextStyle> seg, + BiConsumer applyStyle) { + return seg.getSegment().unify( + text -> StyledTextArea.createStyledTextNode(text, seg.getStyle(), applyStyle), + LinkedImage::createNode + ); + } + + @Deprecated + private Button createButton(String styleClass, Runnable action) { + return createButton(styleClass, action, null); + } + + private Button createButton(String styleClass, Runnable action, String toolTip) { + Button button = new Button(); + button.getStyleClass().add(styleClass); + button.setOnAction(evt -> { + action.run(); + area.requestFocus(); + }); + button.setPrefWidth(25); + button.setPrefHeight(25); + if (toolTip != null) { + button.setTooltip(new Tooltip(toolTip)); + } + return button; + } + + private ToggleButton createToggleButton(ToggleGroup grp, String styleClass, Runnable action, String toolTip) { + ToggleButton button = new ToggleButton(); + if (grp != null) { + button.setToggleGroup(grp); + } + button.getStyleClass().add(styleClass); + if (action != null) { + button.setOnAction(evt -> { + action.run(); + area.requestFocus(); + }); + } + button.setPrefWidth(25); + button.setPrefHeight(25); + if (toolTip != null) { + button.setTooltip(new Tooltip(toolTip)); + } + return button; + } + + private void toggleBold() { + updateStyleInSelection(spans -> TextStyle.bold(!spans.styleStream().allMatch(style -> style.bold.orElse(false)))); + } + + private void toggleItalic() { + updateStyleInSelection(spans -> TextStyle.italic(!spans.styleStream().allMatch(style -> style.italic.orElse(false)))); + } + + private void toggleUnderline() { + updateStyleInSelection(spans -> TextStyle.underline(!spans.styleStream().allMatch(style -> style.underline.orElse(false)))); + } + + private void toggleStrikethrough() { + updateStyleInSelection(spans -> TextStyle.strikethrough(!spans.styleStream().allMatch(style -> style.strikethrough.orElse(false)))); + } + + private void alignLeft() { + updateParagraphStyleInSelection(ParStyle.alignLeft()); + } + + private void alignCenter() { + updateParagraphStyleInSelection(ParStyle.alignCenter()); + } + + private void alignRight() { + updateParagraphStyleInSelection(ParStyle.alignRight()); + } + + private void alignJustify() { + updateParagraphStyleInSelection(ParStyle.alignJustify()); + } + + private void loadDocument() { + String initialDir = System.getProperty("user.dir"); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Load document"); + fileChooser.setInitialDirectory(new File(initialDir)); +// fileChooser.setSelectedExtensionFilter( +// new FileChooser.ExtensionFilter("Arbitrary RTFX file", "*" + RTFX_FILE_EXTENSION)); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("TXT files (*.txt)", "*" + TXT_FILE_EXTENSION)); + if (isSupportRichText()) { + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Arbitrary RTFX file", "*" + RTFX_FILE_EXTENSION)); + } + File selectedFile = fileChooser.showOpenDialog(primaryStage); + if (selectedFile != null) { + area.clear(); + load(selectedFile); + } + } + + public void load(File file) { + if (area.getStyleCodecs().isPresent()) { + if (!isSupportRichText() || !file.getName().endsWith(RTFX_FILE_EXTENSION)) { + // Write the content to the file + try { + FileInputStream fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis); + // Read the file, and set its contents within the editor + StringBuffer sb = new StringBuffer(); + while (bis.available() > 0) { + sb.append((char) bis.read()); + } + area.replaceSelection(sb.toString()); + } catch (Exception e) { + } + } else { + Tuple2, Codec, TextStyle>>> codecs = area.getStyleCodecs().get(); + Codec, TextStyle>> codec = ReadOnlyStyledDocument.codec(codecs._1, codecs._2, area.getSegOps()); + try { + + FileInputStream fis = new FileInputStream(file); + DataInputStream dis = new DataInputStream(fis); + StyledDocument, TextStyle> doc = codec.decode(dis); + fis.close(); + + if (doc != null) { + area.replaceSelection(doc); + return; + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private void saveDocument() { + if (localFile != null) { + saveContent(); + } else { + String initialDir = System.getProperty("user.dir"); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save document"); + fileChooser.setInitialDirectory(new File(initialDir)); + //fileChooser.setInitialFileName("example rtfx file" + RTFX_FILE_EXTENSION); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("TXT files (*.txt)", "*" + TXT_FILE_EXTENSION)); + if (isSupportRichText()) { + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Arbitrary RTFX file", "*" + RTFX_FILE_EXTENSION)); + } + fileChooser.setInitialFileName("proxibox" + TXT_FILE_EXTENSION); + File selectedFile = fileChooser.showSaveDialog(primaryStage); + if (selectedFile != null) { + save(selectedFile); + } + } + } + + private void save(File file) { + // Write the content to the file + if (!isSupportRichText() || !file.getName().endsWith(RTFX_FILE_EXTENSION)) { + try { + FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + String text = area.getText(); + bos.write(text.getBytes()); + bos.flush(); + bos.close(); + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + StyledDocument, TextStyle> doc = area.getDocument(); + + // Use the Codec to save the document in a binary format + area.getStyleCodecs().ifPresent(codecs -> { + Codec, TextStyle>> codec + = ReadOnlyStyledDocument.codec(codecs._1, codecs._2, area.getSegOps()); + try { + FileOutputStream fos = new FileOutputStream(file); + DataOutputStream dos = new DataOutputStream(fos); + codec.encode(dos, doc); + fos.close(); + } catch (IOException fnfe) { + fnfe.printStackTrace(); + } + }); + } + } + + /** + * Action listener which inserts a new image at the current caret position. + */ + private void insertImage() { + String initialDir = System.getProperty("user.dir"); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Insert image"); + fileChooser.setInitialDirectory(new File(initialDir)); + File selectedFile = fileChooser.showOpenDialog(primaryStage); + if (selectedFile != null) { + String imagePath = selectedFile.getAbsolutePath(); + imagePath = imagePath.replace('\\', '/'); + ReadOnlyStyledDocument, TextStyle> ros + = ReadOnlyStyledDocument.fromSegment(Either.right(new RealLinkedImage(imagePath)), + ParStyle.EMPTY, TextStyle.EMPTY, area.getSegOps()); + area.replaceSelection(ros); + } + } + + private void updateStyleInSelection(Function, TextStyle> mixinGetter) { + IndexRange selection = area.getSelection(); + if (selection.getLength() != 0) { + StyleSpans styles = area.getStyleSpans(selection); + TextStyle mixin = mixinGetter.apply(styles); + StyleSpans newStyles = styles.mapStyles(style -> style.updateWith(mixin)); + area.setStyleSpans(selection.getStart(), newStyles); + } + } + + private void updateStyleInSelection(TextStyle mixin) { + IndexRange selection = area.getSelection(); + if (selection.getLength() != 0) { + StyleSpans styles = area.getStyleSpans(selection); + StyleSpans newStyles = styles.mapStyles(style -> style.updateWith(mixin)); + area.setStyleSpans(selection.getStart(), newStyles); + } + } + + private void updateParagraphStyleInSelection(Function updater) { + IndexRange selection = area.getSelection(); + int startPar = area.offsetToPosition(selection.getStart(), TwoDimensional.Bias.Forward).getMajor(); + int endPar = area.offsetToPosition(selection.getEnd(), TwoDimensional.Bias.Backward).getMajor(); + for (int i = startPar; i <= endPar; ++i) { + Paragraph, TextStyle> paragraph = area.getParagraph(i); + area.setParagraphStyle(i, updater.apply(paragraph.getParagraphStyle())); + } + } + + private void updateParagraphStyleInSelection(ParStyle mixin) { + updateParagraphStyleInSelection(style -> style.updateWith(mixin)); + } + + private void updateFontSize(Integer size) { + if (!updatingToolbar.get()) { + updateStyleInSelection(TextStyle.fontSize(size)); + } + } + + private void updateFontFamily(String family) { + if (!updatingToolbar.get()) { + updateStyleInSelection(TextStyle.fontFamily(family)); + } + } + + private void updateTextColor(Color color) { + if (!updatingToolbar.get()) { + updateStyleInSelection(TextStyle.textColor(color)); + } + } + + private void updateBackgroundColor(Color color) { + if (!updatingToolbar.get()) { + updateStyleInSelection(TextStyle.backgroundColor(color)); + } + } + + private void updateParagraphBackground(Color color) { + if (!updatingToolbar.get()) { + updateParagraphStyleInSelection(ParStyle.backgroundColor(color)); + } + } + + public boolean isSupportRichText() { + return isRichText; + } + + @Override + protected void dispose() { + area.dispose(); + + } + + @Override + public String getTitle() { + return CONST.PROXIPAD_TITLE; + } + + @Override + public String getFXML() { + return CONST.PROXIPAD_FXML; + } + +} diff --git a/src/main/java/io/proximax/app/fx/control/text/TextStyle.java b/src/main/java/io/proximax/app/fx/control/text/TextStyle.java new file mode 100644 index 0000000..0cd5b30 --- /dev/null +++ b/src/main/java/io/proximax/app/fx/control/text/TextStyle.java @@ -0,0 +1,324 @@ +package io.proximax.app.fx.control.text; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.MalformedInputException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import javafx.scene.paint.Color; + +import org.fxmisc.richtext.model.Codec; + +/** + * Holds information about the style of a text fragment. + */ +class TextStyle { + + public static final TextStyle EMPTY = new TextStyle(); + + public static final Codec CODEC = new Codec() { + + private final Codec> OPT_STRING_CODEC + = Codec.optionalCodec(Codec.STRING_CODEC); + private final Codec> OPT_COLOR_CODEC + = Codec.optionalCodec(Codec.COLOR_CODEC); + + @Override + public String getName() { + return "text-style"; + } + + @Override + public void encode(DataOutputStream os, TextStyle s) + throws IOException { + os.writeByte(encodeBoldItalicUnderlineStrikethrough(s)); + os.writeInt(encodeOptionalUint(s.fontSize)); + OPT_STRING_CODEC.encode(os, s.fontFamily); + OPT_COLOR_CODEC.encode(os, s.textColor); + OPT_COLOR_CODEC.encode(os, s.backgroundColor); + } + + @Override + public TextStyle decode(DataInputStream is) throws IOException { + byte bius = is.readByte(); + Optional fontSize = decodeOptionalUint(is.readInt()); + Optional fontFamily = OPT_STRING_CODEC.decode(is); + Optional textColor = OPT_COLOR_CODEC.decode(is); + Optional bgrColor = OPT_COLOR_CODEC.decode(is); + return new TextStyle( + bold(bius), italic(bius), underline(bius), strikethrough(bius), + fontSize, fontFamily, textColor, bgrColor); + } + + private int encodeBoldItalicUnderlineStrikethrough(TextStyle s) { + return encodeOptionalBoolean(s.bold) << 6 + | encodeOptionalBoolean(s.italic) << 4 + | encodeOptionalBoolean(s.underline) << 2 + | encodeOptionalBoolean(s.strikethrough); + } + + private Optional bold(byte bius) throws IOException { + return decodeOptionalBoolean((bius >> 6) & 3); + } + + private Optional italic(byte bius) throws IOException { + return decodeOptionalBoolean((bius >> 4) & 3); + } + + private Optional underline(byte bius) throws IOException { + return decodeOptionalBoolean((bius >> 2) & 3); + } + + private Optional strikethrough(byte bius) throws IOException { + return decodeOptionalBoolean((bius >> 0) & 3); + } + + private int encodeOptionalBoolean(Optional ob) { + return ob.map(b -> 2 + (b ? 1 : 0)).orElse(0); + } + + private Optional decodeOptionalBoolean(int i) throws IOException { + switch (i) { + case 0: + return Optional.empty(); + case 2: + return Optional.of(false); + case 3: + return Optional.of(true); + } + throw new MalformedInputException(0); + } + + private int encodeOptionalUint(Optional oi) { + return oi.orElse(-1); + } + + private Optional decodeOptionalUint(int i) { + return (i < 0) ? Optional.empty() : Optional.of(i); + } + }; + + public static TextStyle bold(boolean bold) { + return EMPTY.updateBold(bold); + } + + public static TextStyle italic(boolean italic) { + return EMPTY.updateItalic(italic); + } + + public static TextStyle underline(boolean underline) { + return EMPTY.updateUnderline(underline); + } + + public static TextStyle strikethrough(boolean strikethrough) { + return EMPTY.updateStrikethrough(strikethrough); + } + + public static TextStyle fontSize(int fontSize) { + return EMPTY.updateFontSize(fontSize); + } + + public static TextStyle fontFamily(String family) { + return EMPTY.updateFontFamily(family); + } + + public static TextStyle textColor(Color color) { + return EMPTY.updateTextColor(color); + } + + public static TextStyle backgroundColor(Color color) { + return EMPTY.updateBackgroundColor(color); + } + + static String cssColor(Color color) { + int red = (int) (color.getRed() * 255); + int green = (int) (color.getGreen() * 255); + int blue = (int) (color.getBlue() * 255); + return "rgb(" + red + ", " + green + ", " + blue + ")"; + } + + final Optional bold; + final Optional italic; + final Optional underline; + final Optional strikethrough; + final Optional fontSize; + final Optional fontFamily; + final Optional textColor; + final Optional backgroundColor; + + public TextStyle() { + this( + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty() + ); + } + + public TextStyle( + Optional bold, + Optional italic, + Optional underline, + Optional strikethrough, + Optional fontSize, + Optional fontFamily, + Optional textColor, + Optional backgroundColor) { + this.bold = bold; + this.italic = italic; + this.underline = underline; + this.strikethrough = strikethrough; + this.fontSize = fontSize; + this.fontFamily = fontFamily; + this.textColor = textColor; + this.backgroundColor = backgroundColor; + } + + @Override + public int hashCode() { + return Objects.hash( + bold, italic, underline, strikethrough, + fontSize, fontFamily, textColor, backgroundColor); + } + + @Override + public boolean equals(Object other) { + if (other instanceof TextStyle) { + TextStyle that = (TextStyle) other; + return Objects.equals(this.bold, that.bold) + && Objects.equals(this.italic, that.italic) + && Objects.equals(this.underline, that.underline) + && Objects.equals(this.strikethrough, that.strikethrough) + && Objects.equals(this.fontSize, that.fontSize) + && Objects.equals(this.fontFamily, that.fontFamily) + && Objects.equals(this.textColor, that.textColor) + && Objects.equals(this.backgroundColor, that.backgroundColor); + } else { + return false; + } + } + + @Override + public String toString() { + List styles = new ArrayList<>(); + + bold.ifPresent(b -> styles.add(b.toString())); + italic.ifPresent(i -> styles.add(i.toString())); + underline.ifPresent(u -> styles.add(u.toString())); + strikethrough.ifPresent(s -> styles.add(s.toString())); + fontSize.ifPresent(s -> styles.add(s.toString())); + fontFamily.ifPresent(f -> styles.add(f.toString())); + textColor.ifPresent(c -> styles.add(c.toString())); + backgroundColor.ifPresent(b -> styles.add(b.toString())); + + return String.join(",", styles); + } + + public String toCss() { + StringBuilder sb = new StringBuilder(); + + if (bold.isPresent()) { + if (bold.get()) { + sb.append("-fx-font-weight: bold;"); + } else { + sb.append("-fx-font-weight: normal;"); + } + } + + if (italic.isPresent()) { + if (italic.get()) { + sb.append("-fx-font-style: italic;"); + } else { + sb.append("-fx-font-style: normal;"); + } + } + + if (underline.isPresent()) { + if (underline.get()) { + sb.append("-fx-underline: true;"); + } else { + sb.append("-fx-underline: false;"); + } + } + + if (strikethrough.isPresent()) { + if (strikethrough.get()) { + sb.append("-fx-strikethrough: true;"); + } else { + sb.append("-fx-strikethrough: false;"); + } + } + + if (fontSize.isPresent()) { + sb.append("-fx-font-size: " + fontSize.get() + "pt;"); + } + + if (fontFamily.isPresent()) { + sb.append("-fx-font-family: " + fontFamily.get() + ";"); + } + + if (textColor.isPresent()) { + Color color = textColor.get(); + sb.append("-fx-fill: " + cssColor(color) + ";"); + } + + if (backgroundColor.isPresent()) { + Color color = backgroundColor.get(); + sb.append("-rtfx-background-color: " + cssColor(color) + ";"); + } + + return sb.toString(); + } + + public TextStyle updateWith(TextStyle mixin) { + return new TextStyle( + mixin.bold.isPresent() ? mixin.bold : bold, + mixin.italic.isPresent() ? mixin.italic : italic, + mixin.underline.isPresent() ? mixin.underline : underline, + mixin.strikethrough.isPresent() ? mixin.strikethrough : strikethrough, + mixin.fontSize.isPresent() ? mixin.fontSize : fontSize, + mixin.fontFamily.isPresent() ? mixin.fontFamily : fontFamily, + mixin.textColor.isPresent() ? mixin.textColor : textColor, + mixin.backgroundColor.isPresent() ? mixin.backgroundColor : backgroundColor); + } + + public TextStyle updateBold(boolean bold) { + return new TextStyle(Optional.of(bold), italic, underline, strikethrough, fontSize, fontFamily, textColor, backgroundColor); + } + + public TextStyle updateItalic(boolean italic) { + return new TextStyle(bold, Optional.of(italic), underline, strikethrough, fontSize, fontFamily, textColor, backgroundColor); + } + + public TextStyle updateUnderline(boolean underline) { + return new TextStyle(bold, italic, Optional.of(underline), strikethrough, fontSize, fontFamily, textColor, backgroundColor); + } + + public TextStyle updateStrikethrough(boolean strikethrough) { + return new TextStyle(bold, italic, underline, Optional.of(strikethrough), fontSize, fontFamily, textColor, backgroundColor); + } + + public TextStyle updateFontSize(int fontSize) { + return new TextStyle(bold, italic, underline, strikethrough, Optional.of(fontSize), fontFamily, textColor, backgroundColor); + } + + public TextStyle updateFontFamily(String fontFamily) { + return new TextStyle(bold, italic, underline, strikethrough, fontSize, Optional.of(fontFamily), textColor, backgroundColor); + } + + public TextStyle updateTextColor(Color textColor) { + return new TextStyle(bold, italic, underline, strikethrough, fontSize, fontFamily, Optional.of(textColor), backgroundColor); + } + + public TextStyle updateBackgroundColor(Color backgroundColor) { + return new TextStyle(bold, italic, underline, strikethrough, fontSize, fontFamily, textColor, Optional.of(backgroundColor)); + } +} diff --git a/src/main/java/io/proximax/app/main/FileIt.java b/src/main/java/io/proximax/app/main/FileIt.java new file mode 100644 index 0000000..2a5dc5c --- /dev/null +++ b/src/main/java/io/proximax/app/main/FileIt.java @@ -0,0 +1,102 @@ +package io.proximax.app.main; + +import io.proximax.app.controller.LoginDialog; +import io.proximax.app.core.ui.IApp; +import io.proximax.app.utils.AccountHelpers; +import io.proximax.app.utils.CONST; +import io.proximax.app.utils.StringUtils; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import javafx.application.Application; +import javafx.scene.image.Image; +import javafx.stage.Stage; + +public class FileIt extends Application implements IApp { + + private Stage primaryStage = null; + private int theme = 0; + private Map caches = new HashMap<>(); + + public Stage getPrimaryStage() { + return primaryStage; + } + + public void setTheme(int theme) { + this.theme = theme; + } + + public Image getIcon() { + return new Image(getClass().getResourceAsStream(String.format(CONST.IMAGE_PATH, getCurrentTheme()) + CONST.PROXIBOX_ICON)); + } + + public Image getImageFromResource(String resUrl) { + return new Image(getClass().getResourceAsStream(String.format(CONST.IMAGE_PATH, getCurrentTheme()) + resUrl)); + } + + public Image getImageFromResource(String resUrl, double w, double h) { + return new Image(getClass().getResourceAsStream(String.format(CONST.IMAGE_PATH, getCurrentTheme()) + resUrl), w, h, true, true); + } + + @Override + public String getCurrentTheme() { + return CONST.THEMES[theme]; + } + + @Override + public String getCurrentThemeUrl() { + return getClass().getResource(String.format(CONST.CSS_THEME, getCurrentTheme())).toExternalForm(); + } + + @Override + public String getThemeUrl(int i) { + return getClass().getResource(String.format(CONST.CSS_THEME, CONST.THEMES[i])).toExternalForm(); + } + + @Override + public void start(Stage primaryStage) throws InterruptedException { + try { + CONST.IAPP = this; + this.primaryStage = primaryStage; + AccountHelpers.initUserHome(); + Thread.sleep(10000); + primaryStage.getIcons().add(getIcon()); + LoginDialog.showDialog(primaryStage, this); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + launch(args); + } + + @Override + public void dispose() { + } + + public String getString(String key) { + return (String) caches.get(key); + } + + public void putString(String key, String val) { + caches.put(key, val); + } + + public String getCurrentDir() { + String dir = (String) caches.get("latest.dir"); + if (StringUtils.isEmpty(dir)) { + dir = System.getProperty("user.home"); + } + return dir; + } + + public File getCurrentFolder() { + return new File(getCurrentDir()); + } + + public void saveCurrentDir(String sDir) { + caches.put("latest.dir", sDir); + } + +} diff --git a/src/main/java/io/proximax/app/recovery/AccountInfo.java b/src/main/java/io/proximax/app/recovery/AccountInfo.java new file mode 100644 index 0000000..c52ee20 --- /dev/null +++ b/src/main/java/io/proximax/app/recovery/AccountInfo.java @@ -0,0 +1,131 @@ +package io.proximax.app.recovery; + +/** + * + * @author thcao + */ +public class AccountInfo { + + private String userName = null; + private String email = null; + private String question1 = null; + private String answer1 = null; + private String question2 = null; + private String answer2 = null; + private String question3 = null; + private String answer3 = null; + private String privateKey = null; + private String publicKey = null; + private String address = null; + + AccountInfo() { + } + + public AccountInfo(String userName, String email, String question1, String answer1, String question2, String answer2, String question3, String answer3, String privateKey, String publicKey, String address) { + this.userName = userName; + this.email = email; + this.question1 = question1; + this.question2 = question2; + this.question3 = question3; + this.address = address; + this.answer1 = answer1; + this.answer2 = answer2; + this.answer3 = answer3; + this.privateKey = privateKey; + this.publicKey = publicKey; + this.address = address; + } + + public void setAddress(String address) { + this.address = address; + } + + public void setAnswer1(String answer1) { + this.answer1 = answer1; + } + + public void setAnswer2(String answer2) { + this.answer2 = answer2; + } + + public void setAnswer3(String answer3) { + this.answer3 = answer3; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public void setQuestion1(String question1) { + this.question1 = question1; + } + + public String getAddress() { + return address; + } + + public void setQuestion2(String question2) { + this.question2 = question2; + } + + public void setQuestion3(String question3) { + this.question3 = question3; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getAnswer1() { + return answer1; + } + + public String getAnswer2() { + return answer2; + } + + public String getAnswer3() { + return answer3; + } + + public String getEmail() { + return email; + } + + public String getPrivateKey() { + return privateKey; + } + + public String getPublicKey() { + return publicKey; + } + + public String getQuestion1() { + return question1; + } + + public String getQuestion2() { + return question2; + } + + public String getQuestion3() { + return question3; + } + + public String getUserName() { + return userName; + } + + public boolean compare(String email, String privateKey, String publicKey, String address) { + return this.email.equals(email) && this.privateKey.equals(privateKey) && this.publicKey.equals(publicKey) && this.address.equals(address); + } + +} diff --git a/src/main/java/io/proximax/app/recovery/AccountInfoDTO.java b/src/main/java/io/proximax/app/recovery/AccountInfoDTO.java new file mode 100644 index 0000000..1f17741 --- /dev/null +++ b/src/main/java/io/proximax/app/recovery/AccountInfoDTO.java @@ -0,0 +1,149 @@ +package io.proximax.app.recovery; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; + +/** + * + * @author thcao + */ +@javax.annotation.Generated(value = "io.swagger.codegen.languages.JavaClientCodegen", date = "2018-03-08T14:38:43.120Z") +public class AccountInfoDTO { + + @SerializedName("userName") + private String userName = null; + + @SerializedName("email") + private String email = null; + + @SerializedName("question1") + private String question1 = null; + + @SerializedName("answer1") + private String answer1 = null; + + @SerializedName("question2") + private String question2 = null; + + @SerializedName("answer2") + private String answer2 = null; + + @SerializedName("question3") + private String question3 = null; + + @SerializedName("answer3") + private String answer3 = null; + + @SerializedName("privateKey") + private String privateKey = null; + + @SerializedName("publicKey") + private String publicKey = null; + + @SerializedName("address") + private String address = null; + + @ApiModelProperty(value = "") + public String getUserName() { + return userName; + } + + @ApiModelProperty(value = "") + public String getEmail() { + return email; + } + + @ApiModelProperty(value = "") + public String getQuestion1() { + return question1; + } + + @ApiModelProperty(value = "") + public String getAnswer1() { + return answer1; + } + + @ApiModelProperty(value = "") + public String getQuestion2() { + return question2; + } + + @ApiModelProperty(value = "") + public String getAnswer2() { + return answer2; + } + + @ApiModelProperty(value = "") + public String getQuestion3() { + return question3; + } + + @ApiModelProperty(value = "") + public String getAnswer3() { + return answer3; + } + + @ApiModelProperty(value = "") + public String getAddress() { + return address; + } + + @ApiModelProperty(value = "") + public String getPrivateKey() { + return privateKey; + } + + @ApiModelProperty(value = "") + public String getPublicKey() { + return publicKey; + } + + public void setAnswer1(String answer1) { + this.answer1 = answer1; + } + + public void setAnswer2(String answer2) { + this.answer2 = answer2; + } + + public void setAnswer3(String answer3) { + this.answer3 = answer3; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setQuestion1(String question1) { + this.question1 = question1; + } + + public void setQuestion2(String question2) { + this.question2 = question2; + } + + public void setQuestion3(String question3) { + this.question3 = question3; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setAddress(String address) { + this.address = address; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + @ApiModelProperty(required = true, value = "") + public AccountInfo getAccountInfo() { + return new AccountInfo(userName, email, question1, answer1, question2, answer2, question3, answer3, privateKey, publicKey, address); + } +} diff --git a/src/main/java/io/proximax/app/recovery/AccountStatus.java b/src/main/java/io/proximax/app/recovery/AccountStatus.java new file mode 100644 index 0000000..67cc850 --- /dev/null +++ b/src/main/java/io/proximax/app/recovery/AccountStatus.java @@ -0,0 +1,33 @@ +package io.proximax.app.recovery; + +/** + * + * @author thcao + */ +public class AccountStatus { + + private String status = null; + private String message = null; + + public AccountStatus(String status, String message) { + this.message = message; + this.status = status; + } + + public String getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setStatus(String status) { + this.status = status; + } + +} diff --git a/src/main/java/io/proximax/app/recovery/AccountStatusDTO.java b/src/main/java/io/proximax/app/recovery/AccountStatusDTO.java new file mode 100644 index 0000000..9d663ad --- /dev/null +++ b/src/main/java/io/proximax/app/recovery/AccountStatusDTO.java @@ -0,0 +1,41 @@ +package io.proximax.app.recovery; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; + +/** + * + * @author thcao + */ +@javax.annotation.Generated(value = "io.swagger.codegen.languages.JavaClientCodegen", date = "2018-03-08T14:38:43.120Z") +public class AccountStatusDTO { + + @SerializedName("status") + private String status = null; + + @SerializedName("message") + private String message = null; + + @ApiModelProperty(value = "") + public String getStatus() { + return status; + } + + @ApiModelProperty(value = "") + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setStatus(String status) { + this.status = status; + } + + public AccountStatus getAccountStatus() { + return new AccountStatus(status, message); + } + +} diff --git a/src/main/java/io/proximax/app/recovery/ProxiLicenseHttp.java b/src/main/java/io/proximax/app/recovery/ProxiLicenseHttp.java new file mode 100644 index 0000000..792e699 --- /dev/null +++ b/src/main/java/io/proximax/app/recovery/ProxiLicenseHttp.java @@ -0,0 +1,70 @@ +package io.proximax.app.recovery; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.reactivex.Observable; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.web.client.HttpResponse; +import io.vertx.reactivex.ext.web.client.WebClient; +import io.vertx.reactivex.ext.web.codec.BodyCodec; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * + * @author thcao + */ +public class ProxiLicenseHttp { + + protected final WebClient client; + protected final URL url; + protected final ObjectMapper objectMapper = new ObjectMapper(); + + public ProxiLicenseHttp(String host) throws MalformedURLException { + this.url = new URL(host); + final Vertx vertx = Vertx.vertx(); + this.client = WebClient.create(vertx); + objectMapper.configure(DeserializationFeature.USE_LONG_FOR_INTS, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + static JsonObject mapJsonObjectOrError(final HttpResponse response) { + if (response.statusCode() < 200 || response.statusCode() > 299) { + throw new RuntimeException(response.statusMessage()); + } + return response.body(); + } + + static JsonArray mapJsonArrayOrError(final HttpResponse response) { + if (response.statusCode() < 200 || response.statusCode() > 299) { + throw new RuntimeException(response.statusMessage()); + } + return response.body(); + } + + public Observable getAccountInfo(String email) { + return this.client + .getAbs(this.url + "/api/recovery/get?email=" + email) + .as(BodyCodec.jsonObject()) + .rxSend() + .toObservable() + .map(ProxiLicenseHttp::mapJsonObjectOrError) + .map(json -> objectMapper.readValue(json.toString(), AccountInfoDTO.class)) + .map(AccountInfoDTO::getAccountInfo); + } + + public Observable saveAccountInfo(AccountInfo account) { + JsonObject requestBody = JsonObject.mapFrom(account); + return this.client + .postAbs(this.url + "/api/recovery/save") + .as(BodyCodec.jsonObject()) + .rxSendJson(requestBody) + .toObservable() + .map(ProxiLicenseHttp::mapJsonObjectOrError) + .map(json -> objectMapper.readValue(json.toString(), AccountStatusDTO.class)) + .map(AccountStatusDTO::getAccountStatus); + } + +} diff --git a/src/main/java/io/proximax/app/utils/AccountHelpers.java b/src/main/java/io/proximax/app/utils/AccountHelpers.java new file mode 100644 index 0000000..0a24f05 --- /dev/null +++ b/src/main/java/io/proximax/app/utils/AccountHelpers.java @@ -0,0 +1,357 @@ +package io.proximax.app.utils; + +import java.io.File; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import io.proximax.sdk.model.account.Account; +import io.proximax.sdk.model.account.Address; +import io.proximax.connection.ConnectionConfig; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.recovery.AccountInfo; +import io.proximax.utils.NemUtils; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; + +public class AccountHelpers { + + public static boolean isExistAccount(String fullName, String network) { + String fileName = System.getProperty("user.home") + "/" + CONST.APP_FOLDER + "/users/" + network + "/" + fullName + ".wlt"; + return new File(fileName).exists(); + } + + public static boolean isExistAccountDB(String fullName, String network) { + try { + LocalAccount account = DBHelpers.getUser(fullName, network); + if (account != null) { + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static void initUserHome() { + new File(System.getProperty("user.home") + "/" + CONST.APP_FOLDER + "/node").mkdirs(); + for (String network : NetworkUtils.NETWORK_SUPPORT) { + new File(System.getProperty("user.home") + "/" + CONST.APP_FOLDER + "/users/" + network).mkdirs(); + } + System.setProperty("user.dir", System.getProperty("user.home") + File.separator + CONST.APP_FOLDER); + System.setProperty("vertx.httpServiceFactory.cacheDir", System.getProperty("user.dir")); + } + + public static List getAccounts() { + List list = new ArrayList<>(); + for (String network : NetworkUtils.NETWORK_SUPPORT) { + if (NetworkUtils.isNetworkSupport(network)) { + String appDir = System.getProperty("user.home") + "/" + CONST.APP_FOLDER + "/users/" + network; + File file = new File(appDir); + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + String fileName = child.getName(); + if (child.isFile() && fileName.endsWith(".wlt")) { + String userName = fileName.substring(0, fileName.lastIndexOf(".wlt")); + if (isExistAccountDB(userName, network)) { + list.add(network + "/" + userName); + } + } + } + } + } + } + return list; + } + + public static void addAccountDB(String fullName, String password, String network, String encodedPrivateKey, + String encodedPublicKey, String encodedAddress) { + LocalAccount account = new LocalAccount(fullName, password, network, encodedPrivateKey, encodedPublicKey, encodedAddress, CONST.DB_VERSION); + addAccountDB(account); + } + + public static void addAccountDB(LocalAccount account) { + try { + if (!isExistAccount(account.fullName, account.network)) { + DBHelpers.createUserDB(account.fullName, account.network); + String password = CryptoUtils.encryptToBase64String(account.password.getBytes(), CONST.ENC_STRING); + DBHelpers.addUser(account.fullName, password, account.network, account.privateKey, account.publicKey, account.address); + for (String folder : CONST.DEFAULT_FOLDERS) { + DBHelpers.addFolder(account.fullName, account.network, folder, null); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void updateAccountDB(String fullName, String password, String network, String encodedPrivateKey, + String encodedPublicKey, String encodedAddress) throws Exception { + LocalAccount account = new LocalAccount(fullName, password, network, encodedPrivateKey, encodedPublicKey, encodedAddress, CONST.DB_VERSION); + updateAccountDB(account); + } + + public static void updateAccountDB(LocalAccount account) throws Exception { + String password = CryptoUtils.encryptToBase64String(account.password.getBytes(), CONST.ENC_STRING); + DBHelpers.updateUser(account.fullName, password, account.network, account.privateKey, account.publicKey, account.address); + } + + public static void updateUserStatus(String userName, String network, int status) throws Exception { + DBHelpers.updateUserStatus(userName, network, status); + } + + public static LocalAccount login(String fullName, String network, String password) { + try { + LocalAccount account = DBHelpers.getUser(fullName, network); + if (account != null) { + String enc_password = CryptoUtils.encryptToBase64String(password.getBytes(), CONST.ENC_STRING); + if (account.password.equals(enc_password)) { + return account; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + } + + public static LocalAccount loginDB(String fullName, String network, String password) { + try { + LocalAccount account = DBHelpers.getUser(fullName, network); + if (account != null) { + String enc_password = CryptoUtils.encryptToBase64String(password.getBytes(), CONST.ENC_STRING); + if (account.password.equals(enc_password)) { + if (account.status == 0 || account.status == 1) { + boolean bDone = false; + do { + try { + int ret = account.connectNextNode(); + if (ret == -1) { + break; + } else if (ret == 1) { + bDone = NetworkUtils.activeAccount(network, account); + } + } catch (Exception ex) { + ex.printStackTrace(); + bDone = false; + } + } while (!bDone); + } + return account; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * get public Key from address. + * + * @param connectionCfg + * @param address the address string + * @param connection peer connection + * @return the public key from address + */ + public static String getPublicKeyFromAddress(ConnectionConfig connectionCfg, String address) throws MalformedURLException { + return getPublicKeyFromAddress(connectionCfg.getBlockchainNetworkConnection().getApiHost(), connectionCfg.getBlockchainNetworkConnection().getApiPort(), address); + } + + /** + * get public Key from address. + * + * @param apiUrl + * @param apiPort + * @param addressString the address string + * @return the public key from address + */ + public static String getPublicKeyFromAddress(String apiUrl, int apiPort, String addressString) { + String queryResult = NetworkUtils.sendGetHTTP(apiUrl, apiPort, CONST.URL_ACCOUNT_GET + "/" + addressString); + Gson gson = new Gson(); + JsonObject queryAccount = gson.fromJson(queryResult, JsonObject.class); + return queryAccount.get("account").getAsJsonObject().get("publicKey").getAsString(); + } + + /** + * get address from private key. + * + * @param connectionCfg + * @param privateKeyString the private key string + * @return the address from private key + */ + public static String getAddressFromPrivateKey(ConnectionConfig connectionCfg, String privateKeyString) { + try { + NemUtils nemUtils = new NemUtils(connectionCfg.getBlockchainNetworkConnection().getNetworkType()); + return nemUtils.getAddressFromPrivateKey(privateKeyString).plain(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return ""; + } + + /** + * get address from public key. + * + * @param connectionCfg + * @param publicKeyString the public key string + * @return the address from public key + */ + public static String getAddressFromPublicKey(ConnectionConfig connectionCfg, String publicKeyString) { + try { + NemUtils nemUtils = new NemUtils(connectionCfg.getBlockchainNetworkConnection().getNetworkType()); + return nemUtils.getAddressFromPublicKey(publicKeyString).plain(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return ""; + } + + /** + * Test if a string is hexadecimal + * + * @param str - A string to test + * + * @return True if correct, false otherwise + */ + public static boolean isHexadecimal(String str) { + return str.matches("^(0x|0X)?[a-fA-F0-9]+$"); + } + + /** + * Check if a private key is valid + * + * @param privateKey A private key + * @return True if valid, false otherwise + */ + public static boolean isPrivateKeyValid(String privateKey) { + if (privateKey.length() != 64 && privateKey.length() != 66) { + return false; + } else if (!isHexadecimal(privateKey)) { + return false; + } else { + return true; + } + } + + /** + * Check if a public key is valid + * + * @param publicKey A public key + * + * @return True if valid, false otherwise + */ + public static boolean isPublicKeyValid(String publicKey) { + if (publicKey.length() != 64) { + return false; + } else if (!isHexadecimal(publicKey)) { + return false; + } else { + return true; + } + } + + /** + * Check if a address is valid + * + * @param address A address + * + * @return True if valid, false otherwise + */ + public static boolean isAddressValid(String address) { + try { + if (address.length() == 40) { + Address addr = Address.createFromRawAddress(address); + return addr != null; + } + } catch (Exception ex) { + } + return false; + } + + public static String formatAddressPretty(String address) { + return new StringBuilder().append(address.substring(0, 6)). + append("-"). + append(address.substring(6, 6 + 6)). + append("-"). + append(address.substring(6 * 2, 6 * 2 + 6)). + append("-"). + append(address.substring(6 * 3, 6 * 3 + 6)). + append("-"). + append(address.substring(6 * 4, 6 * 4 + 6)). + append("-"). + append(address.substring(6 * 5, 6 * 5 + 6)). + append("-"). + append(address.substring(6 * 6, 6 * 6 + 4)).toString(); + } + + public static Account generateBasicAccount(String network) { + NemUtils nemUtils = new NemUtils(NetworkUtils.getNetworkType(network)); + Account newAccount = nemUtils.generateAccount(); + return newAccount; + } + + public static LocalAccount createAccount(String fullName, String network, String password, String privateKey) throws Exception { + Account nemAccount = null; + if (StringUtils.isEmpty(privateKey)) { //create new account + nemAccount = generateBasicAccount(network); + } else { + if (isPrivateKeyValid(privateKey)) { + NemUtils nemUtils = new NemUtils(NetworkUtils.getNetworkType(network)); + nemAccount = nemUtils.getAccount(privateKey); + } else { + throw new Exception("Provided private key is not valid!"); + } + } + if (nemAccount != null) { + AccountHelpers.addAccountDB(new LocalAccount(fullName, password, network, nemAccount.getPrivateKey(), nemAccount.getPublicKey(), nemAccount.getAddress().plain(), CONST.DB_VERSION)); + return loginDB(fullName, network, password); + } + return null; + } + + public static AccountInfo getAccountInfo(LocalAccount localAccount) { + AccountInfo account = null; + try { + account = DBHelpers.getAccountInfo(localAccount.fullName, localAccount.network); + } catch (Exception e) { + e.printStackTrace(); + } + return account; + } + + public static void updateAccountInfo(LocalAccount localAccount, AccountInfo accountInfo) { + try { + DBHelpers.updateAccountInfo(localAccount.fullName, localAccount.network, accountInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String findAddressByName(LocalAccount localAccount, String name) { + try { + return DBHelpers.findFriendAddress(localAccount.fullName, localAccount.network, name); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static void addFriend(LocalAccount localAccount, String name, String address, String publicKey) { + try { + DBHelpers.addFriend(localAccount.fullName, localAccount.network, name, address, publicKey); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean isFriendExisted(LocalAccount localAccount, String name, String address) { + try { + return DBHelpers.isFriendExisted(localAccount.fullName, localAccount.network, name, address); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} diff --git a/src/main/java/io/proximax/app/utils/CONST.java b/src/main/java/io/proximax/app/utils/CONST.java new file mode 100644 index 0000000..7903a4a --- /dev/null +++ b/src/main/java/io/proximax/app/utils/CONST.java @@ -0,0 +1,113 @@ +package io.proximax.app.utils; + +import io.proximax.app.core.ui.IApp; +import java.text.SimpleDateFormat; + +/** + * + * @author thcao + */ +public class CONST { + + public static IApp IAPP; + public static final String APP_NAME = "File It!"; + public static final String APP_FOLDER = "file-it"; + public static final String DB_VERSION = "1.0.0"; + public static final char[] ENC_STRING = {'X', 'A', 'M', 'I', 'X', 'O', 'R', 'P'}; + public static final String[] UPLOAD_TYPES = {"Encrypt with keys", "Encrypt with password", "Public"}; + public static final int UTYPE_SECURE_NEMKEYS = 0; + public static final int UTYPE_SECURE_PASSWORD = 1; + public static final int UTYPE_PUBLIC = 2; + public static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + public static final String IMAGE_PATH = "/fxml/css/%s/img/"; + public static final String IMAGE_GREEN = "green.png"; + public static final String IMAGE_RED = "red.png"; + public static final String STR_STATUS = "Status: "; + public static final String STR_CONNECTED = "Connected"; + public static final String STR_DISCONNECTED = "Disconnected"; + public static final String LOGIN_TITLE = APP_NAME + " - Login"; + public static final String LOGIN_FXML = "ProxiBoxLogin.fxml"; + public static final String SIGNUP_TITLE = APP_NAME + " - Sign-up new account"; + public static final String SIGNUP_FXML = "ProxiBoxSignup.fxml"; + public static final String SIGNUP2_TITLE = APP_NAME + " - Sign-up Step 2"; + public static final String SIGNUP2_FXML = "Signup2.fxml"; + public static final String HOME_TITLE = APP_NAME + " - Home"; + public static final String HOME_FXML = "ProxiBoxFiles.fxml"; + public static final String UPLOAD_TITLE = APP_NAME + " - Upload File"; + public static final String UPLOAD_FXML = "ProxiBoxUploader.fxml"; + public static final String SHARING_TITLE = APP_NAME + " - Sharing File"; + public static final String SHARING_FXML = "ProxiBoxSharing.fxml"; + public static final String FILEVIEWER_TITLE = APP_NAME + " - Quick Viewer"; + public static final String FILEVIEWER_FXML = "ProxiBoxFileViewer.fxml"; + public static final String MEDIAVIEWER_FXML = "ProxiBoxMediaViewer.fxml"; + public static final String PROXIPAD_TITLE = APP_NAME + " - ProxiPad"; + public static final String PROXIPAD_FXML = "ProxiPad.fxml"; + public static final String PROXIDLG_TITLE = APP_NAME + " - Dialog"; + public static final String PROXIDLG_FXML = "ProxiDialog.fxml"; + public static final String ABOUTDLG_TITLE = APP_NAME + " - About"; + public static final String ABOUTDLG_FXML = "AboutUs.fxml"; + public static final String ERRORDLG_TITLE = APP_NAME + " - Error Message"; + public static final String ERRORDLG_FXML = "ErrorDialog.fxml"; + public static final String USERDLG_TITLE = APP_NAME + " - User Profile"; + public static final String USERDLG_FXML = "ProxiBoxProfile.fxml"; + public static final String FILEPROPDLG_TITLE = APP_NAME + " - File Properties"; + public static final String FILEPROPDLG_FXML = "ProxiBoxFileProperties.fxml"; + public static final String FOLDERDLG_TITLE = APP_NAME + " - Folder"; + public static final String FOLDERDLG_FXML = "ProxiBoxFolder.fxml"; + public static final int FILE_STATUS_NOR = 0; + public static final int FILE_STATUS_DEL = 1; + public static final int FILE_STATUS_TXN = 100; + public static final int FILE_STATUS_FAILED = 101; + public static final int FILE_STATUS_QUEUE = 102; + public static final int FILE_STATUS_UPLOAD = 103; + public static final int TABLEVIEW_HOME = 0; + public static final int TABLEVIEW_DEL = 1; + public static final int TABLEVIEW_HIS = 2; + public static final int TABLEVIEW_ALL = 3; + public static final int TABLEVIEW_SHAR = 4; + public static final String DB_USER_TABLE = "PROXIBOX_USER"; + public static final String DB_FILE_TABLE = "PROXIBOX_FILES"; + public static final String DB_SHARE_TABLE = "PROXIBOX_SHARES"; + public static final String DB_CATEGORY_TABLE = "PROXIBOX_CATEGORY"; + public static final String DB_FRIEND_TABLE = "PROXIBOX_FRIEND"; + public static final String DB_JOB_TABLE = "PROXIBOX_JOB"; + public static final String PROXIBOX_ICON = "proxibox-icon.png"; + public static final String[] THEMES = {"Light", "Dark"}; + public static final String CSS_PATH = "/fxml/css/"; + public static final String CSS_THEME = "/fxml/css/%s.css"; + public static final String HOME = "/"; + public static final String[] DEFAULT_FOLDERS = {"Document", "Photo", "Video", "Music"}; + public static final long MONITOR_TIMEOUT = 3 * 60 * 1000; //3p + public static final String URL_ACCOUNT_GET = "/account"; + public static final String DB_NETWORK_TABLE = "PROXIMAX_NETWORK"; + public static final String DB_COMMON_TABLE = "PROXIMAX_COMMON"; + public static final String DB_SUPPORT_TABLE = "PROXIMAX_SUPPORT"; + public static final String DB_APP_CONFIGURATION = "proximax.properties"; + public static final String NETWORKDLG_TITLE = APP_NAME + " - Network Configuration"; + public static final String NETWORKDLG_FXML = "NetworkDialog.fxml"; + public static final String FXML_PATH = "/fxml/ui/"; + public static final String[] SECURE_QUESTIONS = { + "What was your childhood nickname?", + "In what city did you meet your spouse/significant other?", + "What is the name of your favorite childhood friend? ", + "What street did you live on in third grade?", + "What is your oldest sibling’s birthday month and year? (e.g., January 1900)", + "What is the middle name of your youngest child?", + "What is your oldest sibling's middle name?", + "What school did you attend for sixth grade?", + "What was your childhood phone number including area code? (e.g., 000-000-0000)", + "What is your oldest cousin's first and last name?", + "What was the name of your first stuffed animal?", + "In what city or town did your mother and father meet?", + "Where were you when you had your first kiss? ", + "What is the first name of the boy or girl that you first kissed?", + "What was the last name of your third grade teacher?", + "In what city does your nearest sibling live?", + "What is your youngest brother’s birthday month and year? (e.g., January 1900)", + "What is your maternal grandmother's maiden name?", + "In what city or town was your first job?", + "What is the name of the place your wedding reception was held?", + "What is the name of a college you applied to but didn't attend?", + "Where were you when you first heard about 9/11?"}; + +} diff --git a/src/main/java/io/proximax/app/utils/CryptoUtils.java b/src/main/java/io/proximax/app/utils/CryptoUtils.java new file mode 100644 index 0000000..d97eb1a --- /dev/null +++ b/src/main/java/io/proximax/app/utils/CryptoUtils.java @@ -0,0 +1,125 @@ +package io.proximax.app.utils; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import io.proximax.app.core.cipher.BinaryPBKDF2CipherEncryption; + +/** + * The Class CryptoUtil. + */ +public class CryptoUtils { + + /** + * Encrypt. + * + * @param binary the binary + * @param password the password + * @return the byte[] + * @throws InvalidKeyException the invalid key exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + */ + public static byte[] encrypt(byte[] binary, char[] password) + throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + BinaryPBKDF2CipherEncryption basicBinaryEncryptor = new BinaryPBKDF2CipherEncryption(); + return basicBinaryEncryptor.encrypt(binary, password); + } + + /** + * Encrypt to base 64 string. + * + * @param binary the binary + * @param password the password + * @return the string + * @throws InvalidKeyException the invalid key exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + */ + public static String encryptToBase64String(byte[] binary, char[] password) + throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + BinaryPBKDF2CipherEncryption basicBinaryEncryptor = new BinaryPBKDF2CipherEncryption(); + return basicBinaryEncryptor.encryptToBase64String(binary, password); + } + + /** + * Decrypt. + * + * @param binary the binary + * @param password the password + * @return the byte[] + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public static byte[] decrypt(byte[] binary, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + BinaryPBKDF2CipherEncryption basicBinaryEncryptor = new BinaryPBKDF2CipherEncryption(); + + return basicBinaryEncryptor.decrypt(binary, password); + } + + /** + * Decrypt to base 64 string. + * + * @param binary the binary + * @param password the password + * @return the string + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public static String decryptToBase64String(byte[] binary, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + BinaryPBKDF2CipherEncryption basicBinaryEncryptor = new BinaryPBKDF2CipherEncryption(); + + return basicBinaryEncryptor.decryptToBase64String(binary, password); + } + + /** + * Decrypt to base 64 string. + * + * @param cipherEncryptedText the cipher encrypted text + * @param password the password + * @return the string + * @throws InvalidKeyException the invalid key exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + * @throws IllegalBlockSizeException the illegal block size exception + * @throws BadPaddingException the bad padding exception + * @throws InvalidKeySpecException the invalid key spec exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchPaddingException the no such padding exception + */ + public static String decryptToBase64String(String cipherEncryptedText, char[] password) + throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, + BadPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { + BinaryPBKDF2CipherEncryption basicBinaryEncryptor = new BinaryPBKDF2CipherEncryption(); + + return basicBinaryEncryptor.decryptToBase64String(cipherEncryptedText, password); + } + +} diff --git a/src/main/java/io/proximax/app/utils/DBHelpers.java b/src/main/java/io/proximax/app/utils/DBHelpers.java new file mode 100644 index 0000000..cce4e1c --- /dev/null +++ b/src/main/java/io/proximax/app/utils/DBHelpers.java @@ -0,0 +1,1325 @@ +package io.proximax.app.utils; + +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.ShareFile; +import io.proximax.app.db.LocalFile; +import io.proximax.app.db.NetworkConfiguration; +import io.proximax.app.recovery.AccountInfo; +import java.io.File; +import java.net.URL; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; + +/** + * + * @author thcao + */ +public class DBHelpers { + + public static Connection createConnection(String userName, String network) throws SQLException { + Connection c = null; + try { + Class.forName("org.sqlite.JDBC"); + String dbName = System.getProperty("user.home") + "/" + CONST.APP_FOLDER + "/users/" + network + "/" + userName + ".wlt"; + c = DriverManager.getConnection("jdbc:sqlite:" + dbName); + c.setAutoCommit(false); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + } + return c; + } + + public static void createUserDB(String userName, String network) throws SQLException { + + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "CREATE TABLE " + CONST.DB_USER_TABLE + + " (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + " USERNAME CHAR(255) NOT NULL, " + + " PASSWORD CHAR(255) NOT NULL, " + + " NETWORK CHAR(255) NOT NULL, " + + " ADDRESS CHAR(255), " + + " PUBLIC_KEY CHAR(255), " + + " PRIVATE_KEY CHAR(255), " + + " LICENSE_KEY CHAR(255), " + + " XPX REAL NOT NULL DEFAULT 0, " + + " USED REAL NOT NULL DEFAULT 0, " + + " CAPACITY REAL NOT NULL DEFAULT 5368709120, " + + " TYPE INTEGER NOT NULL DEFAULT 0, " + + " STATUS INTEGER NOT NULL DEFAULT 0, " + + " EMAIL CHAR(255), " + + " QUESTION1 CHAR(255), " + + " ANSWER1 CHAR(255), " + + " QUESTION2 CHAR(255), " + + " ANSWER2 CHAR(255), " + + " QUESTION3 CHAR(255), " + + " ANSWER3 CHAR(255), " + + " VERSION CHAR(255))"; + stmt.executeUpdate(sql); + sql = "CREATE TABLE " + CONST.DB_FILE_TABLE + + " (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + " FILENAME CHAR(1024) NOT NULL, " + + " FILEPATH CHAR(1024) NOT NULL, " + + " RENAME CHAR(1024), " + + " MODIFIED INTEGER, " + + " FILESIZE INTEGER, " + + " UPLOAD_DATE INTEGER, " + + " HASH CHAR(255) NOT NULL, " + + " NEM_HASH CHAR(255) NOT NULL, " + + " CATEGORY CHAR(1024)," + + " PASSWORD CHAR(255), " + + " ADDRESS CHAR(255), " + + " PUBLIC_KEY CHAR(255), " + + " PRIVATE_KEY CHAR(255)," + + " UTYPE INTEGER," + + " SHARED TEXT," + + " METADATA TEXT," + + " REV INTEGER," + + " FILE_ID INTEGER," + + " CREATED_DATE INTEGER, " + + " UPDATED_DATE INTEGER, " + + " STATUS INTEGER NOT NULL DEFAULT 0" + + ")"; + stmt.executeUpdate(sql); + sql = "CREATE TABLE " + CONST.DB_SHARE_TABLE + + " (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + " FILE_ID INTEGER NOT NULL, " + + " USERNAME CHAR(255), " + + " ADDRESS CHAR(255)," + + " SHARE_DATE INTEGER, " + + " HASH CHAR(255) NOT NULL, " + + " NEM_HASH CHAR(255) NOT NULL, " + + " PASSWORD CHAR(255), " + + " SHARE_TYPE INTEGER, " + + " STATUS INTEGER NOT NULL DEFAULT 0, " + + " CONSTRAINT \"share_file_fk\" FOREIGN KEY (\"FILE_ID\") REFERENCES " + CONST.DB_FILE_TABLE + " (\"ID\")" + + ")"; + stmt.executeUpdate(sql); + sql = "CREATE TABLE " + CONST.DB_CATEGORY_TABLE + + " (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + " CATEGORY CHAR(255) NOT NULL," + + " FILEPATH CHAR(1024) NOT NULL, " + + " UTYPE INTEGER, " + + " PARENT_ID INTEGER, " + + " CREATED_DATE INTEGER, " + + " STATUS INTEGER NOT NULL DEFAULT 0" + + ")"; + stmt.executeUpdate(sql); + sql = "CREATE TABLE " + CONST.DB_FRIEND_TABLE + + " (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + " USERNAME CHAR(255) NOT NULL, " + + " ADDRESS CHAR(255) NOT NULL, " + + " PUBLIC_KEY CHAR(255), " + + " CREATED_DATE INTEGER " + + ")"; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void addUser(String userName, String password, String network, String privateKey, String publicKey, String address) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "INSERT INTO " + CONST.DB_USER_TABLE + + " (USERNAME,PASSWORD,NETWORK,ADDRESS,PUBLIC_KEY,PRIVATE_KEY, VERSION) " + + "VALUES (\"" + userName + "\",\"" + password + "\",\"" + network + "\",\"" + address + "\",\"" + publicKey + "\",\"" + privateKey + "\",\"" + CONST.DB_VERSION + "\");"; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static LocalFile getJob(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS=" + CONST.FILE_STATUS_FAILED + " ORDER BY UPDATED_DATE ASC"); + LocalFile localFile = null; + while (rs.next()) { + localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + break; + } + stmt.close(); + c.close(); + return localFile; + } + + public static List getJobs(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS=" + CONST.FILE_STATUS_FAILED + " ORDER BY UPDATED_DATE ASC"); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static void updateJobStatus(String userName, String network, int id, int status) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_JOB_TABLE + + " SET STATUS=" + status + ", UPDATED_DATE=" + System.currentTimeMillis() + " WHERE ID=" + id; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateUser(String userName, String password, String network, String privateKey, String publicKey, String address) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + + " SET PASSWORD=\"" + password + "\",ADDRESS=\"" + address + "\",PUBLIC_KEY=\"" + publicKey + "\",PRIVATE_KEY=\"" + privateKey + "\""; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateUserType(String userName, String network, int type) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + " SET TYPE=" + type; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateUserLicense(String userName, String network, String licenseKey) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + " SET LICENSE_KEY=\"" + licenseKey + "\""; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateUserSpace(String userName, String network, double bytesUsed) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + " SET USED=" + bytesUsed; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateUserStatus(String userName, String network, int status) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + " SET STATUS=" + status; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void upgradeVersion(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_USER_TABLE); + LocalAccount localAccount = null; + while (rs.next()) { + localAccount = new LocalAccount(rs.getString("USERNAME"), + rs.getString("PASSWORD"), + rs.getString("NETWORK"), + rs.getString("PRIVATE_KEY"), + rs.getString("PUBLIC_KEY"), + rs.getString("ADDRESS"), + rs.getString("VERSION")); + break; + } + stmt.close(); + c.close(); + // check old version and new version + // if (localAccount.version.equals("0.1.0") && CONST.DB_VERSION.equals("0.1.3")) + } + + public static void addFile(String userName, String network, LocalFile localFile) throws SQLException { + List files = getFiles(userName, network, localFile.fileName, localFile.hash, localFile.nemHash); + if (!files.isEmpty()) { + return; + } + String sql = "INSERT INTO " + CONST.DB_FILE_TABLE + + " (FILENAME,FILEPATH,RENAME,MODIFIED,FILESIZE,UPLOAD_DATE,HASH,NEM_HASH,CATEGORY,PASSWORD,ADDRESS,PUBLIC_KEY,PRIVATE_KEY,UTYPE,SHARED,METADATA,REV,FILE_ID,CREATED_DATE,UPDATED_DATE,STATUS) VALUES (" + + "\"" + localFile.fileName + "\"," + + "\"" + localFile.filePath + "\"," + + "\"" + localFile.fileName + "\"," + + localFile.modified + "," + + localFile.fileSize + "," + + localFile.uploadDate + "," + + "\"" + localFile.hash + "\"," + + "\"" + localFile.nemHash + "\"," + + "\"" + localFile.category + "\"," + + "\"" + localFile.password + "\"," + + "\"" + localFile.address + "\"," + + "\"" + localFile.publicKey + "\"," + + "\"" + localFile.privateKey + "\"," + + localFile.uType + "," + + "\"" + localFile.shared + "\"," + + "\"" + localFile.metadata + "\"," + + localFile.rev + "," + + localFile.fileId + "," + + System.currentTimeMillis() + "," + + System.currentTimeMillis() + "," + + localFile.status + ");"; + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement(sql); + stmt.executeUpdate(); + if (localFile.fileId == 0) { + sql = ""; + Statement stmt1 = c.createStatement(); + ResultSet rs = stmt1.executeQuery("select last_insert_rowid()"); + long id = 0; + if (rs.next()) { + id = rs.getLong(1); + } + stmt1.close(); + if (id > 0) { + PreparedStatement stmt2 = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + " SET FILE_ID=" + id + " WHERE id=" + id); + stmt2.executeUpdate(); + stmt2.close(); + } + } + stmt.close(); + c.commit(); + c.close(); + } + + public static LocalAccount getUser(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + LocalAccount account = null; + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_USER_TABLE); + while (rs.next()) { + account = new LocalAccount(rs.getString("USERNAME"), + rs.getString("PASSWORD"), + rs.getString("NETWORK"), + rs.getString("PRIVATE_KEY"), + rs.getString("PUBLIC_KEY"), + rs.getString("ADDRESS"), + rs.getString("VERSION"), + rs.getInt("TYPE"), + rs.getInt("STATUS")); + account.licenseKey = rs.getString("LICENSE_KEY"); + account.xpx = rs.getLong("XPX"); + account.used = rs.getDouble("USED"); + account.capacity = rs.getDouble("CAPACITY"); + break; + } + stmt.close(); + c.close(); + return account; + } + + public static List getOFiles(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static void updateFileStatus(String userName, String network, long timeout) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + " SET STATUS=" + CONST.FILE_STATUS_NOR + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE STATUS=" + CONST.FILE_STATUS_TXN + + " AND UPLOAD_DATE<" + timeout); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateLocalFileStatus(String userName, String network, int fileId, int status) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + " SET STATUS=" + status + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE ID=" + fileId); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static List getFiles(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS!=" + CONST.FILE_STATUS_DEL + " ORDER BY UPLOAD_DATE DESC"); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static List getFilesFolder(String userName, String network, String folder) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql; + if (StringUtils.isEmpty(folder) || CONST.HOME.equals(folder)) { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " (CATEGORY IS NULL OR CATEGORY = \"/\" OR CATEGORY = \"\") " + + " AND (SHARED IS NULL OR SHARED = \"\") " + + " AND (STATUS!=" + CONST.FILE_STATUS_DEL + ") ORDER BY UPLOAD_DATE DESC"; + } else { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " CATEGORY = \"" + folder + "\" " + + " AND (SHARED IS NULL OR SHARED = \"\") " + + " AND (STATUS!=" + CONST.FILE_STATUS_DEL + ") ORDER BY UPLOAD_DATE DESC"; + } + + ResultSet rs = stmt.executeQuery(sql); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static List getSharingFiles(String userName, String network, String folder) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql; + if (StringUtils.isEmpty(folder) || CONST.HOME.equals(folder)) { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " (CATEGORY IS NULL OR CATEGORY = \"/\" OR CATEGORY = \"\") " + + " AND (SHARED IS NOT NULL AND SHARED != \"\") " + + " AND (STATUS!=" + CONST.FILE_STATUS_DEL + ") ORDER BY UPLOAD_DATE DESC"; + } else { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " CATEGORY = \"" + folder + "\" " + + " AND (SHARED IS NOT NULL AND SHARED != \"\") " + + " AND (STATUS!=" + CONST.FILE_STATUS_DEL + ") ORDER BY UPLOAD_DATE DESC"; + } + + ResultSet rs = stmt.executeQuery(sql); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static List getFolders(String userName, String network, LocalFile parentFolder) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + int parentId = 0; + if (parentFolder != null) { + parentId = parentFolder.id; + } + String sql = "SELECT * FROM " + CONST.DB_CATEGORY_TABLE + " WHERE PARENT_ID=" + parentId + " AND STATUS=0 " + + " ORDER BY CATEGORY ASC "; + ResultSet rs = stmt.executeQuery(sql); + List list = new ArrayList(); + while (rs.next()) { + LocalFile file = new LocalFile(true); + file.id = rs.getInt("ID"); + file.category = rs.getString("CATEGORY"); + file.filePath = rs.getString("FILEPATH"); + file.uType = rs.getInt("UTYPE"); + file.fileId = rs.getInt("PARENT_ID"); + file.uploadDate = rs.getLong("CREATED_DATE"); + list.add(file); + } + stmt.close(); + c.close(); + return list; + } + + public static LocalFile getFolder(String userName, String network, int id) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "SELECT * FROM " + CONST.DB_CATEGORY_TABLE + " WHERE ID=" + id + " AND STATUS=0 "; + LocalFile folder = null; + ResultSet rs = stmt.executeQuery(sql); + while (rs.next()) { + folder = new LocalFile(true); + folder.id = rs.getInt("ID"); + folder.category = rs.getString("CATEGORY"); + folder.filePath = rs.getString("FILEPATH"); + folder.uType = rs.getInt("UTYPE"); + folder.fileId = rs.getInt("PARENT_ID"); + folder.uploadDate = rs.getLong("CREATED_DATE"); + break; + } + stmt.close(); + c.close(); + return folder; + } + + public static LocalFile getFolder(String userName, String network, String filePath) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "SELECT * FROM " + CONST.DB_CATEGORY_TABLE + " WHERE FILEPATH=\"" + filePath + "\" AND STATUS=0 "; + LocalFile folder = null; + ResultSet rs = stmt.executeQuery(sql); + while (rs.next()) { + folder = new LocalFile(true); + folder.id = rs.getInt("ID"); + folder.category = rs.getString("CATEGORY"); + folder.filePath = rs.getString("FILEPATH"); + folder.uType = rs.getInt("UTYPE"); + folder.fileId = rs.getInt("PARENT_ID"); + folder.uploadDate = rs.getLong("CREATED_DATE"); + break; + } + stmt.close(); + c.close(); + return folder; + } + + public static boolean isFolderExisted(String userName, String network, String filePath) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "SELECT * FROM " + CONST.DB_CATEGORY_TABLE + " WHERE FILEPATH=\"" + filePath + "\" AND STATUS=0 "; + ResultSet rs = stmt.executeQuery(sql); + boolean bExisted = false; + while (rs.next()) { + bExisted = true; + break; + } + stmt.close(); + c.close(); + return bExisted; + } + + public static List getAllFolder(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT FILEPATH FROM " + CONST.DB_CATEGORY_TABLE + " WHERE STATUS=0 " + + " ORDER BY FILEPATH ASC"); + List list = new ArrayList(); + while (rs.next()) { + list.add(rs.getString("FILEPATH")); + } + stmt.close(); + c.close(); + return list; + } + + public static void addFolder(String userName, String network, String newFolder, LocalFile parentFolder) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String filePath = CONST.HOME + newFolder; + int level = 1; + int parentid = 0; + if (parentFolder != null) { + filePath = parentFolder.filePath + "/" + newFolder; + level = parentFolder.uType + 1; + parentid = parentFolder.id; + } + String sql = "INSERT INTO " + CONST.DB_CATEGORY_TABLE + + " (CATEGORY,FILEPATH,UTYPE,PARENT_ID,CREATED_DATE) " + + "VALUES (" + + "\"" + newFolder + "\"" + + ",\"" + filePath + "\"" + + "," + level + + "," + parentid + + "," + System.currentTimeMillis() + ");"; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void deleteFileInFolder(String userName, String network, String folder) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET STATUS=" + CONST.FILE_STATUS_DEL + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE CATEGORY=\"" + folder + "\""); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateFileFolderPath(String userName, String network, String oldCategory, String newCategory) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET CATEGORY=\"" + newCategory + "\"" + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE CATEGORY=\"" + oldCategory + "\""); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateFolderPath(String userName, String network, int folderId, String newPath, int level) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_CATEGORY_TABLE + " SET FILEPATH=\"" + newPath + "\" "; + if (level != -1) { + sql += " ,UTYPE=" + level; + } + sql += " WHERE ID=" + folderId; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void moveFolderToFolder(String userName, String network, int folderId, int parentId, int level, String newPath) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_CATEGORY_TABLE + " SET PARENT_ID=" + parentId; + sql += " ,UTYPE=" + level; + sql += " ,FILEPATH=\"" + newPath + "\""; + sql += " WHERE ID=" + folderId; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void deleteFolder(String userName, String network, int folderId) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_CATEGORY_TABLE + " SET STATUS=1"; + sql += " WHERE ID=" + folderId; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void renameFolder(String userName, String network, LocalFile folder, String newName) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + int idx = folder.filePath.lastIndexOf(folder.category); + String filePath = folder.filePath.substring(0, idx) + newName; + folder.category = newName; + folder.filePath = filePath; + String sql = "UPDATE " + CONST.DB_CATEGORY_TABLE + " SET CATEGORY=\"" + newName + "\", FILEPATH=\"" + filePath + "\" "; + sql += " WHERE ID=" + folder.id; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + +// public static void updateFolder(String userName, String network, int folderId, String folder, String parent) throws SQLException { +// Connection c = createConnection(userName, network); +// Statement stmt = c.createStatement(); +// String sql = "UPDATE " + CONST.DB_CATEGORY_TABLE + " SET CATEGORY=\"" + folder + "\", PARENT=\"" + parent + "\" "; +// sql += " WHERE ID=" + folderId; +// stmt.executeUpdate(sql); +// stmt.close(); +// c.commit(); +// c.close(); +// } + public static List getFiles(String userName, String network, String fileName, String hash, String nemHash) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE FILENAME=\"" + fileName + "\" AND HASH=\"" + hash + "\" AND NEM_HASH=\"" + nemHash + "\""); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static boolean isFileExisted(String userName, String network, String fileName, long lastModified, int uType) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS!=" + CONST.FILE_STATUS_DEL + + " AND FILENAME=\"" + fileName + "\" AND MODIFIED=" + lastModified + " AND UTYPE=" + uType); + boolean bExisted = false; + while (rs.next()) { + bExisted = true; + break; + } + stmt.close(); + c.close(); + return bExisted; + } + + public static void shareLocalFile(String userName, String network, ShareFile shareFile) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "INSERT INTO " + CONST.DB_SHARE_TABLE + " (FILE_ID, USERNAME, ADDRESS, SHARE_DATE, HASH, NEM_HASH, PASSWORD,SHARE_TYPE,STATUS) VALUES (" + + shareFile.fileId + "," + + "\"" + shareFile.userName + "\"," + + "\"" + shareFile.address + "\"," + + shareFile.shareDate + "," + + "\"" + shareFile.hash + "\"," + + "\"" + shareFile.nemHash + "\"," + + "\"" + shareFile.password + "\"," + + shareFile.shareType + "," + + shareFile.status + ")"; + + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static LocalFile getFile(String userName, String network, int id) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE ID=" + id); + LocalFile localFile = null; + while (rs.next()) { + localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + break; + } + stmt.close(); + c.close(); + return localFile; + } + + public static LocalFile getFile(String userName, String network, String fileName, int status) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS=" + status + " AND FILENAME=\"" + fileName + "\""); + LocalFile localFile = null; + while (rs.next()) { + localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + break; + } + stmt.close(); + c.close(); + return localFile; + } + + public static void updateFile(String userName, String network, LocalFile oldFile, LocalFile newFile) throws SQLException { + newFile.rev = oldFile.rev + 1; + newFile.status = CONST.FILE_STATUS_TXN; + if (oldFile.fileId == oldFile.id) { + newFile.fileId = oldFile.id; + } else { + newFile.fileId = oldFile.fileId; + } + addFile(userName, network, newFile); + delFile(userName, network, oldFile.id); + } + + public static void delFile(String userName, String network, int id) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET STATUS=" + CONST.FILE_STATUS_DEL + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE id=" + id); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static List getDelFiles(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE STATUS=" + CONST.FILE_STATUS_DEL); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static void updateFile(String userName, String network, String nemHash, int status) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET STATUS=" + status + + ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE NEM_HASH=\"" + nemHash + "\""); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static void updateLocalFile(String userName, String network, int fileId, String hash, String nemHash, long uploadDate, int status) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET STATUS=" + status + + ",UPDATED_DATE=" + System.currentTimeMillis() + + ",HASH=\"" + hash + "\"" + + ",NEM_HASH=\"" + nemHash + "\"" + + ",UPLOAD_DATE=" + uploadDate + + " WHERE ID=" + fileId); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static List getHistoryFile(String userName, String network, int fileId) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE FILE_ID=" + fileId + " ORDER BY UPLOAD_DATE DESC"); + List list = new ArrayList(); + while (rs.next()) { + LocalFile localFile = new LocalFile(); + localFile.id = rs.getInt("ID"); + localFile.fileName = rs.getString("FILENAME"); + localFile.filePath = rs.getString("FILEPATH"); + localFile.reName = rs.getString("RENAME"); + localFile.modified = rs.getLong("MODIFIED"); + localFile.fileSize = rs.getLong("FILESIZE"); + localFile.uploadDate = rs.getLong("UPLOAD_DATE"); + localFile.createdDate = rs.getLong("CREATED_DATE"); + localFile.updatedDate = rs.getLong("UPDATED_DATE"); + localFile.hash = rs.getString("HASH"); + localFile.nemHash = rs.getString("NEM_HASH"); + localFile.publicKey = rs.getString("PUBLIC_KEY"); + localFile.address = rs.getString("ADDRESS"); + localFile.category = rs.getString("CATEGORY"); + localFile.password = rs.getString("PASSWORD"); + localFile.privateKey = rs.getString("PRIVATE_KEY"); + localFile.uType = rs.getInt("UTYPE"); + localFile.shared = rs.getString("SHARED"); + localFile.metadata = rs.getString("METADATA"); + localFile.rev = rs.getInt("REV"); + localFile.fileId = rs.getInt("FILE_ID"); + localFile.status = rs.getInt("STATUS"); + list.add(localFile); + } + stmt.close(); + c.close(); + return list; + } + + public static void moveFileFolder(String userName, String network, int id, String sFolder) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET CATEGORY=\"" + sFolder + "\"" + + " WHERE id=" + id); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static boolean isShared(String userName, String network, int fileId, String address) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_SHARE_TABLE + " WHERE FILE_ID=" + fileId + " AND ADDRESS=\"" + address + "\""); + boolean bExisted = false; + while (rs.next()) { + bExisted = true; + break; + } + stmt.close(); + c.close(); + return bExisted; + } + + public static Connection createConnection() throws SQLException { + Connection c = null; + try { + Class.forName("org.sqlite.JDBC"); + String dbName = System.getProperty("user.home") + File.separator + CONST.APP_FOLDER + File.separator + CONST.DB_APP_CONFIGURATION; + if (!new File(dbName).exists()) { + try { + URL configFile = DBHelpers.class.getResource("/" + CONST.DB_APP_CONFIGURATION); + FileUtils.copyURLToFile(configFile, new File(dbName)); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + c = DriverManager.getConnection("jdbc:sqlite:" + dbName); + c.setAutoCommit(false); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + } + return c; + } + + public static Connection createConnectionFromResource() throws SQLException { + Connection c = null; + try { + Class.forName("org.sqlite.JDBC"); + String dbName = DBHelpers.class.getResource("/" + CONST.DB_APP_CONFIGURATION).toString(); + c = DriverManager.getConnection("jdbc:sqlite::resource:" + dbName); + c.setAutoCommit(false); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + } + return c; + } + + public static int getAppConfigVerFromRes() throws SQLException { + Map configs = getAppConfiguration(createConnectionFromResource()); + return StringUtils.parseInt(configs.get("version"), 0); + } + + public static int getAppConfigVerFromLocal() throws SQLException { + Map configs = getAppConfiguration(); + return StringUtils.parseInt(configs.get("version"), 0); + } + + public static Map getAppConfiguration() throws SQLException { + return getAppConfiguration(createConnection()); + } + + public static Map getAppConfiguration(Connection c) throws SQLException { + Map configs = new HashMap<>(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_COMMON_TABLE); + while (rs.next()) { + configs.put(rs.getString("PARAM"), rs.getString("VALUE")); + } + stmt.close(); + c.close(); + return configs; + } + + public static Map getNetworkConfiguration(int status) throws SQLException { + Connection c = createConnection(); + Statement stmt = c.createStatement(); + String str; + if (status >= 0) { + str = "SELECT * FROM " + CONST.DB_NETWORK_TABLE + " WHERE STATUS=" + status; + } else { + str = "SELECT * FROM " + CONST.DB_NETWORK_TABLE; + } + ResultSet rs = stmt.executeQuery(str); + NetworkConfiguration netconf; + Map configs = new HashMap<>(); + while (rs.next()) { + netconf = new NetworkConfiguration(rs.getInt("ID"), rs.getString("NAME"), rs.getInt("VALUE"), rs.getInt("STATUS"), rs.getString("NODES"), rs.getString("IPFS"), rs.getString("enc1")); + configs.put(netconf.name, netconf); + } + stmt.close(); + c.close(); + return configs; + } + + public static NetworkConfiguration getNetworkConfiguration(String name) throws SQLException { + Connection c = createConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_NETWORK_TABLE + " WHERE NAME=\"" + name + "\""); + NetworkConfiguration netconf = null; + while (rs.next()) { + netconf = new NetworkConfiguration(rs.getInt("ID"), rs.getString("NAME"), rs.getInt("VALUE"), rs.getInt("STATUS"), rs.getString("NODES"), rs.getString("IPFS"), rs.getString("enc1")); + break; + } + stmt.close(); + c.close(); + return netconf; + } + + public static void updateNetworkConfiguration(NetworkConfiguration config) throws SQLException { + Connection c = createConnection(); + String nodes = ""; + for (String node : config.getNodes()) { + nodes += node + ","; + } + String ipfss = ""; + for (String ipfs : config.getIpfses()) { + ipfss += ipfs + ","; + } + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_NETWORK_TABLE + + " SET STATUS=" + config.status + "," + + "NODES=\"" + nodes + "\"," + + "IPFS=\"" + ipfss + "\"" + + " WHERE ID=" + config.id); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + + } + + public static AccountInfo getAccountInfo(String userName, String network) throws SQLException { + Connection c = createConnection(userName, network); + AccountInfo account = null; + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_USER_TABLE); + while (rs.next()) { + account = new AccountInfo(rs.getString("USERNAME"), + rs.getString("EMAIL"), + rs.getString("QUESTION1"), + rs.getString("ANSWER1"), + rs.getString("QUESTION2"), + rs.getString("ANSWER2"), + rs.getString("QUESTION3"), + rs.getString("ANSWER3"), + rs.getString("PRIVATE_KEY"), + rs.getString("PUBLIC_KEY"), + rs.getString("ADDRESS")); + break; + } + stmt.close(); + c.close(); + return account; + } + + public static void updateAccountInfo(String userName, String network, AccountInfo account) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "UPDATE " + CONST.DB_USER_TABLE + " SET EMAIL=\"" + account.getEmail() + "\"" + + ",QUESTION1=\"" + account.getQuestion1() + "\"" + + ",QUESTION2=\"" + account.getQuestion2() + "\"" + + ",QUESTION3=\"" + account.getQuestion3() + "\"" + + ",ANSWER1=\"" + account.getAnswer1() + "\"" + + ",ANSWER2=\"" + account.getAnswer2() + "\"" + + ",ANSWER3=\"" + account.getAnswer3() + "\""; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static void renameLocalFile(String userName, String network, int id, String name) throws SQLException { + Connection c = createConnection(userName, network); + PreparedStatement stmt = c.prepareStatement("UPDATE " + CONST.DB_FILE_TABLE + + " SET RENAME=\"" + name + "\"" + //+ ",UPDATED_DATE=" + System.currentTimeMillis() + + " WHERE id=" + id); + stmt.executeUpdate(); + stmt.close(); + c.commit(); + c.close(); + } + + public static boolean isNameExisted(String userName, String network, String folder, String name) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql; + if (StringUtils.isEmpty(folder) || CONST.HOME.equals(folder)) { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " (CATEGORY IS NULL OR CATEGORY = \"/\" OR CATEGORY = \"\") " + + " AND (RENAME = \"" + name + "\") " + + " AND (STATUS =" + CONST.FILE_STATUS_NOR + ")"; + } else { + sql = "SELECT * FROM " + CONST.DB_FILE_TABLE + " WHERE " + + " CATEGORY = \"" + folder + "\" " + + " AND (RENAME = \"" + name + "\") " + + " AND (STATUS =" + CONST.FILE_STATUS_NOR + ")"; + } + + ResultSet rs = stmt.executeQuery(sql); + boolean bExisted = false; + while (rs.next()) { + bExisted = true; + break; + } + stmt.close(); + c.close(); + return bExisted; + } + + public static void addFriend(String userName, String network, String name, String address, String publicKey) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + String sql = "INSERT INTO " + CONST.DB_FRIEND_TABLE + + " (USERNAME,ADDRESS,PUBLIC_KEY,CREATED_DATE) " + + "VALUES (" + + "\"" + name + "\"," + + "\"" + address + "\"," + + "\"" + publicKey + "\"," + + " " + System.currentTimeMillis() + ");"; + stmt.executeUpdate(sql); + stmt.close(); + c.commit(); + c.close(); + } + + public static String findFriendAddress(String userName, String network, String name) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FRIEND_TABLE + + " WHERE USERNAME=\"" + name + "\""); + String address = ""; + while (rs.next()) { + address = rs.getString("ADDRESS"); + break; + } + stmt.close(); + c.commit(); + c.close(); + return address; + } + + public static String findFriendPublicKey(String userName, String network, String name) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FRIEND_TABLE + + " WHERE USERNAME=\"" + name + "\""); + String publicKey = ""; + while (rs.next()) { + publicKey = rs.getString("PUBLIC_KEY"); + break; + } + stmt.close(); + c.commit(); + c.close(); + return publicKey; + } + + public static boolean isFriendExisted(String userName, String network, String name, String address) throws SQLException { + Connection c = createConnection(userName, network); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM " + CONST.DB_FRIEND_TABLE + " WHERE USERNAME=\"" + name + "\" AND ADDRESS=\"" + address + "\""); + boolean bExisted = false; + while (rs.next()) { + bExisted = true; + break; + } + stmt.close(); + c.close(); + return bExisted; + } + +} diff --git a/src/main/java/io/proximax/app/utils/LocalFileHelpers.java b/src/main/java/io/proximax/app/utils/LocalFileHelpers.java new file mode 100644 index 0000000..63b88e6 --- /dev/null +++ b/src/main/java/io/proximax/app/utils/LocalFileHelpers.java @@ -0,0 +1,543 @@ +package io.proximax.app.utils; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.proximax.download.DownloadParameter; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.ShareFile; +import io.proximax.app.db.LocalFile; +import io.proximax.upload.ByteArrayParameterData; +import io.proximax.upload.FileParameterData; +import io.proximax.upload.UploadParameter; +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.activation.MimetypesFileTypeMap; + +/** + * + * @author Thu Cao + */ +public class LocalFileHelpers { + + public static boolean isExisted(LocalAccount localAccount, File file, int shareType) { + try { + return DBHelpers.isFileExisted(localAccount.fullName, localAccount.network, file.getName(), file.lastModified(), shareType); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return false; + } + + public static LocalFile getFile(String fullName, String network, String fileName, int status) { + try { + return DBHelpers.getFile(fullName, network, fileName, status); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static void addFile(LocalAccount localAccount, LocalFile localFile) { + try { + LocalFile oldFile = getFile(localAccount.fullName, localAccount.network, localFile.fileName, CONST.FILE_STATUS_NOR); + if (oldFile == null) { + DBHelpers.addFile(localAccount.fullName, localAccount.network, localFile); + } else { + DBHelpers.updateFile(localAccount.fullName, localAccount.network, oldFile, localFile); + } + localAccount.used += localFile.fileSize; + DBHelpers.updateUserSpace(localAccount.fullName, localAccount.network, localAccount.used); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void addSharedFile(LocalAccount localAccount, LocalFile localFile) { + try { + LocalFile oldFile = getFile(localAccount.fullName, localAccount.network, localFile.fileName, CONST.FILE_STATUS_NOR); + if (oldFile == null) { + DBHelpers.addFile(localAccount.fullName, localAccount.network, localFile); + } else { + DBHelpers.updateFile(localAccount.fullName, localAccount.network, oldFile, localFile); + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static List getFiles(String fullName, String network) { + try { + DBHelpers.updateFileStatus(fullName, network, System.currentTimeMillis() - CONST.MONITOR_TIMEOUT); //timeout 3p + return DBHelpers.getFiles(fullName, network); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static List getFolders(String fullName, String network, LocalFile parentFolder) { + try { + return DBHelpers.getFolders(fullName, network, parentFolder); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static List getFilesFolder(String fullName, String network, String folder) { + try { + DBHelpers.updateFileStatus(fullName, network, System.currentTimeMillis() - CONST.MONITOR_TIMEOUT); //timeout 3p + return DBHelpers.getFilesFolder(fullName, network, folder); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static List getSharingFiles(String fullName, String network, String folder) { + try { + return DBHelpers.getSharingFiles(fullName, network, folder); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static DownloadParameter createDownloadParameter(LocalFile localFile) { + switch (localFile.uType) { + case CONST.UTYPE_SECURE_NEMKEYS: + return DownloadParameter.create(localFile.nemHash) + .withNemKeysPrivacy(localFile.privateKey, localFile.publicKey) + .build(); + case CONST.UTYPE_SECURE_PASSWORD: + return DownloadParameter.create(localFile.nemHash) + .withPasswordPrivacy(localFile.password) + .build(); + default: + return DownloadParameter.create(localFile.nemHash) + .build(); + } + } + + public static void shareLocalFile(LocalAccount localAccount, ShareFile shareFile) { + try { + DBHelpers.shareLocalFile(localAccount.fullName, localAccount.network, shareFile); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static String getContentType(String fileName) { + String mimeType = null; + if (fileName.endsWith(".html")) { + mimeType = "text/html"; + } else if (fileName.endsWith(".css")) { + mimeType = "text/css"; + } else if (fileName.endsWith(".js")) { + mimeType = "application/javascript"; + } else if (fileName.endsWith(".gif")) { + mimeType = "image/gif"; + } else if (fileName.endsWith(".png")) { + mimeType = "image/png"; + } else if (fileName.endsWith(".txt") || fileName.endsWith(".log")) { + mimeType = "text/plain"; + } else if (fileName.endsWith(".xml")) { + mimeType = "application/xml"; + } else if (fileName.endsWith(".json")) { + mimeType = "application/json"; + } else { + MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); + mimeType = mimeTypesMap.getContentType(fileName); + } + return mimeType; + } + + public static String getContentType(File file) { + return getContentType(file.getPath()); + } + + public static UploadParameter createUploadFileParameter(LocalAccount localAccount, LocalFile localFile, File uploadFile) throws IOException { + Map metaData = null; + if (StringUtils.isEmpty(localFile.metadata)) { + metaData = createMetaData(localAccount, localFile); + localFile.metadata = metaData.toString(); + } else { + try { + Gson gson = new Gson(); + metaData = gson.fromJson(localFile.metadata, new TypeToken>() { + }.getType()); + } catch (Exception ex) { + metaData = createMetaData(localAccount, localFile); + } + } + switch (localFile.uType) { + case CONST.UTYPE_SECURE_NEMKEYS: + return UploadParameter + .createForFileUpload( + FileParameterData.create( + uploadFile, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + new MimetypesFileTypeMap().getContentType(uploadFile), + metaData), + localFile.privateKey) + .withRecipientAddress(localFile.address) + .withNemKeysPrivacy(localFile.privateKey, localFile.publicKey) + .build(); + case CONST.UTYPE_SECURE_PASSWORD: + return UploadParameter + .createForFileUpload( + FileParameterData.create( + uploadFile, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + new MimetypesFileTypeMap().getContentType(uploadFile), + metaData), + localFile.privateKey) + .withRecipientAddress(localFile.address) + .withPasswordPrivacy(localFile.password) + .build(); + default: + return UploadParameter + .createForFileUpload( + FileParameterData.create( + uploadFile, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + new MimetypesFileTypeMap().getContentType(uploadFile), + metaData), + localFile.privateKey) + .withRecipientAddress(localFile.address) + .build(); + } + } + + public static void updateFile(String fullName, String network, LocalFile oldFile, LocalFile newFile) { + try { + DBHelpers.updateFile(fullName, network, oldFile, newFile); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void updateLocalFile(String userName, String network, int fileId, String hash, String nemHash, long uploadDate, int status) { + try { + DBHelpers.updateLocalFile(userName, network, fileId, hash, nemHash, uploadDate, status); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void updateLocalFileStatus(String userName, String network, int fileId, int status) { + try { + DBHelpers.updateLocalFileStatus(userName, network, fileId, status); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static List getDelFiles(String fullName, String network) { + try { + return DBHelpers.getDelFiles(fullName, network); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static Map createMetaData(LocalAccount localAccount, LocalFile localFile) { + Map metaData = new HashMap(); + metaData.put("file", localFile.fileName); + metaData.put("app", CONST.APP_NAME); + metaData.put("user", localAccount.fullName); + metaData.put("network", localAccount.network); + metaData.put("utype", "" + localFile.uType); + metaData.put("size", "" + localFile.fileSize); + return metaData; + } + + public static void updateFileFromTransaction(String fullName, String network, String nemHash, int status) { + try { + DBHelpers.updateFile(fullName, network, nemHash, status); + } catch (SQLException ex) { + } + } + + public static UploadParameter createUploadBinaryParameter(LocalAccount localAccount, LocalFile localFile, byte[] data) { + Map metaData = createMetaData(localAccount, localFile); + localFile.metadata = metaData.toString(); + switch (localFile.uType) { + case CONST.UTYPE_SECURE_NEMKEYS: + return UploadParameter + .createForByteArrayUpload( + ByteArrayParameterData.create( + data, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + "", + metaData), + localFile.privateKey) + .withNemKeysPrivacy(localFile.privateKey, localFile.publicKey) + .withRecipientAddress(localFile.address) + .build(); + case CONST.UTYPE_SECURE_PASSWORD: + return UploadParameter + .createForByteArrayUpload( + ByteArrayParameterData.create( + data, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + "", + metaData), + localFile.privateKey) + .withPasswordPrivacy(localFile.password) + .withRecipientAddress(localFile.address) + .build(); + default: + return UploadParameter + .createForByteArrayUpload( + ByteArrayParameterData.create( + data, + "Uploaded by " + CONST.APP_NAME, + localFile.fileName, + "", + metaData), + localFile.privateKey) + .withRecipientAddress(localFile.address) + .build(); + } + } + + public static List getHistoryFile(String fullName, String network, int fileId) { + try { + return DBHelpers.getHistoryFile(fullName, network, fileId); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static List getAllFolder(LocalAccount localAccount) { + try { + return DBHelpers.getAllFolder(localAccount.fullName, localAccount.network); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return null; + } + + public static void addFolder(LocalAccount localAccount, String newFolder, LocalFile parentFolder) { + try { + DBHelpers.addFolder(localAccount.fullName, localAccount.network, newFolder, parentFolder); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + +// public static void updateFolder(String fullName, String network, int folderId, String folder, String parent) { +// try { +// DBHelpers.updateFolder(fullName, network, folderId, folder, parent); +// } catch (SQLException ex) { +// ex.printStackTrace(); +// } +// } + public static void moveFileFolder(LocalAccount localAccount, int id, String sFolder) { + try { + DBHelpers.moveFileFolder(localAccount.fullName, localAccount.network, id, sFolder); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static boolean isShared(String fullName, String network, int fileId, String address) { + try { + return DBHelpers.isShared(fullName, network, fileId, address); + } catch (SQLException ex) { + ex.printStackTrace(); + } + return false; + } + + public static boolean isViewSupport(LocalFile localFile) { + String mimeType = MimeTypes.getMimeType(localFile.fileName); + if (mimeType.contains("video") || mimeType.contains("audio") || mimeType.contains("pdf") || mimeType.contains("image") + || MimeTypes.isPlainText(localFile.fileName)) { + return true; + } + return false; + } + + public static boolean isEditSupport(LocalFile localFile) { + return MimeTypes.isPlainText(localFile.fileName); + } + + public static File getSourceFile(LocalAccount localAccount, LocalFile localFile) { + File file = new File(localFile.filePath); + if (file.exists()) { + if (file.length() == localFile.fileSize && file.lastModified() == localFile.modified) { + return file; + } + } + String ext = getFileExtension(localFile.fileName); + file = new File(getCacheDir(localAccount) + File.separator + localFile.nemHash + ext); + if (file.exists()) { + return file; + } + return null; + } + + public static String getFileExtension(String fileName) { + return getFileExtension(new File(fileName)); + } + + public static String getFileExtension(File file) { + String extension; + try { + String name = file.getName(); + extension = name.substring(name.lastIndexOf(".")); + } catch (Exception e) { + extension = ""; + } + return extension; + } + + public static File createFileCache(LocalAccount localAccount, LocalFile localFile) { + String ext = getFileExtension(localFile.fileName); + return new File(getCacheDir(localAccount) + File.separator + localFile.nemHash + ext); + } + + public static String getCacheDir(LocalAccount localAccount) { + String filePath = System.getProperty("user.home") + File.separator + CONST.APP_FOLDER + File.separator + ".cache" + File.separator + localAccount.network + File.separator + localAccount.fullName; + new File(filePath).mkdirs(); + return filePath; + } + + public static void deleteFile(LocalAccount localAccount, LocalFile localFile) { + try { + DBHelpers.delFile(localAccount.fullName, localAccount.network, localFile.id); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void renameLocalFile(LocalAccount localAccount, LocalFile localFile, String name) { + try { + DBHelpers.renameLocalFile(localAccount.fullName, localAccount.network, localFile.id, name); + localFile.reName = name; + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static int checkNameExisted(LocalAccount localAccount, String folder, String name) { + try { + if (DBHelpers.isNameExisted(localAccount.fullName, localAccount.network, folder, name)) { + return 1; + } + } catch (SQLException ex) { + ex.printStackTrace(); + return -1; + } + return 0; + } + + public static LocalFile getFolder(LocalAccount localAccount, String filePath) { + try { + return DBHelpers.getFolder(localAccount.fullName, localAccount.network, filePath); + } catch (SQLException ex) { + } + return null; + } + + public static int checkFolderExisted(LocalAccount localAccount, String filePath) { + try { + if (DBHelpers.isFolderExisted(localAccount.fullName, localAccount.network, filePath)) { + return 1; + } + } catch (SQLException ex) { + ex.printStackTrace(); + return -1; + } + return 0; + } + + public static LocalFile getFolder(LocalAccount localAccount, int fileId) { + try { + return DBHelpers.getFolder(localAccount.fullName, localAccount.network, fileId); + } catch (SQLException ex) { + } + return null; + + } + + public static void deleteFolder(LocalAccount localAccount, LocalFile folder) { + List folders = getFolders(localAccount.fullName, localAccount.network, folder); + for (LocalFile f : folders) { + deleteFolder(localAccount, f); + } + try { + DBHelpers.deleteFileInFolder(localAccount.fullName, localAccount.network, folder.filePath); + } catch (SQLException ex) { + } + try { + DBHelpers.deleteFolder(localAccount.fullName, localAccount.network, folder.id); + } catch (SQLException ex) { + } + + } + + public static void updateFolderPath(LocalAccount localAccount, LocalFile curFolder, String newPath, int level) { + List folders = getFolders(localAccount.fullName, localAccount.network, curFolder); + if (level != -1) { + level += 1; + } + for (LocalFile f : folders) { + updateFolderPath(localAccount, f, newPath + "/" + f.category, level); + } + try { + DBHelpers.updateFileFolderPath(localAccount.fullName, localAccount.network, curFolder.filePath, newPath); + } catch (SQLException ex) { + } + try { + DBHelpers.updateFolderPath(localAccount.fullName, localAccount.network, curFolder.id, newPath, level); + } catch (SQLException ex) { + } + } + + public static void renameFolder(LocalAccount localAccount, LocalFile curFolder, String newName) { + int idx = curFolder.filePath.lastIndexOf(curFolder.category); + String filePath = curFolder.filePath.substring(0, idx) + newName; + updateFolderPath(localAccount, curFolder, filePath, -1); + try { + DBHelpers.renameFolder(localAccount.fullName, localAccount.network, curFolder, newName); + } catch (SQLException ex) { + } + } + + public static void moveFolderToFolder(LocalAccount localAccount, LocalFile srcFolder, LocalFile destFolder) { + String filePath = CONST.HOME + srcFolder.category; + int level = 0; + int parentId = 0; + if (destFolder != null) { + filePath = destFolder.filePath + "/" + srcFolder.category; + level = destFolder.uType; + parentId = destFolder.id; + } + updateFolderPath(localAccount, srcFolder, filePath, level); + try { + DBHelpers.moveFolderToFolder(localAccount.fullName, localAccount.network, srcFolder.id, parentId, level + 1, filePath); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void moveFolderToFolder(LocalAccount localAccount, LocalFile srcFolder, String sFolder) { + LocalFile destFolder = LocalFileHelpers.getFolder(localAccount, sFolder); + moveFolderToFolder(localAccount, srcFolder, destFolder); + } + +} diff --git a/src/main/java/io/proximax/app/utils/MimeTypes.java b/src/main/java/io/proximax/app/utils/MimeTypes.java new file mode 100644 index 0000000..8e2c6b5 --- /dev/null +++ b/src/main/java/io/proximax/app/utils/MimeTypes.java @@ -0,0 +1,357 @@ +package io.proximax.app.utils; + +/** + * + * @author Thu Cao + */ +import java.io.File; +import java.util.HashMap; + +public class MimeTypes { + + public static final String MIME_APPLICATION_ANDREW_INSET = "application/andrew-inset"; + public static final String MIME_APPLICATION_JSON = "application/json"; + public static final String MIME_APPLICATION_ZIP = "application/zip"; + public static final String MIME_APPLICATION_X_GZIP = "application/x-gzip"; + public static final String MIME_APPLICATION_TGZ = "application/tgz"; + public static final String MIME_APPLICATION_MSWORD = "application/msword"; + public static final String MIME_APPLICATION_POSTSCRIPT = "application/postscript"; + public static final String MIME_APPLICATION_PDF = "application/pdf"; + public static final String MIME_APPLICATION_JNLP = "application/jnlp"; + public static final String MIME_APPLICATION_MAC_BINHEX40 = "application/mac-binhex40"; + public static final String MIME_APPLICATION_MAC_COMPACTPRO = "application/mac-compactpro"; + public static final String MIME_APPLICATION_MATHML_XML = "application/mathml+xml"; + public static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream"; + public static final String MIME_APPLICATION_ODA = "application/oda"; + public static final String MIME_APPLICATION_RDF_XML = "application/rdf+xml"; + public static final String MIME_APPLICATION_JAVA_ARCHIVE = "application/java-archive"; + public static final String MIME_APPLICATION_RDF_SMIL = "application/smil"; + public static final String MIME_APPLICATION_SRGS = "application/srgs"; + public static final String MIME_APPLICATION_SRGS_XML = "application/srgs+xml"; + public static final String MIME_APPLICATION_VND_MIF = "application/vnd.mif"; + public static final String MIME_APPLICATION_VND_MSEXCEL = "application/vnd.ms-excel"; + public static final String MIME_APPLICATION_VND_MSPOWERPOINT = "application/vnd.ms-powerpoint"; + public static final String MIME_APPLICATION_VND_RNREALMEDIA = "application/vnd.rn-realmedia"; + public static final String MIME_APPLICATION_X_BCPIO = "application/x-bcpio"; + public static final String MIME_APPLICATION_X_CDLINK = "application/x-cdlink"; + public static final String MIME_APPLICATION_X_CHESS_PGN = "application/x-chess-pgn"; + public static final String MIME_APPLICATION_X_CPIO = "application/x-cpio"; + public static final String MIME_APPLICATION_X_CSH = "application/x-csh"; + public static final String MIME_APPLICATION_X_DIRECTOR = "application/x-director"; + public static final String MIME_APPLICATION_X_DVI = "application/x-dvi"; + public static final String MIME_APPLICATION_X_FUTURESPLASH = "application/x-futuresplash"; + public static final String MIME_APPLICATION_X_GTAR = "application/x-gtar"; + public static final String MIME_APPLICATION_X_HDF = "application/x-hdf"; + public static final String MIME_APPLICATION_X_JAVASCRIPT = "application/x-javascript"; + public static final String MIME_APPLICATION_X_KOAN = "application/x-koan"; + public static final String MIME_APPLICATION_X_LATEX = "application/x-latex"; + public static final String MIME_APPLICATION_X_NETCDF = "application/x-netcdf"; + public static final String MIME_APPLICATION_X_OGG = "application/x-ogg"; + public static final String MIME_APPLICATION_X_SH = "application/x-sh"; + public static final String MIME_APPLICATION_X_SHAR = "application/x-shar"; + public static final String MIME_APPLICATION_X_SHOCKWAVE_FLASH = "application/x-shockwave-flash"; + public static final String MIME_APPLICATION_X_STUFFIT = "application/x-stuffit"; + public static final String MIME_APPLICATION_X_SV4CPIO = "application/x-sv4cpio"; + public static final String MIME_APPLICATION_X_SV4CRC = "application/x-sv4crc"; + public static final String MIME_APPLICATION_X_TAR = "application/x-tar"; + public static final String MIME_APPLICATION_X_RAR_COMPRESSED = "application/x-rar-compressed"; + public static final String MIME_APPLICATION_X_TCL = "application/x-tcl"; + public static final String MIME_APPLICATION_X_TEX = "application/x-tex"; + public static final String MIME_APPLICATION_X_TEXINFO = "application/x-texinfo"; + public static final String MIME_APPLICATION_X_TROFF = "application/x-troff"; + public static final String MIME_APPLICATION_X_TROFF_MAN = "application/x-troff-man"; + public static final String MIME_APPLICATION_X_TROFF_ME = "application/x-troff-me"; + public static final String MIME_APPLICATION_X_TROFF_MS = "application/x-troff-ms"; + public static final String MIME_APPLICATION_X_USTAR = "application/x-ustar"; + public static final String MIME_APPLICATION_X_WAIS_SOURCE = "application/x-wais-source"; + public static final String MIME_APPLICATION_VND_MOZZILLA_XUL_XML = "application/vnd.mozilla.xul+xml"; + public static final String MIME_APPLICATION_XHTML_XML = "application/xhtml+xml"; + public static final String MIME_APPLICATION_XSLT_XML = "application/xslt+xml"; + public static final String MIME_APPLICATION_XML = "application/xml"; + public static final String MIME_APPLICATION_XML_DTD = "application/xml-dtd"; + public static final String MIME_IMAGE_BMP = "image/bmp"; + public static final String MIME_IMAGE_CGM = "image/cgm"; + public static final String MIME_IMAGE_GIF = "image/gif"; + public static final String MIME_IMAGE_IEF = "image/ief"; + public static final String MIME_IMAGE_JPEG = "image/jpeg"; + public static final String MIME_IMAGE_TIFF = "image/tiff"; + public static final String MIME_IMAGE_PNG = "image/png"; + public static final String MIME_IMAGE_SVG_XML = "image/svg+xml"; + public static final String MIME_IMAGE_VND_DJVU = "image/vnd.djvu"; + public static final String MIME_IMAGE_WAP_WBMP = "image/vnd.wap.wbmp"; + public static final String MIME_IMAGE_X_CMU_RASTER = "image/x-cmu-raster"; + public static final String MIME_IMAGE_X_ICON = "image/x-icon"; + public static final String MIME_IMAGE_X_PORTABLE_ANYMAP = "image/x-portable-anymap"; + public static final String MIME_IMAGE_X_PORTABLE_BITMAP = "image/x-portable-bitmap"; + public static final String MIME_IMAGE_X_PORTABLE_GRAYMAP = "image/x-portable-graymap"; + public static final String MIME_IMAGE_X_PORTABLE_PIXMAP = "image/x-portable-pixmap"; + public static final String MIME_IMAGE_X_RGB = "image/x-rgb"; + public static final String MIME_AUDIO_BASIC = "audio/basic"; + public static final String MIME_AUDIO_MIDI = "audio/midi"; + public static final String MIME_AUDIO_MPEG = "audio/mpeg"; + public static final String MIME_AUDIO_X_AIFF = "audio/x-aiff"; + public static final String MIME_AUDIO_X_MPEGURL = "audio/x-mpegurl"; + public static final String MIME_AUDIO_X_PN_REALAUDIO = "audio/x-pn-realaudio"; + public static final String MIME_AUDIO_X_WAV = "audio/x-wav"; + public static final String MIME_CHEMICAL_X_PDB = "chemical/x-pdb"; + public static final String MIME_CHEMICAL_X_XYZ = "chemical/x-xyz"; + public static final String MIME_MODEL_IGES = "model/iges"; + public static final String MIME_MODEL_MESH = "model/mesh"; + public static final String MIME_MODEL_VRLM = "model/vrml"; + public static final String MIME_TEXT_PLAIN = "text/plain"; + public static final String MIME_TEXT_RICHTEXT = "text/richtext"; + public static final String MIME_TEXT_RTF = "text/rtf"; + public static final String MIME_TEXT_HTML = "text/html"; + public static final String MIME_TEXT_CALENDAR = "text/calendar"; + public static final String MIME_TEXT_CSS = "text/css"; + public static final String MIME_TEXT_SGML = "text/sgml"; + public static final String MIME_TEXT_TAB_SEPARATED_VALUES = "text/tab-separated-values"; + public static final String MIME_TEXT_VND_WAP_XML = "text/vnd.wap.wml"; + public static final String MIME_TEXT_VND_WAP_WMLSCRIPT = "text/vnd.wap.wmlscript"; + public static final String MIME_TEXT_X_SETEXT = "text/x-setext"; + public static final String MIME_TEXT_X_COMPONENT = "text/x-component"; + public static final String MIME_VIDEO_QUICKTIME = "video/quicktime"; + public static final String MIME_VIDEO_MPEG = "video/mpeg"; + public static final String MIME_VIDEO_VND_MPEGURL = "video/vnd.mpegurl"; + public static final String MIME_VIDEO_X_MSVIDEO = "video/x-msvideo"; + public static final String MIME_VIDEO_X_MS_WMV = "video/x-ms-wmv"; + public static final String MIME_VIDEO_X_SGI_MOVIE = "video/x-sgi-movie"; + public static final String MIME_VIDEO_X_FLV = "video/x-flv"; + public static final String MIME_X_CONFERENCE_X_COOLTALK = "x-conference/x-cooltalk"; + + private static HashMap mimeTypeMapping; + + static { + mimeTypeMapping = new HashMap(200) { + private void put1(String key, String value) { + if (put(key, value) != null) { + throw new IllegalArgumentException("Duplicated extension: " + key); + } + } + + { + put1(".xul", MIME_APPLICATION_VND_MOZZILLA_XUL_XML); + put1(".json", MIME_APPLICATION_JSON); + put1(".ice", MIME_X_CONFERENCE_X_COOLTALK); + put1(".movie", MIME_VIDEO_X_SGI_MOVIE); + put1(".avi", MIME_VIDEO_X_MSVIDEO); + put1(".wmv", MIME_VIDEO_X_MS_WMV); + put1(".m4u", MIME_VIDEO_VND_MPEGURL); + put1(".mxu", MIME_VIDEO_VND_MPEGURL); + put1(".htc", MIME_TEXT_X_COMPONENT); + put1(".etx", MIME_TEXT_X_SETEXT); + put1(".wmls", MIME_TEXT_VND_WAP_WMLSCRIPT); + put1(".wml", MIME_TEXT_VND_WAP_XML); + put1(".tsv", MIME_TEXT_TAB_SEPARATED_VALUES); + put1(".sgm", MIME_TEXT_SGML); + put1(".sgml", MIME_TEXT_SGML); + put1(".css", MIME_TEXT_CSS); + put1(".ifb", MIME_TEXT_CALENDAR); + put1(".ics", MIME_TEXT_CALENDAR); + put1(".wrl", MIME_MODEL_VRLM); + put1(".vrlm", MIME_MODEL_VRLM); + put1(".silo", MIME_MODEL_MESH); + put1(".mesh", MIME_MODEL_MESH); + put1(".msh", MIME_MODEL_MESH); + put1(".iges", MIME_MODEL_IGES); + put1(".igs", MIME_MODEL_IGES); + put1(".rgb", MIME_IMAGE_X_RGB); + put1(".ppm", MIME_IMAGE_X_PORTABLE_PIXMAP); + put1(".pgm", MIME_IMAGE_X_PORTABLE_GRAYMAP); + put1(".pbm", MIME_IMAGE_X_PORTABLE_BITMAP); + put1(".pnm", MIME_IMAGE_X_PORTABLE_ANYMAP); + put1(".ico", MIME_IMAGE_X_ICON); + put1(".ras", MIME_IMAGE_X_CMU_RASTER); + put1(".wbmp", MIME_IMAGE_WAP_WBMP); + put1(".djv", MIME_IMAGE_VND_DJVU); + put1(".djvu", MIME_IMAGE_VND_DJVU); + put1(".svg", MIME_IMAGE_SVG_XML); + put1(".ief", MIME_IMAGE_IEF); + put1(".cgm", MIME_IMAGE_CGM); + put1(".bmp", MIME_IMAGE_BMP); + put1(".xyz", MIME_CHEMICAL_X_XYZ); + put1(".pdb", MIME_CHEMICAL_X_PDB); + put1(".ra", MIME_AUDIO_X_PN_REALAUDIO); + put1(".ram", MIME_AUDIO_X_PN_REALAUDIO); + put1(".m3u", MIME_AUDIO_X_MPEGURL); + put1(".aifc", MIME_AUDIO_X_AIFF); + put1(".aif", MIME_AUDIO_X_AIFF); + put1(".aiff", MIME_AUDIO_X_AIFF); + put1(".mp3", MIME_AUDIO_MPEG); + put1(".mp2", MIME_AUDIO_MPEG); + put1(".mp1", MIME_AUDIO_MPEG); + put1(".mpga", MIME_AUDIO_MPEG); + put1(".kar", MIME_AUDIO_MIDI); + put1(".mid", MIME_AUDIO_MIDI); + put1(".midi", MIME_AUDIO_MIDI); + put1(".dtd", MIME_APPLICATION_XML_DTD); + put1(".xsl", MIME_APPLICATION_XML); + put1(".xml", MIME_APPLICATION_XML); + put1(".xslt", MIME_APPLICATION_XSLT_XML); + put1(".xht", MIME_APPLICATION_XHTML_XML); + put1(".xhtml", MIME_APPLICATION_XHTML_XML); + put1(".src", MIME_APPLICATION_X_WAIS_SOURCE); + put1(".ustar", MIME_APPLICATION_X_USTAR); + put1(".ms", MIME_APPLICATION_X_TROFF_MS); + put1(".me", MIME_APPLICATION_X_TROFF_ME); + put1(".man", MIME_APPLICATION_X_TROFF_MAN); + put1(".roff", MIME_APPLICATION_X_TROFF); + put1(".tr", MIME_APPLICATION_X_TROFF); + put1(".t", MIME_APPLICATION_X_TROFF); + put1(".texi", MIME_APPLICATION_X_TEXINFO); + put1(".texinfo", MIME_APPLICATION_X_TEXINFO); + put1(".tex", MIME_APPLICATION_X_TEX); + put1(".tcl", MIME_APPLICATION_X_TCL); + put1(".sv4crc", MIME_APPLICATION_X_SV4CRC); + put1(".sv4cpio", MIME_APPLICATION_X_SV4CPIO); + put1(".sit", MIME_APPLICATION_X_STUFFIT); + put1(".swf", MIME_APPLICATION_X_SHOCKWAVE_FLASH); + put1(".shar", MIME_APPLICATION_X_SHAR); + put1(".sh", MIME_APPLICATION_X_SH); + put1(".cdf", MIME_APPLICATION_X_NETCDF); + put1(".nc", MIME_APPLICATION_X_NETCDF); + put1(".latex", MIME_APPLICATION_X_LATEX); + put1(".skm", MIME_APPLICATION_X_KOAN); + put1(".skt", MIME_APPLICATION_X_KOAN); + put1(".skd", MIME_APPLICATION_X_KOAN); + put1(".skp", MIME_APPLICATION_X_KOAN); + put1(".js", MIME_APPLICATION_X_JAVASCRIPT); + put1(".hdf", MIME_APPLICATION_X_HDF); + put1(".gtar", MIME_APPLICATION_X_GTAR); + put1(".spl", MIME_APPLICATION_X_FUTURESPLASH); + put1(".dvi", MIME_APPLICATION_X_DVI); + put1(".dxr", MIME_APPLICATION_X_DIRECTOR); + put1(".dir", MIME_APPLICATION_X_DIRECTOR); + put1(".dcr", MIME_APPLICATION_X_DIRECTOR); + put1(".csh", MIME_APPLICATION_X_CSH); + put1(".cpio", MIME_APPLICATION_X_CPIO); + put1(".pgn", MIME_APPLICATION_X_CHESS_PGN); + put1(".vcd", MIME_APPLICATION_X_CDLINK); + put1(".bcpio", MIME_APPLICATION_X_BCPIO); + put1(".rm", MIME_APPLICATION_VND_RNREALMEDIA); + put1(".ppt", MIME_APPLICATION_VND_MSPOWERPOINT); + put1(".mif", MIME_APPLICATION_VND_MIF); + put1(".grxml", MIME_APPLICATION_SRGS_XML); + put1(".gram", MIME_APPLICATION_SRGS); + put1(".smil", MIME_APPLICATION_RDF_SMIL); + put1(".smi", MIME_APPLICATION_RDF_SMIL); + put1(".rdf", MIME_APPLICATION_RDF_XML); + put1(".ogg", MIME_APPLICATION_X_OGG); + put1(".oda", MIME_APPLICATION_ODA); + put1(".dmg", MIME_APPLICATION_OCTET_STREAM); + put1(".lzh", MIME_APPLICATION_OCTET_STREAM); + put1(".so", MIME_APPLICATION_OCTET_STREAM); + put1(".lha", MIME_APPLICATION_OCTET_STREAM); + put1(".dms", MIME_APPLICATION_OCTET_STREAM); + put1(".bin", MIME_APPLICATION_OCTET_STREAM); + put1(".mathml", MIME_APPLICATION_MATHML_XML); + put1(".cpt", MIME_APPLICATION_MAC_COMPACTPRO); + put1(".hqx", MIME_APPLICATION_MAC_BINHEX40); + put1(".jnlp", MIME_APPLICATION_JNLP); + put1(".ez", MIME_APPLICATION_ANDREW_INSET); + put1(".txt", MIME_TEXT_PLAIN); + put1(".ini", MIME_TEXT_PLAIN); + put1(".in", MIME_TEXT_PLAIN); + put1(".log", MIME_TEXT_PLAIN); + put1(".c", MIME_TEXT_PLAIN); + put1(".h", MIME_TEXT_PLAIN); + put1(".cpp", MIME_TEXT_PLAIN); + put1(".cxx", MIME_TEXT_PLAIN); + put1(".cc", MIME_TEXT_PLAIN); + put1(".chh", MIME_TEXT_PLAIN); + put1(".java", MIME_TEXT_PLAIN); + put1(".csv", MIME_TEXT_PLAIN); + put1(".bat", MIME_TEXT_PLAIN); + put1(".cmd", MIME_TEXT_PLAIN); + put1(".asc", MIME_TEXT_PLAIN); + put1(".rtf", MIME_TEXT_RTF); + put1(".rtx", MIME_TEXT_RICHTEXT); + put1(".html", MIME_TEXT_HTML); + put1(".htm", MIME_TEXT_HTML); + put1(".zip", MIME_APPLICATION_ZIP); + put1(".rar", MIME_APPLICATION_X_RAR_COMPRESSED); + put1(".gzip", MIME_APPLICATION_X_GZIP); + put1(".gz", MIME_APPLICATION_X_GZIP); + put1(".tgz", MIME_APPLICATION_TGZ); + put1(".tar", MIME_APPLICATION_X_TAR); + put1(".gif", MIME_IMAGE_GIF); + put1(".jpeg", MIME_IMAGE_JPEG); + put1(".jpg", MIME_IMAGE_JPEG); + put1(".jpe", MIME_IMAGE_JPEG); + put1(".tiff", MIME_IMAGE_TIFF); + put1(".tif", MIME_IMAGE_TIFF); + put1(".png", MIME_IMAGE_PNG); + put1(".au", MIME_AUDIO_BASIC); + put1(".snd", MIME_AUDIO_BASIC); + put1(".wav", MIME_AUDIO_X_WAV); + put1(".mov", MIME_VIDEO_QUICKTIME); + put1(".qt", MIME_VIDEO_QUICKTIME); + put1(".mp4", MIME_VIDEO_MPEG); + put1(".mpeg", MIME_VIDEO_MPEG); + put1(".mpg", MIME_VIDEO_MPEG); + put1(".mpe", MIME_VIDEO_MPEG); + put1(".abs", MIME_VIDEO_MPEG); + put1(".doc", MIME_APPLICATION_MSWORD); + put1(".xls", MIME_APPLICATION_VND_MSEXCEL); + put1(".eps", MIME_APPLICATION_POSTSCRIPT); + put1(".ai", MIME_APPLICATION_POSTSCRIPT); + put1(".ps", MIME_APPLICATION_POSTSCRIPT); + put1(".pdf", MIME_APPLICATION_PDF); + put1(".exe", MIME_APPLICATION_OCTET_STREAM); + put1(".dll", MIME_APPLICATION_OCTET_STREAM); + put1(".class", MIME_APPLICATION_OCTET_STREAM); + put1(".jar", MIME_APPLICATION_JAVA_ARCHIVE); + put1(".flv", MIME_VIDEO_X_FLV); + } + }; + } + + /** + * Registers MIME type for provided extension. Existing extension type will + * be overriden. + */ + public static void registerMimeType(String ext, String mimeType) { + mimeTypeMapping.put(ext, mimeType); + } + + /** + * Returns the corresponding MIME type to the given extension. If no MIME + * type was found it returns 'application/octet-stream' type. + */ + public static String getMimeType(String fileName) { + int idx = fileName.lastIndexOf("."); + String mimeType = null; + if (idx != -1) { + String ext = fileName.substring(idx); + mimeType = lookupMimeType(ext); + } + if (mimeType == null) { + mimeType = MIME_APPLICATION_OCTET_STREAM; + } + return mimeType; + } + + /** + * Returns the corresponding MIME type to the given extension. If no MIME + * type was found it returns 'application/octet-stream' type. + */ + public static String getMimeType(File file) { + return getMimeType(file.getPath()); + } + + /** + * Simply returns MIME type or null if no type is found. + */ + public static String lookupMimeType(String ext) { + return mimeTypeMapping.get(ext.toLowerCase()); + } + + + public static boolean isPlainText(String fileName) { + String mimeType = getMimeType(fileName); + if (mimeType.contains("text") || mimeType.contains("xml") || mimeType.contains("json") || mimeType.contains("js")) { + return true; + } + return false; + } +} diff --git a/src/main/java/io/proximax/app/utils/NetworkUtils.java b/src/main/java/io/proximax/app/utils/NetworkUtils.java new file mode 100644 index 0000000..1ebfa6a --- /dev/null +++ b/src/main/java/io/proximax/app/utils/NetworkUtils.java @@ -0,0 +1,386 @@ +package io.proximax.app.utils; + +import io.proximax.sdk.model.account.Account; +import io.proximax.sdk.model.account.AccountInfo; +import io.proximax.sdk.model.account.Address; +import io.proximax.sdk.model.blockchain.NetworkType; +import io.proximax.sdk.model.mosaic.Mosaic; +import io.proximax.sdk.model.transaction.Deadline; +import io.proximax.sdk.model.transaction.PlainMessage; +import io.proximax.sdk.model.transaction.SignedTransaction; +import io.proximax.sdk.model.transaction.TransferTransaction; +import io.proximax.app.db.LocalAccount; +import io.proximax.app.db.NetworkConfiguration; +import io.proximax.connection.BlockchainNetworkConnection; +import io.proximax.connection.ConnectionConfig; +import io.proximax.connection.HttpProtocol; +import io.proximax.connection.IpfsConnection; +import io.proximax.model.BlockchainNetworkType; +import io.proximax.sdk.AccountRepository; +import io.proximax.sdk.BlockchainApi; +import io.proximax.sdk.FeeCalculationStrategy; +import io.proximax.sdk.MosaicRepository; +import io.proximax.sdk.TransactionRepository; +import io.proximax.sdk.model.mosaic.MosaicId; +import io.proximax.sdk.model.mosaic.MosaicNames; +import io.proximax.sdk.model.mosaic.NetworkCurrencyMosaic; +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; + +import java.net.URL; +import java.net.URLDecoder; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.apache.commons.io.FileUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +/** + * + * @author thcao + */ +public class NetworkUtils { + + public static String NETWORK_DEFAULT = "TEST_NET"; + public static final String LOCALHOST = "127.0.0.1"; + public static final String BEARER_TOKEN = "11111"; + public static int NODE_DEFAULT_PORT = 3000; + public static int NODE_DEFAULT_WSPORT = 7778; + public static int CONFIG_VERSION = 0; + public final static BigInteger XPX_10 = BigInteger.valueOf(100000); + public static Map NETWORKS = new HashMap<>(); + public static final List NETWORK_SUPPORT = new ArrayList<>(); + public static final List IPFS_SERVER = new ArrayList<>(); + public static final String IPFS_LOCALHOST_URL = "http://localhost:8080/ipfs/"; + public static int IPFS_PORT = 5001; + + static { + //force override config - need remove when release + try { + boolean bOverride = false; + String dbName = System.getProperty("user.home") + File.separator + CONST.APP_FOLDER + File.separator + CONST.DB_APP_CONFIGURATION; + if (new File(dbName).exists()) { + int verRes = DBHelpers.getAppConfigVerFromRes(); + int verLocal = DBHelpers.getAppConfigVerFromLocal(); + if (verRes > verLocal) { + bOverride = true; + } + } + if (bOverride) { + URL configFile = NetworkUtils.class.getResource("/" + CONST.DB_APP_CONFIGURATION); + FileUtils.copyURLToFile(configFile, new File(dbName)); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + initDefaultNetwork(); + } + + public static void initDefaultNetwork() { + try { + Map configs = DBHelpers.getAppConfiguration(); + if (configs != null) { + NETWORK_SUPPORT.addAll(Arrays.asList(configs.get("networks").split(","))); + NETWORK_DEFAULT = configs.get("default"); + CONFIG_VERSION = StringUtils.parseInt(configs.get("version"), 0); + NODE_DEFAULT_PORT = StringUtils.parseInt(configs.get("port"), 3000); + NODE_DEFAULT_WSPORT = StringUtils.parseInt(configs.get("wsport"), 7778); + IPFS_PORT = StringUtils.parseInt(configs.get("ipfsport"), 5001); + IPFS_SERVER.addAll(Arrays.asList(configs.get("ipfs").split(","))); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + loadNetworkConfig(); + } + + public static boolean isNetworkSupport(String network) { + return NETWORKS.containsKey(network); + } + + public static void loadNetworkConfig() { + NETWORKS.clear(); + for (String network : NETWORK_SUPPORT) { + try { + NetworkConfiguration netconf = DBHelpers.getNetworkConfiguration(network); + if (netconf.status > 0) { + put(network, netconf); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + public static void put(String name, NetworkConfiguration network) { + NETWORKS.put(name, network); + } + + public static NetworkConfiguration get(String name) { + return NETWORKS.get(name); + } + + public static BlockchainNetworkType getBlockchainNetworkType(String network) { + return BlockchainNetworkType.fromString(network); + } + + public static NetworkConfiguration getNetworkConfiguration(String network) { + return NETWORKS.get(network); + } + + public static NetworkType getNetworkType(String network) { + return NetworkType.valueOf(network); + } + + public static long getXPXAmount(NetworkType networkType, String apiUrl, String privateKey) { + try { + final Account account = Account.createFromPrivateKey(privateKey, networkType); + BlockchainApi blockchainApi = new BlockchainApi(new URL(apiUrl), networkType); + return getXPXAmount(blockchainApi, account.getAddress()); + } catch (Exception ex) { + } + return 0; + } + + public static long getXPXAmountWallet(NetworkType networkType, String apiUrl, String address) { + try { + BlockchainApi blockchainApi = new BlockchainApi(new URL(apiUrl), networkType); + return getXPXAmount(blockchainApi, Address.createFromRawAddress(address)); + } catch (Exception ex) { + } + return 0; + } + + public static long getXPXAmount(BlockchainApi blockchainApi, Address address) throws Exception { + AccountRepository accountHttp = blockchainApi.createAccountRepository(); + AccountInfo accountInfo = accountHttp.getAccountInfo(address).toFuture().get(); + List mosaicIds = new ArrayList<>(); + List mosaics = accountInfo.getMosaics(); + for (Mosaic mo : mosaics) { + mosaicIds.add(new MosaicId(mo.getId().getId())); + } + MosaicRepository mosaicHttp = blockchainApi.createMosaicRepository(); + List l = mosaicHttp.getMosaicNames(mosaicIds).toFuture().get(); + for (int i = 0; i < l.size(); i++) { + MosaicNames mo = l.get(i); + if (mo.getNames().contains(NetworkCurrencyMosaic.MOSAIC_NAMESPACE)) { + return mosaics.get(i).getAmount().divide(BigDecimal.valueOf(Math.pow(10, NetworkCurrencyMosaic.DIVISIBILITY)).toBigInteger()).longValue(); + } + } + return 0; + } + + public static long getXPX(LocalAccount localAccount) { + NetworkConfiguration netconf = get(localAccount.network); + try { + //send thanks to supporter, public key will have in network + return getXPXAmount(netconf.getNetworkType(), localAccount.getApiUrl(), localAccount.privateKey); + } catch (Exception ex) { + return 0; + } + } + + public static boolean activeAccount1(String network, LocalAccount localAccount) { + NetworkConfiguration netconf = get(network); + try { + //send thanks to supporter, public key will have in network + sendPlainMessage(netconf.getNetworkType(), localAccount.getApiUrl(), localAccount.privateKey, netconf.enc1, "Thanks from" + localAccount.fullName); + Thread.sleep(1000); + localAccount.status = 1; + DBHelpers.updateUserStatus(localAccount.fullName, network, 1); + return true; + } catch (Exception ex) { + return false; + } + } + + public static boolean activeAccount(String network, LocalAccount localAccount) { + if (localAccount.status == 0) { + try { + return activeAccount1(network, localAccount); + } catch (Exception ex) { + return false; + } + } + return true; + + } + + public static String sendXPX(NetworkType networkType, String apiUrl, String senderPrivateKey, String recipientAddress, BigInteger xpxValue) throws Exception { + final Account account = Account.createFromPrivateKey(senderPrivateKey, networkType); + BlockchainApi blockchainApi = new BlockchainApi(new URL(apiUrl), networkType); + // 10 XPX BigInteger.valueOf(100000) + final TransferTransaction transferTransaction = blockchainApi.transact().transfer() + .deadline(Deadline.create(1, ChronoUnit.HOURS)) + .to(Address.createFromRawAddress(recipientAddress)) + .mosaics(Arrays.asList(NetworkCurrencyMosaic.TEN)) + .message(PlainMessage.create(new String("[" + CONST.APP_NAME + "] - Welcome to Proximax - init 10 XPXs"))) + .networkType(networkType) + .build(); + final SignedTransaction signedTransaction = blockchainApi.sign(transferTransaction, account); + final TransactionRepository transactionHttp = (TransactionRepository) blockchainApi.createTransactionRepository(); + transactionHttp.announce(signedTransaction).toFuture().get(); + System.out.println("Transaction Hash: " + signedTransaction.getHash()); + return signedTransaction.getHash(); + } + + public static String sendPlainMessage(NetworkType networkType, String apiUrl, String senderPrivateKey, String recipientAddress, String message) throws Exception { + final Account account = Account.createFromPrivateKey(senderPrivateKey, networkType); + BlockchainApi blockchainApi = new BlockchainApi(new URL(apiUrl), networkType); + final TransferTransaction transferTransaction = blockchainApi.transact().transfer() + .deadline(Deadline.create(1, ChronoUnit.HOURS)) + .to(Address.createFromRawAddress(recipientAddress)) + //.mosaics(new ArrayList()) + .mosaics(Arrays.asList(NetworkCurrencyMosaic.createAbsolute(BigInteger.ZERO))) + .feeCalculationStrategy(FeeCalculationStrategy.ZERO) + .message(PlainMessage.create(new String("[" + CONST.APP_NAME + "] ") + message)) + .networkType(networkType) + .build(); + final SignedTransaction signedTransaction = blockchainApi.sign(transferTransaction, account); + final TransactionRepository transactionHttp = (TransactionRepository) blockchainApi.createTransactionRepository(); + transactionHttp.announce(signedTransaction).toFuture().get(); + System.out.println("Transaction Hash: " + signedTransaction.getHash()); + return signedTransaction.getHash(); + } + + public static ConnectionConfig createConnectionConfig(String url, int port, String network, String ipfsUrl, int ipfsPort) { + return ConnectionConfig.createWithLocalIpfsConnection( + createBlockchainNetworkConnection( + NetworkUtils.getBlockchainNetworkType(network), + url, + port), + createIpfsConnection( + ipfsUrl, + ipfsPort)); + } + + public static BlockchainNetworkConnection createBlockchainNetworkConnection(BlockchainNetworkType networkType, String apiUrl, int port) { + return new BlockchainNetworkConnection( + networkType, + apiUrl, + port, + HttpProtocol.HTTP); + } + + public static IpfsConnection createIpfsConnection(String ipfsUrl, int ipfsPort) { + return new IpfsConnection( + ipfsUrl, + ipfsPort); + } + + /** + * Http Get. + * + * @param host + * @param port + * @param requestUrl the request url + * @return the string + */ + public static String sendGetHTTP(String host, int port, String requestUrl) { + String result = ""; + CloseableHttpResponse response = null; + try { + CloseableHttpClient httpClient = HttpClients.createDefault(); + String url = "http://" + host + ":" + port + requestUrl; + url = URLDecoder.decode(url, "UTF-8"); + HttpGet method = new HttpGet(url); + response = httpClient.execute(method); + result = EntityUtils.toString(response.getEntity()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + } + + public static boolean testNode(String network, String url) { + try { + if (!url.startsWith("http")) { + url = "http://" + url; + } + URI u = new URI(url); + int port = u.getPort(); + if (port == -1) { + url = url + ":" + NetworkUtils.NODE_DEFAULT_PORT; + } + BlockchainApi blockchainApi = new BlockchainApi(new URL(url), NetworkUtils.getNetworkType(network)); + NetworkType networkType = blockchainApi.getNetworkType(); + if (getNetworkType(network) == networkType) { + return true; + } + } catch (Exception e) { + } + return false; + + } + + public static boolean testIpfs(String url) { + try { + String host; + int port = NetworkUtils.IPFS_PORT; + if (!url.startsWith("http")) { + url = "http://" + url; + } + URI u = new URI(url); + host = u.getHost(); + port = u.getPort(); + if (port == -1) { + port = NetworkUtils.IPFS_PORT; + } + IpfsConnection conn = new IpfsConnection(host, port); + return (conn != null); + } catch (URISyntaxException ex) { + ex.printStackTrace(); + } + return false; + + } + + public static int testAccount(LocalAccount localAccount) { + try { + if (localAccount.createConnection()) { + AccountRepository accountHttp = (AccountRepository) localAccount.getBlockchainNetworkConnection().getBlockchainApi().createAccountRepository(); + AccountInfo accountInfo = accountHttp.getAccountInfo(Address.createFromRawAddress(localAccount.address)).toFuture().get(); + if (localAccount.address.equals(accountInfo.getAddress().plain())) { + return 1; + } + return 0; + } + } catch (ExecutionException ex1) { + ex1.printStackTrace(); + String msg = ex1.getMessage(); + if (msg.contains("Not Found") || msg.contains("Conflict")) { + return 0; + } + } catch (InterruptedException ex2) { + ex2.printStackTrace(); + } + return -1; //server issue + + } + + public static String getIpfs(int idx) { + return IPFS_SERVER.get(idx); + } + +} diff --git a/src/main/java/io/proximax/app/utils/StringUtils.java b/src/main/java/io/proximax/app/utils/StringUtils.java new file mode 100644 index 0000000..affdbf1 --- /dev/null +++ b/src/main/java/io/proximax/app/utils/StringUtils.java @@ -0,0 +1,130 @@ +package io.proximax.app.utils; + +/* + * Copyright 2018 ProximaX Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* + * Proximax P2P Storage REST API + * Proximax P2P Storage REST API + * + * OpenAPI spec version: v0.0.1 + * Contact: alvin.reyes@botmill.io + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ +/** + * The Class StringUtils. + */ +public class StringUtils { + + /** + * Check if the given array contains the given value (with case-insensitive + * comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) { + return true; + } + if (value != null && value.equalsIgnoreCase(str)) { + return true; + } + } + return false; + } + + /** + * Join an array of strings with the given separator. + *

+ * Note: This might be replaced by utility method from commons-lang or guava + * someday if one of those libraries is added as dependency. + *

+ * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) { + return ""; + } + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } + + /** + * Checks if is empty. + * + * @param str the str + * @return true, if is empty + */ + public static boolean isEmpty(String str) { + return null == str || str.isEmpty(); + } + + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + public static long parseLong(String s, long l) { + try { + return Long.parseLong(s); + } catch (NumberFormatException ex) { + } + return l; + } + + public static int parseInt(String s, int i) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException ex) { + } + return i; + } + + public static String getFileSize(long fileSize) { + String str = ""; + if (fileSize < 1024) { + str = "" + fileSize + " bytes"; + } else if (fileSize < 1048576) { + str = "" + (fileSize / 1024) + " Kb"; + } else if (fileSize < 1073741824) { + str = "" + (fileSize / 1048576) + " Mb"; + } else if (fileSize < (1073741824 * 1024)) { + str = "" + (fileSize / 1073741824) + " Gb"; + } + return str; + } + + public static void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (Exception ex) { + } + } +} diff --git a/src/main/resources/fxml/css/Dark.css b/src/main/resources/fxml/css/Dark.css new file mode 100644 index 0000000..ae098b5 --- /dev/null +++ b/src/main/resources/fxml/css/Dark.css @@ -0,0 +1,451 @@ +.root +{ + -fx-font: 12px Lato; +} +#shadow-pane +{ + -fx-background-color: transparent; +} +#main-pane +{ + -fx-background-color: #193549; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0.5, 0.0, 0.0); + +} +#login-line +{ + -fx-background-color: white; +} +#comment-lbl +{ + -fx-text-fill:white; +} +#pwr-lbl +{ + -fx-font:15px "Lato"; + -fx-text-fill:white; +} +#user-lbl +{ + -fx-font:18px "Lato Black"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#level-lbl +{ + -fx-font:11px "Lato Regular"; + -fx-text-fill:#78B6E4; +} +#title-lbl +{ + -fx-font:23px "Lato"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#label20b +{ + -fx-font:20px "Lato Black"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#label13b +{ + -fx-font:13px "Lato Black"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#label13 +{ + -fx-font:13px "Lato Regular"; + -fx-text-fill:white; +} +#error-lbl +{ + -fx-font:13px "Lato Regular"; + -fx-text-fill:red; +} +#error-title +{ + -fx-font:20px "Lato Black"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#error-msg +{ + -fx-font:15px "Lato Regular"; + -fx-text-fill:white; +} +#prxbx-btn +{ + -fx-background-color:#326B91; + -fx-background-radius:5px; + -fx-text-fill:white; + -fx-font:15px "Lato"; + -fx-font-weight:bold; +} +#nav-btn1 +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; + -fx-background-color: #326B91; +} +#nav-btn1:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #326B91; +} +#prfl-img +{ + -fx-background-radius:20px; +} +#app-logo1 +{ + -fx-image: url("Light/img/proxifileitd.png"); +} +#app-logo2 +{ + -fx-image: url("Light/img/proxifileitw.png"); +} +#app-header1 +{ + -fx-image: url("Light/img/fileitw.png"); +} +#app-header2 +{ + -fx-image: url("Light/img/fileitd.png"); +} +#app-logo1 +{ + -fx-image: url("Dark/img/Proxibox-Logo2.png"); +} +#app-logo3 +{ + -fx-image: url("Dark/img/Proxibox-Logo3.png"); +} +#app-icon +{ + -fx-image: url("Dark/img/proxibox-icon.png"); +} +#close-img +{ + -fx-image: url("Dark/img/close.png"); +} +#min-img +{ + -fx-image: url("Dark/img/minimize.png"); +} +#max-img +{ + -fx-image: url("Dark/img/maximize.png"); +} +#user-img +{ + -fx-image: url("Dark/img/user.png"); +} +#home-img +{ + -fx-image: url("Dark/img/home.png"); +} +#allfiles-img +{ + -fx-image: url("Dark/img/allfiles.png"); +} +#sharing-img +{ + -fx-image: url("Dark/img/sharing-w.png"); +} +#filerequest-img +{ + -fx-image: url("Dark/img/filerequest.png"); +} +#logout-img +{ + -fx-image: url("Dark/img/logout.png"); +} +#search-img +{ + -fx-image: url("Dark/img/search.png"); +} +#upload-img +{ + -fx-image: url("Dark/img/upload.png"); +} +#upload-img1 +{ + -fx-image: url("Dark/img/upload-w.png"); +} +#error-img +{ + -fx-image: url("Dark/img/error.png") +} +#folder-img +{ + -fx-image: url("Dark/img/folder.png") +} +#nfolder-img +{ + -fx-image: url("Dark/img/newfolder.png") +} +#view-img +{ + -fx-image: url("Dark/img/view.png") +} +#download-img +{ + -fx-image: url("Dark/img/download.png") +} +#edit-img +{ + -fx-image: url("Dark/img/edit.png") +} +#history-img +{ + -fx-image: url("Dark/img/history.png") +} +#sharing-img-d +{ + -fx-image: url("Dark/img/sharing.png"); +} +#themes-img +{ + -fx-image: url("Dark/img/themes.png") +} +#nightmode-img +{ + -fx-image: url("Dark/img/nightmode.png") +} +#proximax-logo +{ + -fx-image: url("Dark/img/ProximaX-Logo.png") +} +#netcfg-img +{ + -fx-image: url("Dark/img/netcfg.png") +} +#netcfg1-img +{ + -fx-image: url("Dark/img/netcfg.png") +} +#add-img +{ + -fx-image: url("Light/img/add.png") +} +#remove-img +{ + -fx-image: url("Light/img/remove.png") +} +#home-sidebar +{ + -fx-background-color: #193549; +} +#profile-pane +{ + -fx-background-color: #193549; +} +#nav-togbtn1 +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-togbtn1:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-togbtn +{ + -jfx-toggle-color: #D6E1E9; + -jfx-untoggle-color: #FAFAFA; + -jfx-toggle-line-color: #326B91; + -jfx-untoggle-line-color: #999999; + -jfx-size: 5.0; + -jfx-disable-visual-focus: false; +} +#nav-tbtn +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-tbtn:selected +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #192B38; +} +#nav-btn +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; +} +#nav-btn:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #192B38; +} +#upload-btn +{ + -fx-text-fill:white; + -fx-font:15px "Lato"; +} +#upload-btn:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato"; + -fx-font-weight:bold; +} +#header +{ + -fx-background-color: #326B91; + -fx-padding: 16px; +} +#login-account +{ + -fx-background-color:#ffffff; + -fx-font:15px "Lato"; + -fx-padding: 4px; +} +#register-field +{ + -fx-background-color: #ffffff; + -fx-font:15px "Lato"; +} +#reg-checkbox +{ + -fx-text-fill: white; + -fx-font:17px "Lato"; +} +#reg-lbl +{ + -fx-font:24px "Lato"; + -fx-text-fill: white; +} +#title-pane +{ + -fx-background-color: #193549; + -fx-padding: 0 10 0 10; +} +#center-pane +{ + -fx-background-color: #D6E1E9; +} +#bottom-pane +{ + -fx-background-color: #192B38; +} +#node-combo +{ + -fx-background-color: #D6E1E9; +} +#uploader-pane +{ + -fx-border-color: #326B91; +} +#uploader-fields +{ + -fx-background-color:#FFFFFF; + -fx-font:15px "Lato Regular"; + -fx-text-fill: #193549; + -fx-padding: 4px; +} +#search-field +{ + -fx-font:18px "Lato"; + -fx-text-fill: white; + -fx-prompt-text-fill: white; +} +#upload-progress .bar +{ + -fx-background-color: #326B91; +} +.table-view +{ + -fx-font:15px "Lato"; + -fx-table-cell-border-color: #78B6E4; +} +.table-view .table-row-cell +{ + -fx-cell-size: 35px; + -fx-border-width: 1px; +} +.status-bar +{ + -fx-padding: 4px; + -fx-pref-height: 40px; + -fx-background-color: #193549; + -fx-body-color:#193549; + -fx-background-insets: 0, 1; +} +.status-bar > .center-items, +.status-bar > .left-items, +.status-bar > .right-items +{ + -fx-alignment: center; +} +.status-label +{ + -fx-font:15px "Lato"; + -fx-text-fill: white; +} +.status-combox +{ + -fx-font:15px "Lato"; + -jfx-focus-color: #193549; + -jfx-unfocus-color: #193549; +} +.status-combox > .cell +{ + -fx-font:15px "Lato"; + -fx-text-fill: white; +} +.status-combox > .list-view > .list-cell +{ + -fx-font:15px "Lato"; +} +.status-green +{ + -fx-graphic: url("Dark/img/green.png") +} +.status-red +{ + -fx-graphic: url("Dark/img/red.png") +} +.button.pressed { -fx-background-color: ghostwhite; } + +.button, .toggle-button { + -fx-background-position: center; + -fx-background-repeat: no-repeat; +} +.button.loadfile { -fx-background-color: #193549; -fx-background-image: url("Dark/img/new.png"); } +.button.savefile { -fx-background-color: #193549; -fx-background-image: url("Dark/img/save.png"); } +.button.new { -fx-background-color: #193549; -fx-background-image: url("Dark/img/new.png") } +.button.undo { -fx-background-color: #193549; -fx-background-image: url("Dark/img/undo.png"); } +.button.redo { -fx-background-color: #193549; -fx-background-image: url("Dark/img/redo.png"); } +.button.cut { -fx-background-color: #193549; -fx-background-image: url("Dark/img/cut.png"); } +.button.copy { -fx-background-color: #193549; -fx-background-image: url("Dark/img/copy.png"); } +.button.paste { -fx-background-color: #193549; -fx-background-image: url("Dark/img/paste.png"); } +.button.bold { -fx-background-color: #193549; -fx-background-image: url("Dark/img/bold.png"); } +.button.italic { -fx-background-color: #193549; -fx-background-image: url("Dark/img/italic.png"); } +.button.underline { -fx-background-color: #193549; -fx-background-image: url("Dark/img/underline.png"); } +.button.strikethrough { -fx-background-color: #193549; -fx-background-image: url("Dark/img/strikethrough.png"); } +.button.insertimage { -fx-background-color: #193549; -fx-background-image: url("Dark/img/insertimage.png"); } +.toggle-button.wrap { -fx-background-color: #193549; -fx-background-image: url("Dark/img/wrap.png"); } +#view-btn +{ + -fx-background-color:#326B91 ; + -fx-text-fill:white; + -fx-font:15px "Lato Bold"; + -fx-font-weight:bold; + -fx-graphic: url("Dark/img/vieww.png"); +} +#view-btn:selected +{ + -fx-graphic: url("Dark/img/hide.png"); +} diff --git a/src/main/resources/fxml/css/Dark/img/Proxibox-Logo.png b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo.png new file mode 100644 index 0000000..7c6f0d7 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo.png differ diff --git a/src/main/resources/fxml/css/Dark/img/Proxibox-Logo1.png b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo1.png new file mode 100644 index 0000000..37480b4 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo1.png differ diff --git a/src/main/resources/fxml/css/Dark/img/Proxibox-Logo2.png b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo2.png new file mode 100644 index 0000000..8c5f603 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo2.png differ diff --git a/src/main/resources/fxml/css/Dark/img/Proxibox-Logo3.png b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo3.png new file mode 100644 index 0000000..e00f385 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/Proxibox-Logo3.png differ diff --git a/src/main/resources/fxml/css/Dark/img/ProximaX-Logo.png b/src/main/resources/fxml/css/Dark/img/ProximaX-Logo.png new file mode 100644 index 0000000..97e98c6 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/ProximaX-Logo.png differ diff --git a/src/main/resources/fxml/css/Dark/img/add.png b/src/main/resources/fxml/css/Dark/img/add.png new file mode 100644 index 0000000..1a1d370 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/add.png differ diff --git a/src/main/resources/fxml/css/Dark/img/allfiles.png b/src/main/resources/fxml/css/Dark/img/allfiles.png new file mode 100644 index 0000000..863a827 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/allfiles.png differ diff --git a/src/main/resources/fxml/css/Dark/img/close.png b/src/main/resources/fxml/css/Dark/img/close.png new file mode 100644 index 0000000..e8b8381 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/close.png differ diff --git a/src/main/resources/fxml/css/Dark/img/copy.png b/src/main/resources/fxml/css/Dark/img/copy.png new file mode 100644 index 0000000..c92722a Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/copy.png differ diff --git a/src/main/resources/fxml/css/Dark/img/cut.png b/src/main/resources/fxml/css/Dark/img/cut.png new file mode 100644 index 0000000..efc5dd4 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/cut.png differ diff --git a/src/main/resources/fxml/css/Dark/img/download.png b/src/main/resources/fxml/css/Dark/img/download.png new file mode 100644 index 0000000..9b96aa2 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/download.png differ diff --git a/src/main/resources/fxml/css/Dark/img/edit.png b/src/main/resources/fxml/css/Dark/img/edit.png new file mode 100644 index 0000000..db3bb1b Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/edit.png differ diff --git a/src/main/resources/fxml/css/Dark/img/error.png b/src/main/resources/fxml/css/Dark/img/error.png new file mode 100644 index 0000000..3ce9166 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/error.png differ diff --git a/src/main/resources/fxml/css/Dark/img/fileitd.png b/src/main/resources/fxml/css/Dark/img/fileitd.png new file mode 100644 index 0000000..d0f37a6 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/fileitd.png differ diff --git a/src/main/resources/fxml/css/Dark/img/fileitw.png b/src/main/resources/fxml/css/Dark/img/fileitw.png new file mode 100644 index 0000000..c0c57ac Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/fileitw.png differ diff --git a/src/main/resources/fxml/css/Dark/img/filerequest.png b/src/main/resources/fxml/css/Dark/img/filerequest.png new file mode 100644 index 0000000..ab39018 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/filerequest.png differ diff --git a/src/main/resources/fxml/css/Dark/img/folder.png b/src/main/resources/fxml/css/Dark/img/folder.png new file mode 100644 index 0000000..dd547a6 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/folder.png differ diff --git a/src/main/resources/fxml/css/Dark/img/green.png b/src/main/resources/fxml/css/Dark/img/green.png new file mode 100644 index 0000000..b3393c0 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/green.png differ diff --git a/src/main/resources/fxml/css/Dark/img/hide.png b/src/main/resources/fxml/css/Dark/img/hide.png new file mode 100644 index 0000000..f02d4c7 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/hide.png differ diff --git a/src/main/resources/fxml/css/Dark/img/history.png b/src/main/resources/fxml/css/Dark/img/history.png new file mode 100644 index 0000000..a91f9f6 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/history.png differ diff --git a/src/main/resources/fxml/css/Dark/img/home.png b/src/main/resources/fxml/css/Dark/img/home.png new file mode 100644 index 0000000..64d2818 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/home.png differ diff --git a/src/main/resources/fxml/css/Dark/img/logout.png b/src/main/resources/fxml/css/Dark/img/logout.png new file mode 100644 index 0000000..2e0e3e9 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/logout.png differ diff --git a/src/main/resources/fxml/css/Dark/img/maximize.png b/src/main/resources/fxml/css/Dark/img/maximize.png new file mode 100644 index 0000000..45bf4ab Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/maximize.png differ diff --git a/src/main/resources/fxml/css/Dark/img/minimize.png b/src/main/resources/fxml/css/Dark/img/minimize.png new file mode 100644 index 0000000..984d33e Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/minimize.png differ diff --git a/src/main/resources/fxml/css/Dark/img/netcfg.png b/src/main/resources/fxml/css/Dark/img/netcfg.png new file mode 100644 index 0000000..b126578 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/netcfg.png differ diff --git a/src/main/resources/fxml/css/Dark/img/new.png b/src/main/resources/fxml/css/Dark/img/new.png new file mode 100644 index 0000000..54821af Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/new.png differ diff --git a/src/main/resources/fxml/css/Dark/img/newfolder.png b/src/main/resources/fxml/css/Dark/img/newfolder.png new file mode 100644 index 0000000..5b82371 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/newfolder.png differ diff --git a/src/main/resources/fxml/css/Dark/img/nightmode.png b/src/main/resources/fxml/css/Dark/img/nightmode.png new file mode 100644 index 0000000..ca18bda Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/nightmode.png differ diff --git a/src/main/resources/fxml/css/Dark/img/paste.png b/src/main/resources/fxml/css/Dark/img/paste.png new file mode 100644 index 0000000..ef462ab Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/paste.png differ diff --git a/src/main/resources/fxml/css/Dark/img/proxibox-icon.png b/src/main/resources/fxml/css/Dark/img/proxibox-icon.png new file mode 100644 index 0000000..06684c7 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/proxibox-icon.png differ diff --git a/src/main/resources/fxml/css/Dark/img/proxifileitd.png b/src/main/resources/fxml/css/Dark/img/proxifileitd.png new file mode 100644 index 0000000..892b7f7 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/proxifileitd.png differ diff --git a/src/main/resources/fxml/css/Dark/img/proxifileitw.png b/src/main/resources/fxml/css/Dark/img/proxifileitw.png new file mode 100644 index 0000000..1b708e0 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/proxifileitw.png differ diff --git a/src/main/resources/fxml/css/Dark/img/red.png b/src/main/resources/fxml/css/Dark/img/red.png new file mode 100644 index 0000000..ab20d27 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/red.png differ diff --git a/src/main/resources/fxml/css/Dark/img/redo.png b/src/main/resources/fxml/css/Dark/img/redo.png new file mode 100644 index 0000000..75b4081 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/redo.png differ diff --git a/src/main/resources/fxml/css/Dark/img/remove.png b/src/main/resources/fxml/css/Dark/img/remove.png new file mode 100644 index 0000000..5657deb Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/remove.png differ diff --git a/src/main/resources/fxml/css/Dark/img/save.png b/src/main/resources/fxml/css/Dark/img/save.png new file mode 100644 index 0000000..65369e6 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/save.png differ diff --git a/src/main/resources/fxml/css/Dark/img/search.png b/src/main/resources/fxml/css/Dark/img/search.png new file mode 100644 index 0000000..cb3da21 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/search.png differ diff --git a/src/main/resources/fxml/css/Dark/img/sharing-w.png b/src/main/resources/fxml/css/Dark/img/sharing-w.png new file mode 100644 index 0000000..eeb2651 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/sharing-w.png differ diff --git a/src/main/resources/fxml/css/Dark/img/sharing.png b/src/main/resources/fxml/css/Dark/img/sharing.png new file mode 100644 index 0000000..df5ce16 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/sharing.png differ diff --git a/src/main/resources/fxml/css/Dark/img/themes.png b/src/main/resources/fxml/css/Dark/img/themes.png new file mode 100644 index 0000000..27f7e2c Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/themes.png differ diff --git a/src/main/resources/fxml/css/Dark/img/undo.png b/src/main/resources/fxml/css/Dark/img/undo.png new file mode 100644 index 0000000..bd93efa Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/undo.png differ diff --git a/src/main/resources/fxml/css/Dark/img/upload-w.png b/src/main/resources/fxml/css/Dark/img/upload-w.png new file mode 100644 index 0000000..a77cd83 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/upload-w.png differ diff --git a/src/main/resources/fxml/css/Dark/img/upload.png b/src/main/resources/fxml/css/Dark/img/upload.png new file mode 100644 index 0000000..a77cd83 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/upload.png differ diff --git a/src/main/resources/fxml/css/Dark/img/user.png b/src/main/resources/fxml/css/Dark/img/user.png new file mode 100644 index 0000000..18ba81b Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/user.png differ diff --git a/src/main/resources/fxml/css/Dark/img/view.png b/src/main/resources/fxml/css/Dark/img/view.png new file mode 100644 index 0000000..4b90cb3 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/view.png differ diff --git a/src/main/resources/fxml/css/Dark/img/vieww.png b/src/main/resources/fxml/css/Dark/img/vieww.png new file mode 100644 index 0000000..61d541d Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/vieww.png differ diff --git a/src/main/resources/fxml/css/Dark/img/wrap.png b/src/main/resources/fxml/css/Dark/img/wrap.png new file mode 100644 index 0000000..d78e0c1 Binary files /dev/null and b/src/main/resources/fxml/css/Dark/img/wrap.png differ diff --git a/src/main/resources/fxml/css/Light.css b/src/main/resources/fxml/css/Light.css new file mode 100644 index 0000000..0509e12 --- /dev/null +++ b/src/main/resources/fxml/css/Light.css @@ -0,0 +1,451 @@ +.root +{ + -fx-font: 12px Lato; +} +#shadow-pane +{ + -fx-background-color: transparent; +} +#main-pane +{ + -fx-background-color: #D6E1E9; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0.5, 0.0, 0.0); +} +#login-line +{ + -fx-background-color: white; +} +#comment-lbl +{ + -fx-text-fill:#193549; +} +#pwr-lbl +{ + -fx-font:15px "Lato"; + -fx-text-fill:black; +} +#user-lbl +{ + -fx-font:18px "Lato Black"; + -fx-text-fill:#193549; + -fx-font-weight:bold; +} +#level-lbl +{ + -fx-font:11px "Lato Regular"; + -fx-text-fill:#193549; +} +#title-lbl +{ + -fx-font:23px "Lato"; + -fx-text-fill:white; + -fx-font-weight:bold; +} +#label20b +{ + -fx-font:20px "Lato Black"; + -fx-text-fill:#193549; + -fx-font-weight:bold; +} +#label13b +{ + -fx-font:13px "Lato Black"; + -fx-text-fill:#193549; + -fx-font-weight:bold; +} +#label13 +{ + -fx-font:13px "Lato Regular"; + -fx-text-fill:#193549; +} +#error-lbl +{ + -fx-font:13px "Lato Regular"; + -fx-text-fill:red; +} +#error-title +{ + -fx-font:20px "Lato Black"; + -fx-text-fill:#193549; + -fx-font-weight:bold; +} +#error-msg +{ + -fx-font:15px "Lato Regular"; + -fx-text-fill:#193549; +} +#prxbx-btn +{ + -fx-background-color:#326B91; + -fx-background-radius:5px; + -fx-text-fill:white; + -fx-font:15px "Lato"; + -fx-font-weight:bold; +} +#prfl-img +{ + -fx-background-radius:20px; +} +#app-logo1 +{ + -fx-image: url("Light/img/proxifileitw.png"); +} +#app-logo2 +{ + -fx-image: url("Light/img/proxifileitd.png"); +} +#app-header1 +{ + -fx-image: url("Light/img/fileitw.png"); +} +#app-header2 +{ + -fx-image: url("Light/img/fileitd.png"); +} +#app-icon +{ + -fx-image: url("Light/img/proxibox-icon.png"); +} +#close-img +{ + -fx-image: url("Light/img/close.png"); +} +#min-img +{ + -fx-image: url("Light/img/minimize.png"); +} +#max-img +{ + -fx-image: url("Light/img/maximize.png"); +} +#user-img +{ + -fx-image: url("Light/img/user.png"); +} +#home-img +{ + -fx-image: url("Light/img/home.png"); +} +#allfiles-img +{ + -fx-image: url("Light/img/allfiles.png"); +} +#sharing-img +{ + -fx-image: url("Light/img/sharing.png"); +} +#filerequest-img +{ + -fx-image: url("Light/img/filerequest.png"); +} +#logout-img +{ + -fx-image: url("Light/img/logout.png"); +} +#search-img +{ + -fx-image: url("Light/img/search.png"); +} +#upload-img +{ + -fx-image: url("Light/img/upload.png"); +} +#upload-img1 +{ + -fx-image: url("Light/img/upload-w.png"); +} +#proximax-logo +{ + -fx-image: url("Light/img/ProximaX-Logo.png") +} +#error-img +{ + -fx-image: url("Light/img/error.png") +} +#folder-img +{ + -fx-image: url("Light/img/folder.png") +} +#nfolder-img +{ + -fx-image: url("Light/img/newfolder.png") +} +#view-img +{ + -fx-image: url("Light/img/view.png") +} +#download-img +{ + -fx-image: url("Light/img/download.png") +} +#edit-img +{ + -fx-image: url("Light/img/edit.png") +} +#history-img +{ + -fx-image: url("Light/img/history.png") +} +#sharing-img-d +{ + -fx-image: url("Light/img/sharing.png"); +} +#themes-img +{ + -fx-image: url("Light/img/themes.png") +} +#nightmode-img +{ + -fx-image: url("Light/img/nightmode.png") +} +#netcfg-img +{ + -fx-image: url("Light/img/netcfgw.png") +} +#netcfg1-img +{ + -fx-image: url("Light/img/netcfg.png") +} +#add-img +{ + -fx-image: url("Light/img/add.png") +} +#remove-img +{ + -fx-image: url("Light/img/remove.png") +} +#home-sidebar +{ + -fx-background-color: #D6E1E9; +} +#profile-pane +{ + -fx-background-color: #D6E1E9; +} +#nav-togbtn1 +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-togbtn1:focused +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-togbtn +{ + -jfx-toggle-color: #193549; + -jfx-untoggle-color: #FAFAFA; + -jfx-toggle-line-color: #326B91; + -jfx-untoggle-line-color: #999999; + -jfx-size: 5.0; + -jfx-disable-visual-focus: false; +} +#nav-tbtn +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; + -fx-background-color: transparent; +} +#nav-tbtn:selected +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #326B91; +} +#nav-btn +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; +} +#nav-btn:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #326B91; +} +#nav-btn1 +{ + -fx-text-fill:#193549; + -fx-font:15px "Lato Black"; + -fx-background-color: #326B91; +} +#nav-btn1:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato Black"; + -fx-font-weight:bold; + -fx-background-color: #326B91; +} +#upload-btn +{ + -fx-text-fill:white; + -fx-font:15px "Lato"; +} +#upload-btn:focused +{ + -fx-text-fill:white; + -fx-font:15px "Lato"; + -fx-font-weight:bold; +} +#header +{ + -fx-background-color: #326B91; + -fx-padding: 16px; +} +#search-bar +{ + -fx-prompt-text-fill: gray; +} +#login-account +{ + -fx-background-color:#ffffff; + -fx-font:15px "Lato"; + -fx-padding: 4px; +} +#register-field +{ + -fx-background-color: #ffffff; + -fx-font:15px "Lato"; +} +#reg-checkbox +{ + -fx-text-fill: #193549; + -fx-font:17px "Lato"; +} +#reg-lbl +{ + -fx-font:24px "Lato"; + -fx-text-fill: #193549; + -fx-font-weight:bold; +} +#title-pane +{ + -fx-background-color: #326b91; + -fx-padding: 0 10 0 10; +} +#center-pane +{ + -fx-background-color: #f1f2f2; +} +#bottom-pane +{ + -fx-background-color: #192B38; +} + +#node-combo +{ + -fx-background-color: #D6E1E9; +} + +#uploader-pane +{ + -fx-border-color: #326B91; +} +#uploader-fields +{ + -fx-background-color:#FFFFFF; + -fx-font:15px "Lato Regular"; + -fx-text-fill: #193549; + -fx-padding: 4px; +} +#search-field +{ + -fx-font:18px "Lato"; + -fx-text-fill: white; + -fx-prompt-text-fill: white; +} +#upload-progress .bar +{ + -fx-background-color: #326B91; +} +.table-view +{ + -fx-font:15px "Lato"; + -fx-table-cell-border-color: #78B6E4; +} +.table-view .table-row-cell +{ + -fx-cell-size: 35px; + -fx-border-width: 1px; +} +.status-bar +{ + -fx-padding: 4px; + -fx-pref-height: 40px; + -fx-background-color: #D6E1E9; + -fx-body-color:#D6E1E9; + -fx-background-insets: 0, 1; +} +.status-bar > .center-items, +.status-bar > .left-items, +.status-bar > .right-items +{ + -fx-alignment: center; +} +.status-label +{ + -fx-font:15px "Lato"; + -fx-text-fill: #193549; +} +.status-combox +{ + -fx-font:15px "Lato"; + -jfx-focus-color: #D6E1E9; + -jfx-unfocus-color: #D6E1E9; +} +.status-combox > .cell +{ + -fx-font:15px "Lato"; + -fx-text-fill: #193549; +} +.status-combox > .list-view > .list-cell +{ + -fx-font:15px "Lato"; +} +.status-green +{ + -fx-graphic: url("Light/img/green.png") +} +.status-red +{ + -fx-graphic: url("Light/img/red.png") +} + +.button.pressed { -fx-background-color: ghostwhite; } + +.button, .toggle-button { + -fx-background-position: center; + -fx-background-repeat: no-repeat; +} +.button.loadfile { -fx-background-image: url("Light/img/new.png"); } +.button.savefile { -fx-background-image: url("Light/img/save.png"); } +.button.new { -fx-background-image: url("Light/img/new.png") } +.button.undo { -fx-background-image: url("Light/img/undo.png"); } +.button.redo { -fx-background-image: url("Light/img/redo.png"); } +.button.cut { -fx-background-image: url("Light/img/cut.png"); } +.button.copy { -fx-background-image: url("Light/img/copy.png"); } +.button.paste { -fx-background-image: url("Light/img/paste.png"); } +.button.bold { -fx-background-image: url("Light/img/bold.png"); } +.button.italic { -fx-background-image: url("Light/img/italic.png"); } +.button.underline { -fx-background-image: url("Light/img/underline.png"); } +.button.strikethrough { -fx-background-image: url("Light/img/strikethrough.png"); } +.button.insertimage { -fx-background-image: url("Light/img/insertimage.png"); } +.toggle-button.wrap { -fx-background-image: url("Light/img/wrap.png"); } + +#view-btn +{ + -fx-background-color: #326B91; + -fx-text-fill:white; + -fx-font:15px "Lato Bold"; + -fx-font-weight:bold; + -fx-graphic: url("Light/img/vieww.png"); +} +#view-btn:selected +{ + -fx-graphic: url("Light/img/hide.png"); +} diff --git a/src/main/resources/fxml/css/Light/img/Proxibox-Logo.png b/src/main/resources/fxml/css/Light/img/Proxibox-Logo.png new file mode 100644 index 0000000..7c6f0d7 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/Proxibox-Logo.png differ diff --git a/src/main/resources/fxml/css/Light/img/Proxibox-Logo1.png b/src/main/resources/fxml/css/Light/img/Proxibox-Logo1.png new file mode 100644 index 0000000..37480b4 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/Proxibox-Logo1.png differ diff --git a/src/main/resources/fxml/css/Light/img/Proxibox-Logo2.png b/src/main/resources/fxml/css/Light/img/Proxibox-Logo2.png new file mode 100644 index 0000000..ecc9a7e Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/Proxibox-Logo2.png differ diff --git a/src/main/resources/fxml/css/Light/img/Proxibox-Logo3.png b/src/main/resources/fxml/css/Light/img/Proxibox-Logo3.png new file mode 100644 index 0000000..e00f385 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/Proxibox-Logo3.png differ diff --git a/src/main/resources/fxml/css/Light/img/ProximaX-Logo.png b/src/main/resources/fxml/css/Light/img/ProximaX-Logo.png new file mode 100644 index 0000000..97e98c6 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/ProximaX-Logo.png differ diff --git a/src/main/resources/fxml/css/Light/img/add.png b/src/main/resources/fxml/css/Light/img/add.png new file mode 100644 index 0000000..1a1d370 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/add.png differ diff --git a/src/main/resources/fxml/css/Light/img/allfiles.png b/src/main/resources/fxml/css/Light/img/allfiles.png new file mode 100644 index 0000000..06d8c63 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/allfiles.png differ diff --git a/src/main/resources/fxml/css/Light/img/close.png b/src/main/resources/fxml/css/Light/img/close.png new file mode 100644 index 0000000..9378387 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/close.png differ diff --git a/src/main/resources/fxml/css/Light/img/copy.png b/src/main/resources/fxml/css/Light/img/copy.png new file mode 100644 index 0000000..90747c4 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/copy.png differ diff --git a/src/main/resources/fxml/css/Light/img/cut.png b/src/main/resources/fxml/css/Light/img/cut.png new file mode 100644 index 0000000..0896372 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/cut.png differ diff --git a/src/main/resources/fxml/css/Light/img/download.png b/src/main/resources/fxml/css/Light/img/download.png new file mode 100644 index 0000000..9b96aa2 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/download.png differ diff --git a/src/main/resources/fxml/css/Light/img/edit.png b/src/main/resources/fxml/css/Light/img/edit.png new file mode 100644 index 0000000..db3bb1b Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/edit.png differ diff --git a/src/main/resources/fxml/css/Light/img/error.png b/src/main/resources/fxml/css/Light/img/error.png new file mode 100644 index 0000000..3ce9166 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/error.png differ diff --git a/src/main/resources/fxml/css/Light/img/fileitd.png b/src/main/resources/fxml/css/Light/img/fileitd.png new file mode 100644 index 0000000..9126368 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/fileitd.png differ diff --git a/src/main/resources/fxml/css/Light/img/fileitw.png b/src/main/resources/fxml/css/Light/img/fileitw.png new file mode 100644 index 0000000..7d7f3fb Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/fileitw.png differ diff --git a/src/main/resources/fxml/css/Light/img/filerequest.png b/src/main/resources/fxml/css/Light/img/filerequest.png new file mode 100644 index 0000000..4898738 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/filerequest.png differ diff --git a/src/main/resources/fxml/css/Light/img/folder.png b/src/main/resources/fxml/css/Light/img/folder.png new file mode 100644 index 0000000..a0bce96 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/folder.png differ diff --git a/src/main/resources/fxml/css/Light/img/green.png b/src/main/resources/fxml/css/Light/img/green.png new file mode 100644 index 0000000..b3393c0 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/green.png differ diff --git a/src/main/resources/fxml/css/Light/img/hide.png b/src/main/resources/fxml/css/Light/img/hide.png new file mode 100644 index 0000000..f02d4c7 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/hide.png differ diff --git a/src/main/resources/fxml/css/Light/img/hideg.png b/src/main/resources/fxml/css/Light/img/hideg.png new file mode 100644 index 0000000..bdc8dcb Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/hideg.png differ diff --git a/src/main/resources/fxml/css/Light/img/history.png b/src/main/resources/fxml/css/Light/img/history.png new file mode 100644 index 0000000..a91f9f6 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/history.png differ diff --git a/src/main/resources/fxml/css/Light/img/home.png b/src/main/resources/fxml/css/Light/img/home.png new file mode 100644 index 0000000..ff08def Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/home.png differ diff --git a/src/main/resources/fxml/css/Light/img/logout.png b/src/main/resources/fxml/css/Light/img/logout.png new file mode 100644 index 0000000..ad124b2 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/logout.png differ diff --git a/src/main/resources/fxml/css/Light/img/maximize.png b/src/main/resources/fxml/css/Light/img/maximize.png new file mode 100644 index 0000000..45bf4ab Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/maximize.png differ diff --git a/src/main/resources/fxml/css/Light/img/minimize.png b/src/main/resources/fxml/css/Light/img/minimize.png new file mode 100644 index 0000000..984d33e Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/minimize.png differ diff --git a/src/main/resources/fxml/css/Light/img/netcfg.png b/src/main/resources/fxml/css/Light/img/netcfg.png new file mode 100644 index 0000000..2279abf Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/netcfg.png differ diff --git a/src/main/resources/fxml/css/Light/img/netcfgg.png b/src/main/resources/fxml/css/Light/img/netcfgg.png new file mode 100644 index 0000000..66d5757 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/netcfgg.png differ diff --git a/src/main/resources/fxml/css/Light/img/netcfgw.png b/src/main/resources/fxml/css/Light/img/netcfgw.png new file mode 100644 index 0000000..b126578 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/netcfgw.png differ diff --git a/src/main/resources/fxml/css/Light/img/new.png b/src/main/resources/fxml/css/Light/img/new.png new file mode 100644 index 0000000..62717b0 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/new.png differ diff --git a/src/main/resources/fxml/css/Light/img/newfolder.png b/src/main/resources/fxml/css/Light/img/newfolder.png new file mode 100644 index 0000000..695de2e Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/newfolder.png differ diff --git a/src/main/resources/fxml/css/Light/img/nightmode.png b/src/main/resources/fxml/css/Light/img/nightmode.png new file mode 100644 index 0000000..7909b10 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/nightmode.png differ diff --git a/src/main/resources/fxml/css/Light/img/paste.png b/src/main/resources/fxml/css/Light/img/paste.png new file mode 100644 index 0000000..836ed87 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/paste.png differ diff --git a/src/main/resources/fxml/css/Light/img/proxibox-icon.png b/src/main/resources/fxml/css/Light/img/proxibox-icon.png new file mode 100644 index 0000000..06684c7 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/proxibox-icon.png differ diff --git a/src/main/resources/fxml/css/Light/img/proxifileitd.png b/src/main/resources/fxml/css/Light/img/proxifileitd.png new file mode 100644 index 0000000..892b7f7 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/proxifileitd.png differ diff --git a/src/main/resources/fxml/css/Light/img/proxifileitw.png b/src/main/resources/fxml/css/Light/img/proxifileitw.png new file mode 100644 index 0000000..1b708e0 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/proxifileitw.png differ diff --git a/src/main/resources/fxml/css/Light/img/red.png b/src/main/resources/fxml/css/Light/img/red.png new file mode 100644 index 0000000..ab20d27 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/red.png differ diff --git a/src/main/resources/fxml/css/Light/img/redo.png b/src/main/resources/fxml/css/Light/img/redo.png new file mode 100644 index 0000000..a0e3afa Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/redo.png differ diff --git a/src/main/resources/fxml/css/Light/img/remove.png b/src/main/resources/fxml/css/Light/img/remove.png new file mode 100644 index 0000000..5657deb Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/remove.png differ diff --git a/src/main/resources/fxml/css/Light/img/save.png b/src/main/resources/fxml/css/Light/img/save.png new file mode 100644 index 0000000..54adefa Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/save.png differ diff --git a/src/main/resources/fxml/css/Light/img/search.png b/src/main/resources/fxml/css/Light/img/search.png new file mode 100644 index 0000000..cb3da21 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/search.png differ diff --git a/src/main/resources/fxml/css/Light/img/sharing.png b/src/main/resources/fxml/css/Light/img/sharing.png new file mode 100644 index 0000000..df5ce16 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/sharing.png differ diff --git a/src/main/resources/fxml/css/Light/img/themes.png b/src/main/resources/fxml/css/Light/img/themes.png new file mode 100644 index 0000000..27f7e2c Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/themes.png differ diff --git a/src/main/resources/fxml/css/Light/img/undo.png b/src/main/resources/fxml/css/Light/img/undo.png new file mode 100644 index 0000000..8b44a5e Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/undo.png differ diff --git a/src/main/resources/fxml/css/Light/img/upload-w.png b/src/main/resources/fxml/css/Light/img/upload-w.png new file mode 100644 index 0000000..a77cd83 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/upload-w.png differ diff --git a/src/main/resources/fxml/css/Light/img/upload.png b/src/main/resources/fxml/css/Light/img/upload.png new file mode 100644 index 0000000..3b9f5d8 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/upload.png differ diff --git a/src/main/resources/fxml/css/Light/img/user.png b/src/main/resources/fxml/css/Light/img/user.png new file mode 100644 index 0000000..18ba81b Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/user.png differ diff --git a/src/main/resources/fxml/css/Light/img/view.png b/src/main/resources/fxml/css/Light/img/view.png new file mode 100644 index 0000000..4b90cb3 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/view.png differ diff --git a/src/main/resources/fxml/css/Light/img/viewg.png b/src/main/resources/fxml/css/Light/img/viewg.png new file mode 100644 index 0000000..d6d0504 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/viewg.png differ diff --git a/src/main/resources/fxml/css/Light/img/vieww.png b/src/main/resources/fxml/css/Light/img/vieww.png new file mode 100644 index 0000000..61d541d Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/vieww.png differ diff --git a/src/main/resources/fxml/css/Light/img/wrap.png b/src/main/resources/fxml/css/Light/img/wrap.png new file mode 100644 index 0000000..26e3827 Binary files /dev/null and b/src/main/resources/fxml/css/Light/img/wrap.png differ diff --git a/src/main/resources/fxml/ui/AboutUs.fxml b/src/main/resources/fxml/ui/AboutUs.fxml new file mode 100644 index 0000000..fa66601 --- /dev/null +++ b/src/main/resources/fxml/ui/AboutUs.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ErrorDialog.fxml b/src/main/resources/fxml/ui/ErrorDialog.fxml new file mode 100644 index 0000000..09b8380 --- /dev/null +++ b/src/main/resources/fxml/ui/ErrorDialog.fxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/NetworkDialog.fxml b/src/main/resources/fxml/ui/NetworkDialog.fxml new file mode 100644 index 0000000..bfa504b --- /dev/null +++ b/src/main/resources/fxml/ui/NetworkDialog.fxml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/src/main/resources/fxml/ui/ProxiBoxFileProperties.fxml b/src/main/resources/fxml/ui/ProxiBoxFileProperties.fxml new file mode 100644 index 0000000..62b3084 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxFileProperties.fxml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiBoxFileViewer.fxml b/src/main/resources/fxml/ui/ProxiBoxFileViewer.fxml new file mode 100644 index 0000000..eae6c48 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxFileViewer.fxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
diff --git a/src/main/resources/fxml/ui/ProxiBoxFiles.fxml b/src/main/resources/fxml/ui/ProxiBoxFiles.fxml new file mode 100644 index 0000000..99add5b --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxFiles.fxml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/src/main/resources/fxml/ui/ProxiBoxFolder.fxml b/src/main/resources/fxml/ui/ProxiBoxFolder.fxml new file mode 100644 index 0000000..d837308 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxFolder.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiBoxLogin.fxml b/src/main/resources/fxml/ui/ProxiBoxLogin.fxml new file mode 100644 index 0000000..5af9f7f --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxLogin.fxml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+
+
diff --git a/src/main/resources/fxml/ui/ProxiBoxMediaViewer.fxml b/src/main/resources/fxml/ui/ProxiBoxMediaViewer.fxml new file mode 100644 index 0000000..1f91a0a --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxMediaViewer.fxml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
\ No newline at end of file diff --git a/src/main/resources/fxml/ui/ProxiBoxProfile.fxml b/src/main/resources/fxml/ui/ProxiBoxProfile.fxml new file mode 100644 index 0000000..d1419f2 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxProfile.fxml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiBoxSharing.fxml b/src/main/resources/fxml/ui/ProxiBoxSharing.fxml new file mode 100644 index 0000000..6308797 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxSharing.fxml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/fxml/ui/ProxiBoxSignup.fxml b/src/main/resources/fxml/ui/ProxiBoxSignup.fxml new file mode 100644 index 0000000..ad32826 --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxSignup.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiBoxUploader.fxml b/src/main/resources/fxml/ui/ProxiBoxUploader.fxml new file mode 100644 index 0000000..cd34e0f --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiBoxUploader.fxml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiDialog.fxml b/src/main/resources/fxml/ui/ProxiDialog.fxml new file mode 100644 index 0000000..6f70eed --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiDialog.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/ProxiPad.fxml b/src/main/resources/fxml/ui/ProxiPad.fxml new file mode 100644 index 0000000..7bc83ad --- /dev/null +++ b/src/main/resources/fxml/ui/ProxiPad.fxml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
\ No newline at end of file diff --git a/src/main/resources/fxml/ui/Signup2.fxml b/src/main/resources/fxml/ui/Signup2.fxml new file mode 100644 index 0000000..094bc6d --- /dev/null +++ b/src/main/resources/fxml/ui/Signup2.fxml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/AboutUs.fxml b/src/main/resources/fxml/ui/mac/AboutUs.fxml new file mode 100644 index 0000000..cb476a1 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/AboutUs.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ErrorDialog.fxml b/src/main/resources/fxml/ui/mac/ErrorDialog.fxml new file mode 100644 index 0000000..7bbe376 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ErrorDialog.fxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/NetworkDialog.fxml b/src/main/resources/fxml/ui/mac/NetworkDialog.fxml new file mode 100644 index 0000000..970a572 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/NetworkDialog.fxml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxFileProperties.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxFileProperties.fxml new file mode 100644 index 0000000..6459ed1 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxFileProperties.fxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxFileViewer.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxFileViewer.fxml new file mode 100644 index 0000000..ec622a6 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxFileViewer.fxml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + +
+ +
+
diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxFiles.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxFiles.fxml new file mode 100644 index 0000000..47ef040 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxFiles.fxml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxFolder.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxFolder.fxml new file mode 100644 index 0000000..a3f545c --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxFolder.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxLogin.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxLogin.fxml new file mode 100644 index 0000000..13823f0 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxLogin.fxml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxMediaViewer.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxMediaViewer.fxml new file mode 100644 index 0000000..b808684 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxMediaViewer.fxml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxProfile.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxProfile.fxml new file mode 100644 index 0000000..2bd4d01 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxProfile.fxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxSharing.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxSharing.fxml new file mode 100644 index 0000000..7f6debf --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxSharing.fxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxSignup.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxSignup.fxml new file mode 100644 index 0000000..7b5cf21 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxSignup.fxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiBoxUploader.fxml b/src/main/resources/fxml/ui/mac/ProxiBoxUploader.fxml new file mode 100644 index 0000000..4eae0ff --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiBoxUploader.fxml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiDialog.fxml b/src/main/resources/fxml/ui/mac/ProxiDialog.fxml new file mode 100644 index 0000000..73c7f07 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiDialog.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/ui/mac/ProxiPad.fxml b/src/main/resources/fxml/ui/mac/ProxiPad.fxml new file mode 100644 index 0000000..6310da9 --- /dev/null +++ b/src/main/resources/fxml/ui/mac/ProxiPad.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+
diff --git a/src/main/resources/fxml/ui/mac/Signup2.fxml b/src/main/resources/fxml/ui/mac/Signup2.fxml new file mode 100644 index 0000000..057427c --- /dev/null +++ b/src/main/resources/fxml/ui/mac/Signup2.fxml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/proximax.properties b/src/main/resources/proximax.properties new file mode 100644 index 0000000..b314cff Binary files /dev/null and b/src/main/resources/proximax.properties differ diff --git a/src/main/resources/richtext/align-center.png b/src/main/resources/richtext/align-center.png new file mode 100644 index 0000000..8925fc7 Binary files /dev/null and b/src/main/resources/richtext/align-center.png differ diff --git a/src/main/resources/richtext/align-justify.png b/src/main/resources/richtext/align-justify.png new file mode 100644 index 0000000..2c6f317 Binary files /dev/null and b/src/main/resources/richtext/align-justify.png differ diff --git a/src/main/resources/richtext/align-left.png b/src/main/resources/richtext/align-left.png new file mode 100644 index 0000000..2767846 Binary files /dev/null and b/src/main/resources/richtext/align-left.png differ diff --git a/src/main/resources/richtext/align-right.png b/src/main/resources/richtext/align-right.png new file mode 100644 index 0000000..f130c5f Binary files /dev/null and b/src/main/resources/richtext/align-right.png differ diff --git a/src/main/resources/richtext/bold.png b/src/main/resources/richtext/bold.png new file mode 100644 index 0000000..64b243e Binary files /dev/null and b/src/main/resources/richtext/bold.png differ diff --git a/src/main/resources/richtext/copy.png b/src/main/resources/richtext/copy.png new file mode 100644 index 0000000..c8d454e Binary files /dev/null and b/src/main/resources/richtext/copy.png differ diff --git a/src/main/resources/richtext/cut.png b/src/main/resources/richtext/cut.png new file mode 100644 index 0000000..9c43be4 Binary files /dev/null and b/src/main/resources/richtext/cut.png differ diff --git a/src/main/resources/richtext/insertimage.png b/src/main/resources/richtext/insertimage.png new file mode 100644 index 0000000..5cdf307 Binary files /dev/null and b/src/main/resources/richtext/insertimage.png differ diff --git a/src/main/resources/richtext/italic.png b/src/main/resources/richtext/italic.png new file mode 100644 index 0000000..4dbc41f Binary files /dev/null and b/src/main/resources/richtext/italic.png differ diff --git a/src/main/resources/richtext/loadfile.png b/src/main/resources/richtext/loadfile.png new file mode 100644 index 0000000..bbf9881 Binary files /dev/null and b/src/main/resources/richtext/loadfile.png differ diff --git a/src/main/resources/richtext/new.png b/src/main/resources/richtext/new.png new file mode 100644 index 0000000..50d1152 Binary files /dev/null and b/src/main/resources/richtext/new.png differ diff --git a/src/main/resources/richtext/paste.png b/src/main/resources/richtext/paste.png new file mode 100644 index 0000000..7f855d4 Binary files /dev/null and b/src/main/resources/richtext/paste.png differ diff --git a/src/main/resources/richtext/redo.png b/src/main/resources/richtext/redo.png new file mode 100644 index 0000000..f68cf43 Binary files /dev/null and b/src/main/resources/richtext/redo.png differ diff --git a/src/main/resources/richtext/rich-text.css b/src/main/resources/richtext/rich-text.css new file mode 100644 index 0000000..8dd20cc --- /dev/null +++ b/src/main/resources/richtext/rich-text.css @@ -0,0 +1,25 @@ +.button.pressed { -fx-background-color: ghostwhite; } + +.button, .toggle-button { + -fx-background-position: center; + -fx-background-repeat: no-repeat; +} + +.button.loadfile { -fx-background-image: url(loadfile.png); } +.button.savefile { -fx-background-image: url(savefile.png); } +.button.new { -fx-background-image: url(new.png); } +.button.undo { -fx-background-image: url(undo.png); } +.button.redo { -fx-background-image: url(redo.png); } +.button.cut { -fx-background-image: url(cut.png); } +.button.copy { -fx-background-image: url(copy.png); } +.button.paste { -fx-background-image: url(paste.png); } +.button.bold { -fx-background-image: url(bold.png); } +.button.italic { -fx-background-image: url(italic.png); } +.button.underline { -fx-background-image: url(underline.png); } +.button.strikethrough { -fx-background-image: url(strikethrough.png); } +.button.insertimage { -fx-background-image: url(insertimage.png); } + +.toggle-button.align-left { -fx-background-image: url(align-left.png); } +.toggle-button.align-center { -fx-background-image: url(align-center.png); } +.toggle-button.align-right { -fx-background-image: url(align-right.png); } +.toggle-button.align-justify { -fx-background-image: url(align-justify.png); } \ No newline at end of file diff --git a/src/main/resources/richtext/savefile.png b/src/main/resources/richtext/savefile.png new file mode 100644 index 0000000..6684136 Binary files /dev/null and b/src/main/resources/richtext/savefile.png differ diff --git a/src/main/resources/richtext/strikethrough.png b/src/main/resources/richtext/strikethrough.png new file mode 100644 index 0000000..270da36 Binary files /dev/null and b/src/main/resources/richtext/strikethrough.png differ diff --git a/src/main/resources/richtext/underline.png b/src/main/resources/richtext/underline.png new file mode 100644 index 0000000..5d3bced Binary files /dev/null and b/src/main/resources/richtext/underline.png differ diff --git a/src/main/resources/richtext/undo.png b/src/main/resources/richtext/undo.png new file mode 100644 index 0000000..b015452 Binary files /dev/null and b/src/main/resources/richtext/undo.png differ