Quellcode durchsuchen

Make Cryptomator a foreground app when restoring from status bar icon

Sebastian Stenzel vor 8 Jahren
Ursprung
Commit
dc58ba434a

+ 4 - 0
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<MacFunctions> nativeMacFunctions();
 }

+ 2 - 1
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);

+ 8 - 1
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> macFunctions;
 
 	@Inject
-	public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
+	public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional<MacFunctions> 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();
 		});

+ 3 - 0
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);

+ 24 - 0
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 <T> Consumer<T> ignore(Consumer<T> consumer) {
+		return value -> {
+			try {
+				consumer.accept(value);
+			} catch (RuntimeException e) {
+				// no-op
+			}
+		};
+	}
+
+}

+ 33 - 0
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<MacFunctions> 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();
+	}
+
+}

+ 37 - 0
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();
+
+}