diff --git a/NOTICE.md b/NOTICE.md index ec1eec1d0..00b7ca296 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -7,7 +7,6 @@ Cryptomator is licensed under the MIT license. The details can be found in the a Cryptomator uses third party softwares that may be licensed under different licenses. - ### Jackson Jackson is a high-performance, Free/Open Source JSON processing library. It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has @@ -24,7 +23,6 @@ FasterXML.com (http://fasterxml.com). in some artifacts (usually source distributions); but is always available from the source code management (SCM) system project uses. - ### Jetty Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. @@ -39,7 +37,6 @@ Permission to use, copy, modify and distribute UnixCrypt for non-commercial or commercial purposes and without fee is granted provided that the copyright notice appears in all copies. - ### Jackrabbit WebDAV Library Copyright 2004-2014 The Apache Software Foundation @@ -83,6 +80,12 @@ Copyright (c) 2013, ControlsFX Licensed under the accompanying BSD license file. +### Dagger 2 +Copyright 2014 Google, Inc. +Copyright 2012 Square, Inc. + +Licensed under the Apache License, Version 2.0 + ### Apache Log4j Copyright 1999-2012 Apache Software Foundation @@ -93,4 +96,4 @@ ResolverUtil.java Copyright 2005-2006 Tim Fennell ### JUnit Copyright (c) 2000-2006, www.hamcrest.org -Licensed under the accompanying BSD license file. \ No newline at end of file +Licensed under the accompanying BSD license file. diff --git a/main/pom.xml b/main/pom.xml index fe32fde51..67d4ccc0d 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -112,7 +112,7 @@ ${commons-codec.version} - + commons-httpclient commons-httpclient ${commons-httpclient.version} @@ -127,9 +127,9 @@ - com.google.inject - guice - 3.0 + com.google.dagger + dagger + 2.0 diff --git a/main/ui/pom.xml b/main/ui/pom.xml index f1c0c0a0b..cb61cf0d6 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -55,8 +55,35 @@ - com.google.inject - guice + com.google.dagger + dagger + + + javax.inject + javax.inject + 1 + compile + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + + com.google.dagger + dagger-compiler + 2.0 + + + + + true + + + + diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java new file mode 100644 index 000000000..9dd36f9b3 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java @@ -0,0 +1,20 @@ +package org.cryptomator.ui; + +import java.util.concurrent.ExecutorService; + +import javax.inject.Singleton; + +import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.ui.util.DeferredCloser; + +import dagger.Component; + +@Singleton +@Component(modules = CryptomatorModule.class) +interface CryptomatorComponent { + ExecutorService executorService(); + + DeferredCloser deferredCloser(); + + MainController mainController(); +} \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java new file mode 100644 index 000000000..c93392c1f --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -0,0 +1,120 @@ +package org.cryptomator.ui; + +import java.util.Comparator; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.cryptomator.crypto.Cryptor; +import org.cryptomator.crypto.SamplingCryptorDecorator; +import org.cryptomator.crypto.aes256.Aes256Cryptor; +import org.cryptomator.ui.model.VaultFactory; +import org.cryptomator.ui.model.VaultObjectMapperProvider; +import org.cryptomator.ui.settings.Settings; +import org.cryptomator.ui.settings.SettingsProvider; +import org.cryptomator.ui.util.DeferredCloser; +import org.cryptomator.ui.util.DeferredCloser.Closer; +import org.cryptomator.ui.util.SemVerComparator; +import org.cryptomator.ui.util.mount.WebDavMounter; +import org.cryptomator.ui.util.mount.WebDavMounterProvider; +import org.cryptomator.webdav.WebDavServer; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import dagger.Module; +import dagger.Provides; +import javafx.application.Application; + +@Module +class CryptomatorModule { + + private final Application application; + private final DeferredCloser deferredCloser; + + public CryptomatorModule(Application application) { + this.application = application; + this.deferredCloser = new DeferredCloser(); + } + + @Provides + @Singleton + Application provideApplication() { + return application; + } + + @Provides + @Singleton + DeferredCloser provideDeferredCloser() { + return deferredCloser; + } + + @Provides + @Singleton + @Named("SemVer") + Comparator provideSemVerComparator() { + return new SemVerComparator(); + } + + @Provides + @Singleton + VaultObjectMapperProvider provideVaultObjectMapperProvider(VaultFactory vaultFactory) { + return new VaultObjectMapperProvider(vaultFactory); + } + + @Provides + @Singleton + @Named("VaultJsonMapper") + ObjectMapper provideVaultObjectMapper(VaultObjectMapperProvider vaultObjectMapperProvider) { + return vaultObjectMapperProvider.get(); + } + + @Provides + @Singleton + SettingsProvider provideSettingsProvider(DeferredCloser closer, @Named("VaultJsonMapper") Provider objectMapper) { + return new SettingsProvider(closer, objectMapper.get()); + } + + @Provides + Settings provideSettings(SettingsProvider settingsProvider) { + return settingsProvider.get(); + } + + @Provides + @Singleton + ExecutorService provideExecutorService() { + return closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown); + } + + @Provides + @Singleton + VaultFactory provideVaultFactory(WebDavServer server, Provider cryptorProvider, WebDavMounter mounter, DeferredCloser closer) { + return new VaultFactory(server, cryptorProvider, mounter, closer); + } + + @Provides + @Singleton + WebDavMounter provideWebDavMounterProvider(WebDavServer server, ExecutorService executorService) { + return new WebDavMounterProvider(server, executorService).get(); + } + + @Provides + @Singleton + WebDavServer provideWebDavServer() { + final WebDavServer webDavServer = new WebDavServer(); + webDavServer.start(); + return closeLater(webDavServer, WebDavServer::stop); + } + + @Provides + Cryptor provideCryptor() { + return SamplingCryptorDecorator.decorate(new Aes256Cryptor()); + } + + private T closeLater(T object, Closer closer) { + return deferredCloser.closeLater(object, closer).get().get(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java index 600d9e36b..09af7b069 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -15,15 +15,7 @@ import java.nio.file.Path; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; - import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.MainModule.ControllerFactory; import org.cryptomator.ui.controllers.MainController; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.util.ActiveWindowStyleSupport; @@ -34,8 +26,10 @@ import org.cryptomator.ui.util.TrayIconUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.inject.Guice; -import com.google.inject.Injector; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; +import javafx.stage.Stage; public class MainApplication extends Application { @@ -44,28 +38,15 @@ public class MainApplication extends Application { private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); private final ExecutorService executorService; - private final ControllerFactory controllerFactory; private final DeferredCloser closer; + private final MainController mainCtrl; public MainApplication() { - this(getInjector()); - } - - private static Injector getInjector() { - return Guice.createInjector(new MainModule()); - } - - public MainApplication(Injector injector) { - this(injector.getInstance(ExecutorService.class), injector.getInstance(ControllerFactory.class), injector.getInstance(DeferredCloser.class), injector.getInstance(MainApplicationReference.class)); - } - - public MainApplication(ExecutorService executorService, ControllerFactory controllerFactory, DeferredCloser closer, MainApplicationReference appRef) { - super(); - this.executorService = executorService; - this.controllerFactory = controllerFactory; - this.closer = closer; + final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this)).build(); + this.executorService = comp.executorService(); + this.closer = comp.deferredCloser(); + this.mainCtrl = comp.mainController(); Cryptomator.addShutdownTask(closer::close); - appRef.set(this); } @Override @@ -83,34 +64,29 @@ public class MainApplication extends Application { }); chooseNativeStylesheet(); + + mainCtrl.initStage(primaryStage); + final ResourceBundle rb = ResourceBundle.getBundle("localization"); - final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"), rb); - loader.setControllerFactory(controllerFactory); - final Parent root = loader.load(); - final MainController ctrl = loader.getController(); - ctrl.setStage(primaryStage); - final Scene scene = new Scene(root); primaryStage.setTitle(rb.getString("app.name")); - primaryStage.setScene(scene); - primaryStage.sizeToScene(); primaryStage.setResizable(false); primaryStage.show(); + ActiveWindowStyleSupport.startObservingFocus(primaryStage); TrayIconUtil.init(primaryStage, rb, () -> { quit(); }); for (String arg : getParameters().getUnnamed()) { - handleCommandLineArg(ctrl, arg); + handleCommandLineArg(mainCtrl, arg); } if (SystemUtils.IS_OS_MAC_OSX) { - Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(ctrl, file.getAbsolutePath())); + Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(mainCtrl, file.getAbsolutePath())); } LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, executorService), LocalInstance::close).get().get(); - - cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(ctrl, arg)); + cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(mainCtrl, arg)); } void handleCommandLineArg(final MainController ctrl, String arg) { @@ -162,25 +138,4 @@ public class MainApplication extends Application { closer.close(); } - /** - * Needed to inject MainApplication. Problem: Application needs to be set asap after injector creation. - */ - static class MainApplicationReference { - - private Application application; - - private void set(Application application) { - this.application = application; - } - - public Application get() { - if (application == null) { - throw new IllegalStateException("not yet ready."); - } else { - return application; - } - } - - } - } diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainModule.java b/main/ui/src/main/java/org/cryptomator/ui/MainModule.java deleted file mode 100644 index 30f3d78bf..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/MainModule.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 cryptomator.org - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - * - * Contributors: - * Tillmann Gaida - initial implementation - ******************************************************************************/ -package org.cryptomator.ui; - -import java.util.Comparator; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import javafx.application.Application; -import javafx.util.Callback; - -import javax.inject.Named; -import javax.inject.Singleton; - -import org.cryptomator.crypto.Cryptor; -import org.cryptomator.crypto.SamplingCryptorDecorator; -import org.cryptomator.crypto.aes256.Aes256Cryptor; -import org.cryptomator.ui.MainApplication.MainApplicationReference; -import org.cryptomator.ui.model.VaultFactory; -import org.cryptomator.ui.model.VaultObjectMapperProvider; -import org.cryptomator.ui.settings.Settings; -import org.cryptomator.ui.settings.SettingsProvider; -import org.cryptomator.ui.util.DeferredCloser; -import org.cryptomator.ui.util.DeferredCloser.Closer; -import org.cryptomator.ui.util.SemVerComparator; -import org.cryptomator.ui.util.mount.WebDavMounter; -import org.cryptomator.ui.util.mount.WebDavMounterProvider; -import org.cryptomator.webdav.WebDavServer; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.AbstractModule; -import com.google.inject.Injector; -import com.google.inject.Provider; -import com.google.inject.Provides; -import com.google.inject.name.Names; - -public class MainModule extends AbstractModule { - - private final DeferredCloser deferredCloser = new DeferredCloser(); - - public static interface ControllerFactory extends Callback, Object> { - - } - - @Override - protected void configure() { - bind(DeferredCloser.class).toInstance(deferredCloser); - bind(ObjectMapper.class).annotatedWith(Names.named("VaultJsonMapper")).toProvider(VaultObjectMapperProvider.class); - bind(Settings.class).toProvider(SettingsProvider.class); - bind(WebDavMounter.class).toProvider(WebDavMounterProvider.class).asEagerSingleton(); - } - - @Provides - @Singleton - ControllerFactory getControllerFactory(Injector injector) { - return cls -> injector.getInstance(cls); - } - - @Provides - @Singleton - MainApplicationReference getApplicationBinding() { - return new MainApplicationReference(); - } - - @Provides - Application getApplication(MainApplicationReference ref) { - return ref.get(); - } - - @Provides - @Named("SemVer") - @Singleton - Comparator getSemVerComparator() { - return new SemVerComparator(); - } - - @Provides - @Singleton - ExecutorService getExec() { - return closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown); - } - - @Provides - Cryptor getCryptor() { - return SamplingCryptorDecorator.decorate(new Aes256Cryptor()); - } - - @Provides - @Singleton - VaultFactory getVaultFactory(WebDavServer server, Provider cryptorProvider, WebDavMounter mounter, DeferredCloser closer) { - return new VaultFactory(server, cryptorProvider, mounter, closer); - } - - @Provides - @Singleton - WebDavServer getServer() { - final WebDavServer webDavServer = new WebDavServer(); - webDavServer.start(); - return closeLater(webDavServer, WebDavServer::stop); - } - - T closeLater(T object, Closer closer) { - return deferredCloser.closeLater(object, closer).get().get(); - } -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/AbstractFXMLViewController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/AbstractFXMLViewController.java new file mode 100644 index 000000000..326cc42bf --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/AbstractFXMLViewController.java @@ -0,0 +1,105 @@ +package org.cryptomator.ui.controllers; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +/** + * Controller presenting a single view. + */ +abstract class AbstractFXMLViewController implements Initializable { + + private Parent fxmlRoot; + + /** + * URL from #initialize(URL, ResourceBundle) + */ + protected URL rootUrl; + + /** + * ResourceBundle from #initialize(URL, ResourceBundle) + */ + protected ResourceBundle resourceBundle; + + /** + * Gets the URL to the FXML file describing the view presented by this controller.
+ * + * A default implementation would look like this:
+ * + * return getClass().getResource("/myView.fxml"); + * + * + * @return FXML resource URL + */ + protected abstract URL getFxmlResourceUrl(); + + /** + * @return Localization bundle for the FXML labels or null. + */ + protected abstract ResourceBundle getFxmlResourceBundle(); + + @Override + public final void initialize(URL location, ResourceBundle resources) { + this.rootUrl = location; + this.resourceBundle = resources; + this.initialize(); + } + + protected void initialize() { + } + + /** + * Creates a FXML loader used in {@link #loadFxml()}. This method can be overwritten for further loader customization. + * + * @return Configured loader ready to load. + */ + protected FXMLLoader createFxmlLoader() { + final URL fxmlUrl = getFxmlResourceUrl(); + final ResourceBundle rb = getFxmlResourceBundle(); + final FXMLLoader loader = new FXMLLoader(fxmlUrl, rb); + loader.setController(this); + return loader; + } + + /** + * Loads the view presented by this controller from the FXML file return by {@link #getFxmlResourceUrl()}. This method can only be invoked once. + * + * @return Parent view element. + */ + protected final synchronized Parent loadFxml() { + if (fxmlRoot == null) { + final FXMLLoader loader = createFxmlLoader(); + try { + fxmlRoot = loader.load(); + } catch (IOException e) { + throw new IllegalStateException("Could not load FXML file from location: " + loader.getLocation(), e); + } + } + return fxmlRoot; + } + + /** + * Creates a new scene with the root node from the FXML file and applies it to the given stage. + */ + public void initStage(Stage stage) { + final Parent root = loadFxml(); + stage.setScene(new Scene(root)); + stage.sizeToScene(); + } + + /** + * @return Creates a new stage and calls {@link #initStage(Stage)}. + */ + public Stage createStage() { + final Stage stage = new Stage(); + initStage(stage); + return stage; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java index aa603a71f..97c6d8426 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java @@ -10,15 +10,8 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.ResourceBundle; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Hyperlink; -import javafx.scene.text.Text; +import javax.inject.Inject; +import javax.inject.Singleton; import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException; import org.cryptomator.crypto.exceptions.UnsupportedVaultException; @@ -28,13 +21,20 @@ import org.cryptomator.ui.model.Vault; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.inject.Inject; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Hyperlink; +import javafx.scene.text.Text; -public class ChangePasswordController implements Initializable { +@Singleton +public class ChangePasswordController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class); - private ResourceBundle rb; private ChangePasswordListener listener; private Vault vault; @@ -65,9 +65,17 @@ public class ChangePasswordController implements Initializable { } @Override - public void initialize(URL location, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/change_password.fxml"); + } + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { oldPasswordField.textProperty().addListener(this::passwordFieldsDidChange); newPasswordField.textProperty().addListener(this::passwordFieldsDidChange); retypePasswordField.textProperty().addListener(this::passwordFieldsDidChange); @@ -109,19 +117,19 @@ public class ChangePasswordController implements Initializable { vault.getCryptor().decryptMasterKey(masterKeyInputStream, oldPassword); Files.copy(masterKeyPath, masterKeyBackupPath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException ex) { - messageText.setText(rb.getString("changePassword.errorMessage.decryptionFailed")); + messageText.setText(resourceBundle.getString("changePassword.errorMessage.decryptionFailed")); LOG.error("Decryption failed for technical reasons.", ex); newPasswordField.swipe(); retypePasswordField.swipe(); return; } catch (WrongPasswordException e) { - messageText.setText(rb.getString("changePassword.errorMessage.wrongPassword")); + messageText.setText(resourceBundle.getString("changePassword.errorMessage.wrongPassword")); newPasswordField.swipe(); retypePasswordField.swipe(); Platform.runLater(oldPasswordField::requestFocus); return; } catch (UnsupportedKeyLengthException ex) { - messageText.setText(rb.getString("changePassword.errorMessage.unsupportedKeyLengthInstallJCE")); + messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedKeyLengthInstallJCE")); LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex); newPasswordField.swipe(); retypePasswordField.swipe(); @@ -129,9 +137,9 @@ public class ChangePasswordController implements Initializable { } catch (UnsupportedVaultException e) { downloadsPageLink.setVisible(true); if (e.isVaultOlderThanSoftware()) { - messageText.setText(rb.getString("changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); + messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(rb.getString("changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); + messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); } newPasswordField.swipe(); retypePasswordField.swipe(); @@ -146,7 +154,7 @@ public class ChangePasswordController implements Initializable { final CharSequence newPassword = newPasswordField.getCharacters(); try (final OutputStream masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) { vault.getCryptor().encryptMasterKey(masterKeyOutputStream, newPassword); - messageText.setText(rb.getString("changePassword.infoMessage.success")); + messageText.setText(resourceBundle.getString("changePassword.infoMessage.success")); Platform.runLater(this::didChangePassword); // At this point the backup is still using the old password. // It will be changed as soon as the user unlocks the vault the next time. diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java index a7fa2a0a4..ff1488e1e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java @@ -19,23 +19,25 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ResourceBundle; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Label; +import javax.inject.Inject; +import javax.inject.Singleton; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Vault; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class InitializeController implements Initializable { +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; + +@Singleton +public class InitializeController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class); - private ResourceBundle localization; private Vault vault; private InitializationListener listener; @@ -51,9 +53,22 @@ public class InitializeController implements Initializable { @FXML private Label messageLabel; + @Inject + public InitializeController() { + } + @Override - public void initialize(URL url, ResourceBundle rb) { - this.localization = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/initialize.fxml"); + } + + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { passwordField.textProperty().addListener(this::passwordFieldsDidChange); retypePasswordField.textProperty().addListener(this::passwordFieldsDidChange); } @@ -88,9 +103,9 @@ public class InitializeController implements Initializable { listener.didInitialize(this); } } catch (FileAlreadyExistsException ex) { - messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized")); + messageLabel.setText(resourceBundle.getString("initialize.messageLabel.alreadyInitialized")); } catch (InvalidPathException ex) { - messageLabel.setText(localization.getString("initialize.messageLabel.invalidPath")); + messageLabel.setText(resourceBundle.getString("initialize.messageLabel.invalidPath")); } catch (IOException ex) { LOG.error("I/O Exception", ex); } finally { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java index 22fa8ffa5..39ba7cb32 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java @@ -4,6 +4,10 @@ import java.net.URL; import java.util.ResourceBundle; import java.util.stream.Collectors; +import javax.inject.Inject; + +import org.cryptomator.ui.model.Vault; + import javafx.application.Application; import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; @@ -18,18 +22,13 @@ import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.control.cell.CheckBoxListCell; import javafx.stage.Stage; import javafx.util.StringConverter; -import javax.inject.Inject; - -import org.cryptomator.ui.model.Vault; - -public class MacWarningsController implements Initializable { +public class MacWarningsController extends AbstractFXMLViewController { @FXML private ListView warningsList; @@ -43,7 +42,6 @@ public class MacWarningsController implements Initializable { private final ChangeListener stageVisibilityChangeListener = this::windowVisibilityDidChange; private Stage stage; private Vault vault; - private ResourceBundle rb; @Inject public MacWarningsController(Application application) { @@ -51,8 +49,17 @@ public class MacWarningsController implements Initializable { } @Override - public void initialize(URL location, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/mac_warnings.fxml"); + } + + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { warnings.addListener(this::warningsDidInvalidate); warningsList.setItems(warnings); warningsList.setCellFactory(CheckBoxListCell.forListView(Warning::selectedProperty, new StringConverter() { @@ -70,6 +77,13 @@ public class MacWarningsController implements Initializable { })); } + @Override + public void initStage(Stage stage) { + super.initStage(stage); + this.stage = stage; + stage.showingProperty().addListener(new WeakChangeListener<>(stageVisibilityChangeListener)); + } + @FXML private void didClickWhitelistButton(ActionEvent event) { warnings.filtered(w -> w.isSelected()).stream().forEach(w -> { @@ -103,7 +117,7 @@ public class MacWarningsController implements Initializable { private void windowVisibilityDidChange(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (Boolean.TRUE.equals(newValue)) { - stage.setTitle(String.format(rb.getString("macWarnings.windowTitle"), vault.getName())); + stage.setTitle(String.format(resourceBundle.getString("macWarnings.windowTitle"), vault.getName())); warnings.addAll(vault.getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList())); vault.getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener); } else { @@ -116,11 +130,6 @@ public class MacWarningsController implements Initializable { whitelistButton.setDisable(warnings.filtered(w -> w.isSelected()).isEmpty()); } - public void setStage(Stage stage) { - this.stage = stage; - stage.showingProperty().addListener(new WeakChangeListener<>(stageVisibilityChangeListener)); - } - public void setVault(Vault vault) { this.vault = vault; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 3c9c669b6..479cda1dd 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -18,14 +18,28 @@ import java.util.List; import java.util.ResourceBundle; import java.util.stream.Collectors; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.cryptomator.ui.controllers.ChangePasswordController.ChangePasswordListener; +import org.cryptomator.ui.controllers.InitializeController.InitializationListener; +import org.cryptomator.ui.controllers.UnlockController.UnlockListener; +import org.cryptomator.ui.controllers.UnlockedController.LockListener; +import org.cryptomator.ui.controls.DirectoryListCell; +import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.model.VaultFactory; +import org.cryptomator.ui.settings.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dagger.Lazy; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; import javafx.geometry.Side; import javafx.scene.Parent; import javafx.scene.control.ContextMenu; @@ -38,21 +52,8 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.stage.WindowEvent; -import org.cryptomator.ui.MainModule.ControllerFactory; -import org.cryptomator.ui.controllers.ChangePasswordController.ChangePasswordListener; -import org.cryptomator.ui.controllers.InitializeController.InitializationListener; -import org.cryptomator.ui.controllers.UnlockController.UnlockListener; -import org.cryptomator.ui.controllers.UnlockedController.LockListener; -import org.cryptomator.ui.controls.DirectoryListCell; -import org.cryptomator.ui.model.Vault; -import org.cryptomator.ui.model.VaultFactory; -import org.cryptomator.ui.settings.Settings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.inject.Inject; - -public class MainController implements Initializable, InitializationListener, UnlockListener, LockListener, ChangePasswordListener { +@Singleton +public class MainController extends AbstractFXMLViewController implements InitializationListener, UnlockListener, LockListener, ChangePasswordListener { private static final Logger LOG = LoggerFactory.getLogger(MainController.class); @@ -76,28 +77,50 @@ public class MainController implements Initializable, InitializationListener, Un @FXML private Pane contentPane; - private final ControllerFactory controllerFactory; private final Settings settings; private final VaultFactory vaultFactoy; - - private ResourceBundle rb; + private final Lazy welcomeController; + private final Lazy initializeController; + private final Lazy unlockController; + private final Provider unlockedController; + private final Lazy changePasswordController; @Inject - public MainController(ControllerFactory controllerFactory, Settings settings, VaultFactory vaultFactoy) { + public MainController(Settings settings, VaultFactory vaultFactoy, Lazy welcomeController, Lazy initializeController, Lazy unlockController, + Provider unlockedController, Lazy changePasswordController) { super(); - this.controllerFactory = controllerFactory; this.settings = settings; this.vaultFactoy = vaultFactoy; + this.welcomeController = welcomeController; + this.initializeController = initializeController; + this.unlockController = unlockController; + this.unlockedController = unlockedController; + this.changePasswordController = changePasswordController; } @Override - public void initialize(URL url, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/main.fxml"); + } + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { final ObservableList items = FXCollections.observableList(settings.getDirectories()); vaultList.setItems(items); vaultList.setCellFactory(this::createDirecoryListCell); vaultList.getSelectionModel().getSelectedItems().addListener(this::selectedVaultDidChange); + this.showWelcomeView(); + } + + @Override + public void initStage(Stage stage) { + super.initStage(stage); + this.stage = stage; } @FXML @@ -191,14 +214,14 @@ public class MainController implements Initializable, InitializationListener, Un private void selectedVaultDidChange(ListChangeListener.Change change) { final Vault selectedVault = vaultList.getSelectionModel().getSelectedItem(); if (selectedVault == null) { - stage.setTitle(rb.getString("app.name")); + stage.setTitle(resourceBundle.getString("app.name")); showWelcomeView(); } else if (!Files.isDirectory(selectedVault.getPath())) { Platform.runLater(() -> { vaultList.getItems().remove(selectedVault); vaultList.getSelectionModel().clearSelection(); }); - stage.setTitle(rb.getString("app.name")); + stage.setTitle(resourceBundle.getString("app.name")); showWelcomeView(); } else { stage.setTitle(selectedVault.getName()); @@ -237,25 +260,17 @@ public class MainController implements Initializable, InitializationListener, Un } } - private T showView(String fxml) { - try { - final FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml), rb); - loader.setControllerFactory(controllerFactory); - final Parent root = loader.load(); - contentPane.getChildren().clear(); - contentPane.getChildren().add(root); - return loader.getController(); - } catch (IOException e) { - throw new IllegalStateException("Failed to load fxml file.", e); - } - } - private void showWelcomeView() { - this.showView("/fxml/welcome.fxml"); + final Parent root = welcomeController.get().loadFxml(); + contentPane.getChildren().clear(); + contentPane.getChildren().add(root); } private void showInitializeView(Vault vault) { - final InitializeController ctrl = showView("/fxml/initialize.fxml"); + final InitializeController ctrl = initializeController.get(); + final Parent root = ctrl.loadFxml(); + contentPane.getChildren().clear(); + contentPane.getChildren().add(root); ctrl.setVault(vault); ctrl.setListener(this); } @@ -266,7 +281,10 @@ public class MainController implements Initializable, InitializationListener, Un } private void showUnlockView(Vault vault) { - final UnlockController ctrl = showView("/fxml/unlock.fxml"); + final UnlockController ctrl = unlockController.get(); + final Parent root = ctrl.loadFxml(); + contentPane.getChildren().clear(); + contentPane.getChildren().add(root); ctrl.setVault(vault); ctrl.setListener(this); } @@ -278,7 +296,10 @@ public class MainController implements Initializable, InitializationListener, Un } private void showUnlockedView(Vault vault) { - final UnlockedController ctrl = showView("/fxml/unlocked.fxml"); + final UnlockedController ctrl = unlockedController.get(); + final Parent root = ctrl.loadFxml(); + contentPane.getChildren().clear(); + contentPane.getChildren().add(root); ctrl.setVault(vault); ctrl.setListener(this); } @@ -292,7 +313,10 @@ public class MainController implements Initializable, InitializationListener, Un } private void showChangePasswordView(Vault vault) { - final ChangePasswordController ctrl = showView("/fxml/change_password.fxml"); + final ChangePasswordController ctrl = changePasswordController.get(); + final Parent root = ctrl.loadFxml(); + contentPane.getChildren().clear(); + contentPane.getChildren().add(root); ctrl.setVault(vault); ctrl.setListener(this); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java index bc3f991c1..f8de0501c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java @@ -19,19 +19,7 @@ import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.scene.text.Text; - +import javax.inject.Inject; import javax.security.auth.DestroyFailedException; import org.apache.commons.lang3.CharUtils; @@ -44,13 +32,22 @@ import org.cryptomator.ui.util.FXThreads; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.inject.Inject; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; +import javafx.scene.text.Text; -public class UnlockController implements Initializable { +public class UnlockController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); - private ResourceBundle rb; private UnlockListener listener; private Vault vault; @@ -77,15 +74,22 @@ public class UnlockController implements Initializable { @Inject public UnlockController(Application app, ExecutorService exec) { - super(); this.app = app; this.exec = exec; } @Override - public void initialize(URL url, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/unlock.fxml"); + } + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { passwordField.textProperty().addListener(this::passwordFieldsDidChange); mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents); mountName.textProperty().addListener(this::mountNameDidChange); @@ -124,7 +128,7 @@ public class UnlockController implements Initializable { try (final InputStream masterKeyInputStream = Files.newInputStream(masterKeyPath, StandardOpenOption.READ)) { vault.getCryptor().decryptMasterKey(masterKeyInputStream, password); if (!vault.startServer()) { - messageText.setText(rb.getString("unlock.messageLabel.startServerFailed")); + messageText.setText(resourceBundle.getString("unlock.messageLabel.startServerFailed")); vault.getCryptor().destroy(); return; } @@ -136,26 +140,26 @@ public class UnlockController implements Initializable { } catch (IOException ex) { setControlsDisabled(false); progressIndicator.setVisible(false); - messageText.setText(rb.getString("unlock.errorMessage.decryptionFailed")); + messageText.setText(resourceBundle.getString("unlock.errorMessage.decryptionFailed")); LOG.error("Decryption failed for technical reasons.", ex); } catch (WrongPasswordException e) { setControlsDisabled(false); progressIndicator.setVisible(false); - messageText.setText(rb.getString("unlock.errorMessage.wrongPassword")); + messageText.setText(resourceBundle.getString("unlock.errorMessage.wrongPassword")); Platform.runLater(passwordField::requestFocus); } catch (UnsupportedKeyLengthException ex) { setControlsDisabled(false); progressIndicator.setVisible(false); - messageText.setText(rb.getString("unlock.errorMessage.unsupportedKeyLengthInstallJCE")); + messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedKeyLengthInstallJCE")); LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex); } catch (UnsupportedVaultException e) { setControlsDisabled(false); progressIndicator.setVisible(false); downloadsPageLink.setVisible(true); if (e.isVaultOlderThanSoftware()) { - messageText.setText(rb.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); + messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(rb.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); + messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); } } catch (DestroyFailedException e) { setControlsDisabled(false); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java index f99933562..ba1896ef9 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java @@ -8,10 +8,17 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import javax.inject.Inject; +import javax.inject.Provider; + +import org.cryptomator.crypto.CryptorIOSampling; +import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.util.ActiveWindowStyleSupport; +import org.cryptomator.ui.util.mount.CommandFailedException; + import javafx.animation.Animation; import javafx.animation.KeyFrame; import javafx.animation.Timeline; @@ -20,10 +27,6 @@ import javafx.collections.ListChangeListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Parent; -import javafx.scene.Scene; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart.Data; @@ -32,21 +35,10 @@ import javafx.scene.control.Label; import javafx.stage.Stage; import javafx.util.Duration; -import org.cryptomator.crypto.CryptorIOSampling; -import org.cryptomator.ui.MainModule.ControllerFactory; -import org.cryptomator.ui.model.Vault; -import org.cryptomator.ui.util.ActiveWindowStyleSupport; -import org.cryptomator.ui.util.mount.CommandFailedException; - -import com.google.inject.Inject; - -public class UnlockedController implements Initializable { +public class UnlockedController extends AbstractFXMLViewController { private static final int IO_SAMPLING_STEPS = 100; private static final double IO_SAMPLING_INTERVAL = 0.25; - private final ControllerFactory controllerFactory; - private final Stage macWarningWindow = new Stage(); - private MacWarningsController macWarningCtrl; private LockListener listener; private Vault vault; private Timeline ioAnimation; @@ -60,32 +52,28 @@ public class UnlockedController implements Initializable { @FXML private NumberAxis xAxis; - private ResourceBundle rb; + private final Stage macWarningsWindow = new Stage(); + private final MacWarningsController macWarningsController; @Inject - public UnlockedController(ControllerFactory controllerFactory) { - this.controllerFactory = controllerFactory; + public UnlockedController(Provider macWarningsControllerProvider) { + this.macWarningsController = macWarningsControllerProvider.get(); } @Override - public void initialize(URL url, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/unlocked.fxml"); + } - try { - final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/mac_warnings.fxml"), rb); - loader.setControllerFactory(controllerFactory); + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } - final Parent root = loader.load(); - macWarningWindow.setScene(new Scene(root)); - macWarningWindow.sizeToScene(); - macWarningWindow.setResizable(false); - ActiveWindowStyleSupport.startObservingFocus(macWarningWindow); - - macWarningCtrl = loader.getController(); - macWarningCtrl.setStage(macWarningWindow); - } catch (IOException e) { - throw new IllegalStateException("Failed to load fxml file.", e); - } + @Override + public void initialize() { + macWarningsController.initStage(macWarningsWindow); + ActiveWindowStyleSupport.startObservingFocus(macWarningsWindow); } @FXML @@ -93,7 +81,7 @@ public class UnlockedController implements Initializable { try { vault.unmount(); } catch (CommandFailedException e) { - messageLabel.setText(rb.getString("unlocked.label.unmountFailed")); + messageLabel.setText(resourceBundle.getString("unlocked.label.unmountFailed")); return; } vault.stopServer(); @@ -110,11 +98,11 @@ public class UnlockedController implements Initializable { private void macWarningsDidChange(ListChangeListener.Change change) { if (change.getList().size() > 0) { Platform.runLater(() -> { - macWarningWindow.show(); + macWarningsWindow.show(); }); } else { Platform.runLater(() -> { - macWarningWindow.hide(); + macWarningsWindow.hide(); }); } } @@ -138,6 +126,13 @@ public class UnlockedController implements Initializable { ioAnimation.play(); } + private void stopIoSampling() { + if (ioAnimation != null) { + ioGraph.getData().clear(); + ioAnimation.stop(); + } + } + private class IoSamplingAnimationHandler implements EventHandler { private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0; @@ -181,7 +176,7 @@ public class UnlockedController implements Initializable { public void setVault(Vault vault) { this.vault = vault; - macWarningCtrl.setVault(vault); + macWarningsController.setVault(vault); // listen to MAC warnings as long as this vault is unlocked: final ListChangeListener macWarningsListener = this::macWarningsDidChange; @@ -193,6 +188,7 @@ public class UnlockedController implements Initializable { }); // sample crypto-throughput: + stopIoSampling(); if (vault.getCryptor() instanceof CryptorIOSampling) { startIoSampling((CryptorIOSampling) vault.getCryptor()); } else { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java index 0b3f39d49..5c915c81d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java @@ -16,17 +16,9 @@ import java.util.Map; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Hyperlink; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; - import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; @@ -40,7 +32,16 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -public class WelcomeController implements Initializable { +import javafx.application.Application; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Hyperlink; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +@Singleton +public class WelcomeController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); @@ -53,7 +54,6 @@ public class WelcomeController implements Initializable { private final Application app; private final Comparator semVerComparator; private final ExecutorService executor; - private ResourceBundle rb; @Inject public WelcomeController(Application app, @Named("SemVer") Comparator semVerComparator, ExecutorService executor) { @@ -63,8 +63,17 @@ public class WelcomeController implements Initializable { } @Override - public void initialize(URL url, ResourceBundle rb) { - this.rb = rb; + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/welcome.fxml"); + } + + @Override + protected ResourceBundle getFxmlResourceBundle() { + return ResourceBundle.getBundle("localization"); + } + + @Override + public void initialize() { this.botImageView.setImage(new Image(WelcomeController.class.getResource("/bot_welcome.png").toString())); executor.execute(this::checkForUpdates); } @@ -103,7 +112,7 @@ public class WelcomeController implements Initializable { final String currentVersion = WelcomeController.class.getPackage().getImplementationVersion(); LOG.debug("Current version: {}, lastest version: {}", currentVersion, latestVersion); if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) { - final String msg = String.format(rb.getString("welcome.newVersionMessage"), latestVersion, currentVersion); + final String msg = String.format(resourceBundle.getString("welcome.newVersionMessage"), latestVersion, currentVersion); Platform.runLater(() -> { this.updateLink.setText(msg); this.updateLink.setVisible(true); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java index e4509bb85..715b526ad 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java @@ -2,14 +2,14 @@ package org.cryptomator.ui.model; import java.nio.file.Path; +import javax.inject.Inject; +import javax.inject.Provider; + import org.cryptomator.crypto.Cryptor; import org.cryptomator.ui.util.DeferredCloser; import org.cryptomator.ui.util.mount.WebDavMounter; import org.cryptomator.webdav.WebDavServer; -import com.google.inject.Inject; -import com.google.inject.Provider; - public class VaultFactory { private final WebDavServer server; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java index 9c35838f2..d4b6dce8a 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java @@ -5,6 +5,7 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import javax.inject.Inject; +import javax.inject.Provider; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -16,7 +17,6 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.google.inject.Provider; public class VaultObjectMapperProvider implements Provider { diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsModule.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsModule.java new file mode 100644 index 000000000..a9dde775d --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsModule.java @@ -0,0 +1,20 @@ +package org.cryptomator.ui.settings; + +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.cryptomator.ui.util.DeferredCloser; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import dagger.Module; + +@Module +public class SettingsModule { + + @Singleton + Provider provideSettingsProvider(Provider objectMapper) { + return new SettingsProvider(new DeferredCloser(), objectMapper.get()); + } + +} \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java index bc60720d1..afcaa7b54 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java @@ -11,6 +11,7 @@ import java.nio.file.StandardOpenOption; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.util.DeferredCloser; @@ -18,7 +19,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.Provider; public class SettingsProvider implements Provider { @@ -50,7 +50,7 @@ public class SettingsProvider implements Provider { this.deferredCloser = deferredCloser; this.objectMapper = objectMapper; } - + private Path getSettingsPath() throws IOException { String settingsPathProperty = System.getProperty("settingsPath"); if (settingsPathProperty == null) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java index 56414ad93..f9fab2c8b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java @@ -20,6 +20,8 @@ import org.cryptomator.ui.controllers.MainController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.VisibleForTesting; + /** *

* Tries to bring open-close symmetry in contexts where the resource outlives @@ -57,8 +59,10 @@ public class DeferredCloser implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(MainController.class); + @VisibleForTesting final Map> cleanups = new ConcurrentSkipListMap<>(); + @VisibleForTesting final AtomicLong counter = new AtomicLong(); public class ManagedResource implements DeferredClosable { @@ -73,6 +77,7 @@ public class DeferredCloser implements AutoCloseable { this.closer = closer; } + @Override public void close() { final T oldObject = object.getAndSet(null); if (oldObject != null) { @@ -86,6 +91,7 @@ public class DeferredCloser implements AutoCloseable { } } + @Override public Optional get() throws IllegalStateException { return Optional.ofNullable(object.get()); } @@ -94,6 +100,7 @@ public class DeferredCloser implements AutoCloseable { /** * Closes all added objects which have not been closed before and releases references. */ + @Override public void close() { for (Iterator> iterator = cleanups.values().iterator(); iterator.hasNext();) { final ManagedResource closableProvider = iterator.next(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/mount/WebDavMounterProvider.java b/main/ui/src/main/java/org/cryptomator/ui/util/mount/WebDavMounterProvider.java index 17f886796..a1707e6d5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/mount/WebDavMounterProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/mount/WebDavMounterProvider.java @@ -12,13 +12,12 @@ package org.cryptomator.ui.util.mount; import java.util.concurrent.ExecutorService; import javax.inject.Inject; +import javax.inject.Provider; import org.cryptomator.webdav.WebDavServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.inject.Provider; - public class WebDavMounterProvider implements Provider { private static final Logger LOG = LoggerFactory.getLogger(WebDavMounterProvider.class); diff --git a/main/ui/src/main/resources/fxml/change_password.fxml b/main/ui/src/main/resources/fxml/change_password.fxml index 36db54e94..4f566d07d 100644 --- a/main/ui/src/main/resources/fxml/change_password.fxml +++ b/main/ui/src/main/resources/fxml/change_password.fxml @@ -22,7 +22,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/initialize.fxml b/main/ui/src/main/resources/fxml/initialize.fxml index a543ac961..8880f0c31 100644 --- a/main/ui/src/main/resources/fxml/initialize.fxml +++ b/main/ui/src/main/resources/fxml/initialize.fxml @@ -16,7 +16,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/mac_warnings.fxml b/main/ui/src/main/resources/fxml/mac_warnings.fxml index 17322ff19..1270014dd 100644 --- a/main/ui/src/main/resources/fxml/mac_warnings.fxml +++ b/main/ui/src/main/resources/fxml/mac_warnings.fxml @@ -15,7 +15,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/main.fxml b/main/ui/src/main/resources/fxml/main.fxml index ede7a0358..08aa8cea3 100644 --- a/main/ui/src/main/resources/fxml/main.fxml +++ b/main/ui/src/main/resources/fxml/main.fxml @@ -18,12 +18,11 @@ - + - @@ -49,11 +48,7 @@ - - - - - + diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml index fb1ba82e4..8944d2518 100644 --- a/main/ui/src/main/resources/fxml/unlock.fxml +++ b/main/ui/src/main/resources/fxml/unlock.fxml @@ -22,7 +22,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/unlocked.fxml b/main/ui/src/main/resources/fxml/unlocked.fxml index 9a1e98bb5..9ba45eeef 100644 --- a/main/ui/src/main/resources/fxml/unlocked.fxml +++ b/main/ui/src/main/resources/fxml/unlocked.fxml @@ -17,7 +17,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/welcome.fxml b/main/ui/src/main/resources/fxml/welcome.fxml index 1e71b2b15..0c8ccd0de 100644 --- a/main/ui/src/main/resources/fxml/welcome.fxml +++ b/main/ui/src/main/resources/fxml/welcome.fxml @@ -18,7 +18,7 @@ - +