From 45c714a1238756e34919295f8ede26ba76d3c08d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 16 Dec 2020 16:53:49 +0100 Subject: [PATCH] Added checkbox in settings to start without a tray icon references #1113, #1078, #1079, #1344 --- .../cryptomator/ui/fxapp/FxApplication.java | 9 +++- .../ui/launcher/AppLaunchEventHandler.java | 14 ++--- .../ui/launcher/AppLifecycleListener.java | 6 +-- .../ui/launcher/FxApplicationStarter.java | 13 +++-- .../cryptomator/ui/launcher/UiLauncher.java | 30 ++++++----- .../ui/mainwindow/MainWindowComponent.java | 1 - .../mainwindow/MainWindowTitleController.java | 12 ++--- .../GeneralPreferencesController.java | 10 ++-- .../ui/traymenu/TrayMenuController.java | 53 ++++++++++--------- .../resources/fxml/main_window_title.fxml | 2 +- .../resources/fxml/preferences_general.fxml | 4 +- .../main/resources/i18n/strings.properties | 1 + 12 files changed, 89 insertions(+), 66 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 1a4cb84de..65e6d1c6c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -32,6 +32,8 @@ import javafx.stage.Stage; import javafx.stage.Window; import java.awt.desktop.QuitResponse; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; @FxApplicationScoped public class FxApplication extends Application { @@ -99,11 +101,14 @@ public class FxApplication extends Application { }); } - public void showMainWindow() { + public CompletionStage showMainWindow() { + CompletableFuture future = new CompletableFuture<>(); Platform.runLater(() -> { - mainWindow.get().showMainWindow(); + var win = mainWindow.get().showMainWindow(); LOG.debug("Showing MainWindow"); + future.complete(win); }); + return future; } public void startUnlockWorkflow(Vault vault, Optional owner) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java index b48023687..b8d1f5372 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java @@ -34,15 +34,15 @@ class AppLaunchEventHandler { this.vaultListManager = vaultListManager; } - public void startHandlingLaunchEvents(boolean hasTrayIcon) { - executorService.submit(() -> handleLaunchEvents(hasTrayIcon)); + public void startHandlingLaunchEvents() { + executorService.submit(this::handleLaunchEvents); } - private void handleLaunchEvents(boolean hasTrayIcon) { + private void handleLaunchEvents() { try { while (!Thread.interrupted()) { AppLaunchEvent event = launchEventQueue.take(); - handleLaunchEvent(hasTrayIcon, event); + handleLaunchEvent(event); } } catch (InterruptedException e) { LOG.warn("Interrupted launch event handler."); @@ -50,10 +50,10 @@ class AppLaunchEventHandler { } } - private void handleLaunchEvent(boolean hasTrayIcon, AppLaunchEvent event) { + private void handleLaunchEvent(AppLaunchEvent event) { switch (event.getType()) { - case REVEAL_APP -> fxApplicationStarter.get(hasTrayIcon).thenAccept(FxApplication::showMainWindow); - case OPEN_FILE -> fxApplicationStarter.get(hasTrayIcon).thenRun(() -> { + case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow); + case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> { Platform.runLater(() -> { event.getPathsToOpen().forEach(this::addVault); }); diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java index fcf9bc735..646c3ce6b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java @@ -83,7 +83,7 @@ public class AppLifecycleListener { if (allowQuitWithoutPrompt.get()) { decoratedQuitResponse.performQuit(); } else { - fxApplicationStarter.get(true).thenAccept(app -> app.showQuitWindow(decoratedQuitResponse)); + fxApplicationStarter.get().thenAccept(app -> app.showQuitWindow(decoratedQuitResponse)); } } @@ -113,11 +113,11 @@ public class AppLifecycleListener { } private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) { - fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY)); + fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY)); } private void showAboutWindow(@SuppressWarnings("unused") AboutEvent aboutEvent) { - fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT)); + fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT)); } private void forceUnmountRemainingVaults() { diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java index 033c719d0..cc2122069 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/FxApplicationStarter.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.launcher; +import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.fxapp.FxApplicationComponent; import org.slf4j.Logger; @@ -8,6 +9,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import javafx.application.Platform; +import java.awt.SystemTray; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutorService; @@ -22,23 +24,25 @@ public class FxApplicationStarter { private final ExecutorService executor; private final AtomicBoolean started; private final CompletableFuture future; + private final boolean hasTrayIcon; @Inject - public FxApplicationStarter(FxApplicationComponent.Builder fxAppComponent, ExecutorService executor) { + public FxApplicationStarter(FxApplicationComponent.Builder fxAppComponent, ExecutorService executor, Settings settings) { this.fxAppComponent = fxAppComponent; this.executor = executor; this.started = new AtomicBoolean(); this.future = new CompletableFuture<>(); + this.hasTrayIcon = SystemTray.isSupported() && settings.showTrayIcon().get(); } - public CompletionStage get(boolean hasTrayIcon) { + public CompletionStage get() { if (!started.getAndSet(true)) { - start(hasTrayIcon); + start(); } return future; } - private void start(boolean hasTrayIcon) { + private void start() { executor.submit(() -> { LOG.debug("Starting JavaFX runtime..."); Platform.startup(() -> { @@ -50,5 +54,4 @@ public class FxApplicationStarter { }); }); } - } diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java index ecef51dbf..78bfd0a97 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java @@ -40,44 +40,50 @@ public class UiLauncher { } public void launch() { - final boolean hasTrayIcon; - if (SystemTray.isSupported()) { + boolean hidden = settings.startHidden().get(); + if (SystemTray.isSupported() && settings.showTrayIcon().get()) { trayComponent.build().addIconToSystemTray(); - hasTrayIcon = true; + launch(true, hidden); } else { - hasTrayIcon = false; + launch(false, hidden); } + } - // show window on start? - if (hasTrayIcon && settings.startHidden().get()) { + 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 { - showMainWindowAsync(hasTrayIcon); + LOG.debug("Showing application..."); + showMainWindowAsync(false); } // register app reopen listener - Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(hasTrayIcon)); + Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(false)); // auto unlock Collection vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock); if (!vaultsToAutoUnlock.isEmpty()) { - fxApplicationStarter.get(hasTrayIcon).thenAccept(app -> { + fxApplicationStarter.get().thenAccept(app -> { for (Vault vault : vaultsToAutoUnlock) { app.startUnlockWorkflow(vault, Optional.empty()); } }); } - launchEventHandler.startHandlingLaunchEvents(hasTrayIcon); + launchEventHandler.startHandlingLaunchEvents(); } private boolean shouldAttemptAutoUnlock(Vault vault) { return vault.isLocked() && vault.getVaultSettings().unlockAfterStartup().get(); } - private void showMainWindowAsync(boolean hasTrayIcon) { - fxApplicationStarter.get(hasTrayIcon).thenAccept(FxApplication::showMainWindow); + private void showMainWindowAsync(boolean minimize) { + fxApplicationStarter.get().thenCompose(FxApplication::showMainWindow).thenAccept(win -> win.setIconified(minimize)); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java index 40885b387..07710e688 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java @@ -26,7 +26,6 @@ public interface MainWindowComponent { default Stage showMainWindow() { Stage stage = window(); stage.setScene(scene().get()); - stage.setIconified(false); stage.show(); stage.toFront(); stage.requestFocus(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java index d7eda58c8..29b2990ce 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java @@ -28,7 +28,7 @@ public class MainWindowTitleController implements FxController { private final AppLifecycleListener appLifecycle; private final Stage window; private final FxApplication application; - private final boolean minimizeToSysTray; + private final boolean isTrayIconPresent; private final UpdateChecker updateChecker; private final BooleanBinding updateAvailable; private final LicenseHolder licenseHolder; @@ -39,11 +39,11 @@ public class MainWindowTitleController implements FxController { private double yOffset; @Inject - MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) { + MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean isTrayIconPresent, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) { this.appLifecycle = appLifecycle; this.window = window; this.application = application; - this.minimizeToSysTray = minimizeToSysTray; + this.isTrayIconPresent = isTrayIconPresent; this.updateChecker = updateChecker; this.updateAvailable = updateChecker.latestVersionProperty().isNotNull(); this.licenseHolder = licenseHolder; @@ -71,7 +71,7 @@ public class MainWindowTitleController implements FxController { @FXML public void close() { - if (minimizeToSysTray) { + if (isTrayIconPresent) { window.close(); } else { appLifecycle.quit(); @@ -112,8 +112,8 @@ public class MainWindowTitleController implements FxController { return updateAvailable.get(); } - public boolean isMinimizeToSysTray() { - return minimizeToSysTray; + public boolean isTrayIconPresent() { + return isTrayIconPresent; } public BooleanBinding debugModeEnabledProperty() { diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 6af9a156e..e6445ac9c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -27,6 +27,7 @@ import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.stage.Stage; import javafx.util.StringConverter; +import java.awt.SystemTray; import java.util.Arrays; import java.util.Optional; import java.util.ResourceBundle; @@ -41,7 +42,6 @@ public class GeneralPreferencesController implements FxController { private final Stage window; private final Settings settings; - private final boolean trayMenuSupported; private final Optional autoStartProvider; private final ObjectProperty selectedTabProperty; private final LicenseHolder licenseHolder; @@ -53,6 +53,7 @@ public class GeneralPreferencesController implements FxController { private final ErrorComponent.Builder errorComponent; public ChoiceBox themeChoiceBox; public ChoiceBox keychainBackendChoiceBox; + public CheckBox showTrayIconCheckbox; public CheckBox startHiddenCheckbox; public CheckBox debugModeCheckbox; public CheckBox autoStartCheckbox; @@ -61,10 +62,9 @@ public class GeneralPreferencesController implements FxController { public RadioButton nodeOrientationRtl; @Inject - GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional autoStartProvider, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) { + GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional autoStartProvider, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) { this.window = window; this.settings = settings; - this.trayMenuSupported = trayMenuSupported; this.autoStartProvider = autoStartProvider; this.keychainAccessProviders = keychainAccessProviders; this.selectedTabProperty = selectedTabProperty; @@ -85,6 +85,8 @@ public class GeneralPreferencesController implements FxController { themeChoiceBox.valueProperty().bindBidirectional(settings.theme()); themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle)); + showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon()); + startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden()); debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode()); @@ -106,7 +108,7 @@ public class GeneralPreferencesController implements FxController { } public boolean isTrayMenuSupported() { - return this.trayMenuSupported; + return SystemTray.isSupported(); } public boolean isAutoStartSupported() { diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java index a65f1e493..96529c5fb 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java @@ -1,6 +1,7 @@ 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.preferences.SelectedPreferencesTab; @@ -103,32 +104,36 @@ class TrayMenuController { return actionEvent -> consumer.accept(vault); } - private void unlockVault(Vault vault) { - fxApplicationStarter.get(true).thenAccept(app -> app.startUnlockWorkflow(vault, Optional.empty())); - } - - private void lockVault(Vault vault) { - fxApplicationStarter.get(true).thenAccept(app -> app.startLockWorkflow(vault, Optional.empty())); - } - - private void lockAllVaults(ActionEvent actionEvent) { - fxApplicationStarter.get(true).thenAccept(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false)); - } - - private void revealVault(Vault vault) { - fxApplicationStarter.get(true).thenAccept(app -> app.getVaultService().reveal(vault)); - } - - void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) { - fxApplicationStarter.get(true).thenAccept(app -> app.showMainWindow()); - } - - private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) { - fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY)); - } - private void quitApplication(EventObject actionEvent) { appLifecycle.quit(); } + private void unlockVault(Vault vault) { + showMainAppAndThen(app -> app.startUnlockWorkflow(vault, Optional.empty())); + } + + private void lockVault(Vault vault) { + showMainAppAndThen(app -> app.startLockWorkflow(vault, Optional.empty())); + } + + private void lockAllVaults(ActionEvent actionEvent) { + showMainAppAndThen(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false)); + } + + private void revealVault(Vault vault) { + showMainAppAndThen(app -> app.getVaultService().reveal(vault)); + } + + void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) { + showMainAppAndThen(app -> app.showMainWindow()); + } + + private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) { + showMainAppAndThen(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY)); + } + + private void showMainAppAndThen(Consumer action) { + fxApplicationStarter.get().thenAccept(action); + } + } diff --git a/main/ui/src/main/resources/fxml/main_window_title.fxml b/main/ui/src/main/resources/fxml/main_window_title.fxml index 624d64936..4b83cc650 100644 --- a/main/ui/src/main/resources/fxml/main_window_title.fxml +++ b/main/ui/src/main/resources/fxml/main_window_title.fxml @@ -54,7 +54,7 @@ -