From dc58ba434a061b0ac6a759edb31d6b6e8f6c5b3f Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 29 Aug 2016 20:14:48 +0200 Subject: [PATCH] Make Cryptomator a foreground app when restoring from status bar icon --- .../cryptomator/ui/CryptomatorComponent.java | 4 ++ .../org/cryptomator/ui/CryptomatorModule.java | 3 +- .../java/org/cryptomator/ui/ExitUtil.java | 9 ++++- .../org/cryptomator/ui/MainApplication.java | 3 ++ .../org/cryptomator/ui/jni/JniException.java | 24 ++++++++++++ .../org/cryptomator/ui/jni/JniModule.java | 33 +++++++++++++++++ .../org/cryptomator/ui/jni/MacFunctions.java | 37 +++++++++++++++++++ 7 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/jni/JniException.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/jni/JniModule.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/jni/MacFunctions.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java index 095caeb89..7c047b213 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java @@ -8,11 +8,13 @@ *******************************************************************************/ package org.cryptomator.ui; +import java.util.Optional; import java.util.concurrent.ExecutorService; import javax.inject.Singleton; import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.ui.jni.MacFunctions; import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.util.AsyncTaskService; import org.cryptomator.ui.util.DeferredCloser; @@ -34,4 +36,6 @@ interface CryptomatorComponent { Localization localization(); ExitUtil exitUtil(); + + Optional nativeMacFunctions(); } \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java index b62f631c4..7d8d86d0c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -19,6 +19,7 @@ import org.cryptomator.crypto.engine.impl.CryptoEngineModule; import org.cryptomator.frontend.FrontendFactory; import org.cryptomator.frontend.webdav.WebDavModule; import org.cryptomator.frontend.webdav.WebDavServer; +import org.cryptomator.ui.jni.JniModule; import org.cryptomator.ui.model.VaultObjectMapperProvider; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.settings.SettingsProvider; @@ -33,7 +34,7 @@ import dagger.Provides; import javafx.application.Application; import javafx.stage.Stage; -@Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class}) +@Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class, JniModule.class}) class CryptomatorModule { private static final Logger LOG = LoggerFactory.getLogger(CryptomatorModule.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index ff0cbe0dd..8bbac3ea6 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -21,6 +21,7 @@ import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; +import java.util.Optional; import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -32,6 +33,8 @@ import javax.script.ScriptException; import javax.swing.SwingUtilities; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.ui.jni.JniException; +import org.cryptomator.ui.jni.MacFunctions; import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.settings.Settings; import org.slf4j.Logger; @@ -48,12 +51,14 @@ class ExitUtil { private final Stage mainWindow; private final Localization localization; private final Settings settings; + private final Optional macFunctions; @Inject - public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) { + public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional macFunctions) { this.mainWindow = mainWindow; this.localization = localization; this.settings = settings; + this.macFunctions = macFunctions; } public void initExitHandler(Runnable exitCommand) { @@ -88,6 +93,7 @@ class ExitUtil { if (Platform.isImplicitExit()) { exitCommand.run(); } else { + macFunctions.ifPresent(JniException.ignore(MacFunctions::transformToAgentApplication)); mainWindow.close(); this.showTrayNotification(trayIcon); } @@ -189,6 +195,7 @@ class ExitUtil { private void restoreFromTray(ActionEvent event) { Platform.runLater(() -> { + macFunctions.ifPresent(JniException.ignore(MacFunctions::transformToForegroundApplication)); mainWindow.show(); mainWindow.requestFocus(); }); diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java index d28721ffa..a6c4509de 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -16,6 +16,8 @@ import java.util.concurrent.ExecutionException; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.ui.jni.JniException; +import org.cryptomator.ui.jni.MacFunctions; import org.cryptomator.ui.util.ActiveWindowStyleSupport; import org.cryptomator.ui.util.DeferredCloser; import org.cryptomator.ui.util.SingleInstanceManager; @@ -67,6 +69,7 @@ public class MainApplication extends Application { } // show window and start observing its focus: + comp.nativeMacFunctions().ifPresent(JniException.ignore(MacFunctions::transformToForegroundApplication)); primaryStage.show(); ActiveWindowStyleSupport.startObservingFocus(primaryStage); comp.exitUtil().initExitHandler(this::quit); diff --git a/main/ui/src/main/java/org/cryptomator/ui/jni/JniException.java b/main/ui/src/main/java/org/cryptomator/ui/jni/JniException.java new file mode 100644 index 000000000..2fd06eb07 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/jni/JniException.java @@ -0,0 +1,24 @@ +package org.cryptomator.ui.jni; + +import java.util.function.Consumer; + +/** + * Thrown to indicate that a JNI call didn't succeed, i.e. returned an unexpected return value. + */ +public class JniException extends RuntimeException { + + protected JniException(String message) { + super(message); + } + + public static Consumer ignore(Consumer consumer) { + return value -> { + try { + consumer.accept(value); + } catch (RuntimeException e) { + // no-op + } + }; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/jni/JniModule.java b/main/ui/src/main/java/org/cryptomator/ui/jni/JniModule.java new file mode 100644 index 000000000..de72d0228 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/jni/JniModule.java @@ -0,0 +1,33 @@ +package org.cryptomator.ui.jni; + +import java.util.Optional; + +import javax.inject.Singleton; + +import org.apache.commons.lang3.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dagger.Module; +import dagger.Provides; + +@Module +public class JniModule { + + private static final Logger LOG = LoggerFactory.getLogger(JniModule.class); + + @Provides + @Singleton + Optional provideMacFunctions(MacFunctions macFunctions) { + if (SystemUtils.IS_OS_MAC) { + try { + System.loadLibrary("MacFunctions"); + return Optional.of(macFunctions); + } catch (UnsatisfiedLinkError e) { + LOG.error("Could not load JNI lib from path {}", System.getProperty("java.library.path")); + } + } + return Optional.empty(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/jni/MacFunctions.java b/main/ui/src/main/java/org/cryptomator/ui/jni/MacFunctions.java new file mode 100644 index 000000000..3cc58d7db --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/jni/MacFunctions.java @@ -0,0 +1,37 @@ +package org.cryptomator.ui.jni; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class MacFunctions { + + @Inject + MacFunctions() { + } + + /** + * Makes the current application a foreground application, which appears in the Dock and the Application Switcher. + */ + public void transformToForegroundApplication() { + int errorCode = transformToForegroundApplication0(); + if (errorCode != 0) { + throw new JniException("Failed to make app a foreground app. Error code " + errorCode); + } + } + + private native int transformToForegroundApplication0(); + + /** + * Makes the current application an agent app. Agent apps do not appear in the Dock or in the Force Quit window. + */ + public void transformToAgentApplication() { + int errorCode = transformToAgentApplication0(); + if (errorCode != 0) { + throw new JniException("Failed to make app an agent app. Error code " + errorCode); + } + } + + private native int transformToAgentApplication0(); + +}