diff --git a/main/pom.xml b/main/pom.xml
index 838b871de..7d54dbf90 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -25,9 +25,9 @@
1.9.12
- 0.1.4
+ 0.1.5
0.1.0-beta1
- 0.1.0-beta1
+ 0.1.0-beta2
0.1.0-beta1
2.2.3
1.2.5
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 decf50718..6d6a36fd7 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
@@ -6,8 +6,10 @@ 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.jni.JniException;
-import org.cryptomator.jni.MacApplicationUiAppearance;
+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.jni.MacApplicationUiState;
import org.cryptomator.jni.MacFunctions;
import org.cryptomator.ui.common.VaultService;
@@ -42,20 +44,21 @@ public class FxApplication extends Application {
private final Provider unlockWindowBuilderProvider;
private final Provider quitWindowBuilderProvider;
private final Optional macFunctions;
+ private final Optional appearanceProvider;
private final VaultService vaultService;
private final LicenseHolder licenseHolder;
private final BooleanBinding hasVisibleStages;
-
- private Optional macApperanceObserverIdentifier = Optional.empty();
+ private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject
- FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, Provider unlockWindowBuilderProvider, Provider quitWindowBuilderProvider, Optional macFunctions, VaultService vaultService, LicenseHolder licenseHolder, ObservableSet visibleStages) {
+ FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, Provider unlockWindowBuilderProvider, Provider quitWindowBuilderProvider, Optional macFunctions, Optional appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder, ObservableSet visibleStages) {
this.settings = settings;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.unlockWindowBuilderProvider = unlockWindowBuilderProvider;
this.quitWindowBuilderProvider = quitWindowBuilderProvider;
this.macFunctions = macFunctions;
+ this.appearanceProvider = appearanceProvider;
this.vaultService = vaultService;
this.licenseHolder = licenseHolder;
this.hasVisibleStages = Bindings.isNotEmpty(visibleStages);
@@ -67,7 +70,7 @@ public class FxApplication extends Application {
EasyBind.subscribe(hasVisibleStages, this::hasVisibleStagesChanged);
- settings.theme().addListener(this::themeChanged);
+ settings.theme().addListener(this::appThemeChanged);
loadSelectedStyleSheet(settings.theme().get());
}
@@ -116,45 +119,60 @@ public class FxApplication extends Application {
return vaultService;
}
- private void themeChanged(@SuppressWarnings("unused") ObservableValue extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
- if (macApperanceObserverIdentifier.isPresent()) {
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(uiAppearance -> uiAppearance.removeListener(macApperanceObserverIdentifier.get()));
- macApperanceObserverIdentifier = Optional.empty();
- }
+ private void appThemeChanged(@SuppressWarnings("unused") ObservableValue extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ try {
+ appearanceProvider.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 -> setToLightTheme();
- case DARK -> setToDarkTheme();
+ case LIGHT -> applyLightTheme();
+ case DARK -> applyDarkTheme();
case AUTOMATIC -> {
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(uiAppearance -> {
- macApperanceObserverIdentifier = Optional.of(uiAppearance.addListener(this::macInterfaceThemeChanged));
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ try {
+ appearanceProvider.addListener(systemInterfaceThemeListener);
+ } catch (UiAppearanceException e) {
+ LOG.error("Failed to enable automatic theme switching.");
+ }
});
- macInterfaceThemeChanged();
+ applySystemTheme();
}
}
}
- private void macInterfaceThemeChanged() {
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(uiAppearance -> {
- switch (uiAppearance.getCurrentInterfaceStyle()) {
- case LIGHT -> setToLightTheme();
- case DARK -> setToDarkTheme();
- }
+ private void systemInterfaceThemeChanged(Theme theme) {
+ switch (theme) {
+ case LIGHT -> applyLightTheme();
+ case DARK -> applyDarkTheme();
+ }
+ }
+
+ private void applySystemTheme() {
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ systemInterfaceThemeChanged(appearanceProvider.getSystemTheme());
});
}
- private void setToLightTheme() {
+ private void applyLightTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(JniException.ignore(MacApplicationUiAppearance::setToAqua));
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ appearanceProvider.adjustToTheme(Theme.LIGHT);
+ });
}
- private void setToDarkTheme() {
+ private void applyDarkTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(JniException.ignore(MacApplicationUiAppearance::setToDarkAqua));
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ appearanceProvider.adjustToTheme(Theme.DARK);
+ });
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
index 2ac585763..492d5028c 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
@@ -3,18 +3,28 @@ package org.cryptomator.ui.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.JniModule;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
+import org.cryptomator.ui.fxapp.FxApplicationScoped;
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(includes = {JniModule.class}, subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class})
public abstract class UiLauncherModule {
+ @Provides
+ @Singleton
+ static Optional provideAppearanceProvider() {
+ return ServiceLoader.load(UiAppearanceProvider.class).findFirst();
+ }
+
@Provides
@Singleton
static ResourceBundle provideLocalization() {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java
index 13183a36e..b7231c948 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java
@@ -1,6 +1,9 @@
package org.cryptomator.ui.traymenu;
import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.integrations.uiappearance.Theme;
+import org.cryptomator.integrations.uiappearance.UiAppearanceException;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.jni.MacFunctions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,20 +20,26 @@ public class TrayIconController {
private static final Logger LOG = LoggerFactory.getLogger(TrayIconController.class);
private final TrayImageFactory imageFactory;
+ private final Optional appearanceProvider;
private final TrayMenuController trayMenuController;
private final TrayIcon trayIcon;
- private final Optional macFunctions;
@Inject
- TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController, Optional macFunctions) {
+ TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController, Optional appearanceProvider) {
this.trayMenuController = trayMenuController;
this.imageFactory = imageFactory;
+ this.appearanceProvider = appearanceProvider;
this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu());
- this.macFunctions = macFunctions;
}
public void initializeTrayIcon() {
- macFunctions.map(MacFunctions::uiAppearance).ifPresent(uiAppearance -> uiAppearance.addListener(this::macInterfaceThemeChanged));
+ appearanceProvider.ifPresent(appearanceProvider -> {
+ try {
+ appearanceProvider.addListener(this::systemInterfaceThemeChanged);
+ } catch (UiAppearanceException e) {
+ LOG.error("Failed to enable automatic tray icon theme switching.");
+ }
+ });
trayIcon.setImageAutoSize(true);
if (SystemUtils.IS_OS_WINDOWS) {
@@ -47,8 +56,8 @@ public class TrayIconController {
trayMenuController.initTrayMenu();
}
- private void macInterfaceThemeChanged() {
- trayIcon.setImage(imageFactory.loadImage());
+ private void systemInterfaceThemeChanged(Theme theme) {
+ trayIcon.setImage(imageFactory.loadImage()); // TODO refactor "theme" is re-queried in loadImage()
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java
index cbdf651b4..d9fcdfc29 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayImageFactory.java
@@ -1,9 +1,8 @@
package org.cryptomator.ui.traymenu;
import org.apache.commons.lang3.SystemUtils;
-import org.cryptomator.jni.MacApplicationUiAppearance;
-import org.cryptomator.jni.MacApplicationUiInterfaceStyle;
-import org.cryptomator.jni.MacFunctions;
+import org.cryptomator.integrations.uiappearance.Theme;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import javax.inject.Inject;
import java.awt.Image;
@@ -13,11 +12,11 @@ import java.util.Optional;
@TrayMenuScoped
class TrayImageFactory {
- private final Optional macFunctions;
+ private final Optional appearanceProvider;
@Inject
- TrayImageFactory(Optional macFunctions) {
- this.macFunctions = macFunctions;
+ TrayImageFactory(Optional appearanceProvider) {
+ this.appearanceProvider = appearanceProvider;
}
public Image loadImage() {
@@ -26,10 +25,8 @@ class TrayImageFactory {
}
private String getMacResourceName() {
- MacApplicationUiInterfaceStyle interfaceStyle = macFunctions.map(MacFunctions::uiAppearance) //
- .map(MacApplicationUiAppearance::getCurrentInterfaceStyle) //
- .orElse(MacApplicationUiInterfaceStyle.LIGHT);
- return switch (interfaceStyle) {
+ var theme = appearanceProvider.map(UiAppearanceProvider::getSystemTheme).orElse(Theme.LIGHT);
+ return switch (theme) {
case DARK -> "/img/tray_icon_mac_white.png";
case LIGHT -> "/img/tray_icon_mac_black.png";
};