diff --git a/pom.xml b/pom.xml index 8975b8693..2c18c2170 100644 --- a/pom.xml +++ b/pom.xml @@ -28,10 +28,10 @@ 2.3.1 - 1.1.0-beta1 - 1.0.0 - 1.0.0 - 1.0.1 + 1.1.0-beta2 + 1.1.0-beta1 + 1.1.0-beta1 + 1.1.0-beta1 1.3.3 1.3.3 1.2.6 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index f13308be5..a8d85c27f 100644 --- a/src/main/java/module-info.java +++ b/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; diff --git a/src/main/java/org/cryptomator/common/PluginClassLoader.java b/src/main/java/org/cryptomator/common/PluginClassLoader.java index 16932923b..28c73cd2c 100644 --- a/src/main/java/org/cryptomator/common/PluginClassLoader.java +++ b/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 { diff --git a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java index d6adadfe4..e0269348a 100644 --- a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java +++ b/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 diff --git a/src/main/java/org/cryptomator/common/keychain/KeychainModule.java b/src/main/java/org/cryptomator/common/keychain/KeychainModule.java index 6356c4966..63749b445 100644 --- a/src/main/java/org/cryptomator/common/keychain/KeychainModule.java +++ b/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> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) { - return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet()); + static List provideSupportedKeychainAccessProviders() { + return KeychainAccessProvider.get().toList(); } @Provides @Singleton - static Set provideSupportedKeychainAccessProviders(Set> availableFactories) { - return availableFactories.stream() // - .map(ServiceLoader.Provider::get) // - .filter(KeychainAccessProvider::isSupported) // - .collect(Collectors.toUnmodifiableSet()); - } - - @Provides - @Singleton - static ObjectExpression provideKeychainAccessProvider(Settings settings, Set providers) { + static ObjectExpression provideKeychainAccessProvider(Settings settings, List 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()); } diff --git a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java index c30efa30e..64b05bdab 100644 --- a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java +++ b/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java @@ -34,21 +34,20 @@ public abstract class UiLauncherModule { @Provides @Singleton - static Optional provideAppearanceProvider(PluginClassLoader classLoader) { - return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst(); + static Optional provideAppearanceProvider() { + return UiAppearanceProvider.get(); } @Provides @Singleton - static Optional provideAutostartProvider(PluginClassLoader classLoader) { - return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst(); + static Optional provideAutostartProvider() { + return AutoStartProvider.get(); } - @Provides @Singleton - static Optional provideTrayIntegrationProvider(PluginClassLoader classLoader) { - return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst(); + static Optional provideTrayIntegrationProvider() { + return TrayIntegrationProvider.get(); } @Provides diff --git a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index fafea3f2f..424134a3e 100644 --- a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/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 keychainAccessProviders; + private final List keychainAccessProviders; private final ErrorComponent.Builder errorComponent; public ChoiceBox themeChoiceBox; public ChoiceBox keychainBackendChoiceBox; @@ -61,7 +62,7 @@ public class GeneralPreferencesController implements FxController { @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, List keychainAccessProviders, ObjectProperty 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 { - private final Set keychainAccessProviders; + private final List keychainAccessProviders; - public KeychainProviderClassNameConverter(Set keychainAccessProviders) { + public KeychainProviderClassNameConverter(List keychainAccessProviders) { this.keychainAccessProviders = keychainAccessProviders; } diff --git a/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java b/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java new file mode 100644 index 000000000..7c1a0998b --- /dev/null +++ b/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 items) { + menu.removeAll(); + addChildren(menu, items); + } + + private void addChildren(Menu menu, List 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); + } + } + } + +} diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java b/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java deleted file mode 100644 index 2c176df76..000000000 --- a/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java b/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java deleted file mode 100644 index aa55ca766..000000000 --- a/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java +++ /dev/null @@ -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 appearanceProvider; - - @Inject - TrayImageFactory(Optional 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"; - } - -} diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java b/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java index c4cbfd456..e50269007 100644 --- a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuComponent.java +++ b/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(); + Optional trayMenuController(); + + org.cryptomator.ui.traymenu.TrayMenuController trayMenuController2(); // TODO tmp name /** * @return true 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 true 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 diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java index 4ce3808c4..8189406d2 100644 --- a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java +++ b/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 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 vaults) { + TrayMenuController(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList vaults, Optional 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(); + List menu = new ArrayList<>(); - 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); + 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 submenu = buildSubmenu(vault); + menu.add(new SubMenuItem(vault.getDisplayName(), submenu)); } - menu.addSeparator(); + 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 lockAllItem = new MenuItem(resourceBundle.getString("traymenu.lockAllVaults")); - lockAllItem.addActionListener(this::lockAllVaults); - lockAllItem.setEnabled(!vaults.filtered(Vault::isUnlocked).isEmpty()); - menu.add(lockAllItem); - - 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 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 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)); } diff --git a/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java b/src/main/java/org/cryptomator/ui/traymenu/TrayMenuModule.java new file mode 100644 index 000000000..3110be883 --- /dev/null +++ b/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 provideSupportedKeychainAccessProviders() { + return TrayMenuController.get(); + } + +}