Prechádzať zdrojové kódy

added cmd+, shortcut to macOS menubar

Sebastian Stenzel 8 rokov pred
rodič
commit
f4265e1d73

+ 6 - 49
main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java

@@ -7,17 +7,14 @@
 package org.cryptomator.launcher;
 
 import java.io.File;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
-import java.util.List;
 import java.util.concurrent.BlockingQueue;
 
 import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.ui.util.EawtApplicationWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,7 +26,11 @@ class FileOpenRequestHandler {
 	public FileOpenRequestHandler(BlockingQueue<Path> fileOpenRequests) {
 		this.fileOpenRequests = fileOpenRequests;
 		if (SystemUtils.IS_OS_MAC_OSX) {
-			addOsxFileOpenHandler();
+			EawtApplicationWrapper.getApplication().ifPresent(app -> {
+				app.setOpenFileHandler(files -> {
+					files.stream().map(File::toPath).forEach(fileOpenRequests::add);
+				});
+			});
 		}
 	}
 
@@ -55,48 +56,4 @@ class FileOpenRequestHandler {
 		}
 	}
 
-	/**
-	 * Event subscription code inspired by https://gitlab.com/axet/desktop/blob/master/java/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java
-	 */
-	private void addOsxFileOpenHandler() {
-		try {
-			final Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
-			final Class<?> openFilesHandlerClass = Class.forName("com.apple.eawt.OpenFilesHandler");
-			final Method getApplication = applicationClass.getMethod("getApplication");
-			final Object application = getApplication.invoke(null);
-			final Method setOpenFileHandler = applicationClass.getMethod("setOpenFileHandler", openFilesHandlerClass);
-			final ClassLoader openFilesHandlerClassLoader = openFilesHandlerClass.getClassLoader();
-			final OpenFilesEventInvocationHandler openFilesHandler = new OpenFilesEventInvocationHandler();
-			final Object openFilesHandlerObject = Proxy.newProxyInstance(openFilesHandlerClassLoader, new Class<?>[] {openFilesHandlerClass}, openFilesHandler);
-			setOpenFileHandler.invoke(application, openFilesHandlerObject);
-		} catch (ReflectiveOperationException | RuntimeException e) {
-			// Since we're trying to call OS-specific code, we'll just have to hope for the best.
-			LOG.error("Exception adding OS X file open handler", e);
-		}
-	}
-
-	/**
-	 * Handler class inspired by https://gitlab.com/axet/desktop/blob/master/java/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java
-	 */
-	private class OpenFilesEventInvocationHandler implements InvocationHandler {
-		@Override
-		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-			if (method.getName().equals("openFiles")) {
-				final Class<?> openFilesEventClass = Class.forName("com.apple.eawt.AppEvent$OpenFilesEvent");
-				final Method getFiles = openFilesEventClass.getMethod("getFiles");
-				Object e = args[0];
-				try {
-					@SuppressWarnings("unchecked")
-					final List<File> ff = (List<File>) getFiles.invoke(e);
-					ff.stream().map(File::toPath).forEach(fileOpenRequests::add);
-				} catch (RuntimeException ee) {
-					throw ee;
-				} catch (Exception ee) {
-					throw new RuntimeException(ee);
-				}
-			}
-			return null;
-		}
-	}
-
 }

+ 21 - 2
main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java

@@ -37,6 +37,7 @@ import org.cryptomator.ui.model.Vault;
 import org.cryptomator.ui.model.VaultFactory;
 import org.cryptomator.ui.model.VaultList;
 import org.cryptomator.ui.util.DialogBuilderUtil;
+import org.cryptomator.ui.util.EawtApplicationWrapper;
 import org.fxmisc.easybind.EasyBind;
 import org.fxmisc.easybind.Subscription;
 import org.fxmisc.easybind.monadic.MonadicBinding;
@@ -119,6 +120,12 @@ public class MainController implements ViewController {
 
 		EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit);
 		autoUnlocker.unlockAllSilently();
+
+		EawtApplicationWrapper.getApplication().ifPresent(app -> {
+			app.setPreferencesHandler(() -> {
+				Platform.runLater(this::toggleShowSettings);
+			});
+		});
 	}
 
 	@FXML
@@ -328,10 +335,14 @@ public class MainController implements ViewController {
 
 	@FXML
 	private void didClickShowSettings(ActionEvent e) {
+		toggleShowSettings();
+	}
+
+	private void toggleShowSettings() {
 		if (isShowingSettings.get()) {
-			activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
+			showWelcomeView();
 		} else {
-			activeController.set(viewControllerLoader.load("/fxml/settings.fxml"));
+			showPreferencesView();
 		}
 		vaultList.getSelectionModel().clearSelection();
 	}
@@ -375,6 +386,14 @@ public class MainController implements ViewController {
 	// Subcontroller for right panel
 	// ****************************************
 
+	private void showWelcomeView() {
+		activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
+	}
+
+	private void showPreferencesView() {
+		activeController.set(viewControllerLoader.load("/fxml/settings.fxml"));
+	}
+
 	private void showNotFoundView() {
 		activeController.set(viewControllerLoader.load("/fxml/notfound.fxml"));
 	}

+ 105 - 0
main/ui/src/main/java/org/cryptomator/ui/util/EawtApplicationWrapper.java

@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the accompanying LICENSE file.
+ *******************************************************************************/
+package org.cryptomator.ui.util;
+
+import java.io.File;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Reflection-based wrapper for com.apple.eawt.Application.
+ */
+public class EawtApplicationWrapper {
+
+	private static final Logger LOG = LoggerFactory.getLogger(EawtApplicationWrapper.class);
+
+	private final Class<?> applicationClass;
+	private final Object application;
+
+	private EawtApplicationWrapper() throws ReflectiveOperationException {
+		this.applicationClass = Class.forName("com.apple.eawt.Application");
+		this.application = applicationClass.getMethod("getApplication").invoke(null);
+	}
+
+	public static Optional<EawtApplicationWrapper> getApplication() {
+		try {
+			return Optional.of(new EawtApplicationWrapper());
+		} catch (ReflectiveOperationException e) {
+			return Optional.empty();
+		}
+	}
+
+	private void setOpenFileHandler(InvocationHandler handler) throws ReflectiveOperationException {
+		Class<?> handlerClass = Class.forName("com.apple.eawt.OpenFilesHandler");
+		Method setter = applicationClass.getMethod("setOpenFileHandler", handlerClass);
+		Object proxy = Proxy.newProxyInstance(applicationClass.getClassLoader(), new Class<?>[] {handlerClass}, handler);
+		setter.invoke(application, proxy);
+	}
+
+	public void setOpenFileHandler(Consumer<List<File>> handler) {
+		try {
+			Class<?> openFilesEventClass = Class.forName("com.apple.eawt.AppEvent$OpenFilesEvent");
+			Method getFiles = openFilesEventClass.getMethod("getFiles");
+			setOpenFileHandler(newMethodSpecificInvocationHandler("openFiles", args -> {
+				try {
+					Object openFilesEvent = args[0];
+					@SuppressWarnings("unchecked")
+					List<File> files = (List<File>) getFiles.invoke(openFilesEvent);
+					handler.accept(files);
+				} catch (ReflectiveOperationException e) {
+					LOG.error("Error invoking openFileHandler.", e);
+				}
+				return null;
+			}));
+		} catch (ReflectiveOperationException e) {
+			LOG.error("Exception setting openFileHandler.", e);
+		}
+	}
+
+	private void setPreferencesHandler(InvocationHandler handler) throws ReflectiveOperationException {
+		Class<?> handlerClass = Class.forName("com.apple.eawt.PreferencesHandler");
+		Method setter = applicationClass.getMethod("setPreferencesHandler", handlerClass);
+		Object proxy = Proxy.newProxyInstance(applicationClass.getClassLoader(), new Class<?>[] {handlerClass}, handler);
+		setter.invoke(application, proxy);
+	}
+
+	public void setPreferencesHandler(Runnable handler) {
+		try {
+			setPreferencesHandler(newMethodSpecificInvocationHandler("handlePreferences", args -> {
+				handler.run();
+				return null;
+			}));
+		} catch (ReflectiveOperationException e) {
+			LOG.error("Exception setting preferencesHandler.", e);
+		}
+	}
+
+	@FunctionalInterface
+	private static interface MethodSpecificInvocationHandler {
+		Object invoke(Object[] args);
+	}
+
+	private static InvocationHandler newMethodSpecificInvocationHandler(String methodName, MethodSpecificInvocationHandler handler) {
+		return new InvocationHandler() {
+			@Override
+			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+				if (method.getName().equals(methodName)) {
+					return handler.invoke(args);
+				} else {
+					return null;
+				}
+			}
+		};
+	}
+
+}