From 10e842e457748cf3f406eb071e35bedf906a9acd Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 29 Jul 2019 12:38:24 +0200 Subject: [PATCH] added tray icon, refactored dagger component graph: singleton -> tray -> fxapplication -> mainwindow --- .../org/cryptomator/launcher/Cryptomator.java | 9 +-- .../launcher/CryptomatorComponent.java | 4 +- .../launcher/CryptomatorModule.java | 6 +- .../java/org/cryptomator/ui/ExitUtil.java | 1 + .../java/org/cryptomator/ui/UiModule.java | 15 +++-- .../ui/controllers/MainController.java | 2 +- .../ui/controllers/NotFoundController.java | 2 +- .../ui/controllers/SettingsController.java | 2 +- .../ui/controllers/ViewControllerLoader.java | 2 +- .../ui/controllers/WelcomeController.java | 2 +- .../ui/{ => fxapp}/FxApplication.java | 17 ++++-- .../{ => fxapp}/FxApplicationComponent.java | 14 ++--- .../ui/{ => fxapp}/FxApplicationModule.java | 5 +- .../ui/{ => fxapp}/FxApplicationScoped.java | 2 +- .../org/cryptomator/ui/l10n/Localization.java | 2 +- .../ui/mainwindow/MainWindowComponent.java | 2 + .../ui/mainwindow/MainWindowController.java | 8 +-- .../cryptomator/ui/model/AutoUnlocker.java | 2 +- .../cryptomator/ui/model/VaultFactory.java | 4 +- .../org/cryptomator/ui/model/VaultList.java | 4 +- .../ui/model/WindowsDriveLetters.java | 5 +- .../ui/model/upgrade/UpgradeStrategies.java | 2 +- .../UpgradeVersion3DropBundleExtension.java | 2 +- .../ui/model/upgrade/UpgradeVersion3to4.java | 2 +- .../ui/model/upgrade/UpgradeVersion4to5.java | 2 +- .../ui/model/upgrade/UpgradeVersion5toX.java | 2 +- .../ui/traymenu/FxApplicationStarter.java | 46 ++++++++++++++ .../ui/traymenu/TrayIconController.java | 49 +++++++++++++++ .../ui/traymenu/TrayImageFactory.java | 38 ++++++++++++ .../ui/traymenu/TrayMenuComponent.java | 32 ++++++++++ .../ui/traymenu/TrayMenuController.java | 60 +++++++++++++++++++ .../ui/traymenu/TrayMenuModule.java | 9 +++ .../ui/traymenu/TrayMenuScoped.java | 13 ++++ .../ui/util/PasswordStrengthUtil.java | 2 +- .../java/org/cryptomator/ui/controls/Foo.java | 27 +++++++++ 35 files changed, 340 insertions(+), 56 deletions(-) rename main/ui/src/main/java/org/cryptomator/ui/{ => fxapp}/FxApplication.java (90%) rename main/ui/src/main/java/org/cryptomator/ui/{ => fxapp}/FxApplicationComponent.java (73%) rename main/ui/src/main/java/org/cryptomator/ui/{ => fxapp}/FxApplicationModule.java (89%) rename main/ui/src/main/java/org/cryptomator/ui/{ => fxapp}/FxApplicationScoped.java (88%) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/FxApplicationStarter.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuScoped.java create mode 100644 main/ui/src/test/java/org/cryptomator/ui/controls/Foo.java diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 30156ea0c..efd63ab1a 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -5,11 +5,10 @@ *******************************************************************************/ package org.cryptomator.launcher; -import javafx.application.Platform; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.logging.DebugMode; import org.cryptomator.logging.LoggerConfiguration; -import org.cryptomator.ui.FxApplication; +import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,15 +33,17 @@ public class Cryptomator { private final Optional applicationVersion; private final CountDownLatch shutdownLatch; private final CleanShutdownPerformer shutdownPerformer; + private final TrayMenuComponent.Builder trayComponent; @Inject - Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, CleanShutdownPerformer shutdownPerformer) { + Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, CleanShutdownPerformer shutdownPerformer, TrayMenuComponent.Builder trayComponent) { this.logConfig = logConfig; this.debugMode = debugMode; this.ipcFactory = ipcFactory; this.applicationVersion = applicationVersion; this.shutdownLatch = shutdownLatch; this.shutdownPerformer = shutdownPerformer; + this.trayComponent = trayComponent; } public static void main(String[] args) { @@ -89,7 +90,7 @@ public class Cryptomator { private int runGuiApplication() { try { shutdownPerformer.registerShutdownHook(); - CRYPTOMATOR_COMPONENT.fxApplicationComponent().start(); + trayComponent.build().addIconToSystemTray(); shutdownLatch.await(); LOG.info("UI shut down"); return 0; diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java index 251e0ce48..88bbf9800 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -3,7 +3,7 @@ package org.cryptomator.launcher; import dagger.Component; import org.cryptomator.common.CommonsModule; import org.cryptomator.logging.LoggerModule; -import org.cryptomator.ui.FxApplicationComponent; +import org.cryptomator.ui.fxapp.FxApplicationComponent; import javax.inject.Singleton; @@ -13,6 +13,4 @@ public interface CryptomatorComponent { Cryptomator application(); - FxApplicationComponent fxApplicationComponent(); - } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java index 37c8b2216..63bb66f44 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -4,8 +4,10 @@ import dagger.Module; import dagger.Provides; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.SettingsProvider; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.UiModule; import org.cryptomator.ui.model.AppLaunchEvent; +import org.cryptomator.ui.model.VaultComponent; +import org.cryptomator.ui.traymenu.TrayMenuComponent; import javax.inject.Named; import javax.inject.Singleton; @@ -15,7 +17,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; -@Module +@Module(includes = {UiModule.class}, subcomponents = {VaultComponent.class, TrayMenuComponent.class}) class CryptomatorModule { @Provides diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index 335f1d1ca..3c1869d66 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -16,6 +16,7 @@ import org.cryptomator.common.settings.Settings; import org.cryptomator.jni.JniException; import org.cryptomator.jni.MacApplicationUiState; import org.cryptomator.jni.MacFunctions; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.ui.l10n.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java index ef5aed16e..6c68c654a 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java @@ -16,10 +16,11 @@ import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.Settings; import org.cryptomator.frontend.webdav.WebDavServer; import org.cryptomator.keychain.KeychainModule; -import org.cryptomator.ui.model.VaultComponent; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.fxmisc.easybind.EasyBind; import javax.inject.Named; +import javax.inject.Singleton; import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -27,13 +28,15 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -@Module(includes = {KeychainModule.class}, subcomponents = {VaultComponent.class}) +// TODO move to common... +@Deprecated(forRemoval = true, since = "1.5.0") +@Module(includes = {KeychainModule.class}) public class UiModule { private static final int NUM_SCHEDULER_THREADS = 4; @Provides - @FxApplicationScoped + @Singleton ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { final AtomicInteger threadNumber = new AtomicInteger(1); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> { @@ -48,7 +51,7 @@ public class UiModule { // TODO @Binds abstract ExecutorService bindExecutorService(ScheduledExecutorService executor); ? @Provides - @FxApplicationScoped + @Singleton ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { final AtomicInteger threadNumber = new AtomicInteger(1); ExecutorService executorService = Executors.newCachedThreadPool(r -> { @@ -62,7 +65,7 @@ public class UiModule { } @Provides - @FxApplicationScoped + @Singleton Binding provideServerSocketAddressBinding(Settings settings) { return Bindings.createObjectBinding(() -> { String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost"; @@ -71,7 +74,7 @@ public class UiModule { } @Provides - @FxApplicationScoped + @Singleton WebDavServer provideWebDavServer(Binding serverSocketAddressBinding) { WebDavServer server = WebDavServer.create(); // no need to unsubscribe eventually, because server is a singleton 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 cbdaf8d4f..8304e13ab 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 @@ -44,7 +44,7 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.util.Duration; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.ui.ExitUtil; import org.cryptomator.ui.controls.DirectoryListCell; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java index f579d51d0..94ae1d220 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java @@ -8,7 +8,7 @@ package org.cryptomator.ui.controllers; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.VBox; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import javax.inject.Inject; 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 b84235632..e78fef3c8 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 @@ -24,7 +24,7 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.util.StringConverter; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VolumeImpl; import org.cryptomator.ui.l10n.Localization; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java index 3e29d0f49..c012696d1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java @@ -6,7 +6,7 @@ package org.cryptomator.ui.controllers; import javafx.fxml.FXMLLoader; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.ui.l10n.Localization; import javax.inject.Inject; 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 48ff0692f..b93ebc553 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 @@ -23,7 +23,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.layout.VBox; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.l10n.Localization; import org.cryptomator.ui.common.Tasks; diff --git a/main/ui/src/main/java/org/cryptomator/ui/FxApplication.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java similarity index 90% rename from main/ui/src/main/java/org/cryptomator/ui/FxApplication.java rename to main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 05faf7671..d9b136774 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/FxApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui; +package org.cryptomator.ui.fxapp; import javafx.application.Application; import javafx.application.Platform; @@ -38,6 +38,7 @@ public class FxApplication extends Application { public void start() { LOG.trace("FxApplication.start()"); + Platform.setImplicitExit(false); settings.theme().addListener(this::themeChanged); loadSelectedStyleSheet(settings.theme().get()); @@ -45,8 +46,6 @@ public class FxApplication extends Application { if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) { Desktop.getDesktop().setPreferencesHandler(this::handlePreferences); } - - mainWindow.build().showMainWindow(); } @Override @@ -55,11 +54,19 @@ public class FxApplication extends Application { } private void handlePreferences(PreferencesEvent preferencesEvent) { - Platform.runLater(this::showPreferencesWindow); + showPreferencesWindow(); } public void showPreferencesWindow() { - preferencesWindow.build().showPreferencesWindow(); + Platform.runLater(() -> { + preferencesWindow.build().showPreferencesWindow(); + }); + } + + public void showMainWindow() { + Platform.runLater(() -> { + mainWindow.build().showMainWindow(); + }); } private void themeChanged(@SuppressWarnings("unused") ObservableValue observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationComponent.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java similarity index 73% rename from main/ui/src/main/java/org/cryptomator/ui/FxApplicationComponent.java rename to main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java index 3a8eb9d2b..7d5fd55bf 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java @@ -3,22 +3,20 @@ * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ -package org.cryptomator.ui; +package org.cryptomator.ui.fxapp; import dagger.Subcomponent; -import javafx.application.Platform; @FxApplicationScoped @Subcomponent(modules = FxApplicationModule.class) public interface FxApplicationComponent { FxApplication application(); - - default void start() { - Platform.startup(() -> { - assert Platform.isFxApplicationThread(); - application().start(); - }); + + @Subcomponent.Builder + interface Builder { + + FxApplicationComponent build(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationModule.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java similarity index 89% rename from main/ui/src/main/java/org/cryptomator/ui/FxApplicationModule.java rename to main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java index 04a3bc03d..dad95aaa4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java @@ -3,7 +3,7 @@ * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ -package org.cryptomator.ui; +package org.cryptomator.ui.fxapp; import dagger.Binds; import dagger.Module; @@ -12,6 +12,7 @@ import javafx.application.Application; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; +import org.cryptomator.ui.UiModule; import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.model.VaultList; @@ -19,7 +20,7 @@ import org.cryptomator.ui.preferences.PreferencesComponent; import java.util.ResourceBundle; -@Module(includes = {UiModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class}) +@Module(subcomponents = {MainWindowComponent.class, PreferencesComponent.class}) abstract class FxApplicationModule { @Binds diff --git a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationScoped.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationScoped.java similarity index 88% rename from main/ui/src/main/java/org/cryptomator/ui/FxApplicationScoped.java rename to main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationScoped.java index e60c167df..0b720a250 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/FxApplicationScoped.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplicationScoped.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui; +package org.cryptomator.ui.fxapp; import javax.inject.Scope; import java.lang.annotation.Documented; diff --git a/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java b/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java index edf470d88..3ef4db621 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java +++ b/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java @@ -7,7 +7,7 @@ package org.cryptomator.ui.l10n; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java index 8d107c8bf..1455bd173 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java @@ -8,6 +8,8 @@ package org.cryptomator.ui.mainwindow; import dagger.Subcomponent; import javafx.stage.Stage; import org.cryptomator.ui.common.FXMLLoaderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @MainWindowScoped @Subcomponent(modules = {MainWindowModule.class}) diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java index 79c094156..0f072f341 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java @@ -4,7 +4,7 @@ import javafx.fxml.FXML; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; import javafx.stage.Stage; -import org.cryptomator.ui.FxApplication; +import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +18,6 @@ public class MainWindowController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(MainWindowController.class); - private final CountDownLatch shutdownLatch; private final Stage window; private final FxApplication application; public HBox titleBar; @@ -27,8 +26,7 @@ public class MainWindowController implements FxController { private double yOffset; @Inject - public MainWindowController(@Named("shutdownLatch") CountDownLatch shutdownLatch, @MainWindow Stage window, FxApplication application) { - this.shutdownLatch = shutdownLatch; + public MainWindowController(@MainWindow Stage window, FxApplication application) { this.window = window; this.application = application; } @@ -54,8 +52,6 @@ public class MainWindowController implements FxController { @FXML public void close() { window.close(); - LOG.info("closed..."); - shutdownLatch.countDown(); } @FXML diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java index 235a52b01..b9dafc6c0 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java @@ -5,7 +5,7 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.keychain.KeychainAccess; import org.slf4j.Logger; 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 78351a5fd..c01ede518 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 @@ -8,14 +8,14 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import org.cryptomator.ui.FxApplicationScoped; import org.cryptomator.common.settings.VaultSettings; import javax.inject.Inject; +import javax.inject.Singleton; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -@FxApplicationScoped +@Singleton public class VaultFactory { private final VaultComponent.Builder vaultComponentBuilder; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java index f5f7d8be4..ee5fdd1b0 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java @@ -9,15 +9,15 @@ import com.google.common.collect.Lists; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.TransformationList; -import org.cryptomator.ui.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import javax.inject.Inject; +import javax.inject.Singleton; import java.util.List; import java.util.stream.IntStream; -@FxApplicationScoped +@Singleton public class VaultList extends TransformationList { private final VaultFactory vaultFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java index c457deefd..055c3f513 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java @@ -6,11 +6,12 @@ package org.cryptomator.ui.model; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Singleton; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Set; @@ -19,7 +20,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.StreamSupport; -@FxApplicationScoped +@Singleton public final class WindowsDriveLetters { private static final Logger LOG = LoggerFactory.getLogger(WindowsDriveLetters.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java index 239597e51..fdf106aee 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java @@ -5,7 +5,7 @@ *******************************************************************************/ package org.cryptomator.ui.model.upgrade; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.ui.model.Vault; import javax.inject.Inject; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java index 084296a26..3c703ba42 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java @@ -7,7 +7,7 @@ package org.cryptomator.ui.model.upgrade; import javafx.application.Platform; import org.apache.commons.lang3.StringUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.ui.l10n.Localization; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java index 7bd443a37..49ce1fca2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java @@ -7,7 +7,7 @@ package org.cryptomator.ui.model.upgrade; import com.google.common.io.BaseEncoding; import org.apache.commons.lang3.StringUtils; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.common.MessageDigestSupplier; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java index 907154e2e..f8eef5884 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java @@ -5,7 +5,7 @@ *******************************************************************************/ package org.cryptomator.ui.model.upgrade; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.FileHeader; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java index ecc9f1a0c..fabf1b9f6 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java @@ -5,7 +5,7 @@ *******************************************************************************/ package org.cryptomator.ui.model.upgrade; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; import org.cryptomator.cryptolib.Cryptors; diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/FxApplicationStarter.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/FxApplicationStarter.java new file mode 100644 index 000000000..1f30e89d4 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/FxApplicationStarter.java @@ -0,0 +1,46 @@ +package org.cryptomator.ui.traymenu; + +import javafx.application.Platform; +import org.cryptomator.ui.fxapp.FxApplication; +import org.cryptomator.ui.fxapp.FxApplicationComponent; + +import javax.inject.Inject; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +@TrayMenuScoped +public class FxApplicationStarter { + + private final CompletableFuture future; + private final FxApplicationComponent.Builder fxAppComponent; + + @Inject + public FxApplicationStarter(FxApplicationComponent.Builder fxAppComponent) { + this.fxAppComponent = fxAppComponent; + this.future = new CompletableFuture<>(); + } + + public synchronized FxApplication get() { + if (!future.isDone()) { + start(); + } + try { + return future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while waiting for FxApplication startup.", e); + } catch (ExecutionException e) { + throw new IllegalStateException("FxApplication startup failed.", e); + } + } + + private void start() { + Platform.startup(() -> { + assert Platform.isFxApplicationThread(); + FxApplication app = fxAppComponent.build().application(); + app.start(); + future.complete(app); + }); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java new file mode 100644 index 000000000..d3fb34a41 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java @@ -0,0 +1,49 @@ +package org.cryptomator.ui.traymenu; + +import javafx.beans.Observable; +import org.cryptomator.common.settings.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.awt.AWTException; +import java.awt.SystemTray; +import java.awt.TrayIcon; + +@TrayMenuScoped +public class TrayIconController { + + private static final Logger LOG = LoggerFactory.getLogger(TrayIconController.class); + + private final Settings settings; + private final TrayImageFactory imageFactory; + private final TrayMenuController trayMenuController; + private final TrayIcon trayIcon; + + @Inject + TrayIconController(Settings settings, TrayImageFactory imageFactory, TrayMenuController trayMenuController) { + this.settings = settings; + this.trayMenuController = trayMenuController; + this.imageFactory = imageFactory; + this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu()); + } + + public void initializeTrayIcon() { + settings.theme().addListener(this::themeChanged); + + trayMenuController.initTrayMenu(); + + try { + SystemTray.getSystemTray().add(trayIcon); + LOG.info("initialized tray icon"); + } catch (AWTException e) { + LOG.error("Error adding tray icon", e); + } + } + + private void themeChanged(@SuppressWarnings("unused") Observable observable) { + trayIcon.setImage(imageFactory.loadImage()); + } + + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java new file mode 100644 index 000000000..4f2468de1 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java @@ -0,0 +1,38 @@ +package org.cryptomator.ui.traymenu; + +import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.settings.Settings; + +import javax.inject.Inject; +import java.awt.Image; +import java.awt.Toolkit; + +@TrayMenuScoped +class TrayImageFactory { + + private final Settings settings; + + @Inject + TrayImageFactory(Settings settings) { + this.settings = settings; + } + + public Image loadImage() { + String resourceName = SystemUtils.IS_OS_MAC_OSX ? getMacResourceName() : getWinOrLinuxResourceName(); + return Toolkit.getDefaultToolkit().getImage(getClass().getResource(resourceName)); + } + + private String getMacResourceName() { + switch (settings.theme().get()) { + case DARK: + return "/tray_icon_mac_white.png"; + default: + return "/tray_icon_mac_black.png"; + } + } + + private String getWinOrLinuxResourceName() { + return "/tray_icon.png"; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java new file mode 100644 index 000000000..4575da95d --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.ui.traymenu; + +import dagger.Subcomponent; + +import java.awt.SystemTray; + +@TrayMenuScoped +@Subcomponent(modules = {TrayMenuModule.class}) +public interface TrayMenuComponent { + + TrayIconController trayIconController(); + + default void addIconToSystemTray() { + if (SystemTray.isSupported()) { + trayIconController().initializeTrayIcon(); + } else { + // TODO what? + } + } + + @Subcomponent.Builder + interface Builder { + + TrayMenuComponent build(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java new file mode 100644 index 000000000..a0253a13b --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java @@ -0,0 +1,60 @@ +package org.cryptomator.ui.traymenu; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.event.ActionEvent; +import java.util.concurrent.CountDownLatch; + +@TrayMenuScoped +public class TrayMenuController { + + private static final Logger LOG = LoggerFactory.getLogger(TrayMenuController.class); + + private final FxApplicationStarter fxApplicationStarter; + private final CountDownLatch shutdownLatch; + private final PopupMenu menu; + + @Inject + TrayMenuController(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch) { + this.fxApplicationStarter = fxApplicationStarter; + this.shutdownLatch = shutdownLatch; + this.menu = new PopupMenu(); + } + + public PopupMenu getMenu() { + return menu; + } + + public void initTrayMenu() { + // TODO add listeners + rebuildMenu(); + } + + private void rebuildMenu() { + MenuItem showMainWindowItem = new MenuItem("TODO show"); + showMainWindowItem.addActionListener(this::showMainWindow); + menu.add(showMainWindowItem); + + menu.addSeparator(); + // foreach vault: add submenu + menu.addSeparator(); + + MenuItem quitApplicationItem = new MenuItem("TODO quit"); + quitApplicationItem.addActionListener(this::quitApplication); + menu.add(quitApplicationItem); + + } + + private void showMainWindow(ActionEvent actionEvent) { + fxApplicationStarter.get().showMainWindow(); + } + + private void quitApplication(ActionEvent actionEvent) { + shutdownLatch.countDown(); + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java new file mode 100644 index 000000000..87573e3b0 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java @@ -0,0 +1,9 @@ +package org.cryptomator.ui.traymenu; + +import dagger.Module; +import org.cryptomator.ui.fxapp.FxApplicationComponent; + +@Module(subcomponents = {FxApplicationComponent.class}) +abstract class TrayMenuModule { + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuScoped.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuScoped.java new file mode 100644 index 000000000..5121afae6 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.ui.traymenu; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +@interface TrayMenuScoped { + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java b/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java index dea9114eb..0aba6bf40 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java @@ -15,7 +15,7 @@ import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.paint.Color; -import org.cryptomator.ui.FxApplicationScoped; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.ui.l10n.Localization; import javax.inject.Inject; diff --git a/main/ui/src/test/java/org/cryptomator/ui/controls/Foo.java b/main/ui/src/test/java/org/cryptomator/ui/controls/Foo.java new file mode 100644 index 000000000..638f5679b --- /dev/null +++ b/main/ui/src/test/java/org/cryptomator/ui/controls/Foo.java @@ -0,0 +1,27 @@ +package org.cryptomator.ui.controls; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +public class Foo { + + @Test + public void foo() { + StringProperty str = new SimpleStringProperty("foo"); + CountDownLatch done = new CountDownLatch(1); + str.addListener(observable -> { + Assertions.assertEquals("bar", str.get()); + done.countDown(); + }); + str.set("bar"); + Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> { + done.await(); + }); + } + +}