isAutoStartEnabled();
-
- void enableAutoStart() throws TogglingAutoStartFailedException;
-
- void disableAutoStart() throws TogglingAutoStartFailedException;
-
- class TogglingAutoStartFailedException extends Exception {
-
- public TogglingAutoStartFailedException(String message) {
- super(message);
- }
-
- public TogglingAutoStartFailedException(String message, Throwable cause) {
- super(message, cause);
- }
-
- }
-}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java
deleted file mode 100644
index b3c0a4674..000000000
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package org.cryptomator.ui.preferences;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * OS specific class to check, en- and disable the auto start on Windows.
- *
- * Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder.
- *
- * The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator.
- * The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}.
- *
- * To check if the feature is active, both strategies are applied.
- * To enable the feature, first the registry is tried and only on failure the autostart folder is used.
- * To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed.
- *
- * @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads.
- * @deprecated To be moved to integration-win project
- */
-@Deprecated
-class AutoStartWinStrategy implements AutoStartStrategy {
-
- private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class);
- private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\"";
- private static final String AUTOSTART_VALUE = "Cryptomator";
- private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk";
-
- private final String exePath;
-
- private boolean activatedUsingFolder;
- private boolean activatedUsingRegistry;
-
- public AutoStartWinStrategy(String exePath) {
- this.exePath = exePath;
- this.activatedUsingFolder = false;
- this.activatedUsingRegistry = false;
- }
-
- @Override
- public CompletionStage isAutoStartEnabled() {
- return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder);
- }
-
- private CompletableFuture isAutoStartEnabledUsingFolder() {
- Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY);
- this.activatedUsingFolder = Files.exists(autoStartEntry);
- return CompletableFuture.completedFuture(activatedUsingFolder);
- }
-
- private CompletableFuture isAutoStartEnabledUsingRegistry() {
- ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, //
- "/v", AUTOSTART_VALUE);
- try {
- Process proc = regQuery.start();
- return proc.onExit().thenApply(p -> {
- this.activatedUsingRegistry = p.exitValue() == 0;
- return activatedUsingRegistry;
- });
- } catch (IOException e) {
- LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
- return CompletableFuture.completedFuture(false);
- }
- }
-
- @Override
- public void enableAutoStart() throws TogglingAutoStartFailedException {
- try {
- enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> {
- LOG.debug("Falling back to using autostart folder.");
- return this.enableAutoStartUsingFolder();
- }).thenAccept((Void v) -> this.activatedUsingFolder = true).get();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted.");
- } catch (ExecutionException e) {
- throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder.");
- }
- }
-
- private CompletableFuture enableAutoStartUsingRegistry() {
- ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, //
- "/v", AUTOSTART_VALUE, //
- "/t", "REG_SZ", //
- "/d", "\"" + exePath + "\"", //
- "/f");
- try {
- Process proc = regAdd.start();
- boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
- if (finishedInTime && proc.exitValue() == 0) {
- LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
- return CompletableFuture.completedFuture(null);
- } else {
- throw new IOException("Process exited with error code " + proc.exitValue());
- }
- } catch (IOException e) {
- LOG.debug("Registry could not be edited to set auto start.", e);
- return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed."));
- }
- }
-
- private CompletableFuture enableAutoStartUsingFolder() {
- String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY;
- String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();";
- ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand);
- try {
- Process proc = shortcutAdd.start();
- boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
- if (finishedInTime && proc.exitValue() == 0) {
- LOG.debug("Created file {} for auto start.", autoStartFolderEntry);
- return CompletableFuture.completedFuture(null);
- } else {
- throw new IOException("Process exited with error code " + proc.exitValue());
- }
- } catch (IOException e) {
- LOG.debug("Adding entry to auto start folder failed.", e);
- return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed."));
- }
- }
-
-
- @Override
- public void disableAutoStart() throws TogglingAutoStartFailedException {
- if (activatedUsingRegistry) {
- disableAutoStartUsingRegistry().whenComplete((voit, ex) -> {
- if (ex == null) {
- this.activatedUsingRegistry = false;
- }
- });
- }
-
- if (activatedUsingFolder) {
- disableAutoStartUsingFolder().whenComplete((voit, ex) -> {
- if (ex == null) {
- this.activatedUsingFolder = false;
- }
- });
- }
-
- if (activatedUsingRegistry || activatedUsingFolder) {
- throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder.");
- }
- }
-
- public CompletableFuture disableAutoStartUsingRegistry() {
- ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, //
- "/v", AUTOSTART_VALUE, //
- "/f");
- try {
- Process proc = regRemove.start();
- boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
- if (finishedInTime && proc.exitValue() == 0) {
- LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
- return CompletableFuture.completedFuture(null);
- } else {
- throw new IOException("Process exited with error code " + proc.exitValue());
- }
- } catch (IOException e) {
- LOG.debug("Registry could not be edited to remove auto start.", e);
- return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed."));
- }
- }
-
- private CompletableFuture disableAutoStartUsingFolder() {
- try {
- Files.delete(Path.of(WINDOWS_START_MENU_ENTRY));
- LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY);
- return CompletableFuture.completedFuture(null);
- } catch (NoSuchFileException e) {
- //that is also okay
- return CompletableFuture.completedFuture(null);
- } catch (IOException e) {
- LOG.debug("Failed to delete entry from auto start folder.", e);
- return CompletableFuture.failedFuture(e);
- }
- }
-
- private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) {
- boolean finishedInTime = false;
- try {
- finishedInTime = proc.waitFor(timeout, timeUnit);
- } catch (InterruptedException e) {
- LOG.error("Timeout while reading registry", e);
- Thread.currentThread().interrupt();
- } finally {
- if (!finishedInTime) {
- proc.destroyForcibly();
- }
- }
- return finishedInTime;
- }
-
- private class SystemCommandException extends RuntimeException {
-
- public SystemCommandException(String msg) {
- super(msg);
- }
- }
-
-}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
index 496ada2ce..d17cc6a51 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
@@ -5,6 +5,8 @@ import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.KeychainBackend;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
+import org.cryptomator.integrations.autostart.AutoStartProvider;
+import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
@@ -14,10 +16,8 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
-import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ObservableValue;
-import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.geometry.NodeOrientation;
import javafx.scene.control.CheckBox;
@@ -42,7 +42,7 @@ public class GeneralPreferencesController implements FxController {
private final Stage window;
private final Settings settings;
private final boolean trayMenuSupported;
- private final Optional autoStartStrategy;
+ private final Optional autoStartProvider;
private final ObjectProperty selectedTabProperty;
private final LicenseHolder licenseHolder;
private final ExecutorService executor;
@@ -61,11 +61,11 @@ public class GeneralPreferencesController implements FxController {
public RadioButton nodeOrientationRtl;
@Inject
- GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional autoStartStrategy, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
+ GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional autoStartProvider, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
this.window = window;
this.settings = settings;
this.trayMenuSupported = trayMenuSupported;
- this.autoStartStrategy = autoStartStrategy;
+ this.autoStartProvider = autoStartProvider;
this.keychainAccessProviders = keychainAccessProviders;
this.selectedTabProperty = selectedTabProperty;
this.licenseHolder = licenseHolder;
@@ -89,11 +89,7 @@ public class GeneralPreferencesController implements FxController {
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode());
- autoStartStrategy.ifPresent(autoStart -> {
- autoStart.isAutoStartEnabled().thenAccept(enabled -> {
- Platform.runLater(() -> autoStartCheckbox.setSelected(enabled));
- });
- });
+ autoStartProvider.ifPresent(realProvider -> autoStartCheckbox.setSelected(realProvider.isEnabled()));
nodeOrientationLtr.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.LEFT_TO_RIGHT);
nodeOrientationRtl.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.RIGHT_TO_LEFT);
@@ -114,7 +110,7 @@ public class GeneralPreferencesController implements FxController {
}
public boolean isAutoStartSupported() {
- return autoStartStrategy.isPresent();
+ return autoStartProvider.isPresent();
}
private void toggleNodeOrientation(@SuppressWarnings("unused") ObservableValue extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
@@ -129,15 +125,19 @@ public class GeneralPreferencesController implements FxController {
@FXML
public void toggleAutoStart() {
- autoStartStrategy.ifPresent(autoStart -> {
+ autoStartProvider.ifPresent(autoStart -> {
boolean enableAutoStart = autoStartCheckbox.isSelected();
- Task toggleTask = new ToggleAutoStartTask(autoStart, enableAutoStart);
- toggleTask.setOnFailed(event -> {
+ try {
+ if (enableAutoStart) {
+ autoStart.enable();
+ } else {
+ autoStart.disable();
+ }
+ } catch (ToggleAutoStartFailedException e) {
autoStartCheckbox.setSelected(!enableAutoStart); // restore previous state
- LOG.error("Failed to toggle autostart.", event.getSource().getException());
- errorComponent.cause(event.getSource().getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
- });
- executor.execute(toggleTask);
+ LOG.error("Failed to toggle autostart.", e);
+ errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ }
});
}
@@ -196,27 +196,4 @@ public class GeneralPreferencesController implements FxController {
}
}
- private static class ToggleAutoStartTask extends Task {
-
- private final AutoStartStrategy autoStart;
- private final boolean enable;
-
- public ToggleAutoStartTask(AutoStartStrategy autoStart, boolean enable) {
- this.autoStart = autoStart;
- this.enable = enable;
-
- setOnFailed(event -> LOG.error("Failed to toggle Autostart", getException()));
- }
-
- @Override
- protected Void call() throws Exception {
- if (enable) {
- autoStart.enableAutoStart();
- } else {
- autoStart.disableAutoStart();
- }
- return null;
- }
- }
-
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
index 2558582fd..3cc012b03 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
@@ -20,7 +20,7 @@ import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
-@Module(includes = {AutoStartModule.class})
+@Module
abstract class PreferencesModule {
@Provides