Browse Source

Updated to integrations-api 1.1.0-beta2

Sebastian Stenzel 3 years ago
parent
commit
a404740ceb

+ 4 - 4
pom.xml

@@ -28,10 +28,10 @@
 
 		<!-- cryptomator dependencies -->
 		<cryptomator.cryptofs.version>2.3.1</cryptomator.cryptofs.version>
-		<cryptomator.integrations.version>1.1.0-beta1</cryptomator.integrations.version>
-		<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version>
-		<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version>
-		<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version>
+		<cryptomator.integrations.version>1.1.0-beta2</cryptomator.integrations.version>
+		<cryptomator.integrations.win.version>1.1.0-beta1</cryptomator.integrations.win.version>
+		<cryptomator.integrations.mac.version>1.1.0-beta1</cryptomator.integrations.mac.version>
+		<cryptomator.integrations.linux.version>1.1.0-beta1</cryptomator.integrations.linux.version>
 		<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version>
 		<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
 		<cryptomator.webdav.version>1.2.6</cryptomator.webdav.version>

+ 3 - 8
src/main/java/module-info.java

@@ -1,7 +1,5 @@
-import org.cryptomator.integrations.autostart.AutoStartProvider;
-import org.cryptomator.integrations.keychain.KeychainAccessProvider;
-import org.cryptomator.integrations.tray.TrayIntegrationProvider;
-import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
+import org.cryptomator.integrations.tray.TrayMenuController;
+import org.cryptomator.ui.traymenu.AwtTrayMenuController;
 
 module org.cryptomator.desktop {
 	requires org.cryptomator.cryptofs;
@@ -29,10 +27,7 @@ module org.cryptomator.desktop {
 	requires logback.classic;
 	requires logback.core;
 
-	uses AutoStartProvider;
-	uses KeychainAccessProvider;
-	uses TrayIntegrationProvider;
-	uses UiAppearanceProvider;
+	provides TrayMenuController with AwtTrayMenuController;
 
 	opens org.cryptomator.common.settings to com.google.gson;
 

+ 1 - 0
src/main/java/org/cryptomator/common/PluginClassLoader.java

@@ -19,6 +19,7 @@ import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 
+@Deprecated // to be moved to integrations-api 1.1.0
 @Singleton
 public class PluginClassLoader extends URLClassLoader {
 

+ 4 - 6
src/main/java/org/cryptomator/common/keychain/KeychainManager.java

@@ -44,9 +44,9 @@ public class KeychainManager implements KeychainAccessProvider {
 	}
 
 	@Override
+	@Deprecated
 	public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
-		getKeychainOrFail().storePassphrase(key, passphrase);
-		setPassphraseStored(key, true);
+		storePassphrase(key, null, passphrase);
 	}
 
 	@Override
@@ -69,11 +69,9 @@ public class KeychainManager implements KeychainAccessProvider {
 	}
 
 	@Override
+	@Deprecated
 	public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
-		if (isPassphraseStored(key)) {
-			getKeychainOrFail().changePassphrase(key, passphrase);
-			setPassphraseStored(key, true);
-		}
+		changePassphrase(key, null, passphrase);
 	}
 
 	@Override

+ 5 - 17
src/main/java/org/cryptomator/common/keychain/KeychainModule.java

@@ -2,42 +2,30 @@ package org.cryptomator.common.keychain;
 
 import dagger.Module;
 import dagger.Provides;
-import org.cryptomator.common.PluginClassLoader;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.integrations.keychain.KeychainAccessProvider;
 
 import javax.inject.Singleton;
 import javafx.beans.binding.Bindings;
 import javafx.beans.binding.ObjectExpression;
-import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.List;
 
 @Module
 public class KeychainModule {
 
 	@Provides
 	@Singleton
-	static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
-		return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
+	static List<KeychainAccessProvider> provideSupportedKeychainAccessProviders() {
+		return KeychainAccessProvider.get().toList();
 	}
 
 	@Provides
 	@Singleton
-	static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) {
-		return availableFactories.stream() //
-				.map(ServiceLoader.Provider::get) //
-				.filter(KeychainAccessProvider::isSupported) //
-				.collect(Collectors.toUnmodifiableSet());
-	}
-
-	@Provides
-	@Singleton
-	static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
+	static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
 		return Bindings.createObjectBinding(() -> {
 			var selectedProviderClass = settings.keychainProvider().get();
 			var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
-			var fallbackProvider = providers.stream().findAny().orElse(null);
+			var fallbackProvider = providers.stream().findFirst().orElse(null);
 			return selectedProvider.orElse(fallbackProvider);
 		}, settings.keychainProvider());
 	}

+ 6 - 7
src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java

@@ -34,21 +34,20 @@ public abstract class UiLauncherModule {
 
 	@Provides
 	@Singleton
-	static Optional<UiAppearanceProvider> provideAppearanceProvider(PluginClassLoader classLoader) {
-		return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
+	static Optional<UiAppearanceProvider> provideAppearanceProvider() {
+		return UiAppearanceProvider.get();
 	}
 
 	@Provides
 	@Singleton
-	static Optional<AutoStartProvider> provideAutostartProvider(PluginClassLoader classLoader) {
-		return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
+	static Optional<AutoStartProvider> provideAutostartProvider() {
+		return AutoStartProvider.get();
 	}
 
-
 	@Provides
 	@Singleton
-	static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider(PluginClassLoader classLoader) {
-		return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
+	static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
+		return TrayIntegrationProvider.get();
 	}
 
 	@Provides

+ 5 - 4
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.util.List;
 import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.Set;
@@ -46,7 +47,7 @@ public class GeneralPreferencesController implements FxController {
 	private final ResourceBundle resourceBundle;
 	private final Application application;
 	private final Environment environment;
-	private final Set<KeychainAccessProvider> keychainAccessProviders;
+	private final List<KeychainAccessProvider> keychainAccessProviders;
 	private final ErrorComponent.Builder errorComponent;
 	public ChoiceBox<UiTheme> themeChoiceBox;
 	public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
@@ -61,7 +62,7 @@ public class GeneralPreferencesController implements FxController {
 
 
 	@Inject
-	GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
+	GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
 		this.window = window;
 		this.settings = settings;
 		this.trayMenuInitialized = trayMenu.isInitialized();
@@ -204,9 +205,9 @@ public class GeneralPreferencesController implements FxController {
 
 	private static class KeychainProviderClassNameConverter extends StringConverter<KeychainAccessProvider> {
 
-		private final Set<KeychainAccessProvider> keychainAccessProviders;
+		private final List<KeychainAccessProvider> keychainAccessProviders;
 
-		public KeychainProviderClassNameConverter(Set<KeychainAccessProvider> keychainAccessProviders) {
+		public KeychainProviderClassNameConverter(List<KeychainAccessProvider> keychainAccessProviders) {
 			this.keychainAccessProviders = keychainAccessProviders;
 		}
 

+ 72 - 0
src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java

@@ -0,0 +1,72 @@
+package org.cryptomator.ui.traymenu;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.integrations.common.Priority;
+import org.cryptomator.integrations.tray.ActionItem;
+import org.cryptomator.integrations.tray.SeparatorItem;
+import org.cryptomator.integrations.tray.SubMenuItem;
+import org.cryptomator.integrations.tray.TrayMenuItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.awt.AWTException;
+import java.awt.Menu;
+import java.awt.MenuItem;
+import java.awt.PopupMenu;
+import java.awt.SystemTray;
+import java.awt.Toolkit;
+import java.awt.TrayIcon;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+@Priority(Priority.FALLBACK)
+public class AwtTrayMenuController implements org.cryptomator.integrations.tray.TrayMenuController {
+
+	private static final Logger LOG = LoggerFactory.getLogger(AwtTrayMenuController.class);
+
+	private TrayIcon trayIcon;
+	private PopupMenu menu = new PopupMenu();
+
+	@Override
+	public void showTrayIcon(InputStream rawImageData, Runnable defaultAction, String tooltip) throws IOException {
+		var image = Toolkit.getDefaultToolkit().createImage(rawImageData.readAllBytes());
+		trayIcon = new TrayIcon(image, tooltip, menu);
+
+		trayIcon.setImageAutoSize(true);
+		if (SystemUtils.IS_OS_WINDOWS) {
+			trayIcon.addActionListener(evt -> defaultAction.run());
+		}
+
+		try {
+			SystemTray.getSystemTray().add(trayIcon);
+			LOG.debug("initialized tray icon");
+		} catch (AWTException e) {
+			LOG.error("Error adding tray icon", e);
+		}
+	}
+
+	@Override
+	public void updateTrayMenu(List<TrayMenuItem> items) {
+		menu.removeAll();
+		addChildren(menu, items);
+	}
+
+	private void addChildren(Menu menu, List<TrayMenuItem> items) {
+		for (var item : items) {
+			// TODO: use Pattern Matching for switch, once available
+			if (item instanceof ActionItem a) {
+				var menuItem = new MenuItem(a.title());
+				menuItem.addActionListener(evt -> a.action().run());
+				menu.add(menuItem);
+			} else if (item instanceof SeparatorItem) {
+				menu.addSeparator();
+			} else if (item instanceof SubMenuItem s) {
+				var submenu = new Menu(s.title());
+				addChildren(submenu, s.items());
+				menu.add(submenu);
+			}
+		}
+	}
+
+}

+ 0 - 51
src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java

@@ -1,51 +0,0 @@
-package org.cryptomator.ui.traymenu;
-
-import com.google.common.base.Preconditions;
-import org.apache.commons.lang3.SystemUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import java.awt.AWTException;
-import java.awt.SystemTray;
-import java.awt.TrayIcon;
-
-@TrayMenuScoped
-public class TrayIconController {
-
-	private static final Logger LOG = LoggerFactory.getLogger(TrayIconController.class);
-
-	private final TrayMenuController trayMenuController;
-	private final TrayIcon trayIcon;
-	private volatile boolean initialized;
-
-	@Inject
-	TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController) {
-		this.trayMenuController = trayMenuController;
-		this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu());
-	}
-
-	public synchronized void initializeTrayIcon() throws IllegalStateException {
-		Preconditions.checkState(!initialized);
-
-		trayIcon.setImageAutoSize(true);
-		if (SystemUtils.IS_OS_WINDOWS) {
-			trayIcon.addActionListener(trayMenuController::showMainWindow);
-		}
-
-		try {
-			SystemTray.getSystemTray().add(trayIcon);
-			LOG.debug("initialized tray icon");
-		} catch (AWTException e) {
-			LOG.error("Error adding tray icon", e);
-		}
-
-		trayMenuController.initTrayMenu();
-
-		this.initialized = true;
-	}
-
-	public boolean isInitialized() {
-		return initialized;
-	}
-}

+ 0 - 35
src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java

@@ -1,35 +0,0 @@
-package org.cryptomator.ui.traymenu;
-
-import org.apache.commons.lang3.SystemUtils;
-import org.cryptomator.integrations.uiappearance.Theme;
-import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
-
-import javax.inject.Inject;
-import java.awt.Image;
-import java.awt.Toolkit;
-import java.util.Optional;
-
-@TrayMenuScoped
-class TrayImageFactory {
-
-	private final Optional<UiAppearanceProvider> appearanceProvider;
-
-	@Inject
-	TrayImageFactory(Optional<UiAppearanceProvider> appearanceProvider) {
-		this.appearanceProvider = appearanceProvider;
-	}
-
-	public Image loadImage() {
-		String resourceName = SystemUtils.IS_OS_MAC_OSX ? getMacResourceName() : getWinOrLinuxResourceName();
-		return Toolkit.getDefaultToolkit().getImage(getClass().getResource(resourceName));
-	}
-
-	private String getMacResourceName() {
-		return "/img/tray_icon_mac.png";
-	}
-
-	private String getWinOrLinuxResourceName() {
-		return "/img/tray_icon.png";
-	}
-
-}

+ 14 - 7
src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java

@@ -5,28 +5,34 @@
  *******************************************************************************/
 package org.cryptomator.ui.traymenu;
 
-import dagger.Lazy;
+import com.google.common.base.Preconditions;
 import dagger.Subcomponent;
+import org.cryptomator.integrations.tray.TrayMenuController;
+
 import java.awt.SystemTray;
+import java.util.Optional;
 
 @TrayMenuScoped
-@Subcomponent
+@Subcomponent(modules = {TrayMenuModule.class})
 public interface TrayMenuComponent {
 
-	Lazy<TrayIconController> trayIconController();
+	Optional<TrayMenuController> trayMenuController();
+
+	org.cryptomator.ui.traymenu.TrayMenuController trayMenuController2(); // TODO tmp name
 
 	/**
 	 * @return <code>true</code> if a tray icon can be installed
 	 */
 	default boolean isSupported() {
-		return SystemTray.isSupported();
+		// TODO add isSupported to API and move SystemTray.isSupported() to impl
+		return trayMenuController().isPresent() && SystemTray.isSupported();
 	}
 
 	/**
 	 * @return <code>true</code> if a tray icon has been installed
 	 */
 	default boolean isInitialized() {
-		return isSupported() && trayIconController().get().isInitialized();
+		return isSupported() && trayMenuController2().isInitialized();
 	}
 
 	/**
@@ -35,8 +41,9 @@ public interface TrayMenuComponent {
 	 * @throws IllegalStateException If already added
 	 */
 	default void initializeTrayIcon() throws IllegalStateException {
-		assert isSupported();
-		trayIconController().get().initializeTrayIcon();
+		Preconditions.checkState(isSupported(), "system tray not supported");
+
+		trayMenuController2().initTrayMenu();
 	}
 
 	@Subcomponent.Builder

+ 64 - 57
src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java

@@ -1,6 +1,12 @@
 package org.cryptomator.ui.traymenu;
 
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang3.SystemUtils;
 import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.integrations.tray.ActionItem;
+import org.cryptomator.integrations.tray.SeparatorItem;
+import org.cryptomator.integrations.tray.SubMenuItem;
+import org.cryptomator.integrations.tray.TrayMenuItem;
 import org.cryptomator.ui.fxapp.FxApplication;
 import org.cryptomator.ui.launcher.AppLifecycleListener;
 import org.cryptomator.ui.launcher.FxApplicationStarter;
@@ -10,44 +16,61 @@ import javax.inject.Inject;
 import javafx.application.Platform;
 import javafx.beans.Observable;
 import javafx.collections.ObservableList;
-import java.awt.Menu;
-import java.awt.MenuItem;
-import java.awt.PopupMenu;
-import java.awt.event.ActionEvent;
+import java.awt.Image;
+import java.awt.Toolkit;
 import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
 import java.util.EventObject;
+import java.util.List;
 import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.function.Consumer;
 
 @TrayMenuScoped
-class TrayMenuController {
+public class TrayMenuController {
+
+	private static final String TRAY_ICON_MAC = "/img/tray_icon_mac.png";
+	private static final String TRAY_ICON = "/img/tray_icon.png";
 
 	private final ResourceBundle resourceBundle;
 	private final AppLifecycleListener appLifecycle;
 	private final FxApplicationStarter fxApplicationStarter;
 	private final ObservableList<Vault> vaults;
-	private final PopupMenu menu;
+	private final org.cryptomator.integrations.tray.TrayMenuController trayMenu;
+
+	private volatile boolean initialized;
 
 	@Inject
-	TrayMenuController(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList<Vault> vaults) {
+	TrayMenuController(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList<Vault> vaults, Optional<org.cryptomator.integrations.tray.TrayMenuController> trayMenu) {
 		this.resourceBundle = resourceBundle;
 		this.appLifecycle = appLifecycle;
 		this.fxApplicationStarter = fxApplicationStarter;
 		this.vaults = vaults;
-		this.menu = new PopupMenu();
+		this.trayMenu = trayMenu.orElse(null);
 	}
 
-	public PopupMenu getMenu() {
-		return menu;
-	}
+	public synchronized void initTrayMenu() {
+		Preconditions.checkState(!initialized, "tray icon already initialized");
 
-	public void initTrayMenu() {
 		vaults.addListener(this::vaultListChanged);
 		vaults.forEach(v -> {
 			v.displayNameProperty().addListener(this::vaultListChanged);
 		});
 		rebuildMenu();
+
+		try (var image = getClass().getResourceAsStream(SystemUtils.IS_OS_MAC_OSX ? TRAY_ICON_MAC : TRAY_ICON)) {
+			trayMenu.showTrayIcon(image, this::showMainWindow, "Cryptomator");
+		} catch (IOException e) {
+			throw new UncheckedIOException("Failed to load embedded resource", e);
+		}
+
+		initialized = true;
+	}
+
+	public boolean isInitialized() {
+		return initialized;
 	}
 
 	private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
@@ -56,58 +79,42 @@ class TrayMenuController {
 	}
 
 	private void rebuildMenu() {
-		menu.removeAll();
-
-		MenuItem showMainWindowItem = new MenuItem(resourceBundle.getString("traymenu.showMainWindow"));
-		showMainWindowItem.addActionListener(this::showMainWindow);
-		menu.add(showMainWindowItem);
-
-		MenuItem showPreferencesItem = new MenuItem(resourceBundle.getString("traymenu.showPreferencesWindow"));
-		showPreferencesItem.addActionListener(this::showPreferencesWindow);
-		menu.add(showPreferencesItem);
-
-		menu.addSeparator();
-		for (Vault v : vaults) {
-			MenuItem submenu = buildSubmenu(v);
-			menu.add(submenu);
+		List<TrayMenuItem> menu = new ArrayList<>();
+
+		menu.add(new ActionItem(resourceBundle.getString("traymenu.showMainWindow"), this::showMainWindow));
+		menu.add(new ActionItem(resourceBundle.getString("traymenu.showPreferencesWindow"), this::showPreferencesWindow));
+		menu.add(new SeparatorItem());
+		for (Vault vault : vaults) {
+			List<TrayMenuItem> submenu = buildSubmenu(vault);
+			menu.add(new SubMenuItem(vault.getDisplayName(), submenu));
 		}
-		menu.addSeparator();
-
-		MenuItem lockAllItem = new MenuItem(resourceBundle.getString("traymenu.lockAllVaults"));
-		lockAllItem.addActionListener(this::lockAllVaults);
-		lockAllItem.setEnabled(!vaults.filtered(Vault::isUnlocked).isEmpty());
-		menu.add(lockAllItem);
+		menu.add(new SeparatorItem());
+		menu.add(new ActionItem(resourceBundle.getString("traymenu.lockAllVaults"), this::lockAllVaults));
+		menu.add(new ActionItem(resourceBundle.getString("traymenu.quitApplication"), this::quitApplication));
+// 		lockAllItem.setEnabled(!vaults.filtered(Vault::isUnlocked).isEmpty());
 
-		MenuItem quitApplicationItem = new MenuItem(resourceBundle.getString("traymenu.quitApplication"));
-		quitApplicationItem.addActionListener(this::quitApplication);
-		menu.add(quitApplicationItem);
+		trayMenu.updateTrayMenu(menu);
 	}
 
-	private Menu buildSubmenu(Vault vault) {
-		Menu submenu = new Menu(vault.getDisplayName());
-
+	private List<TrayMenuItem> buildSubmenu(Vault vault) {
 		if (vault.isLocked()) {
-			MenuItem unlockItem = new MenuItem(resourceBundle.getString("traymenu.vault.unlock"));
-			unlockItem.addActionListener(createActionListenerForVault(vault, this::unlockVault));
-			submenu.add(unlockItem);
+			return List.of(
+					new ActionItem(resourceBundle.getString("traymenu.vault.unlock"), () -> this.unlockVault(vault))
+			);
 		} else if (vault.isUnlocked()) {
-			MenuItem lockItem = new MenuItem(resourceBundle.getString("traymenu.vault.lock"));
-			lockItem.addActionListener(createActionListenerForVault(vault, this::lockVault));
-			submenu.add(lockItem);
+			return List.of(
+					new ActionItem(resourceBundle.getString("traymenu.vault.lock"), () -> this.lockVault(vault)),
+					new ActionItem(resourceBundle.getString("traymenu.vault.reveal"), () -> this.revealVault(vault))
 
-			MenuItem revealItem = new MenuItem(resourceBundle.getString("traymenu.vault.reveal"));
-			revealItem.addActionListener(createActionListenerForVault(vault, this::revealVault));
-			submenu.add(revealItem);
+			);
+		} else {
+			return List.of();
 		}
-
-		return submenu;
 	}
 
-	private ActionListener createActionListenerForVault(Vault vault, Consumer<Vault> consumer) {
-		return actionEvent -> consumer.accept(vault);
-	}
+	/* action listeners: */
 
-	private void quitApplication(EventObject actionEvent) {
+	private void quitApplication() {
 		appLifecycle.quit();
 	}
 
@@ -119,7 +126,7 @@ class TrayMenuController {
 		showMainAppAndThen(app -> app.startLockWorkflow(vault, Optional.empty()));
 	}
 
-	private void lockAllVaults(ActionEvent actionEvent) {
+	private void lockAllVaults() {
 		showMainAppAndThen(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false));
 	}
 
@@ -127,11 +134,11 @@ class TrayMenuController {
 		showMainAppAndThen(app -> app.getVaultService().reveal(vault));
 	}
 
-	void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) {
-		showMainAppAndThen(app -> app.showMainWindow());
+	void showMainWindow() {
+		showMainAppAndThen(FxApplication::showMainWindow);
 	}
 
-	private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
+	private void showPreferencesWindow() {
 		showMainAppAndThen(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
 	}
 

+ 18 - 0
src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java

@@ -0,0 +1,18 @@
+package org.cryptomator.ui.traymenu;
+
+import dagger.Module;
+import dagger.Provides;
+import org.cryptomator.integrations.tray.TrayMenuController;
+
+import java.util.Optional;
+
+@Module
+public class TrayMenuModule {
+
+	@Provides
+	@TrayMenuScoped
+	static Optional<TrayMenuController> provideSupportedKeychainAccessProviders() {
+		return TrayMenuController.get();
+	}
+
+}