diff --git a/pom.xml b/pom.xml
index be940d961..bee198a77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,7 +53,8 @@
4.4.0
2.2
-
+
+ 23.0.0
7.0.2
0.8.7
@@ -224,6 +225,13 @@
1.2
test
+
+
+ org.jetbrains
+ annotations
+ ${jetbrains.annotations.version}
+ provided
+
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index f13308be5..90125d7cc 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -4,6 +4,8 @@ import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
module org.cryptomator.desktop {
+ requires static org.jetbrains.annotations;
+
requires org.cryptomator.cryptofs;
requires org.cryptomator.frontend.dokany;
requires org.cryptomator.frontend.fuse;
@@ -36,6 +38,8 @@ module org.cryptomator.desktop {
opens org.cryptomator.common.settings to com.google.gson;
+ opens org.cryptomator.launcher to javafx.graphics;
+
opens org.cryptomator.common to javafx.fxml;
opens org.cryptomator.common.vaults to javafx.fxml;
opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
diff --git a/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java b/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
index 68b61688b..4f3a8ff15 100644
--- a/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
+++ b/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
-public @interface DefaultMountFlags {
+@interface DefaultMountFlags {
}
diff --git a/src/main/java/org/cryptomator/launcher/AppLaunchEvent.java b/src/main/java/org/cryptomator/launcher/AppLaunchEvent.java
new file mode 100644
index 000000000..7fde984e8
--- /dev/null
+++ b/src/main/java/org/cryptomator/launcher/AppLaunchEvent.java
@@ -0,0 +1,13 @@
+package org.cryptomator.launcher;
+
+import java.nio.file.Path;
+import java.util.Collection;
+
+public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection pathsToOpen) {
+
+ public enum EventType {
+ REVEAL_APP,
+ OPEN_FILE
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/launcher/Cryptomator.java b/src/main/java/org/cryptomator/launcher/Cryptomator.java
index 6c4751bdf..a358126d4 100644
--- a/src/main/java/org/cryptomator/launcher/Cryptomator.java
+++ b/src/main/java/org/cryptomator/launcher/Cryptomator.java
@@ -13,17 +13,15 @@ import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration;
-import org.cryptomator.ui.launcher.UiLauncher;
+import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
-import java.io.IOException;
+import javafx.application.Application;
+import javafx.stage.Stage;
import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@Singleton
@@ -38,19 +36,15 @@ public class Cryptomator {
private final DebugMode debugMode;
private final Environment env;
private final Lazy ipcMessageHandler;
- private final CountDownLatch shutdownLatch;
private final ShutdownHook shutdownHook;
- private final Lazy uiLauncher;
@Inject
- Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy uiLauncher) {
+ Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy ipcMessageHandler, ShutdownHook shutdownHook) {
this.logConfig = logConfig;
this.debugMode = debugMode;
this.env = env;
this.ipcMessageHandler = ipcMessageHandler;
- this.shutdownLatch = shutdownLatch;
this.shutdownHook = shutdownHook;
- this.uiLauncher = uiLauncher;
}
public static void main(String[] args) {
@@ -96,21 +90,38 @@ public class Cryptomator {
}
/**
- * Launches the JavaFX application and waits until shutdown is requested.
+ * Launches the JavaFX application, blocking the main thread until shuts down.
*
* @return Nonzero exit code in case of an error.
- * @implNote This method blocks until {@link #shutdownLatch} reached zero.
*/
private int runGuiApplication() {
try {
- uiLauncher.get().launch();
- shutdownLatch.await();
+ Application.launch(MainApp.class);
LOG.info("UI shut down");
return 0;
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ } catch (Exception e) {
+ LOG.error("Terminating due to error", e);
return 1;
}
}
+ public static class MainApp extends Application {
+
+ @Override
+ public void start(Stage primaryStage) {
+ LOG.info("JavaFX application started.");
+ FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
+ .fxApplication(this) //
+ .primaryStage(primaryStage) //
+ .build();
+ component.application().start();
+ }
+
+ @Override
+ public void stop() {
+ LOG.info("JavaFX application stopped.");
+ }
+
+ }
+
}
diff --git a/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
index 70bf9e772..b43c0eca0 100644
--- a/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
+++ b/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
@@ -3,14 +3,16 @@ package org.cryptomator.launcher;
import dagger.Component;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.logging.LoggerModule;
-import org.cryptomator.ui.launcher.UiLauncherModule;
+import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Singleton;
@Singleton
-@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class})
+@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
public interface CryptomatorComponent {
Cryptomator application();
+ FxApplicationComponent.Builder fxAppComponentBuilder();
+
}
diff --git a/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
index 906971492..e6aab0309 100644
--- a/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
+++ b/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
@@ -2,20 +2,55 @@ package org.cryptomator.launcher;
import dagger.Module;
import dagger.Provides;
+import org.cryptomator.common.PluginClassLoader;
+import org.cryptomator.integrations.autostart.AutoStartProvider;
+import org.cryptomator.integrations.tray.TrayIntegrationProvider;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
+import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
+import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
-@Module
+@Module(subcomponents = {FxApplicationComponent.class})
class CryptomatorModule {
@Provides
@Singleton
- @Named("shutdownLatch")
- static CountDownLatch provideShutdownLatch() {
- return new CountDownLatch(1);
+ static ResourceBundle provideLocalization() {
+ return ResourceBundle.getBundle("i18n.strings");
}
+ @Provides
+ @Singleton
+ @Named("launchEventQueue")
+ static BlockingQueue provideFileOpenRequests() {
+ return new ArrayBlockingQueue<>(10);
+ }
+
+ // TODO: still needed after integrations-api 1.1.0?
+
+ @Provides
+ @Singleton
+ static Optional provideAppearanceProvider(PluginClassLoader classLoader) {
+ return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
+ }
+
+ @Provides
+ @Singleton
+ static Optional provideAutostartProvider(PluginClassLoader classLoader) {
+ return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
+ }
+
+ @Provides
+ @Singleton
+ static Optional provideTrayIntegrationProvider(PluginClassLoader classLoader) {
+ return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
+ }
+
+
}
diff --git a/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
index b4e37e1f9..eb2418c69 100644
--- a/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
+++ b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
@@ -6,7 +6,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
-import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
-import java.util.stream.Collectors;
@Singleton
class FileOpenRequestHandler {
diff --git a/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java b/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
index 5c28d05a4..05565f97d 100644
--- a/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
+++ b/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
@@ -1,7 +1,6 @@
package org.cryptomator.launcher;
import org.cryptomator.ipc.IpcMessageListener;
-import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
index 8a5a776ea..c6acbadf6 100644
--- a/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
@@ -14,7 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
import javax.inject.Named;
@@ -43,12 +43,12 @@ public abstract class AddVaultModule {
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
- static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
+ static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
- stage.initOwner(owner);
+ stage.initOwner(primaryStage);
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
index 99d01577f..e0306b4e7 100644
--- a/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
@@ -2,25 +2,24 @@ package org.cryptomator.ui.addvaultwizard;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.util.Optional;
@AddVaultWizardScoped
public class AddVaultSuccessController implements FxController {
- private final FxApplication fxApplication;
+ private final FxApplicationWindows appWindows;
private final Stage window;
private final ReadOnlyObjectProperty vault;
@Inject
- AddVaultSuccessController(FxApplication fxApplication, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty vault) {
- this.fxApplication = fxApplication;
+ AddVaultSuccessController(FxApplicationWindows appWindows, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty vault) {
+ this.appWindows = appWindows;
this.window = window;
this.vault = vault;
}
@@ -28,7 +27,7 @@ public class AddVaultSuccessController implements FxController {
@FXML
public void unlockAndClose() {
close();
- fxApplication.startUnlockWorkflow(vault.get(), Optional.of(window));
+ appWindows.startUnlockWorkflow(vault.get(), window);
}
@FXML
diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
index 4fceaa929..01a8a6758 100644
--- a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
@@ -4,10 +4,10 @@ import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,9 +20,6 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ResourceBundle;
@@ -34,7 +31,7 @@ public class ChooseExistingVaultController implements FxController {
private final Stage window;
private final Lazy welcomeScene;
private final Lazy successScene;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
private final ObjectProperty vaultPath;
private final ObjectProperty vault;
private final VaultListManager vaultListManager;
@@ -43,11 +40,11 @@ public class ChooseExistingVaultController implements FxController {
private Image screenshot;
@Inject
- ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
+ ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, FxApplicationWindows appWindows, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
this.window = window;
this.welcomeScene = welcomeScene;
this.successScene = successScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
this.vaultPath = vaultPath;
this.vault = vault;
this.vaultListManager = vaultListManager;
@@ -82,7 +79,7 @@ public class ChooseExistingVaultController implements FxController {
window.setScene(successScene.get());
} catch (IOException e) {
LOG.error("Failed to open existing vault.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}
}
}
diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
index 578b90969..51a8a1147 100644
--- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
@@ -10,12 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.Tasks;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger;
@@ -60,7 +60,7 @@ public class CreateNewVaultPasswordController implements FxController {
private final Lazy chooseLocationScene;
private final Lazy recoveryKeyScene;
private final Lazy successScene;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty vaultNameProperty;
@@ -83,12 +83,12 @@ public class CreateNewVaultPasswordController implements FxController {
public NewPasswordController newPasswordSceneController;
@Inject
- CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
+ CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, FxApplicationWindows appWindows, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.chooseLocationScene = chooseLocationScene;
this.recoveryKeyScene = recoveryKeyScene;
this.successScene = successScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory;
this.vaultNameProperty = vaultName;
@@ -127,7 +127,7 @@ public class CreateNewVaultPasswordController implements FxController {
Files.createDirectory(pathToVault);
} catch (IOException e) {
LOG.error("Failed to create vault directory.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
return;
}
@@ -152,7 +152,7 @@ public class CreateNewVaultPasswordController implements FxController {
window.setScene(recoveryKeyScene.get());
}).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> {
processing.set(false);
}).runOnce(executor);
@@ -168,7 +168,7 @@ public class CreateNewVaultPasswordController implements FxController {
window.setScene(successScene.get());
}).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> {
processing.set(false);
}).runOnce(executor);
diff --git a/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
index c715f0466..200a70328 100644
--- a/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
+++ b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
@@ -8,10 +8,10 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
-import java.security.SecureRandom;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -38,9 +37,8 @@ public class ChangePasswordController implements FxController {
private final Stage window;
private final Vault vault;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
private final KeychainManager keychain;
- private final SecureRandom csprng;
private final MasterkeyFileAccess masterkeyFileAccess;
public NiceSecurePasswordField oldPasswordField;
@@ -49,12 +47,11 @@ public class ChangePasswordController implements FxController {
public NewPasswordController newPasswordController;
@Inject
- public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
+ public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, FxApplicationWindows appWindows, KeychainManager keychain, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.vault = vault;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
this.keychain = keychain;
- this.csprng = csprng;
this.masterkeyFileAccess = masterkeyFileAccess;
}
@@ -95,7 +92,7 @@ public class ChangePasswordController implements FxController {
oldPasswordField.requestFocus();
} catch (IOException | CryptoException e) {
LOG.error("Password change failed. Unable to perform operation.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}
}
diff --git a/src/main/java/org/cryptomator/ui/common/ErrorComponent.java b/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
index 92276f5bd..8cb430584 100644
--- a/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
+++ b/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
@@ -4,7 +4,6 @@ import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.Nullable;
-import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -16,34 +15,17 @@ public interface ErrorComponent {
@FxmlScene(FxmlFile.ERROR)
Scene scene();
- default void showErrorScene() {
- if (Platform.isFxApplicationThread()) {
- show();
- } else {
- Platform.runLater(this::show);
- }
- }
-
- private void show() {
+ default Stage show() {
Stage stage = window();
stage.setScene(scene());
stage.show();
+ return stage;
}
- @Subcomponent.Builder
- interface Builder {
-
- @BindsInstance
- Builder cause(Throwable cause);
-
- @BindsInstance
- Builder window(Stage window);
-
- @BindsInstance
- Builder returnToScene(@Nullable Scene previousScene);
-
- ErrorComponent build();
+ @Subcomponent.Factory
+ interface Factory {
+ ErrorComponent create(@BindsInstance Throwable cause, @BindsInstance Stage window, @BindsInstance @Nullable Scene previousScene);
}
}
diff --git a/src/main/java/org/cryptomator/ui/common/StageFactory.java b/src/main/java/org/cryptomator/ui/common/StageFactory.java
index 9a0dcb1c5..3a8c20cb3 100644
--- a/src/main/java/org/cryptomator/ui/common/StageFactory.java
+++ b/src/main/java/org/cryptomator/ui/common/StageFactory.java
@@ -1,23 +1,24 @@
package org.cryptomator.ui.common;
+import org.cryptomator.ui.fxapp.FxApplicationScoped;
+
+import javax.inject.Inject;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.function.Consumer;
+@FxApplicationScoped
public class StageFactory {
private final Consumer initializer;
- public StageFactory(Consumer initializer) {
+ @Inject
+ public StageFactory(StageInitializer initializer) {
this.initializer = initializer;
}
public Stage create() {
- return create(StageStyle.DECORATED);
- }
-
- public Stage create(StageStyle stageStyle) {
- Stage stage = new Stage(stageStyle);
+ Stage stage = new Stage(StageStyle.DECORATED);
initializer.accept(stage);
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/common/StageInitializer.java b/src/main/java/org/cryptomator/ui/common/StageInitializer.java
new file mode 100644
index 000000000..1534deb52
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/common/StageInitializer.java
@@ -0,0 +1,32 @@
+package org.cryptomator.ui.common;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.ui.fxapp.FxApplicationScoped;
+
+import javax.inject.Inject;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Performs common setup for all stages
+ */
+@FxApplicationScoped
+public class StageInitializer implements Consumer {
+
+ private final List windowIcons;
+
+ @Inject
+ public StageInitializer() {
+ this.windowIcons = SystemUtils.IS_OS_MAC ? List.of() : List.of( //
+ new Image(StageInitializer.class.getResource("/img/window_icon_32.png").toString()), //
+ new Image(StageInitializer.class.getResource("/img/window_icon_512.png").toString()) //
+ );
+ }
+
+ @Override
+ public void accept(Stage stage) {
+ stage.getIcons().setAll(windowIcons);
+ }
+}
diff --git a/src/main/java/org/cryptomator/ui/common/VaultService.java b/src/main/java/org/cryptomator/ui/common/VaultService.java
index b81ddec49..a6486f35f 100644
--- a/src/main/java/org/cryptomator/ui/common/VaultService.java
+++ b/src/main/java/org/cryptomator/ui/common/VaultService.java
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.concurrent.Task;
+import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -53,7 +54,9 @@ public class VaultService {
*
* @param vault The vault to lock
* @param forced Whether to attempt a forced lock
+ * @deprecated use {@link org.cryptomator.ui.fxapp.FxApplicationWindows#startLockWorkflow(Vault, Stage)}
*/
+ @Deprecated
public void lock(Vault vault, boolean forced) {
executorService.execute(createLockTask(vault, forced));
}
@@ -90,7 +93,7 @@ public class VaultService {
* @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask
*/
public Task> createLockAllTask(Collection vaults, boolean forced) {
- List> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList());
+ List> lockTasks = vaults.stream().>map(v -> new LockVaultTask(v, forced)).toList();
lockTasks.forEach(executorService::execute);
Task> task = new WaitForTasksTask(lockTasks);
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
diff --git a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java b/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java
similarity index 72%
rename from src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java
rename to src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java
index 52ba838c0..39e40600e 100644
--- a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java
@@ -1,14 +1,14 @@
-package org.cryptomator.ui.launcher;
+package org.cryptomator.ui.fxapp;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
-import org.cryptomator.ui.fxapp.FxApplication;
+import org.cryptomator.launcher.AppLaunchEvent;
+import org.cryptomator.ui.common.VaultService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Singleton;
import javafx.application.Platform;
import java.io.IOException;
import java.nio.file.Path;
@@ -17,22 +17,25 @@ import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
-@Singleton
+// TODO: use message bus
+@FxApplicationScoped
class AppLaunchEventHandler {
private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class);
private final BlockingQueue launchEventQueue;
private final ExecutorService executorService;
- private final FxApplicationStarter fxApplicationStarter;
+ private final FxApplicationWindows appWindows;
private final VaultListManager vaultListManager;
+ private final VaultService vaultService;
@Inject
- public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue launchEventQueue, ExecutorService executorService, FxApplicationStarter fxApplicationStarter, VaultListManager vaultListManager) {
+ public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
this.launchEventQueue = launchEventQueue;
this.executorService = executorService;
- this.fxApplicationStarter = fxApplicationStarter;
+ this.appWindows = appWindows;
this.vaultListManager = vaultListManager;
+ this.vaultService = vaultService;
}
public void startHandlingLaunchEvents() {
@@ -52,14 +55,12 @@ class AppLaunchEventHandler {
}
private void handleLaunchEvent(AppLaunchEvent event) {
- switch (event.getType()) {
- case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow);
- case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> {
- Platform.runLater(() -> {
- event.getPathsToOpen().forEach(this::addOrRevealVault);
- });
+ switch (event.type()) {
+ case REVEAL_APP -> appWindows.showMainWindow();
+ case OPEN_FILE -> Platform.runLater(() -> {
+ event.pathsToOpen().forEach(this::addOrRevealVault);
});
- default -> LOG.warn("Unsupported event type: {}", event.getType());
+ default -> LOG.warn("Unsupported event type: {}", event.type());
}
}
@@ -75,7 +76,7 @@ class AppLaunchEventHandler {
}
if (v.isUnlocked()) {
- fxApplicationStarter.get().thenAccept(app -> app.getVaultService().reveal(v));
+ vaultService.reveal(v);
}
LOG.debug("Added vault {}", potentialVaultPath);
} catch (IOException e) {
diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
new file mode 100644
index 000000000..9d6a73fa0
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
@@ -0,0 +1,26 @@
+package org.cryptomator.ui.fxapp;
+
+import org.cryptomator.common.vaults.Vault;
+
+import javax.inject.Inject;
+import javafx.collections.ObservableList;
+
+@FxApplicationScoped
+public class AutoUnlocker {
+
+ private final ObservableList vaults;
+ private final FxApplicationWindows appWindows;
+
+ @Inject
+ public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows) {
+ this.vaults = vaults;
+ this.appWindows = appWindows;
+ }
+
+ public void unlock() {
+ vaults.stream().filter(Vault::isLocked).filter(v -> v.getVaultSettings().unlockAfterStartup().get()).forEach(v -> {
+ appWindows.startUnlockWorkflow(v, null);
+ });
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/ExitingQuitResponse.java b/src/main/java/org/cryptomator/ui/fxapp/ExitingQuitResponse.java
new file mode 100644
index 000000000..ae2bcd438
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/ExitingQuitResponse.java
@@ -0,0 +1,20 @@
+package org.cryptomator.ui.fxapp;
+
+import javafx.application.Platform;
+import java.awt.desktop.QuitResponse;
+
+record ExitingQuitResponse(QuitResponse delegate) implements QuitResponse {
+
+ @Override
+ public void performQuit() {
+ Platform.exit();
+ // TODO wait a moment for javafx to terminate?
+ delegate.performQuit();
+ }
+
+ @Override
+ public void cancelQuit() {
+ delegate.cancelQuit();
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
index 1812d38bd..55f76d321 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
@@ -1,213 +1,70 @@
package org.cryptomator.ui.fxapp;
import dagger.Lazy;
-import javafx.application.Application;
-import javafx.application.Platform;
-import javafx.beans.binding.Bindings;
-import javafx.beans.binding.BooleanBinding;
-import javafx.beans.value.ObservableValue;
-import javafx.collections.ObservableList;
-import javafx.stage.Stage;
-import javafx.stage.Window;
-import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
-import org.cryptomator.common.settings.UiTheme;
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.common.vaults.VaultListManager;
-import org.cryptomator.common.vaults.VaultState;
-import org.cryptomator.integrations.tray.TrayIntegrationProvider;
-import org.cryptomator.integrations.uiappearance.Theme;
-import org.cryptomator.integrations.uiappearance.UiAppearanceException;
-import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
-import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
-import org.cryptomator.ui.common.ErrorComponent;
-import org.cryptomator.ui.common.VaultService;
-import org.cryptomator.ui.lock.LockComponent;
-import org.cryptomator.ui.mainwindow.MainWindowComponent;
-import org.cryptomator.ui.preferences.PreferencesComponent;
-import org.cryptomator.ui.preferences.SelectedPreferencesTab;
-import org.cryptomator.ui.quit.QuitComponent;
-import org.cryptomator.ui.unlock.UnlockComponent;
+import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
-import javax.inject.Provider;
-import java.awt.desktop.QuitResponse;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
+import javafx.application.Platform;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import java.awt.SystemTray;
@FxApplicationScoped
-public class FxApplication extends Application {
+public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
private final Settings settings;
- private final Lazy mainWindow;
- private final Lazy preferencesWindow;
- private final Lazy quitWindow;
- private final Provider unlockWorkflowBuilderProvider;
- private final Provider lockWorkflowBuilderProvider;
- private final ErrorComponent.Builder errorWindowBuilder;
- private final Optional trayIntegration;
- private final Optional appearanceProvider;
- private final VaultService vaultService;
- private final LicenseHolder licenseHolder;
- private final ObservableList visibleWindows;
- private final BooleanBinding hasVisibleWindows;
- private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
+ private final AppLaunchEventHandler launchEventHandler;
+ private final Lazy trayMenu;
+ private final FxApplicationWindows appWindows;
+ private final FxApplicationStyle applicationStyle;
+ private final FxApplicationTerminator applicationTerminator;
+ private final AutoUnlocker autoUnlocker;
@Inject
- FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, Provider unlockWorkflowBuilderProvider, Provider lockWorkflowBuilderProvider, Lazy quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional trayIntegration, Optional appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
+ FxApplication(Settings settings, AppLaunchEventHandler launchEventHandler, Lazy trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
this.settings = settings;
- this.mainWindow = mainWindow;
- this.preferencesWindow = preferencesWindow;
- this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
- this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
- this.quitWindow = quitWindow;
- this.errorWindowBuilder = errorWindowBuilder;
- this.trayIntegration = trayIntegration;
- this.appearanceProvider = appearanceProvider;
- this.vaultService = vaultService;
- this.licenseHolder = licenseHolder;
- this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
- this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
+ this.launchEventHandler = launchEventHandler;
+ this.trayMenu = trayMenu;
+ this.appWindows = appWindows;
+ this.applicationStyle = applicationStyle;
+ this.applicationTerminator = applicationTerminator;
+ this.autoUnlocker = autoUnlocker;
}
public void start() {
LOG.trace("FxApplication.start()");
- Platform.setImplicitExit(false);
+ applicationStyle.initialize();
+ appWindows.initialize();
+ applicationTerminator.initialize();
- hasVisibleWindows.addListener(this::hasVisibleStagesChanged);
-
- settings.theme().addListener(this::appThemeChanged);
- loadSelectedStyleSheet(settings.theme().get());
- }
-
- @Override
- public void start(Stage stage) {
- throw new UnsupportedOperationException("Use start() instead.");
- }
-
- private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
- LOG.debug("has visible stages: {}", newValue);
- if (newValue) {
- trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
+ // init system tray
+ final boolean hasTrayIcon;
+ if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
+ trayMenu.get().initializeTrayIcon();
+ Platform.setImplicitExit(false); // don't quit when closing all windows
+ hasTrayIcon = true;
} else {
- trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
+ hasTrayIcon = false;
}
- }
- public void showPreferencesWindow(SelectedPreferencesTab selectedTab) {
- Platform.runLater(() -> {
- preferencesWindow.get().showPreferencesWindow(selectedTab);
- LOG.debug("Showing Preferences");
- });
- }
-
- public CompletionStage showMainWindow() {
- CompletableFuture future = new CompletableFuture<>();
- Platform.runLater(() -> {
- var win = mainWindow.get().showMainWindow();
- LOG.debug("Showing MainWindow");
- future.complete(win);
- });
- return future;
- }
-
- public void startUnlockWorkflow(Vault vault, Optional owner) {
- Platform.runLater(() -> {
- if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
- unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
- LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
- } else {
- showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
+ // show main window
+ appWindows.showMainWindow().thenAccept(stage -> {
+ if (settings.startHidden().get()) {
+ if (hasTrayIcon) {
+ stage.hide();
+ } else {
+ stage.setIconified(true);
+ }
}
});
- }
- public void startLockWorkflow(Vault vault, Optional owner) {
- Platform.runLater(() -> {
- if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
- lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
- LOG.debug("Start lock workflow for {}", vault.getDisplayName());
- } else {
- showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
- }
- });
- }
-
- public void showQuitWindow(QuitResponse response) {
- Platform.runLater(() -> {
- quitWindow.get().showQuitWindow(response);
- LOG.debug("Showing QuitWindow");
- });
- }
-
- public VaultService getVaultService() {
- return vaultService;
- }
-
- private void appThemeChanged(@SuppressWarnings("unused") ObservableValue extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
- if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
- try {
- appearanceProvider.get().removeListener(systemInterfaceThemeListener);
- } catch (UiAppearanceException e) {
- LOG.error("Failed to disable automatic theme switching.");
- }
- }
- loadSelectedStyleSheet(newValue);
- }
-
- private void loadSelectedStyleSheet(UiTheme desiredTheme) {
- UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
- switch (theme) {
- case LIGHT -> applyLightTheme();
- case DARK -> applyDarkTheme();
- case AUTOMATIC -> {
- appearanceProvider.ifPresent(appearanceProvider -> {
- try {
- appearanceProvider.addListener(systemInterfaceThemeListener);
- } catch (UiAppearanceException e) {
- LOG.error("Failed to enable automatic theme switching.");
- }
- });
- applySystemTheme();
- }
- }
- }
-
- private void systemInterfaceThemeChanged(Theme theme) {
- switch (theme) {
- case LIGHT -> applyLightTheme();
- case DARK -> applyDarkTheme();
- }
- }
-
- private void applySystemTheme() {
- if (appearanceProvider.isPresent()) {
- systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
- } else {
- LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
- applyLightTheme();
- }
- }
-
- private void applyLightTheme() {
- Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
- appearanceProvider.ifPresent(appearanceProvider -> {
- appearanceProvider.adjustToTheme(Theme.LIGHT);
- });
- }
-
- private void applyDarkTheme() {
- Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
- appearanceProvider.ifPresent(appearanceProvider -> {
- appearanceProvider.adjustToTheme(Theme.DARK);
- });
+ launchEventHandler.startHandlingLaunchEvents();
+ autoUnlocker.unlock();
}
}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java
index 7d5fd55bf..2557aa9ee 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationComponent.java
@@ -5,8 +5,12 @@
*******************************************************************************/
package org.cryptomator.ui.fxapp;
+import dagger.BindsInstance;
import dagger.Subcomponent;
+import javafx.application.Application;
+import javafx.stage.Stage;
+
@FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class)
public interface FxApplicationComponent {
@@ -16,6 +20,12 @@ public interface FxApplicationComponent {
@Subcomponent.Builder
interface Builder {
+ @BindsInstance
+ Builder fxApplication(Application application);
+
+ @BindsInstance
+ Builder primaryStage(@PrimaryStage Stage primaryStage);
+
FxApplicationComponent build();
}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java
index 74c201372..85e46dffa 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java
@@ -5,7 +5,6 @@
*******************************************************************************/
package org.cryptomator.ui.fxapp;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
@@ -15,67 +14,46 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
+import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import javax.inject.Named;
-import javafx.application.Application;
-import javafx.collections.ObservableSet;
import javafx.scene.image.Image;
-import javafx.stage.Stage;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
-@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
+@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
abstract class FxApplicationModule {
- @Provides
- @Named("windowIcons")
- @FxApplicationScoped
- static List provideWindowIcons() {
- if (SystemUtils.IS_OS_MAC) {
- return Collections.emptyList();
- }
- try {
- return List.of( //
- createImageFromResource("/img/window_icon_32.png"), //
- createImageFromResource("/img/window_icon_512.png") //
- );
- } catch (IOException e) {
- throw new UncheckedIOException("Failed to load embedded resource.", e);
- }
- }
-
- @Provides
- @FxApplicationScoped
- static StageFactory provideStageFactory(@Named("windowIcons") List windowIcons) {
- return new StageFactory(stage -> {
- stage.getIcons().addAll(windowIcons);
- });
- }
-
private static Image createImageFromResource(String resourceName) throws IOException {
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
return new Image(in);
}
}
- @Binds
- abstract Application bindApplication(FxApplication application);
+ @Provides
+ @FxApplicationScoped
+ static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
+ return builder.build();
+ }
@Provides
+ @FxApplicationScoped
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
return builder.build();
}
@Provides
+ @FxApplicationScoped
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
return builder.build();
}
@Provides
+ @FxApplicationScoped
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
return builder.build();
}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java
new file mode 100644
index 000000000..da2a4a800
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java
@@ -0,0 +1,94 @@
+package org.cryptomator.ui.fxapp;
+
+import org.cryptomator.common.LicenseHolder;
+import org.cryptomator.common.settings.Settings;
+import org.cryptomator.common.settings.UiTheme;
+import org.cryptomator.integrations.uiappearance.Theme;
+import org.cryptomator.integrations.uiappearance.UiAppearanceException;
+import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javafx.application.Application;
+import javafx.beans.value.ObservableValue;
+import java.util.Optional;
+
+@FxApplicationScoped
+public class FxApplicationStyle {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStyle.class);
+
+ private final Settings settings;
+ private final Optional appearanceProvider;
+ private final LicenseHolder licenseHolder;
+ private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
+
+ @Inject
+ public FxApplicationStyle(Settings settings, Optional appearanceProvider, LicenseHolder licenseHolder){
+ this.settings = settings;
+ this.appearanceProvider = appearanceProvider;
+ this.licenseHolder = licenseHolder;
+ }
+
+ public void initialize() {
+ settings.theme().addListener(this::appThemeChanged);
+ loadSelectedStyleSheet(settings.theme().get());
+ }
+
+ private void appThemeChanged(@SuppressWarnings("unused") ObservableValue extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
+ if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
+ try {
+ appearanceProvider.get().removeListener(systemInterfaceThemeListener);
+ } catch (UiAppearanceException e) {
+ LOG.error("Failed to disable automatic theme switching.");
+ }
+ }
+ loadSelectedStyleSheet(newValue);
+ }
+
+ private void loadSelectedStyleSheet(UiTheme desiredTheme) {
+ UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
+ switch (theme) {
+ case LIGHT -> applyLightTheme();
+ case DARK -> applyDarkTheme();
+ case AUTOMATIC -> {
+ appearanceProvider.ifPresent(provider -> {
+ try {
+ provider.addListener(systemInterfaceThemeListener);
+ } catch (UiAppearanceException e) {
+ LOG.error("Failed to enable automatic theme switching.");
+ }
+ });
+ applySystemTheme();
+ }
+ }
+ }
+
+ private void systemInterfaceThemeChanged(Theme theme) {
+ switch (theme) {
+ case LIGHT -> applyLightTheme();
+ case DARK -> applyDarkTheme();
+ }
+ }
+
+ private void applySystemTheme() {
+ if (appearanceProvider.isPresent()) {
+ systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
+ } else {
+ LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
+ applyLightTheme();
+ }
+ }
+
+ private void applyLightTheme() {
+ Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
+ appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
+ }
+
+ private void applyDarkTheme() {
+ Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
+ appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
+ }
+}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationTerminator.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationTerminator.java
new file mode 100644
index 000000000..7c7b07c1e
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationTerminator.java
@@ -0,0 +1,134 @@
+package org.cryptomator.ui.fxapp;
+
+import com.google.common.base.Preconditions;
+import org.cryptomator.common.ShutdownHook;
+import org.cryptomator.common.vaults.LockNotCompletedException;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.common.vaults.VaultState;
+import org.cryptomator.common.vaults.Volume;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javafx.beans.Observable;
+import javafx.collections.ObservableList;
+import java.awt.Desktop;
+import java.awt.desktop.QuitResponse;
+import java.awt.desktop.QuitStrategy;
+import java.util.EnumSet;
+import java.util.EventObject;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.cryptomator.common.vaults.VaultState.Value.*;
+
+@FxApplicationScoped
+public class FxApplicationTerminator {
+
+ private static final Set STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
+ private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
+
+ private final ObservableList vaults;
+ private final ShutdownHook shutdownHook;
+ private final FxApplicationWindows appWindows;
+ private final AtomicBoolean allowQuitWithoutPrompt = new AtomicBoolean();
+
+ @Inject
+ public FxApplicationTerminator(ObservableList vaults, ShutdownHook shutdownHook, FxApplicationWindows appWindows) {
+ this.vaults = vaults;
+ this.shutdownHook = shutdownHook;
+ this.appWindows = appWindows;
+ }
+
+ public void initialize() {
+ Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
+ Desktop desktop = Desktop.getDesktop();
+
+ // register quit handler
+ if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
+ desktop.setQuitHandler(this::handleQuitRequest);
+ }
+
+ // set quit strategy (cmd+q would call `System.exit(0)` otherwise)
+ if (desktop.isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
+ desktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
+ }
+
+ // allow sudden termination?
+ vaultListChanged(vaults);
+ vaults.addListener(this::vaultListChanged);
+
+ shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
+ }
+
+ /**
+ * Gracefully terminates the application.
+ */
+ public void terminate() {
+ handleQuitRequest(null, new NoopQuitResponse());
+ }
+
+ private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
+ boolean allowSuddenTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
+ boolean stateChanged = allowQuitWithoutPrompt.compareAndSet(!allowSuddenTermination, allowSuddenTermination);
+ Desktop desktop = Desktop.getDesktop();
+ if (stateChanged && desktop.isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
+ if (allowSuddenTermination) {
+ LOG.debug("Enabling sudden termination");
+ desktop.enableSuddenTermination();
+ } else {
+ LOG.debug("Disabling sudden termination");
+ desktop.disableSuddenTermination();
+ }
+ }
+ }
+
+ /**
+ * Asks the app to quit. If confirmed, the JavaFX application will exit before giving a {@code response}.
+ *
+ * @param e ignored
+ * @param response a quit response that will be {@link ExitingQuitResponse decorated in order to exit the JavaFX application}.
+ */
+ private void handleQuitRequest(@SuppressWarnings("unused") @Nullable EventObject e, QuitResponse response) {
+ var exitingResponse = new ExitingQuitResponse(response);
+ if (allowQuitWithoutPrompt.get()) {
+ exitingResponse.performQuit();
+ } else {
+ appWindows.showQuitWindow(exitingResponse);
+ }
+ }
+
+ private void forceUnmountRemainingVaults() {
+ for (Vault vault : vaults) {
+ if (vault.isUnlocked()) {
+ try {
+ vault.lock(true);
+ } catch (Volume.VolumeException e) {
+ LOG.error("Failed to unmount vault " + vault.getPath(), e);
+ } catch (LockNotCompletedException e) {
+ LOG.error("Failed to lock vault " + vault.getPath(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * A dummy QuitResponse that ignores the response.
+ *
+ * To be used with {@link #handleQuitRequest(EventObject, QuitResponse)} if the invoking method is not interested in the response.
+ */
+ private static class NoopQuitResponse implements QuitResponse {
+
+ @Override
+ public void performQuit() {
+ // no-op
+ }
+
+ @Override
+ public void cancelQuit() {
+ // no-op
+ }
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
new file mode 100644
index 000000000..2fcf2718b
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
@@ -0,0 +1,149 @@
+package org.cryptomator.ui.fxapp;
+
+import com.google.common.base.Preconditions;
+import dagger.Lazy;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.common.vaults.VaultState;
+import org.cryptomator.integrations.tray.TrayIntegrationProvider;
+import org.cryptomator.ui.common.ErrorComponent;
+import org.cryptomator.ui.lock.LockComponent;
+import org.cryptomator.ui.mainwindow.MainWindowComponent;
+import org.cryptomator.ui.preferences.PreferencesComponent;
+import org.cryptomator.ui.preferences.SelectedPreferencesTab;
+import org.cryptomator.ui.quit.QuitComponent;
+import org.cryptomator.ui.unlock.UnlockComponent;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javafx.application.Platform;
+import javafx.collections.ListChangeListener;
+import javafx.collections.transformation.FilteredList;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.stage.Window;
+import java.awt.Desktop;
+import java.awt.desktop.AppReopenedListener;
+import java.awt.desktop.QuitResponse;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutorService;
+
+@FxApplicationScoped
+public class FxApplicationWindows {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FxApplicationWindows.class);
+
+ private final Stage primaryStage;
+ private final Optional trayIntegration;
+ private final Lazy mainWindow;
+ private final Lazy preferencesWindow;
+ private final Lazy quitWindow;
+ private final UnlockComponent.Factory unlockWorkflowFactory;
+ private final LockComponent.Factory lockWorkflowFactory;
+ private final ErrorComponent.Factory errorWindowFactory;
+ private final ExecutorService executor;
+ private final FilteredList visibleWindows;
+
+ @Inject
+ public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional trayIntegration, Lazy mainWindow, Lazy preferencesWindow, Lazy quitWindow, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor) {
+ this.primaryStage = primaryStage;
+ this.trayIntegration = trayIntegration;
+ this.mainWindow = mainWindow;
+ this.preferencesWindow = preferencesWindow;
+ this.quitWindow = quitWindow;
+ this.unlockWorkflowFactory = unlockWorkflowFactory;
+ this.lockWorkflowFactory = lockWorkflowFactory;
+ this.errorWindowFactory = errorWindowFactory;
+ this.executor = executor;
+ this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
+ }
+
+ public void initialize() {
+ Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
+ Desktop desktop = Desktop.getDesktop();
+
+ // register preferences shortcut
+ if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
+ desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
+ }
+
+ // register preferences shortcut
+ if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
+ desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
+ }
+
+ // register app reopen listener
+ if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
+ desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
+ }
+
+ // observe visible windows
+ if (trayIntegration.isPresent()) {
+ visibleWindows.addListener(this::visibleWindowsChanged);
+ }
+ }
+
+ private void visibleWindowsChanged(ListChangeListener.Change extends Window> change) {
+ int visibleWindows = change.getList().size();
+ LOG.debug("visible windows: {}", visibleWindows);
+ if (visibleWindows > 0) {
+ trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
+ } else {
+ trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
+ }
+ }
+
+ public CompletionStage showMainWindow() {
+ return CompletableFuture.supplyAsync(mainWindow.get()::showMainWindow, Platform::runLater);
+ }
+
+ public CompletionStage showPreferencesWindow(SelectedPreferencesTab selectedTab) {
+ return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater);
+ }
+
+ public CompletionStage showQuitWindow(QuitResponse response) {
+ return CompletableFuture.supplyAsync(() -> quitWindow.get().showQuitWindow(response), Platform::runLater);
+ }
+
+ public CompletionStage startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
+ return CompletableFuture.supplyAsync(() -> {
+ Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
+ LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
+ return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
+ }, Platform::runLater) //
+ .thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
+ .exceptionally(e -> {
+ showErrorWindow(e, owner == null ? primaryStage : owner, null);
+ return null;
+ });
+ }
+
+ public CompletionStage startLockWorkflow(Vault vault, @Nullable Stage owner) {
+ return CompletableFuture.supplyAsync(() -> {
+ Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING), "Vault not unlocked.");
+ LOG.debug("Start lock workflow for {}", vault.getDisplayName());
+ return lockWorkflowFactory.create(vault, owner).lockWorkflow();
+ }, Platform::runLater) //
+ .thenCompose(lockWorkflow -> CompletableFuture.runAsync(lockWorkflow, executor)) //
+ .exceptionally(e -> {
+ showErrorWindow(e, owner == null ? primaryStage : owner, null);
+ return null;
+ });
+ }
+
+ /**
+ * Displays the generic error scene in the given window.
+ *
+ * @param cause The exception to show
+ * @param window What window to display the scene in
+ * @param previousScene To what scene to return to when pressing "back". Back button will be hidden, if null
+ * @return A
+ */
+ public CompletionStage showErrorWindow(Throwable cause, Stage window, @Nullable Scene previousScene) {
+ return CompletableFuture.supplyAsync(() -> errorWindowFactory.create(cause, window, previousScene).show(), Platform::runLater);
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/PrimaryStage.java b/src/main/java/org/cryptomator/ui/fxapp/PrimaryStage.java
new file mode 100644
index 000000000..e20b43525
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/fxapp/PrimaryStage.java
@@ -0,0 +1,14 @@
+package org.cryptomator.ui.fxapp;
+
+import javax.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface PrimaryStage {
+
+}
diff --git a/src/main/java/org/cryptomator/ui/health/CheckListController.java b/src/main/java/org/cryptomator/ui/health/CheckListController.java
index 75ecdef52..22ec37b48 100644
--- a/src/main/java/org/cryptomator/ui/health/CheckListController.java
+++ b/src/main/java/org/cryptomator/ui/health/CheckListController.java
@@ -1,9 +1,8 @@
package org.cryptomator.ui.health;
import com.google.common.base.Preconditions;
-import dagger.Lazy;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,9 +14,7 @@ import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
-import javafx.event.ActionEvent;
import javafx.fxml.FXML;
-import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.stage.Stage;
@@ -37,7 +34,7 @@ public class CheckListController implements FxController {
private final ObjectProperty selectedCheck;
private final BooleanBinding mainRunStarted; //TODO: rerunning not considered for now
private final BooleanBinding somethingsRunning;
- private final Lazy errorComponentBuilder;
+ private final FxApplicationWindows appWindows;
private final IntegerBinding chosenTaskCount;
private final BooleanBinding anyCheckSelected;
private final CheckListCellFactory listCellFactory;
@@ -46,7 +43,7 @@ public class CheckListController implements FxController {
public ListView checksListView;
@Inject
- public CheckListController(@HealthCheckWindow Stage window, List checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty selectedCheck, Lazy errorComponentBuilder, CheckListCellFactory listCellFactory) {
+ public CheckListController(@HealthCheckWindow Stage window, List checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty selectedCheck, FxApplicationWindows appWindows, CheckListCellFactory listCellFactory) {
this.window = window;
this.checks = FXCollections.observableList(checks, Check::observables);
this.checkExecutor = checkExecutor;
@@ -54,7 +51,7 @@ public class CheckListController implements FxController {
this.chosenChecks = this.checks.filtered(Check::isChosenForExecution);
this.reportWriter = reportWriteTask;
this.selectedCheck = selectedCheck;
- this.errorComponentBuilder = errorComponentBuilder;
+ this.appWindows = appWindows;
this.chosenTaskCount = Bindings.size(this.chosenChecks);
this.mainRunStarted = Bindings.isEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.RUNNABLE));
this.somethingsRunning = Bindings.isNotEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.SCHEDULED || c.getState() == Check.CheckState.RUNNING));
@@ -104,7 +101,7 @@ public class CheckListController implements FxController {
reportWriter.writeReport(chosenChecks);
} catch (IOException e) {
LOG.error("Failed to write health check report.", e);
- errorComponentBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}
}
diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java
index 44c3f3a8f..fa41a7fdc 100644
--- a/src/main/java/org/cryptomator/ui/health/StartController.java
+++ b/src/main/java/org/cryptomator/ui/health/StartController.java
@@ -7,10 +7,10 @@ import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.cryptolib.api.Masterkey;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.unlock.UnlockCancelledException;
import org.slf4j.Logger;
@@ -40,10 +40,10 @@ public class StartController implements FxController {
private final AtomicReference masterkeyRef;
private final AtomicReference vaultConfigRef;
private final Lazy checkScene;
- private final Lazy errorComponent;
+ private final FxApplicationWindows appWindows;
@Inject
- public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent, @Named("unlockWindow") Stage unlockWindow) {
+ public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, FxApplicationWindows appWindows, @Named("unlockWindow") Stage unlockWindow) {
this.window = window;
this.unlockWindow = unlockWindow;
this.vaultConfig = vault.getVaultConfigCache();
@@ -52,7 +52,7 @@ public class StartController implements FxController {
this.masterkeyRef = masterkeyRef;
this.vaultConfigRef = vaultConfigRef;
this.checkScene = checkScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
}
@FXML
@@ -106,10 +106,10 @@ public class StartController implements FxController {
// ok
} else if (e instanceof VaultKeyInvalidException) {
LOG.error("Invalid key"); //TODO: specific error screen
- errorComponent.get().window(window).cause(e).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, null);
} else {
LOG.error("Failed to load key.", e);
- errorComponent.get().window(window).cause(e).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, null);
}
}
diff --git a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEvent.java b/src/main/java/org/cryptomator/ui/launcher/AppLaunchEvent.java
deleted file mode 100644
index 710c6d435..000000000
--- a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.cryptomator.ui.launcher;
-
-import java.nio.file.Path;
-import java.util.Collection;
-
-public class AppLaunchEvent {
-
- private final EventType type;
- private final Collection pathsToOpen;
-
- public enum EventType {
- REVEAL_APP,
- OPEN_FILE
- }
-
- public AppLaunchEvent(EventType type, Collection pathsToOpen) {
- this.type = type;
- this.pathsToOpen = pathsToOpen;
- }
-
- public EventType getType() {
- return type;
- }
-
- public Collection getPathsToOpen() {
- return pathsToOpen;
- }
-}
diff --git a/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java b/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java
deleted file mode 100644
index 66b75840b..000000000
--- a/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.cryptomator.ui.launcher;
-
-import org.cryptomator.common.ShutdownHook;
-import org.cryptomator.common.vaults.LockNotCompletedException;
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.common.vaults.VaultState;
-import org.cryptomator.common.vaults.Volume;
-import org.cryptomator.ui.preferences.SelectedPreferencesTab;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import javafx.application.Platform;
-import javafx.beans.Observable;
-import javafx.collections.ObservableList;
-import java.awt.Desktop;
-import java.awt.EventQueue;
-import java.awt.desktop.AboutEvent;
-import java.awt.desktop.QuitResponse;
-import java.awt.desktop.QuitStrategy;
-import java.util.EnumSet;
-import java.util.EventObject;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.cryptomator.common.vaults.VaultState.Value.*;
-
-@Singleton
-public class AppLifecycleListener {
-
- private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
- public static final Set STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
-
- private final FxApplicationStarter fxApplicationStarter;
- private final CountDownLatch shutdownLatch;
- private final ObservableList vaults;
- private final AtomicBoolean allowQuitWithoutPrompt;
-
- @Inject
- AppLifecycleListener(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList vaults) {
- this.fxApplicationStarter = fxApplicationStarter;
- this.shutdownLatch = shutdownLatch;
- this.vaults = vaults;
- this.allowQuitWithoutPrompt = new AtomicBoolean(true);
- vaults.addListener(this::vaultListChanged);
-
- // register preferences shortcut
- if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
- Desktop.getDesktop().setPreferencesHandler(this::showPreferencesWindow);
- }
-
- // register preferences shortcut
- if (Desktop.getDesktop().isSupported(Desktop.Action.APP_ABOUT)) {
- Desktop.getDesktop().setAboutHandler(this::showAboutWindow);
- }
-
- // register quit handler
- if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
- Desktop.getDesktop().setQuitHandler(this::handleQuitRequest);
- }
-
- // set quit strategy (cmd+q would call `System.exit(0)` otherwise)
- if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
- Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
- }
-
- shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
- }
-
- /**
- * Gracefully terminates the application.
- */
- public void quit() {
- handleQuitRequest(null, new QuitResponse() {
- @Override
- public void performQuit() {
- // no-op
- }
-
- @Override
- public void cancelQuit() {
- // no-op
- }
- });
- }
-
- private void handleQuitRequest(@SuppressWarnings("unused") EventObject e, QuitResponse response) {
- QuitResponse decoratedQuitResponse = decorateQuitResponse(response);
- if (allowQuitWithoutPrompt.get()) {
- decoratedQuitResponse.performQuit();
- } else {
- fxApplicationStarter.get().thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
- }
- }
-
- private QuitResponse decorateQuitResponse(QuitResponse originalQuitResponse) {
- return new QuitResponse() {
- @Override
- public void performQuit() {
- Platform.exit(); // will be no-op, if JavaFX never started.
- shutdownLatch.countDown(); // main thread is waiting for this latch
- originalQuitResponse.performQuit();
- }
-
- @Override
- public void cancelQuit() {
- originalQuitResponse.cancelQuit();
- }
- };
- }
-
- private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
- assert Platform.isFxApplicationThread();
- boolean allVaultsAllowTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
- boolean suddenTerminationChanged = allowQuitWithoutPrompt.compareAndSet(!allVaultsAllowTermination, allVaultsAllowTermination);
- if (suddenTerminationChanged) {
- LOG.debug("Allow quitting without prompt: {}", allVaultsAllowTermination);
- }
- }
-
- private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
- fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
- }
-
- private void showAboutWindow(@SuppressWarnings("unused") AboutEvent aboutEvent) {
- fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT));
- }
-
- private void forceUnmountRemainingVaults() {
- for (Vault vault : vaults) {
- if (vault.isUnlocked()) {
- try {
- vault.lock(true);
- } catch (Volume.VolumeException e) {
- LOG.error("Failed to unmount vault " + vault.getPath(), e);
- } catch (LockNotCompletedException e) {
- LOG.error("Failed to lock vault " + vault.getPath(), e);
- }
- }
- }
- }
-
-}
diff --git a/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java b/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java
deleted file mode 100644
index 1799a9700..000000000
--- a/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.cryptomator.ui.launcher;
-
-import dagger.Lazy;
-import org.cryptomator.ui.fxapp.FxApplication;
-import org.cryptomator.ui.fxapp.FxApplicationComponent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javafx.application.Platform;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@Singleton
-public class FxApplicationStarter {
-
- private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStarter.class);
-
- private final Lazy fxAppComponent;
- private final ExecutorService executor;
- private final AtomicBoolean started;
- private final CompletableFuture future;
-
- @Inject
- public FxApplicationStarter(Lazy fxAppComponent, ExecutorService executor) {
- this.fxAppComponent = fxAppComponent;
- this.executor = executor;
- this.started = new AtomicBoolean();
- this.future = new CompletableFuture<>();
- }
-
- public CompletionStage get() {
- if (!started.getAndSet(true)) {
- start();
- }
- return future;
- }
-
- private void start() {
- executor.submit(() -> {
- LOG.debug("Starting JavaFX runtime...");
- Platform.startup(() -> {
- assert Platform.isFxApplicationThread();
- LOG.info("JavaFX Runtime started.");
- FxApplication app = fxAppComponent.get().application();
- app.start();
- future.complete(app);
- });
- });
- }
-}
diff --git a/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java b/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java
deleted file mode 100644
index 08461a56d..000000000
--- a/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.cryptomator.ui.launcher;
-
-import dagger.Lazy;
-import org.cryptomator.common.settings.Settings;
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.integrations.tray.TrayIntegrationProvider;
-import org.cryptomator.ui.fxapp.FxApplication;
-import org.cryptomator.ui.traymenu.TrayMenuComponent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javafx.collections.ObservableList;
-import java.awt.Desktop;
-import java.awt.SystemTray;
-import java.awt.desktop.AppReopenedListener;
-import java.util.Collection;
-import java.util.Optional;
-
-@Singleton
-public class UiLauncher {
-
- private static final Logger LOG = LoggerFactory.getLogger(UiLauncher.class);
-
- private final Settings settings;
- private final ObservableList vaults;
- private final Lazy trayMenu;
- private final FxApplicationStarter fxApplicationStarter;
- private final AppLaunchEventHandler launchEventHandler;
- private final Optional trayIntegration;
-
- @Inject
- public UiLauncher(Settings settings, ObservableList vaults, Lazy trayMenu, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional trayIntegration) {
- this.settings = settings;
- this.vaults = vaults;
- this.trayMenu = trayMenu;
- this.fxApplicationStarter = fxApplicationStarter;
- this.launchEventHandler = launchEventHandler;
- this.trayIntegration = trayIntegration;
- }
-
- public void launch() {
- boolean hidden = settings.startHidden().get();
- if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
- trayMenu.get().initializeTrayIcon();
- launch(true, hidden);
- } else {
- launch(false, hidden);
- }
- }
-
- private void launch(boolean withTrayIcon, boolean hidden) {
- // start hidden, minimized or normal?
- if (withTrayIcon && hidden) {
- LOG.debug("Hiding application...");
- trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
- } else if (!withTrayIcon && hidden) {
- LOG.debug("Minimizing application...");
- showMainWindowAsync(true);
- } else {
- LOG.debug("Showing application...");
- showMainWindowAsync(false);
- }
-
- // register app reopen listener
- Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(false));
-
- // auto unlock
- Collection vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock);
- if (!vaultsToAutoUnlock.isEmpty()) {
- fxApplicationStarter.get().thenAccept(app -> {
- for (Vault vault : vaultsToAutoUnlock) {
- app.startUnlockWorkflow(vault, Optional.empty());
- }
- });
- }
-
- launchEventHandler.startHandlingLaunchEvents();
- }
-
- private boolean shouldAttemptAutoUnlock(Vault vault) {
- return vault.isLocked() && vault.getVaultSettings().unlockAfterStartup().get();
- }
-
- private void showMainWindowAsync(boolean minimize) {
- fxApplicationStarter.get().thenCompose(FxApplication::showMainWindow).thenAccept(win -> win.setIconified(minimize));
- }
-
-}
diff --git a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
deleted file mode 100644
index c30efa30e..000000000
--- a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.cryptomator.ui.launcher;
-
-import dagger.Module;
-import dagger.Provides;
-import org.cryptomator.common.PluginClassLoader;
-import org.cryptomator.integrations.autostart.AutoStartProvider;
-import org.cryptomator.integrations.tray.TrayIntegrationProvider;
-import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
-import org.cryptomator.ui.fxapp.FxApplicationComponent;
-import org.cryptomator.ui.traymenu.TrayMenuComponent;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
-import java.util.Optional;
-import java.util.ResourceBundle;
-import java.util.ServiceLoader;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-
-@Module(subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class})
-public abstract class UiLauncherModule {
-
- @Provides
- @Singleton
- static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
- return builder.build();
- }
-
- @Provides
- @Singleton
- static FxApplicationComponent provideFxApplicationComponent(FxApplicationComponent.Builder builder) {
- return builder.build();
- }
-
- @Provides
- @Singleton
- static Optional provideAppearanceProvider(PluginClassLoader classLoader) {
- return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
- }
-
- @Provides
- @Singleton
- static Optional provideAutostartProvider(PluginClassLoader classLoader) {
- return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
- }
-
-
- @Provides
- @Singleton
- static Optional provideTrayIntegrationProvider(PluginClassLoader classLoader) {
- return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
- }
-
- @Provides
- @Singleton
- static ResourceBundle provideLocalization() {
- return ResourceBundle.getBundle("i18n.strings");
- }
-
- @Provides
- @Singleton
- @Named("launchEventQueue")
- static BlockingQueue provideFileOpenRequests() {
- return new ArrayBlockingQueue<>(10);
- }
-
-}
diff --git a/src/main/java/org/cryptomator/ui/lock/LockComponent.java b/src/main/java/org/cryptomator/ui/lock/LockComponent.java
index 9796c88c7..eda81f7f6 100644
--- a/src/main/java/org/cryptomator/ui/lock/LockComponent.java
+++ b/src/main/java/org/cryptomator/ui/lock/LockComponent.java
@@ -2,11 +2,11 @@ package org.cryptomator.ui.lock;
import dagger.BindsInstance;
import dagger.Subcomponent;
+import org.cryptomator.common.Nullable;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Named;
import javafx.stage.Stage;
-import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -25,15 +25,9 @@ public interface LockComponent {
return workflow;
}
- @Subcomponent.Builder
- interface Builder {
-
- @BindsInstance
- LockComponent.Builder vault(@LockWindow Vault vault);
-
- @BindsInstance
- LockComponent.Builder owner(@Named("lockWindowOwner") Optional owner);
-
- LockComponent build();
+ @Subcomponent.Factory
+ interface Factory {
+ LockComponent create(@BindsInstance @LockWindow Vault vault, @BindsInstance @Named("lockWindowOwner") @Nullable Stage owner);
}
+
}
diff --git a/src/main/java/org/cryptomator/ui/lock/LockModule.java b/src/main/java/org/cryptomator/ui/lock/LockModule.java
index ddee13dff..c5657a488 100644
--- a/src/main/java/org/cryptomator/ui/lock/LockModule.java
+++ b/src/main/java/org/cryptomator/ui/lock/LockModule.java
@@ -12,6 +12,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
+import org.jetbrains.annotations.Nullable;
import javax.inject.Named;
import javax.inject.Provider;
@@ -19,7 +20,6 @@ import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
-import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
@@ -43,12 +43,12 @@ abstract class LockModule {
@Provides
@LockWindow
@LockScoped
- static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional owner) {
+ static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Nullable @Named("lockWindowOwner") Stage owner) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(false);
- if (owner.isPresent()) {
- stage.initOwner(owner.get());
+ if (owner != null) {
+ stage.initOwner(owner);
stage.initModality(Modality.WINDOW_MODAL);
} else {
stage.initModality(Modality.APPLICATION_MODAL);
diff --git a/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java b/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
index 1e05ceb73..2d4961dde 100644
--- a/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
+++ b/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
@@ -5,9 +5,9 @@ import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,16 +40,16 @@ public class LockWorkflow extends Task {
private final AtomicReference> forceRetryDecision;
private final Lazy lockForcedScene;
private final Lazy lockFailedScene;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
@Inject
- public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy lockFailedScene, ErrorComponent.Builder errorComponent) {
+ public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy lockFailedScene, FxApplicationWindows appWindows) {
this.lockWindow = lockWindow;
this.vault = vault;
this.forceRetryDecision = forceRetryDecision;
this.lockForcedScene = lockForcedScene;
this.lockFailedScene = lockFailedScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
}
@Override
@@ -109,7 +109,7 @@ public class LockWorkflow extends Task {
lockWindow.setScene(lockFailedScene.get());
lockWindow.show();
} else {
- errorComponent.cause(throwable).window(lockWindow).build().showErrorScene();
+ appWindows.showErrorWindow(throwable, lockWindow, null);
}
}
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindow.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindow.java
index 51143e1f6..22f3616ea 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindow.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindow.java
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
-public @interface MainWindow {
+@interface MainWindow {
}
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
index 6f63db888..94acba3cc 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
@@ -13,6 +13,8 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
+import org.cryptomator.ui.common.StageInitializer;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
@@ -34,6 +36,18 @@ import java.util.ResourceBundle;
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
abstract class MainWindowModule {
+ @Provides
+ @MainWindow
+ @MainWindowScoped
+ static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
+ initializer.accept(stage);
+ stage.setTitle("Cryptomator");
+ stage.initStyle(StageStyle.UNDECORATED);
+ stage.setMinWidth(650);
+ stage.setMinHeight(440);
+ return stage;
+ }
+
@Provides
@MainWindowScoped
static ObjectProperty provideSelectedVault() {
@@ -47,22 +61,11 @@ abstract class MainWindowModule {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
- @Provides
- @MainWindow
- @MainWindowScoped
- static Stage provideStage(StageFactory factory) {
- Stage stage = factory.create(StageStyle.UNDECORATED);
- stage.setMinWidth(650);
- stage.setMinHeight(440);
- stage.setTitle("Cryptomator");
- return stage;
- }
-
@Provides
@MainWindowScoped
@Named("errorWindow")
static Stage provideErrorStage(@MainWindow Stage window, StageFactory factory, ResourceBundle resourceBundle) {
- Stage stage = factory.create(StageStyle.DECORATED);
+ Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("main.vaultDetail.error.windowTitle"));
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(window);
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
index c8107415e..76eee0cb4 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
@@ -3,9 +3,9 @@ package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
+import org.cryptomator.ui.fxapp.FxApplicationTerminator;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.fxapp.UpdateChecker;
-import org.cryptomator.ui.launcher.AppLifecycleListener;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
@@ -25,9 +25,9 @@ public class MainWindowTitleController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class);
- private final AppLifecycleListener appLifecycle;
private final Stage window;
- private final FxApplication application;
+ private final FxApplicationTerminator terminator;
+ private final FxApplicationWindows appWindows;
private final boolean trayMenuInitialized;
private final UpdateChecker updateChecker;
private final BooleanBinding updateAvailable;
@@ -40,10 +40,10 @@ public class MainWindowTitleController implements FxController {
private double yOffset;
@Inject
- MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
- this.appLifecycle = appLifecycle;
+ MainWindowTitleController(@MainWindow Stage window, FxApplicationTerminator terminator, FxApplicationWindows appWindows, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
this.window = window;
- this.application = application;
+ this.terminator = terminator;
+ this.appWindows = appWindows;
this.trayMenuInitialized = trayMenu.isInitialized();
this.updateChecker = updateChecker;
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
@@ -96,7 +96,7 @@ public class MainWindowTitleController implements FxController {
if (trayMenuInitialized) {
window.close();
} else {
- appLifecycle.quit();
+ terminator.terminate();
}
}
@@ -107,17 +107,17 @@ public class MainWindowTitleController implements FxController {
@FXML
public void showPreferences() {
- application.showPreferencesWindow(SelectedPreferencesTab.ANY);
+ appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
}
@FXML
public void showGeneralPreferences() {
- application.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
+ appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
}
@FXML
public void showDonationKeyPreferences() {
- application.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
+ appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
}
/* Getter/Setter */
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
index 87a419a94..b38710023 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
@@ -1,7 +1,6 @@
package org.cryptomator.ui.mainwindow;
import com.tobiasdiez.easybind.EasyBind;
-import com.tobiasdiez.easybind.Subscription;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.Animations;
@@ -9,9 +8,9 @@ import org.cryptomator.ui.common.AutoAnimator;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
-import org.cryptomator.ui.fxapp.FxApplication;
import javax.inject.Inject;
+import javafx.application.Application;
import javafx.beans.binding.Binding;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
@@ -22,7 +21,7 @@ import javafx.fxml.FXML;
public class VaultDetailController implements FxController {
private final ReadOnlyObjectProperty vault;
- private final FxApplication application;
+ private final Application application;
private final Binding glyph;
private final BooleanBinding anyVaultSelected;
@@ -33,7 +32,7 @@ public class VaultDetailController implements FxController {
@Inject
- VaultDetailController(ObjectProperty vault, FxApplication application) {
+ VaultDetailController(ObjectProperty vault, Application application) {
this.vault = vault;
this.application = application;
this.glyph = EasyBind.select(vault) //
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java
index 5fee2e6d1..dab1f7a54 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java
@@ -4,8 +4,7 @@ import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
-import org.cryptomator.ui.health.HealthCheckComponent;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
@@ -14,25 +13,23 @@ import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
-import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.util.Optional;
@MainWindowScoped
public class VaultDetailLockedController implements FxController {
private final ReadOnlyObjectProperty vault;
- private final FxApplication application;
+ private final FxApplicationWindows appWindows;
private final VaultOptionsComponent.Builder vaultOptionsWindow;
private final KeychainManager keychain;
private final Stage mainWindow;
private final BooleanExpression passwordSaved;
@Inject
- VaultDetailLockedController(ObjectProperty vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
+ VaultDetailLockedController(ObjectProperty vault, FxApplicationWindows appWindows, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
this.vault = vault;
- this.application = application;
+ this.appWindows = appWindows;
this.vaultOptionsWindow = vaultOptionsWindow;
this.keychain = keychain;
this.mainWindow = mainWindow;
@@ -45,7 +42,7 @@ public class VaultDetailLockedController implements FxController {
@FXML
public void unlock() {
- application.startUnlockWorkflow(vault.get(), Optional.of(mainWindow));
+ appWindows.startUnlockWorkflow(vault.get(), mainWindow);
}
@FXML
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java
index 22365da7c..6e40d54b3 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java
@@ -1,15 +1,13 @@
package org.cryptomator.ui.mainwindow;
-import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import javax.inject.Inject;
import javax.inject.Named;
-import javafx.beans.binding.Binding;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@@ -18,21 +16,21 @@ import javafx.stage.Stage;
public class VaultDetailUnknownErrorController implements FxController {
private final ObjectProperty vault;
- private final ErrorComponent.Builder errorComponentBuilder;
+ private final FxApplicationWindows appWindows;
private final Stage errorWindow;
private final RemoveVaultComponent.Builder removeVault;
@Inject
- public VaultDetailUnknownErrorController(ObjectProperty vault, ErrorComponent.Builder errorComponentBuilder, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
+ public VaultDetailUnknownErrorController(ObjectProperty vault, FxApplicationWindows appWindows, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
this.vault = vault;
- this.errorComponentBuilder = errorComponentBuilder;
+ this.appWindows = appWindows;
this.errorWindow = errorWindow;
this.removeVault = removeVault;
}
@FXML
public void showError() {
- errorComponentBuilder.window(errorWindow).cause(vault.get().getLastKnownException()).build().showErrorScene();
+ appWindows.showErrorWindow(vault.get().getLastKnownException(), errorWindow, null);
}
@FXML
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java
index 0af909bbc..63d88a6a4 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java
@@ -6,7 +6,7 @@ import com.google.common.cache.LoadingCache;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
-import org.cryptomator.ui.fxapp.FxApplication;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import javax.inject.Inject;
@@ -14,22 +14,21 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.util.Optional;
@MainWindowScoped
public class VaultDetailUnlockedController implements FxController {
private final ReadOnlyObjectProperty vault;
- private final FxApplication application;
+ private final FxApplicationWindows appWindows;
private final VaultService vaultService;
private final Stage mainWindow;
private final LoadingCache vaultStats;
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
@Inject
- public VaultDetailUnlockedController(ObjectProperty vault, FxApplication application, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, @MainWindow Stage mainWindow) {
+ public VaultDetailUnlockedController(ObjectProperty vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, @MainWindow Stage mainWindow) {
this.vault = vault;
- this.application = application;
+ this.appWindows = appWindows;
this.vaultService = vaultService;
this.mainWindow = mainWindow;
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
@@ -47,7 +46,7 @@ public class VaultDetailUnlockedController implements FxController {
@FXML
public void lock() {
- application.startLockWorkflow(vault.get(), Optional.of(mainWindow));
+ appWindows.startLockWorkflow(vault.get(), mainWindow);
}
@FXML
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
index 145618fa8..c9d788b90 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
@@ -7,7 +7,8 @@ import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
+import org.cryptomator.ui.common.VaultService;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
@@ -18,7 +19,6 @@ import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.EnumSet;
-import java.util.Optional;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@@ -27,7 +27,8 @@ public class VaultListContextMenuController implements FxController {
private final ObservableOptionalValue selectedVault;
private final Stage mainWindow;
- private final FxApplication application;
+ private final FxApplicationWindows appWindows;
+ private final VaultService vaultService;
private final KeychainManager keychain;
private final RemoveVaultComponent.Builder removeVault;
private final VaultOptionsComponent.Builder vaultOptionsWindow;
@@ -38,10 +39,11 @@ public class VaultListContextMenuController implements FxController {
private final Binding selectedVaultLockable;
@Inject
- VaultListContextMenuController(ObjectProperty selectedVault, @MainWindow Stage mainWindow, FxApplication application, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
+ VaultListContextMenuController(ObjectProperty selectedVault, @MainWindow Stage mainWindow, FxApplicationWindows appWindows, VaultService vaultService, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
this.selectedVault = EasyBind.wrapNullable(selectedVault);
this.mainWindow = mainWindow;
- this.application = application;
+ this.appWindows = appWindows;
+ this.vaultService = vaultService;
this.keychain = keychain;
this.removeVault = removeVault;
this.vaultOptionsWindow = vaultOptionsWindow;
@@ -74,22 +76,20 @@ public class VaultListContextMenuController implements FxController {
@FXML
public void didClickUnlockVault() {
selectedVault.ifValuePresent(v -> {
- application.startUnlockWorkflow(v, Optional.of(mainWindow));
+ appWindows.startUnlockWorkflow(v, mainWindow);
});
}
@FXML
public void didClickLockVault() {
selectedVault.ifValuePresent(v -> {
- application.startLockWorkflow(v, Optional.of(mainWindow));
+ appWindows.startLockWorkflow(v, mainWindow);
});
}
@FXML
public void didClickRevealVault() {
- selectedVault.ifValuePresent(v -> {
- application.getVaultService().reveal(v);
- });
+ selectedVault.ifValuePresent(vaultService::reveal);
}
// Getter and Setter
diff --git a/src/main/java/org/cryptomator/ui/migration/MigrationImpossibleController.java b/src/main/java/org/cryptomator/ui/migration/MigrationImpossibleController.java
index 191fc7a8f..4799d0325 100644
--- a/src/main/java/org/cryptomator/ui/migration/MigrationImpossibleController.java
+++ b/src/main/java/org/cryptomator/ui/migration/MigrationImpossibleController.java
@@ -2,9 +2,9 @@ package org.cryptomator.ui.migration;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
import javax.inject.Inject;
+import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@@ -12,13 +12,13 @@ public class MigrationImpossibleController implements FxController {
private static final String HELP_URI = "https://docs.cryptomator.org/en/1.5/help/manual-migration/";
- private final FxApplication fxApplication;
+ private final Application application;
private final Stage window;
private final Vault vault;
@Inject
- MigrationImpossibleController(FxApplication fxApplication, @MigrationWindow Stage window, @MigrationWindow Vault vault) {
- this.fxApplication = fxApplication;
+ MigrationImpossibleController(Application application, @MigrationWindow Stage window, @MigrationWindow Vault vault) {
+ this.application = application;
this.window = window;
this.vault = vault;
}
@@ -30,7 +30,7 @@ public class MigrationImpossibleController implements FxController {
@FXML
public void getMigrationHelp() {
- fxApplication.getHostServices().showDocument(HELP_URI);
+ application.getHostServices().showDocument(HELP_URI);
}
/* Getter/Setters */
diff --git a/src/main/java/org/cryptomator/ui/migration/MigrationModule.java b/src/main/java/org/cryptomator/ui/migration/MigrationModule.java
index 44f6960b1..76f6ac161 100644
--- a/src/main/java/org/cryptomator/ui/migration/MigrationModule.java
+++ b/src/main/java/org/cryptomator/ui/migration/MigrationModule.java
@@ -6,13 +6,13 @@ import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import javax.inject.Named;
import javax.inject.Provider;
@@ -37,7 +37,7 @@ abstract class MigrationModule {
@Provides
@MigrationWindow
@MigrationScoped
- static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
+ static Stage provideStage(StageFactory factory, @PrimaryStage Stage owner, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("migration.title"));
stage.setResizable(false);
diff --git a/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
index 503814b25..c68456523 100644
--- a/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
+++ b/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
@@ -12,12 +12,12 @@ import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.Tasks;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,7 +58,7 @@ public class MigrationRunController implements FxController {
private final ScheduledExecutorService scheduler;
private final KeychainManager keychain;
private final ObjectProperty missingCapability;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
private final Lazy startScene;
private final Lazy successScene;
private final Lazy impossibleScene;
@@ -73,14 +73,14 @@ public class MigrationRunController implements FxController {
public NiceSecurePasswordField passwordField;
@Inject
- public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, ErrorComponent.Builder errorComponent) {
+ public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, FxApplicationWindows appWindows) {
this.window = window;
this.vault = vault;
this.executor = executor;
this.scheduler = scheduler;
this.keychain = keychain;
this.missingCapability = missingCapability;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
this.startScene = startScene;
this.successScene = successScene;
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
@@ -146,12 +146,12 @@ public class MigrationRunController implements FxController {
}).onError(FileNameTooLongException.class, e -> {
LOG.error("Migration failed because the underlying file system does not support long filenames.", e);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
- errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, startScene.get());
window.setScene(impossibleScene.get());
}).onError(Exception.class, e -> { // including RuntimeExceptions
LOG.error("Migration failed for technical reasons.", e);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
- errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, startScene.get());
}).andFinally(() -> {
passwordField.setDisable(false);
progressSyncTask.cancel(true);
diff --git a/src/main/java/org/cryptomator/ui/migration/MigrationSuccessController.java b/src/main/java/org/cryptomator/ui/migration/MigrationSuccessController.java
index 1a5de5647..fab680eac 100644
--- a/src/main/java/org/cryptomator/ui/migration/MigrationSuccessController.java
+++ b/src/main/java/org/cryptomator/ui/migration/MigrationSuccessController.java
@@ -2,25 +2,24 @@ package org.cryptomator.ui.migration;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.fxapp.FxApplication;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.util.Optional;
@MigrationScoped
public class MigrationSuccessController implements FxController {
- private final FxApplication fxApplication;
+ private final FxApplicationWindows appWindows;
private final Stage window;
private final Vault vault;
private final Stage mainWindow;
@Inject
- MigrationSuccessController(FxApplication fxApplication, @MigrationWindow Stage window, @MigrationWindow Vault vault, @MainWindow Stage mainWindow) {
- this.fxApplication = fxApplication;
+ MigrationSuccessController(FxApplicationWindows appWindows, @MigrationWindow Stage window, @MigrationWindow Vault vault, @PrimaryStage Stage mainWindow) {
+ this.appWindows = appWindows;
this.window = window;
this.vault = vault;
this.mainWindow = mainWindow;
@@ -29,7 +28,7 @@ public class MigrationSuccessController implements FxController {
@FXML
public void unlockAndClose() {
close();
- fxApplication.startUnlockWorkflow(vault, Optional.of(mainWindow));
+ appWindows.startUnlockWorkflow(vault, mainWindow);
}
@FXML
diff --git a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
index fafea3f2f..7430cf207 100644
--- a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
+++ b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
@@ -7,8 +7,8 @@ import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,7 +47,7 @@ public class GeneralPreferencesController implements FxController {
private final Application application;
private final Environment environment;
private final Set keychainAccessProviders;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
public ChoiceBox themeChoiceBox;
public ChoiceBox keychainBackendChoiceBox;
public CheckBox showMinimizeButtonCheckbox;
@@ -59,9 +59,8 @@ public class GeneralPreferencesController implements FxController {
public RadioButton nodeOrientationLtr;
public RadioButton nodeOrientationRtl;
-
@Inject
- GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional autoStartProvider, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
+ GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional autoStartProvider, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, FxApplicationWindows appWindows) {
this.window = window;
this.settings = settings;
this.trayMenuInitialized = trayMenu.isInitialized();
@@ -73,7 +72,7 @@ public class GeneralPreferencesController implements FxController {
this.resourceBundle = resourceBundle;
this.application = application;
this.environment = environment;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
}
@FXML
@@ -142,7 +141,7 @@ public class GeneralPreferencesController implements FxController {
} catch (ToggleAutoStartFailedException e) {
autoStartCheckbox.setSelected(!enableAutoStart); // restore previous state
LOG.error("Failed to toggle autostart.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, window.getScene());
}
});
}
diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java
index 104794604..87dcbd0e8 100644
--- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java
+++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java
@@ -5,11 +5,11 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.ui.common.Animations;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,18 +33,18 @@ public class RecoveryKeyCreationController implements FxController {
private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty recoveryKeyProperty;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
public NiceSecurePasswordField passwordField;
@Inject
- public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, ErrorComponent.Builder errorComponent) {
+ public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, FxApplicationWindows appWindows) {
this.window = window;
this.successScene = successScene;
this.vault = vault;
this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory;
this.recoveryKeyProperty = recoveryKey;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
}
@FXML
@@ -63,7 +63,7 @@ public class RecoveryKeyCreationController implements FxController {
Animations.createShakeWindowAnimation(window).play();
} else {
LOG.error("Creation of recovery key failed.", task.getException());
- errorComponent.cause(task.getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ appWindows.showErrorWindow(task.getException(), window, window.getScene());
}
});
executor.submit(task);
diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java
index 705991287..ca3c4e041 100644
--- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java
+++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java
@@ -2,11 +2,11 @@ package org.cryptomator.ui.recoverykey;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,19 +31,19 @@ public class RecoveryKeyResetPasswordController implements FxController {
private final ExecutorService executor;
private final StringProperty recoveryKey;
private final Lazy recoverScene;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
public NewPasswordController newPasswordController;
@Inject
- public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy recoverScene, ErrorComponent.Builder errorComponent) {
+ public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy recoverScene, FxApplicationWindows appWindows) {
this.window = window;
this.vault = vault;
this.recoveryKeyFactory = recoveryKeyFactory;
this.executor = executor;
this.recoveryKey = recoveryKey;
this.recoverScene = recoverScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
}
@FXML
@@ -64,7 +64,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
});
task.setOnFailed(event -> {
LOG.error("Resetting password failed.", task.getException());
- errorComponent.cause(task.getException()).window(window).returnToScene(recoverScene.get()).build().showErrorScene();
+ appWindows.showErrorWindow(task.getException(), window, recoverScene.get());
});
executor.submit(task);
}
diff --git a/src/main/java/org/cryptomator/ui/removevault/RemoveVaultModule.java b/src/main/java/org/cryptomator/ui/removevault/RemoveVaultModule.java
index 5915766ed..4288e9c7c 100644
--- a/src/main/java/org/cryptomator/ui/removevault/RemoveVaultModule.java
+++ b/src/main/java/org/cryptomator/ui/removevault/RemoveVaultModule.java
@@ -5,13 +5,13 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import javax.inject.Provider;
import javafx.scene.Scene;
@@ -33,12 +33,12 @@ abstract class RemoveVaultModule {
@Provides
@RemoveVaultWindow
@RemoveVaultScoped
- static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
+ static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("removeVault.title"));
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
- stage.initOwner(owner);
+ stage.initOwner(primaryStage);
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
index dd08d5dc0..32f6cbc52 100644
--- a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
+++ b/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
@@ -1,9 +1,9 @@
package org.cryptomator.ui.traymenu;
import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.ui.fxapp.FxApplication;
-import org.cryptomator.ui.launcher.AppLifecycleListener;
-import org.cryptomator.ui.launcher.FxApplicationStarter;
+import org.cryptomator.ui.common.VaultService;
+import org.cryptomator.ui.fxapp.FxApplicationTerminator;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import javax.inject.Inject;
@@ -16,7 +16,6 @@ import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
-import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Consumer;
@@ -24,16 +23,18 @@ import java.util.function.Consumer;
class TrayMenuController {
private final ResourceBundle resourceBundle;
- private final AppLifecycleListener appLifecycle;
- private final FxApplicationStarter fxApplicationStarter;
+ private final VaultService vaultService;
+ private final FxApplicationWindows appWindows;
+ private final FxApplicationTerminator appTerminator;
private final ObservableList vaults;
private final PopupMenu menu;
@Inject
- TrayMenuController(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList vaults) {
+ TrayMenuController(ResourceBundle resourceBundle, VaultService vaultService, FxApplicationWindows appWindows, FxApplicationTerminator appTerminator, ObservableList vaults) {
this.resourceBundle = resourceBundle;
- this.appLifecycle = appLifecycle;
- this.fxApplicationStarter = fxApplicationStarter;
+ this.vaultService = vaultService;
+ this.appWindows = appWindows;
+ this.appTerminator = appTerminator;
this.vaults = vaults;
this.menu = new PopupMenu();
}
@@ -110,35 +111,31 @@ class TrayMenuController {
}
private void quitApplication(EventObject actionEvent) {
- appLifecycle.quit();
+ appTerminator.terminate();
}
private void unlockVault(Vault vault) {
- showMainAppAndThen(app -> app.startUnlockWorkflow(vault, Optional.empty()));
+ appWindows.startUnlockWorkflow(vault, null);
}
private void lockVault(Vault vault) {
- showMainAppAndThen(app -> app.startLockWorkflow(vault, Optional.empty()));
+ appWindows.startLockWorkflow(vault, null);
}
private void lockAllVaults(ActionEvent actionEvent) {
- showMainAppAndThen(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false));
+ vaultService.lockAll(vaults.filtered(Vault::isUnlocked), false);
}
private void revealVault(Vault vault) {
- showMainAppAndThen(app -> app.getVaultService().reveal(vault));
+ vaultService.reveal(vault);
}
void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) {
- showMainAppAndThen(app -> app.showMainWindow());
+ appWindows.showMainWindow();
}
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
- showMainAppAndThen(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
- }
-
- private void showMainAppAndThen(Consumer action) {
- fxApplicationStarter.get().thenAccept(action);
+ appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
}
}
diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java b/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java
index 9c0338c5b..67e905200 100644
--- a/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java
+++ b/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java
@@ -7,11 +7,11 @@ package org.cryptomator.ui.unlock;
import dagger.BindsInstance;
import dagger.Subcomponent;
+import org.cryptomator.common.Nullable;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Named;
import javafx.stage.Stage;
-import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -29,16 +29,9 @@ public interface UnlockComponent {
return workflow;
}
- @Subcomponent.Builder
- interface Builder {
-
- @BindsInstance
- Builder vault(@UnlockWindow Vault vault);
-
- @BindsInstance
- Builder owner(@Named("unlockWindowOwner") Optional owner);
-
- UnlockComponent build();
+ @Subcomponent.Factory
+ interface Factory {
+ UnlockComponent create(@BindsInstance @UnlockWindow Vault vault, @BindsInstance @Named("unlockWindowOwner") @Nullable Stage owner);
}
}
diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java
index 3c1267e65..b14286698 100644
--- a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java
+++ b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java
@@ -14,6 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
+import org.jetbrains.annotations.Nullable;
import javax.inject.Named;
import javax.inject.Provider;
@@ -21,7 +22,6 @@ import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
-import java.util.Optional;
import java.util.ResourceBundle;
@Module(subcomponents = {KeyLoadingComponent.class})
@@ -37,12 +37,12 @@ abstract class UnlockModule {
@Provides
@UnlockWindow
@UnlockScoped
- static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Named("unlockWindowOwner") Optional owner) {
+ static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Nullable @Named("unlockWindowOwner") Stage owner) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(false);
- if (owner.isPresent()) {
- stage.initOwner(owner.get());
+ if (owner != null) {
+ stage.initOwner(owner);
stage.initModality(Modality.WINDOW_MODAL);
} else {
stage.initModality(Modality.APPLICATION_MODAL);
diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
index 6964c3c86..45b9bf000 100644
--- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
+++ b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
@@ -9,10 +9,10 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume.VolumeException;
import org.cryptomator.cryptolib.api.CryptoException;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.VaultService;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,17 +42,17 @@ public class UnlockWorkflow extends Task {
private final VaultService vaultService;
private final Lazy successScene;
private final Lazy invalidMountPointScene;
- private final ErrorComponent.Builder errorComponent;
+ private final FxApplicationWindows appWindows;
private final KeyLoadingStrategy keyLoadingStrategy;
@Inject
- UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
+ UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
this.successScene = successScene;
this.invalidMountPointScene = invalidMountPointScene;
- this.errorComponent = errorComponent;
+ this.appWindows = appWindows;
this.keyLoadingStrategy = keyLoadingStrategy;
}
@@ -118,7 +118,7 @@ public class UnlockWorkflow extends Task {
private void handleGenericError(Throwable e) {
LOG.error("Unlock failed for technical reasons.", e);
- errorComponent.cause(e).window(window).build().showErrorScene();
+ appWindows.showErrorWindow(e, window, null);
}
@Override
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
index cb6c109b9..e6966da64 100644
--- a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
@@ -13,7 +13,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Provider;
@@ -44,14 +44,14 @@ abstract class VaultOptionsModule {
@Provides
@VaultOptionsWindow
@VaultOptionsScoped
- static Stage provideStage(StageFactory factory, @MainWindow Stage owner, @VaultOptionsWindow Vault vault) {
+ static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, @VaultOptionsWindow Vault vault) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(true);
stage.setMinWidth(400);
stage.setMinHeight(300);
stage.initModality(Modality.WINDOW_MODAL);
- stage.initOwner(owner);
+ stage.initOwner(primaryStage);
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/wrongfilealert/WrongFileAlertModule.java b/src/main/java/org/cryptomator/ui/wrongfilealert/WrongFileAlertModule.java
index 7600146e5..19dd486fb 100644
--- a/src/main/java/org/cryptomator/ui/wrongfilealert/WrongFileAlertModule.java
+++ b/src/main/java/org/cryptomator/ui/wrongfilealert/WrongFileAlertModule.java
@@ -5,13 +5,13 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
-import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.fxapp.PrimaryStage;
import javax.inject.Provider;
import javafx.scene.Scene;
@@ -33,11 +33,11 @@ abstract class WrongFileAlertModule {
@Provides
@WrongFileAlertWindow
@WrongFileAlertScoped
- static Stage provideStage(StageFactory factory, @MainWindow Stage mainWindow, ResourceBundle resourceBundle) {
+ static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("wrongFileAlert.title"));
stage.setResizable(false);
- stage.initOwner(mainWindow);
+ stage.initOwner(primaryStage);
stage.initModality(Modality.WINDOW_MODAL);
return stage;
}
diff --git a/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java b/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java
index e249e6412..bb9cabbf3 100644
--- a/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java
+++ b/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java
@@ -5,7 +5,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
-import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
@@ -43,7 +42,7 @@ public class FileOpenRequestHandlerTest {
AppLaunchEvent evt = queue.poll();
Assertions.assertNotNull(evt);
- Collection paths = evt.getPathsToOpen();
+ Collection paths = evt.pathsToOpen();
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
}