From c0e2d0129777b8f80f0cee40ad9bd9f1cb2a8501 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 5 Nov 2020 17:49:39 +0100 Subject: [PATCH] Migrated from deprecated MacFunctions to UiAppearanceProvider for UI theme switching --- main/pom.xml | 4 +- .../cryptomator/ui/fxapp/FxApplication.java | 70 ++++++++++++------- .../ui/launcher/UiLauncherModule.java | 10 +++ .../ui/traymenu/TrayIconController.java | 21 ++++-- .../ui/traymenu/TrayImageFactory.java | 17 ++--- 5 files changed, 78 insertions(+), 44 deletions(-) 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 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 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"; };