From f05440fe7ac44b4b41b9d5b21e46bb2c16a5f86c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 3 Mar 2016 14:05:50 +0100 Subject: [PATCH] UI code cleanup --- .../java/org/cryptomator/ui/Cryptomator.java | 7 +- .../cryptomator/ui/CryptomatorComponent.java | 6 ++ .../org/cryptomator/ui/CryptomatorModule.java | 31 +++++---- .../org/cryptomator/ui/MainApplication.java | 53 ++++++--------- .../AbstractFXMLViewController.java | 12 ---- .../controllers/ChangePasswordController.java | 17 +++-- .../ui/controllers/InitializeController.java | 11 +-- .../ui/controllers/MacWarningsController.java | 9 ++- .../ui/controllers/MainController.java | 36 ++++------ .../ui/controllers/SettingsController.java | 7 +- .../ui/controllers/UnlockController.java | 25 ++++--- .../ui/controllers/UnlockedController.java | 11 +-- .../ui/controllers/WelcomeController.java | 11 +-- .../cryptomator/ui/settings/Localization.java | 27 ++++++++ .../org/cryptomator/ui/settings/Settings.java | 14 +++- .../cryptomator/ui/util/DeferredCloser.java | 44 ++++++------ .../org/cryptomator/ui/util/TrayIconUtil.java | 68 ++++++++----------- 17 files changed, 205 insertions(+), 184 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java b/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java index f2b62d17d..f4fbdb1a0 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java +++ b/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java @@ -19,8 +19,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import javafx.application.Application; - import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.util.SingleInstanceManager; import org.cryptomator.ui.util.SingleInstanceManager.RemoteInstance; @@ -28,11 +26,12 @@ import org.eclipse.jetty.util.ConcurrentHashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javafx.application.Application; + public class Cryptomator { - public static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); public static final CompletableFuture> OPEN_FILE_HANDLER = new CompletableFuture<>(); - + private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class); private static final Set SHUTDOWN_TASKS = new ConcurrentHashSet<>(); private static final CleanShutdownPerformer CLEAN_SHUTDOWN_PERFORMER = new CleanShutdownPerformer(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java index 0eef1238f..eb551b6ae 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java @@ -13,7 +13,9 @@ import java.util.concurrent.ExecutorService; import javax.inject.Singleton; import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.util.DeferredCloser; +import org.cryptomator.ui.util.TrayIconUtil; import dagger.Component; @@ -25,4 +27,8 @@ interface CryptomatorComponent { DeferredCloser deferredCloser(); MainController mainController(); + + Localization localization(); + + TrayIconUtil trayIconUtil(); } \ 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 index 7db4de607..fa432cc9d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -24,7 +24,6 @@ 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 com.fasterxml.jackson.databind.ObjectMapper; @@ -32,16 +31,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import dagger.Module; import dagger.Provides; import javafx.application.Application; +import javafx.stage.Stage; @Module(includes = CryptoEngineModule.class) class CryptomatorModule { private final Application application; - private final DeferredCloser deferredCloser; + private final Stage mainWindow; - public CryptomatorModule(Application application) { + public CryptomatorModule(Application application, Stage mainWindow) { this.application = application; - this.deferredCloser = new DeferredCloser(); + this.mainWindow = mainWindow; } @Provides @@ -50,10 +50,19 @@ class CryptomatorModule { return application; } + @Provides + @Singleton + @Named("mainWindow") + Stage provideMainWindow() { + return mainWindow; + } + @Provides @Singleton DeferredCloser provideDeferredCloser() { - return deferredCloser; + DeferredCloser closer = new DeferredCloser(); + Cryptomator.addShutdownTask(closer::close); + return closer; } @Provides @@ -78,8 +87,8 @@ class CryptomatorModule { @Provides @Singleton - ExecutorService provideExecutorService() { - return closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown); + ExecutorService provideExecutorService(DeferredCloser closer) { + return closer.closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown).get().orElseThrow(IllegalStateException::new); } @Provides @@ -90,14 +99,10 @@ class CryptomatorModule { @Provides @Singleton - FrontendFactory provideFrontendFactory(WebDavServer webDavServer, Settings settings) { + FrontendFactory provideFrontendFactory(DeferredCloser closer, WebDavServer webDavServer, Settings settings) { webDavServer.setPort(settings.getPort()); webDavServer.start(); - return closeLater(webDavServer, WebDavServer::stop); - } - - private T closeLater(T object, Closer closer) { - return deferredCloser.closeLater(object, closer).get().get(); + return closer.closeLater(webDavServer, WebDavServer::stop).get().orElseThrow(IllegalStateException::new); } } 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 226831192..f87614344 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -12,8 +12,6 @@ import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ResourceBundle; -import java.util.concurrent.ExecutorService; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.controllers.MainController; @@ -21,7 +19,6 @@ import org.cryptomator.ui.util.ActiveWindowStyleSupport; import org.cryptomator.ui.util.DeferredCloser; import org.cryptomator.ui.util.SingleInstanceManager; import org.cryptomator.ui.util.SingleInstanceManager.LocalInstance; -import org.cryptomator.ui.util.TrayIconUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,24 +32,16 @@ import javafx.stage.Stage; public class MainApplication extends Application { public static final String APPLICATION_KEY = "CryptomatorGUI"; - private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); - private final ExecutorService executorService; - private final DeferredCloser closer; - private final MainController mainCtrl; - - public MainApplication() { - 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); - } + private DeferredCloser closer; @Override - public void start(final Stage primaryStage) throws IOException { - Font.loadFont(getClass().getResourceAsStream("/css/ionicons.ttf"), 12.0); + public void start(Stage primaryStage) throws IOException { + final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this, primaryStage)).build(); + final MainController mainCtrl = comp.mainController(); + closer = comp.deferredCloser(); + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); FXMLLoader.setDefaultClassLoader(contextClassLoader); Platform.runLater(() -> { @@ -65,36 +54,35 @@ public class MainApplication extends Application { } }); + // Set stylesheets and initialize stage: + Font.loadFont(getClass().getResourceAsStream("/css/ionicons.ttf"), 12.0); chooseNativeStylesheet(); - mainCtrl.initStage(primaryStage); - - final ResourceBundle rb = ResourceBundle.getBundle("localization"); primaryStage.titleProperty().bind(mainCtrl.windowTitle()); primaryStage.setResizable(false); if (SystemUtils.IS_OS_WINDOWS) { primaryStage.getIcons().add(new Image(MainApplication.class.getResourceAsStream("/window_icon.png"))); } + + // show window and start observing its focus: primaryStage.show(); - ActiveWindowStyleSupport.startObservingFocus(primaryStage); - TrayIconUtil.init(primaryStage, rb, () -> { - quit(); - }); + comp.trayIconUtil().initTrayIcon(this::quit); + // open files, if requested during startup: for (String arg : getParameters().getUnnamed()) { - handleCommandLineArg(arg); + handleCommandLineArg(arg, primaryStage, mainCtrl); } - if (SystemUtils.IS_OS_MAC_OSX) { - Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(file.getAbsolutePath())); + Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(file.getAbsolutePath(), primaryStage, mainCtrl)); } - LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, executorService), LocalInstance::close).get().get(); - cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(arg)); + // register this application instance as primary application, that other instances can send open file requests to: + LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, comp.executorService()), LocalInstance::close).get().get(); + cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(arg, primaryStage, mainCtrl)); } - private void handleCommandLineArg(String arg) { + private void handleCommandLineArg(String arg, Stage primaryStage, MainController mainCtrl) { // find correct location: final Path path = FileSystems.getDefault().getPath(arg); final Path vaultPath; @@ -110,7 +98,10 @@ public class MainApplication extends Application { // add vault to ctrl: Platform.runLater(() -> { mainCtrl.addVault(vaultPath, true); - mainCtrl.toFront(); + primaryStage.setIconified(false); + primaryStage.show(); + primaryStage.toFront(); + primaryStage.requestFocus(); }); } 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 index 91de748ef..fac38677b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/AbstractFXMLViewController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/AbstractFXMLViewController.java @@ -28,16 +28,6 @@ abstract class AbstractFXMLViewController implements Initializable { private final AtomicReference fxmlRoot = new AtomicReference<>(); - /** - * 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.
* @@ -57,8 +47,6 @@ abstract class AbstractFXMLViewController implements Initializable { @Override public final void initialize(URL location, ResourceBundle resources) { - this.rootUrl = location; - this.resourceBundle = resources; this.initialize(); } 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 5d9367702..4af980def 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 @@ -21,6 +21,7 @@ import org.cryptomator.crypto.engine.InvalidPassphraseException; import org.cryptomator.crypto.engine.UnsupportedVaultFormatException; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,12 +42,14 @@ public class ChangePasswordController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class); private final Application app; + private final Localization localization; final ObjectProperty vault = new SimpleObjectProperty<>(); private Optional listener = Optional.empty(); @Inject - public ChangePasswordController(Application app) { + public ChangePasswordController(Application app, Localization localization) { this.app = app; + this.localization = localization; } @FXML @@ -82,7 +85,7 @@ public class ChangePasswordController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } // **************************************** @@ -103,21 +106,21 @@ public class ChangePasswordController extends AbstractFXMLViewController { downloadsPageLink.setVisible(false); try { vault.get().changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters()); - messageText.setText(resourceBundle.getString("changePassword.infoMessage.success")); + messageText.setText(localization.getString("changePassword.infoMessage.success")); listener.ifPresent(this::invokeListenerLater); } catch (InvalidPassphraseException e) { - messageText.setText(resourceBundle.getString("changePassword.errorMessage.wrongPassword")); + messageText.setText(localization.getString("changePassword.errorMessage.wrongPassword")); Platform.runLater(oldPasswordField::requestFocus); } catch (UncheckedIOException | IOException ex) { - messageText.setText(resourceBundle.getString("changePassword.errorMessage.decryptionFailed")); + messageText.setText(localization.getString("changePassword.errorMessage.decryptionFailed")); LOG.error("Decryption failed for technical reasons.", ex); } catch (UnsupportedVaultFormatException e) { downloadsPageLink.setVisible(true); LOG.warn("Unable to unlock vault: " + e.getMessage()); if (e.isVaultOlderThanSoftware()) { - messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); + messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); + messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); } } finally { oldPasswordField.swipe(); 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 4a47b9d36..a037a8b69 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 @@ -20,6 +20,7 @@ import javax.inject.Singleton; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,11 +38,13 @@ public class InitializeController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class); + private final Localization localization; final ObjectProperty vault = new SimpleObjectProperty<>(); private Optional listener = Optional.empty(); @Inject - public InitializeController() { + public InitializeController(Localization localization) { + this.localization = localization; } @FXML @@ -70,7 +73,7 @@ public class InitializeController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } // **************************************** @@ -84,10 +87,10 @@ public class InitializeController extends AbstractFXMLViewController { vault.get().create(passphrase); listener.ifPresent(this::invokeListenerLater); } catch (FileAlreadyExistsException ex) { - messageLabel.setText(resourceBundle.getString("initialize.messageLabel.alreadyInitialized")); + messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized")); } catch (UncheckedIOException | IOException ex) { LOG.error("I/O Exception", ex); - messageLabel.setText(resourceBundle.getString("initialize.messageLabel.initializationFailed")); + messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed")); } finally { passwordField.swipe(); retypePasswordField.swipe(); 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 ed2148881..a57ec5725 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 @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; import javafx.application.Application; import javafx.beans.Observable; @@ -41,6 +42,7 @@ import javafx.util.StringConverter; public class MacWarningsController extends AbstractFXMLViewController { private final Application application; + private final Localization localization; private final ObservableList warnings = FXCollections.observableArrayList(); private final ListChangeListener unauthenticatedResourcesChangeListener = this::unauthenticatedResourcesDidChange; private final ChangeListener stageVisibilityChangeListener = this::windowVisibilityDidChange; @@ -48,8 +50,9 @@ public class MacWarningsController extends AbstractFXMLViewController { private Stage stage; @Inject - public MacWarningsController(Application application) { + public MacWarningsController(Application application, Localization localization) { this.application = application; + this.localization = localization; } @FXML @@ -84,7 +87,7 @@ public class MacWarningsController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } @Override @@ -127,7 +130,7 @@ public class MacWarningsController extends AbstractFXMLViewController { private void windowVisibilityDidChange(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (Boolean.TRUE.equals(newValue)) { - stage.setTitle(String.format(resourceBundle.getString("macWarnings.windowTitle"), vault.get().getName())); + stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().getName())); warnings.addAll(vault.get().getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList())); vault.get().getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener); } else { 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 ca7abbff2..b492514a7 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 @@ -19,12 +19,14 @@ import java.util.Map; import java.util.ResourceBundle; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; import org.cryptomator.ui.controls.DirectoryListCell; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.model.VaultFactory; +import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.settings.Settings; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.monadic.MonadicBinding; @@ -58,6 +60,8 @@ public class MainController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(MainController.class); + private final Stage mainWindow; + private final Localization localization; private final VaultFactory vaultFactoy; private final Lazy welcomeController; private final Lazy initializeController; @@ -74,8 +78,11 @@ public class MainController extends AbstractFXMLViewController { private final Map unlockedVaults = new HashMap<>(); @Inject - public MainController(Settings settings, VaultFactory vaultFactoy, Lazy welcomeController, Lazy initializeController, Lazy unlockController, - Provider unlockedControllerProvider, Lazy changePasswordController, Lazy settingsController) { + public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy welcomeController, + Lazy initializeController, Lazy unlockController, Provider unlockedControllerProvider, Lazy changePasswordController, + Lazy settingsController) { + this.mainWindow = mainWindow; + this.localization = localization; this.vaultFactoy = vaultFactoy; this.welcomeController = welcomeController; this.initializeController = initializeController; @@ -89,8 +96,6 @@ public class MainController extends AbstractFXMLViewController { this.isShowingSettings = activeController.isEqualTo(settingsController.get()); } - private Stage stage; - @FXML private ContextMenu vaultListCellContextMenu; @@ -137,7 +142,7 @@ public class MainController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } private ListCell createDirecoryListCell(ListView param) { @@ -146,12 +151,6 @@ public class MainController extends AbstractFXMLViewController { return cell; } - @Override - public void initStage(Stage stage) { - super.initStage(stage); - this.stage = stage; - } - // **************************************** // UI Events // **************************************** @@ -169,7 +168,7 @@ public class MainController extends AbstractFXMLViewController { private void didClickCreateNewVault(ActionEvent event) { final FileChooser fileChooser = new FileChooser(); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION)); - final File file = fileChooser.showSaveDialog(stage); + final File file = fileChooser.showSaveDialog(mainWindow); if (file == null) { return; } @@ -194,7 +193,7 @@ public class MainController extends AbstractFXMLViewController { private void didClickAddExistingVaults(ActionEvent event) { final FileChooser fileChooser = new FileChooser(); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION)); - final List files = fileChooser.showOpenMultipleDialog(stage); + final List files = fileChooser.showOpenMultipleDialog(mainWindow); if (files != null) { for (final File file : files) { addVault(file.toPath(), false); @@ -288,7 +287,7 @@ public class MainController extends AbstractFXMLViewController { // **************************************** public Binding windowTitle() { - return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(resourceBundle.getString("app.name")); + return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(localization.getString("app.name")); } // **************************************** @@ -340,13 +339,4 @@ public class MainController extends AbstractFXMLViewController { showUnlockView(); } - /** - * Attempts to make the application window visible. - */ - public void toFront() { - stage.setIconified(false); - stage.show(); - stage.toFront(); - } - } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index e51c50d4e..b7e768f23 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -15,6 +15,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.lang3.CharUtils; +import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.settings.Settings; import org.fxmisc.easybind.EasyBind; @@ -26,10 +27,12 @@ import javafx.scene.input.KeyEvent; @Singleton public class SettingsController extends AbstractFXMLViewController { + private final Localization localization; private final Settings settings; @Inject - public SettingsController(Settings settings) { + public SettingsController(Localization localization, Settings settings) { + this.localization = localization; this.settings = settings; } @@ -57,7 +60,7 @@ public class SettingsController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } private void portDidChange(String newValue) { 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 f8af1891e..697b9948b 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 @@ -25,6 +25,7 @@ import org.cryptomator.frontend.FrontendFactory; import org.cryptomator.frontend.webdav.mount.WindowsDriveLetters; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +55,7 @@ public class UnlockController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); private final Application app; + private final Localization localization; private final ExecutorService exec; private final Lazy frontendFactory; private final WindowsDriveLetters driveLetters; @@ -61,8 +63,9 @@ public class UnlockController extends AbstractFXMLViewController { final ObjectProperty vault = new SimpleObjectProperty<>(); @Inject - public UnlockController(Application app, ExecutorService exec, Lazy frontendFactory, WindowsDriveLetters driveLetters) { + public UnlockController(Application app, Localization localization, ExecutorService exec, Lazy frontendFactory, WindowsDriveLetters driveLetters) { this.app = app; + this.localization = localization; this.exec = exec; this.frontendFactory = frontendFactory; this.driveLetters = driveLetters; @@ -123,7 +126,7 @@ public class UnlockController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } private void vaultChanged(Vault newVault) { @@ -132,7 +135,7 @@ public class UnlockController extends AbstractFXMLViewController { } passwordField.clear(); advancedOptions.setVisible(false); - advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show")); + advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); progressIndicator.setVisible(false); if (SystemUtils.IS_OS_WINDOWS) { winDriveLetter.valueProperty().removeListener(driveLetterChangeListener); @@ -167,9 +170,9 @@ public class UnlockController extends AbstractFXMLViewController { private void didClickAdvancedOptionsButton(ActionEvent event) { advancedOptions.setVisible(!advancedOptions.isVisible()); if (advancedOptions.isVisible()) { - advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.hide")); + advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.hide")); } else { - advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show")); + advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); } } @@ -203,7 +206,7 @@ public class UnlockController extends AbstractFXMLViewController { @Override public String toString(Character letter) { if (letter == null) { - return resourceBundle.getString("unlock.choicebox.winDriveLetter.auto"); + return localization.getString("unlock.choicebox.winDriveLetter.auto"); } else { return Character.toString(letter) + ":"; } @@ -211,7 +214,7 @@ public class UnlockController extends AbstractFXMLViewController { @Override public Character fromString(String string) { - if (resourceBundle.getString("unlock.choicebox.winDriveLetter.auto").equals(string)) { + if (localization.getString("unlock.choicebox.winDriveLetter.auto").equals(string)) { return null; } else { return CharUtils.toCharacterObject(string); @@ -280,7 +283,7 @@ public class UnlockController extends AbstractFXMLViewController { vault.get().reveal(); } catch (InvalidPassphraseException e) { Platform.runLater(() -> { - messageText.setText(resourceBundle.getString("unlock.errorMessage.wrongPassword")); + messageText.setText(localization.getString("unlock.errorMessage.wrongPassword")); passwordField.requestFocus(); }); } catch (UnsupportedVaultFormatException e) { @@ -288,15 +291,15 @@ public class UnlockController extends AbstractFXMLViewController { Platform.runLater(() -> { downloadsPageLink.setVisible(true); if (e.isVaultOlderThanSoftware()) { - messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); + messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); + messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); } }); } catch (FrontendCreationFailedException | CommandFailedException e) { LOG.error("Decryption failed for technical reasons.", e); Platform.runLater(() -> { - messageText.setText(resourceBundle.getString("unlock.errorMessage.mountingFailed")); + messageText.setText(localization.getString("unlock.errorMessage.mountingFailed")); }); } finally { Platform.runLater(() -> { 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 36c9f8d59..1208bad68 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 @@ -18,6 +18,7 @@ import javax.inject.Provider; import org.cryptomator.frontend.CommandFailedException; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.util.ActiveWindowStyleSupport; import org.fxmisc.easybind.EasyBind; @@ -50,6 +51,7 @@ public class UnlockedController extends AbstractFXMLViewController { private static final int IO_SAMPLING_STEPS = 100; private static final double IO_SAMPLING_INTERVAL = 0.25; + private final Localization localization; private final Stage macWarningsWindow = new Stage(); private final MacWarningsController macWarningsController; private final ExecutorService exec; @@ -58,7 +60,8 @@ public class UnlockedController extends AbstractFXMLViewController { private Timeline ioAnimation; @Inject - public UnlockedController(Provider macWarningsControllerProvider, ExecutorService exec) { + public UnlockedController(Localization localization, Provider macWarningsControllerProvider, ExecutorService exec) { + this.localization = localization; this.macWarningsController = macWarningsControllerProvider.get(); this.exec = exec; @@ -96,7 +99,7 @@ public class UnlockedController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } private void vaultChanged(Vault newVault) { @@ -125,7 +128,7 @@ public class UnlockedController extends AbstractFXMLViewController { vault.get().unmount(); } catch (CommandFailedException e) { Platform.runLater(() -> { - messageLabel.setText(resourceBundle.getString("unlocked.label.unmountFailed")); + messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); }); return; } @@ -151,7 +154,7 @@ public class UnlockedController extends AbstractFXMLViewController { vault.get().reveal(); } catch (CommandFailedException e) { Platform.runLater(() -> { - messageLabel.setText(resourceBundle.getString("unlocked.label.revealFailed")); + messageLabel.setText(localization.getString("unlocked.label.revealFailed")); }); } }); 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 d6e9af5c4..9b61b5374 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 @@ -30,6 +30,7 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.settings.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,13 +55,15 @@ public class WelcomeController extends AbstractFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); private final Application app; + private final Localization localization; private final Settings settings; private final Comparator semVerComparator; private final ExecutorService executor; @Inject - public WelcomeController(Application app, Settings settings, @Named("SemVer") Comparator semVerComparator, ExecutorService executor) { + public WelcomeController(Application app, Localization localization, Settings settings, @Named("SemVer") Comparator semVerComparator, ExecutorService executor) { this.app = app; + this.localization = localization; this.settings = settings; this.semVerComparator = semVerComparator; this.executor = executor; @@ -99,7 +102,7 @@ public class WelcomeController extends AbstractFXMLViewController { @Override protected ResourceBundle getFxmlResourceBundle() { - return ResourceBundle.getBundle("localization"); + return localization; } // **************************************** @@ -115,7 +118,7 @@ public class WelcomeController extends AbstractFXMLViewController { return; } Platform.runLater(() -> { - checkForUpdatesStatus.setText(resourceBundle.getString("welcome.checkForUpdates.label.currentlyChecking")); + checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking")); checkForUpdatesIndicator.setVisible(true); }); final HttpClient client = new HttpClient(); @@ -162,7 +165,7 @@ public class WelcomeController extends AbstractFXMLViewController { final String currentVersion = applicationVersion().orElse(null); LOG.debug("Current version: {}, lastest version: {}", currentVersion, latestVersion); if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) { - final String msg = String.format(resourceBundle.getString("welcome.newVersionMessage"), latestVersion, currentVersion); + final String msg = String.format(localization.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/settings/Localization.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java new file mode 100644 index 000000000..d12082ef5 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java @@ -0,0 +1,27 @@ +package org.cryptomator.ui.settings; + +import java.util.Enumeration; +import java.util.ResourceBundle; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class Localization extends ResourceBundle { + + @Inject + public Localization() { + this.parent = ResourceBundle.getBundle("localization"); + } + + @Override + protected Object handleGetObject(String key) { + return parent.getObject(key); + } + + @Override + public Enumeration getKeys() { + return parent.getKeys(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java index 1addad7c4..d83aba3ea 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java @@ -17,13 +17,14 @@ import org.cryptomator.ui.model.Vault; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -@JsonPropertyOrder(value = {"directories", "checkForUpdatesEnabled", "port"}) +@JsonPropertyOrder(value = {"directories", "checkForUpdatesEnabled", "port", "numTrayNotifications"}) public class Settings implements Serializable { private static final long serialVersionUID = 7609959894417878744L; public static final int MIN_PORT = 1024; public static final int MAX_PORT = 65535; public static final int DEFAULT_PORT = 0; + public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; @JsonProperty("directories") private List directories; @@ -34,6 +35,9 @@ public class Settings implements Serializable { @JsonProperty("port") private Integer port; + @JsonProperty("numTrayNotifications") + private Integer numTrayNotifications; + /** * Package-private constructor; use {@link SettingsProvider}. */ @@ -82,4 +86,12 @@ public class Settings implements Serializable { return port == DEFAULT_PORT || port >= MIN_PORT && port <= MAX_PORT; } + public Integer getNumTrayNotifications() { + return numTrayNotifications == null ? DEFAULT_NUM_TRAY_NOTIFICATIONS : numTrayNotifications; + } + + public void setNumTrayNotifications(Integer numTrayNotifications) { + this.numTrayNotifications = numTrayNotifications; + } + } 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 4f95c4035..c738e9f59 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 @@ -16,7 +16,7 @@ import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.common.ConsumerThrowingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,23 +41,8 @@ import com.google.common.annotations.VisibleForTesting; * @author Tillmann Gaida */ public class DeferredCloser implements AutoCloseable { - public static interface Closer { - void close(T object) throws Exception; - } - static class EmptyResource implements DeferredClosable { - @Override - public Optional get() { - return Optional.empty(); - } - - @Override - public void close() { - - } - } - - private static final Logger LOG = LoggerFactory.getLogger(MainController.class); + private static final Logger LOG = LoggerFactory.getLogger(DeferredCloser.class); @VisibleForTesting final Map> cleanups = new ConcurrentSkipListMap<>(); @@ -65,13 +50,13 @@ public class DeferredCloser implements AutoCloseable { @VisibleForTesting final AtomicLong counter = new AtomicLong(); - public class ManagedResource implements DeferredClosable { + private class ManagedResource implements DeferredClosable { private final long number = counter.incrementAndGet(); private final AtomicReference object = new AtomicReference<>(); - private final Closer closer; + private final ConsumerThrowingException closer; - ManagedResource(T object, Closer closer) { + public ManagedResource(T object, ConsumerThrowingException closer) { super(); this.object.set(object); this.closer = closer; @@ -82,11 +67,10 @@ public class DeferredCloser implements AutoCloseable { final T oldObject = object.getAndSet(null); if (oldObject != null) { cleanups.remove(number); - try { - closer.close(oldObject); + closer.accept(oldObject); } catch (Exception e) { - LOG.error("exception closing resource", e); + LOG.error("Closing resource failed.", e); } } } @@ -109,7 +93,7 @@ public class DeferredCloser implements AutoCloseable { } } - public DeferredClosable closeLater(T object, Closer closer) { + public DeferredClosable closeLater(T object, ConsumerThrowingException closer) { Objects.requireNonNull(object); Objects.requireNonNull(closer); final ManagedResource resource = new ManagedResource(object, closer); @@ -130,4 +114,16 @@ public class DeferredCloser implements AutoCloseable { public static DeferredClosable empty() { return (DeferredClosable) EMPTY_RESOURCE; } + + static class EmptyResource implements DeferredClosable { + @Override + public Optional get() { + return Optional.empty(); + } + + @Override + public void close() { + + } + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/TrayIconUtil.java b/main/ui/src/main/java/org/cryptomator/ui/util/TrayIconUtil.java index 1bd308a05..f8ce23acb 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/TrayIconUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/TrayIconUtil.java @@ -18,77 +18,67 @@ import java.awt.TrayIcon; import java.awt.TrayIcon.MessageType; import java.awt.event.ActionEvent; import java.io.IOException; -import java.util.ResourceBundle; import java.util.concurrent.TimeUnit; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.swing.SwingUtilities; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.ui.settings.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; import javafx.stage.Stage; +@Singleton public final class TrayIconUtil { - private static TrayIconUtil INSTANCE; private static final Logger LOG = LoggerFactory.getLogger(TrayIconUtil.class); - private final Stage mainApplicationWindow; - private final ResourceBundle rb; - private final Runnable exitCommand; + private final Stage mainWindow; + private final Localization localization; - /** - * This will add an icon to the system tray and modify the application shutdown procedure. Depending on - * {@link Platform#isImplicitExit()} the application may still be running, allowing shutdown using the tray menu. - */ - public synchronized static void init(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) { - if (INSTANCE == null && SystemTray.isSupported()) { - INSTANCE = new TrayIconUtil(mainApplicationWindow, rb, exitCommand); - } + @Inject + public TrayIconUtil(@Named("mainWindow") Stage mainWindow, Localization localization) { + this.mainWindow = mainWindow; + this.localization = localization; } - private TrayIconUtil(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) { - this.mainApplicationWindow = mainApplicationWindow; - this.rb = rb; - this.exitCommand = exitCommand; - - initTrayIcon(); - } - - private void initTrayIcon() { - final TrayIcon trayIcon = createTrayIcon(); + public void initTrayIcon(Runnable exitCommand) { + final TrayIcon trayIcon = createTrayIcon(exitCommand); try { SystemTray.getSystemTray().add(trayIcon); - mainApplicationWindow.setOnCloseRequest((e) -> { + mainWindow.setOnCloseRequest((e) -> { if (Platform.isImplicitExit()) { exitCommand.run(); } else { - mainApplicationWindow.close(); + mainWindow.close(); this.showTrayNotification(trayIcon); } }); } catch (SecurityException | AWTException ex) { // not working? then just go ahead and close the app - mainApplicationWindow.setOnCloseRequest((ev) -> { + mainWindow.setOnCloseRequest((ev) -> { exitCommand.run(); }); } } - private TrayIcon createTrayIcon() { + private TrayIcon createTrayIcon(Runnable exitCommand) { final PopupMenu popup = new PopupMenu(); - final MenuItem showItem = new MenuItem(rb.getString("tray.menu.open")); + final MenuItem showItem = new MenuItem(localization.getString("tray.menu.open")); showItem.addActionListener(this::restoreFromTray); popup.add(showItem); - final MenuItem exitItem = new MenuItem(rb.getString("tray.menu.quit")); - exitItem.addActionListener(this::quitFromTray); + final MenuItem exitItem = new MenuItem(localization.getString("tray.menu.quit")); + exitItem.addActionListener(e -> exitCommand.run()); popup.add(exitItem); final Image image; @@ -98,7 +88,7 @@ public final class TrayIconUtil { image = Toolkit.getDefaultToolkit().getImage(TrayIconUtil.class.getResource("/tray_icon.png")); } - return new TrayIcon(image, rb.getString("app.name"), popup); + return new TrayIcon(image, localization.getString("app.name"), popup); } /** @@ -120,8 +110,8 @@ public final class TrayIconUtil { private void showTrayNotification(TrayIcon trayIcon) { final Runnable notificationCmd; if (SystemUtils.IS_OS_MAC_OSX) { - final String title = rb.getString("tray.infoMsg.title"); - final String msg = rb.getString("tray.infoMsg.msg.osx"); + final String title = localization.getString("tray.infoMsg.title"); + final String msg = localization.getString("tray.infoMsg.msg.osx"); final String notificationCenterAppleScript = String.format("display notification \"%s\" with title \"%s\"", msg, title); notificationCmd = () -> { try { @@ -137,8 +127,8 @@ public final class TrayIconUtil { } }; } else { - final String title = rb.getString("tray.infoMsg.title"); - final String msg = rb.getString("tray.infoMsg.msg"); + final String title = localization.getString("tray.infoMsg.title"); + final String msg = localization.getString("tray.infoMsg.msg"); notificationCmd = () -> { trayIcon.displayMessage(title, msg, MessageType.INFO); }; @@ -150,13 +140,9 @@ public final class TrayIconUtil { private void restoreFromTray(ActionEvent event) { Platform.runLater(() -> { - mainApplicationWindow.show(); - mainApplicationWindow.requestFocus(); + mainWindow.show(); + mainWindow.requestFocus(); }); } - private void quitFromTray(ActionEvent event) { - exitCommand.run(); - } - }