diff --git a/pom.xml b/pom.xml
index 6c016d277..7063b72fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,6 @@
2.2
32.0.1-jre
2.15.2
- 2.10.1
20.0.1
4.4.0
9.31
@@ -212,11 +211,6 @@
dagger
${dagger.version}
-
- com.google.code.gson
- gson
- ${gson.version}
-
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 2213ff0d9..b353b7a8a 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -39,7 +39,6 @@ open module org.cryptomator.desktop {
requires com.auth0.jwt;
requires com.google.common;
requires com.fasterxml.jackson.databind;
- requires com.google.gson; // TODO replace with jackson?
requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind;
diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java
index 46aed5ea0..4100b4714 100644
--- a/src/main/java/org/cryptomator/common/settings/Settings.java
+++ b/src/main/java/org/cryptomator/common/settings/Settings.java
@@ -10,6 +10,8 @@ package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
@@ -27,59 +29,85 @@ import java.util.function.Consumer;
public class Settings {
- public static final int MIN_PORT = 1024;
- public static final int MAX_PORT = 65535;
- public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
- public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
- public static final boolean DEFAULT_START_HIDDEN = false;
- public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
- public static final boolean DEFAULT_USE_KEYCHAIN = true;
- public static final int DEFAULT_PORT = 42427;
- public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
- public static final boolean DEFAULT_DEBUG_MODE = false;
- public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
+ private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
+
+ static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
+ static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
+ static final boolean DEFAULT_START_HIDDEN = false;
+ static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
+ static final boolean DEFAULT_USE_KEYCHAIN = true;
+ static final int DEFAULT_PORT = 42427;
+ static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
+ static final boolean DEFAULT_DEBUG_MODE = false;
+ static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually
- public static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
- public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
- public static final String DEFAULT_LICENSE_KEY = "";
- public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
- public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
- public static final String DEFAULT_LANGUAGE = null;
+ static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
+ static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
+ static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
-
- private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables);
- private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
- private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
- private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
- private final BooleanProperty autoCloseVaults = new SimpleBooleanProperty(DEFAULT_AUTO_CLOSE_VAULTS);
- private final BooleanProperty useKeychain = new SimpleBooleanProperty(DEFAULT_USE_KEYCHAIN);
- private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
- private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
- private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
- private final ObjectProperty theme = new SimpleObjectProperty<>(DEFAULT_THEME);
- private final ObjectProperty keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
- private final ObjectProperty userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
- private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
- private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
+ private final ObservableList directories;
+ private final BooleanProperty askedForUpdateCheck;
+ private final BooleanProperty checkForUpdates;
+ private final BooleanProperty startHidden;
+ private final BooleanProperty autoCloseVaults;
+ private final BooleanProperty useKeychain;
+ private final IntegerProperty port;
+ private final IntegerProperty numTrayNotifications;
+ private final BooleanProperty debugMode;
+ private final ObjectProperty theme;
+ private final StringProperty keychainProvider;
+ private final ObjectProperty userInterfaceOrientation;
+ private final StringProperty licenseKey;
+ private final BooleanProperty showMinimizeButton;
private final BooleanProperty showTrayIcon;
- private final IntegerProperty windowXPosition = new SimpleIntegerProperty();
- private final IntegerProperty windowYPosition = new SimpleIntegerProperty();
- private final IntegerProperty windowWidth = new SimpleIntegerProperty();
- private final IntegerProperty windowHeight = new SimpleIntegerProperty();
- private final ObjectProperty displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
- private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
-
-
- private final StringProperty mountService = new SimpleStringProperty();
-
+ private final IntegerProperty windowXPosition;
+ private final IntegerProperty windowYPosition;
+ private final IntegerProperty windowWidth;
+ private final IntegerProperty windowHeight;
+ private final StringProperty displayConfiguration;
+ private final StringProperty language;
+ private final StringProperty mountService;
private Consumer saveCmd;
+ public static Settings create(Environment env) {
+ var defaults = new SettingsJson();
+ defaults.showTrayIcon = env.showTrayIcon();
+ return new Settings(defaults);
+ }
+
/**
- * Package-private constructor; use {@link SettingsProvider}.
+ * Recreate settings from json
+ *
+ * @param json The parsed settings.json
*/
- Settings(Environment env) {
- this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
+ Settings(SettingsJson json) {
+ this.directories = FXCollections.observableArrayList(VaultSettings::observables);
+ this.askedForUpdateCheck = new SimpleBooleanProperty(this, "askedForUpdateCheck", json.askedForUpdateCheck);
+ this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
+ this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
+ this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
+ this.useKeychain = new SimpleBooleanProperty(this, "useKeychain", json.useKeychain);
+ this.port = new SimpleIntegerProperty(this, "webDavPort", json.port);
+ this.numTrayNotifications = new SimpleIntegerProperty(this, "numTrayNotifications", json.numTrayNotifications);
+ this.debugMode = new SimpleBooleanProperty(this, "debugMode", json.debugMode);
+ this.theme = new SimpleObjectProperty<>(this, "theme", json.theme);
+ this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider);
+ this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT));
+ this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey);
+ this.showMinimizeButton = new SimpleBooleanProperty(this, "showMinimizeButton", json.showMinimizeButton);
+ this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon);
+ this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition);
+ this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
+ this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
+ this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
+ this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
+ this.language = new SimpleStringProperty(this, "language", json.language);
+ this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
+
+ this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
+
+ migrateLegacySettings(json);
directories.addListener(this::somethingChanged);
askedForUpdateCheck.addListener(this::somethingChanged);
@@ -105,6 +133,72 @@ public class Settings {
mountService.addListener(this::somethingChanged);
}
+ @SuppressWarnings("deprecation")
+ private void migrateLegacySettings(SettingsJson json) {
+ // implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
+ if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
+ this.mountService.set(switch (json.preferredVolumeImpl) {
+ case "Dokany" -> "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
+ case "FUSE" -> {
+ if (SystemUtils.IS_OS_WINDOWS) {
+ yield "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
+ } else if (SystemUtils.IS_OS_MAC) {
+ yield "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
+ } else {
+ yield "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
+ }
+ }
+ default -> {
+ if (SystemUtils.IS_OS_WINDOWS) {
+ yield "org.cryptomator.frontend.webdav.mount.WindowsMounter";
+ } else if (SystemUtils.IS_OS_MAC) {
+ yield "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
+ } else {
+ yield "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
+ }
+ }
+ });
+ }
+ }
+
+ public SettingsJson serialized() {
+ var json = new SettingsJson();
+ json.directories = directories.stream().map(VaultSettings::serialized).toList();
+ json.askedForUpdateCheck = askedForUpdateCheck.get();
+ json.checkForUpdatesEnabled = checkForUpdates.get();
+ json.startHidden = startHidden.get();
+ json.autoCloseVaults = autoCloseVaults.get();
+ json.useKeychain = useKeychain.get();
+ json.port = port.get();
+ json.numTrayNotifications = numTrayNotifications.get();
+ json.debugMode = debugMode.get();
+ json.theme = theme.get();
+ json.keychainProvider = keychainProvider.get();
+ json.uiOrientation = userInterfaceOrientation.get().name();
+ json.licenseKey = licenseKey.get();
+ json.showMinimizeButton = showMinimizeButton.get();
+ json.showTrayIcon = showTrayIcon.get();
+ json.windowXPosition = windowXPosition.get();
+ json.windowYPosition = windowYPosition.get();
+ json.windowWidth = windowWidth.get();
+ json.windowHeight = windowHeight.get();
+ json.displayConfiguration = displayConfiguration.get();
+ json.language = language.get();
+ json.mountService = mountService.get();
+ return json;
+ }
+
+ private > E parseEnum(String value, Class clazz, E defaultValue) {
+ try {
+ return Enum.valueOf(clazz, value.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ LOG.warn("No value {}.{}. Defaulting to {}.", clazz.getSimpleName(), value, defaultValue);
+ return defaultValue;
+ }
+ }
+
+
+ // TODO rename to setChangeListener
void setSaveCmd(Consumer saveCmd) {
this.saveCmd = saveCmd;
}
@@ -120,6 +214,7 @@ public class Settings {
}
/* Getter/Setter */
+ // TODO: remove accessors, make fields public
public ObservableList getDirectories() {
return directories;
@@ -141,7 +236,7 @@ public class Settings {
return autoCloseVaults;
}
- public BooleanProperty useKeychain() { return useKeychain; }
+ public BooleanProperty useKeychain() {return useKeychain;}
public IntegerProperty port() {
return port;
@@ -163,7 +258,7 @@ public class Settings {
return theme;
}
- public ObjectProperty keychainProvider() {return keychainProvider;}
+ public StringProperty keychainProvider() {return keychainProvider;}
public ObjectProperty userInterfaceOrientation() {
return userInterfaceOrientation;
@@ -197,7 +292,7 @@ public class Settings {
return windowHeight;
}
- public ObjectProperty displayConfigurationProperty() {
+ public StringProperty displayConfigurationProperty() {
return displayConfiguration;
}
diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java
new file mode 100644
index 000000000..4bd23bec7
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java
@@ -0,0 +1,86 @@
+package org.cryptomator.common.settings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+class SettingsJson {
+
+ @JsonProperty("directories")
+ List directories = List.of();
+
+ @JsonProperty("writtenByVersion")
+ String writtenByVersion;
+
+ @JsonProperty("askedForUpdateCheck")
+ boolean askedForUpdateCheck = Settings.DEFAULT_ASKED_FOR_UPDATE_CHECK;
+
+ @JsonProperty("autoCloseVaults")
+ boolean autoCloseVaults = Settings.DEFAULT_AUTO_CLOSE_VAULTS;
+
+ @JsonProperty("checkForUpdatesEnabled")
+ boolean checkForUpdatesEnabled = Settings.DEFAULT_CHECK_FOR_UPDATES;
+
+ @JsonProperty("debugMode")
+ boolean debugMode = Settings.DEFAULT_DEBUG_MODE;
+
+ @JsonProperty("theme")
+ UiTheme theme = Settings.DEFAULT_THEME;
+
+ @JsonProperty("displayConfiguration")
+ String displayConfiguration;
+
+ @JsonProperty("keychainProvider")
+ String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
+
+ @JsonProperty("language")
+ String language;
+
+ @JsonProperty("licenseKey")
+ String licenseKey;
+
+ @JsonProperty("mountService")
+ String mountService;
+
+ @JsonProperty("numTrayNotifications")
+ int numTrayNotifications = Settings.DEFAULT_NUM_TRAY_NOTIFICATIONS;
+
+ @JsonProperty("port")
+ int port = Settings.DEFAULT_PORT;
+
+ @JsonProperty("showMinimizeButton")
+ boolean showMinimizeButton = Settings.DEFAULT_SHOW_MINIMIZE_BUTTON;
+
+ @JsonProperty("showTrayIcon")
+ boolean showTrayIcon;
+
+ @JsonProperty("startHidden")
+ boolean startHidden = Settings.DEFAULT_START_HIDDEN;
+
+ @JsonProperty("uiOrientation")
+ String uiOrientation = Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
+
+ @JsonProperty("useKeychain")
+ boolean useKeychain = Settings.DEFAULT_USE_KEYCHAIN;
+
+ @JsonProperty("windowHeight")
+ int windowHeight;
+
+ @JsonProperty("windowWidth")
+ int windowWidth;
+
+ @JsonProperty("windowXPosition")
+ int windowXPosition;
+
+ @JsonProperty("windowYPosition")
+ int windowYPosition;
+
+ @Deprecated(since = "1.7.0")
+ @JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
+ String preferredVolumeImpl;
+
+}
diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
deleted file mode 100644
index abc6f629f..000000000
--- a/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*******************************************************************************
- * 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.common.settings;
-
-import com.google.gson.TypeAdapter;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
-import com.google.gson.stream.JsonWriter;
-import org.apache.commons.lang3.SystemUtils;
-import org.cryptomator.common.Environment;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javafx.geometry.NodeOrientation;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-@Singleton
-public class SettingsJsonAdapter extends TypeAdapter {
-
- private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
-
- private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
- private final Environment env;
-
- @Inject
- public SettingsJsonAdapter(Environment env) {
- this.env = env;
- }
-
- @Override
- public void write(JsonWriter out, Settings value) throws IOException {
- out.beginObject();
- out.name("writtenByVersion").value(env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse(""));
- out.name("directories");
- writeVaultSettingsArray(out, value.getDirectories());
- out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
- out.name("autoCloseVaults").value(value.autoCloseVaults().get());
- out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
- out.name("debugMode").value(value.debugMode().get());
- out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
- out.name("keychainProvider").value(value.keychainProvider().get());
- out.name("language").value((value.languageProperty().get()));
- out.name("licenseKey").value(value.licenseKey().get());
- out.name("mountService").value(value.mountService().get());
- out.name("numTrayNotifications").value(value.numTrayNotifications().get());
- out.name("port").value(value.port().get());
- out.name("showMinimizeButton").value(value.showMinimizeButton().get());
- out.name("showTrayIcon").value(value.showTrayIcon().get());
- out.name("startHidden").value(value.startHidden().get());
- out.name("theme").value(value.theme().get().name());
- out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
- out.name("useKeychain").value(value.useKeychain().get());
- out.name("windowHeight").value((value.windowHeightProperty().get()));
- out.name("windowWidth").value((value.windowWidthProperty().get()));
- out.name("windowXPosition").value((value.windowXPositionProperty().get()));
- out.name("windowYPosition").value((value.windowYPositionProperty().get()));
- out.endObject();
- }
-
- private void writeVaultSettingsArray(JsonWriter out, Iterable vaultSettings) throws IOException {
- out.beginArray();
- for (VaultSettings value : vaultSettings) {
- vaultSettingsJsonAdapter.write(out, value);
- }
- out.endArray();
- }
-
- @Override
- public Settings read(JsonReader in) throws IOException {
- Settings settings = new Settings(env);
- //1.6.x legacy
- String volumeImpl = null;
- //legacy end
- in.beginObject();
- while (in.hasNext()) {
- String name = in.nextName();
- switch (name) {
- case "writtenByVersion" -> in.skipValue(); //noop
- case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
- case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
- case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
- case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
- case "debugMode" -> settings.debugMode().set(in.nextBoolean());
- case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
- case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
- case "language" -> settings.languageProperty().set(in.nextString());
- case "licenseKey" -> settings.licenseKey().set(in.nextString());
- case "mountService" -> {
- var token = in.peek();
- if (JsonToken.STRING == token) {
- settings.mountService().set(in.nextString());
- }
- }
- case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
- case "port" -> settings.port().set(in.nextInt());
- case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
- case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
- case "startHidden" -> settings.startHidden().set(in.nextBoolean());
- case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
- case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
- case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
- case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
- case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
- case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
- case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
- //1.6.x legacy
- case "preferredVolumeImpl" -> volumeImpl = in.nextString();
- //legacy end
- default -> {
- LOG.warn("Unsupported vault setting found in JSON: {}", name);
- in.skipValue();
- }
- }
-
- }
- in.endObject();
-
- //1.6.x legacy
- if (volumeImpl != null) {
- settings.mountService().set(convertLegacyVolumeImplToMountService(volumeImpl));
- }
- //legacy end
-
- return settings;
- }
-
- private String convertLegacyVolumeImplToMountService(String volumeImpl) {
- if (volumeImpl.equals("Dokany")) {
- return "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
- } else if (volumeImpl.equals("FUSE")) {
- if (SystemUtils.IS_OS_WINDOWS) {
- return "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
- } else if (SystemUtils.IS_OS_MAC) {
- return "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
- } else {
- return "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
- }
- } else {
- if (SystemUtils.IS_OS_WINDOWS) {
- return "org.cryptomator.frontend.webdav.mount.WindowsMounter";
- } else if (SystemUtils.IS_OS_MAC) {
- return "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
- } else {
- return "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
- }
- }
- }
-
- private UiTheme parseUiTheme(String uiThemeName) {
- try {
- return UiTheme.valueOf(uiThemeName.toUpperCase());
- } catch (IllegalArgumentException e) {
- LOG.warn("Invalid ui theme {}. Defaulting to {}.", uiThemeName, Settings.DEFAULT_THEME);
- return Settings.DEFAULT_THEME;
- }
- }
-
- private NodeOrientation parseUiOrientation(String uiOrientationName) {
- try {
- return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
- } catch (IllegalArgumentException e) {
- LOG.warn("Invalid ui orientation {}. Defaulting to {}.", uiOrientationName, Settings.DEFAULT_USER_INTERFACE_ORIENTATION);
- return Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
- }
- }
-
- private List readVaultSettingsArray(JsonReader in) throws IOException {
- List result = new ArrayList<>();
- in.beginArray();
- while (!JsonToken.END_ARRAY.equals(in.peek())) {
- result.add(vaultSettingsJsonAdapter.read(in));
- }
- in.endArray();
- return result;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
index 594a36e2e..586708ed1 100644
--- a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
@@ -8,12 +8,9 @@
*******************************************************************************/
package org.cryptomator.common.settings;
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Suppliers;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,12 +19,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -44,6 +36,7 @@ import java.util.stream.Stream;
@Singleton
public class SettingsProvider implements Supplier {
+ private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final long SAVE_DELAY_MS = 1000;
@@ -51,16 +44,11 @@ public class SettingsProvider implements Supplier {
private final Supplier settings = Suppliers.memoize(this::load);
private final Environment env;
private final ScheduledExecutorService scheduler;
- private final Gson gson;
@Inject
- public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
+ public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
this.env = env;
this.scheduler = scheduler;
- this.gson = new GsonBuilder() //
- .setPrettyPrinting().setLenient().disableHtmlEscaping() //
- .registerTypeAdapter(Settings.class, settingsJsonAdapter) //
- .create();
}
@Override
@@ -69,28 +57,25 @@ public class SettingsProvider implements Supplier {
}
private Settings load() {
- Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings(env));
+ Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElseGet(() -> Settings.create(env));
settings.setSaveCmd(this::scheduleSave);
return settings;
}
private Stream tryLoad(Path path) {
LOG.debug("Attempting to load settings from {}", path);
- try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
- Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
- JsonElement json = JsonParser.parseReader(reader);
- if (json.isJsonObject()) {
- Settings settings = gson.fromJson(json, Settings.class);
- LOG.info("Settings loaded from {}", path);
- return Stream.of(settings);
- } else {
- LOG.warn("Invalid json file {}", path);
- return Stream.empty();
- }
+ try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
+ var json = JSON.reader().readValue(in, SettingsJson.class);
+ LOG.info("Settings loaded from {}", path);
+ var settings = new Settings(json);
+ return Stream.of(settings);
+ } catch (JacksonException e) {
+ LOG.warn("Failed to parse json file {}", path, e);
+ return Stream.empty();
} catch (NoSuchFileException e) {
return Stream.empty();
- } catch (IOException | JsonParseException e) {
- LOG.warn("Exception while loading settings from " + path, e);
+ } catch (IOException e) {
+ LOG.warn("Failed to load json file {}", path, e);
return Stream.empty();
}
}
@@ -116,13 +101,14 @@ public class SettingsProvider implements Supplier {
try {
Files.createDirectories(settingsPath.getParent());
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
- try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
- Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
- gson.toJson(settings, writer);
+ try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
+ var jsonObj = settings.serialized();
+ jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
+ JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
}
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
LOG.info("Settings saved to {}", settingsPath);
- } catch (IOException | JsonParseException e) {
+ } catch (IOException e) {
LOG.error("Failed to save settings.", e);
}
}
diff --git a/src/main/java/org/cryptomator/common/settings/UiTheme.java b/src/main/java/org/cryptomator/common/settings/UiTheme.java
index 24e73dad6..9c3153060 100644
--- a/src/main/java/org/cryptomator/common/settings/UiTheme.java
+++ b/src/main/java/org/cryptomator/common/settings/UiTheme.java
@@ -1,9 +1,12 @@
package org.cryptomator.common.settings;
+import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
+import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.SystemUtils;
+@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum UiTheme {
- LIGHT("preferences.interface.theme.light"), //
+ @JsonEnumDefaultValue LIGHT("preferences.interface.theme.light"), //
DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic");
diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
index 3a116b4ce..8989858e1 100644
--- a/src/main/java/org/cryptomator/common/settings/VaultSettings.java
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
@@ -6,7 +6,9 @@
package org.cryptomator.common.settings;
import com.google.common.base.CharMatcher;
+import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
+import org.apache.commons.lang3.SystemUtils;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
@@ -20,6 +22,7 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Objects;
import java.util.Random;
@@ -28,33 +31,45 @@ import java.util.Random;
*/
public class VaultSettings {
- public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
- public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
- public static final boolean DEFAULT_USES_READONLY_MODE = false;
- public static final String DEFAULT_MOUNT_FLAGS = "";
- public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
- public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
- public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
- public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
+ static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
+ static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
+ static final boolean DEFAULT_USES_READONLY_MODE = false;
+ static final String DEFAULT_MOUNT_FLAGS = ""; // TODO: remove empty default mount flags and let this property be null if not used
+ static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
+ static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
+ static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
+ static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
private static final Random RNG = new Random();
private final String id;
- private final ObjectProperty path = new SimpleObjectProperty<>();
- private final StringProperty displayName = new SimpleStringProperty();
- private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
- private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
- private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
- private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS); //TODO: remove empty default mount flags and let this property be null if not used
- private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
- private final ObjectProperty actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
- private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
- private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
+ private final ObjectProperty path;
+ private final StringProperty displayName;
+ private final BooleanProperty unlockAfterStartup;
+ private final BooleanProperty revealAfterMount;
+ private final BooleanProperty usesReadOnlyMode;
+ private final StringProperty mountFlags;
+ private final IntegerProperty maxCleartextFilenameLength;
+ private final ObjectProperty actionAfterUnlock;
+ private final BooleanProperty autoLockWhenIdle;
+ private final IntegerProperty autoLockIdleSeconds;
+ private final ObjectProperty mountPoint;
private final StringExpression mountName;
- private final ObjectProperty mountPoint = new SimpleObjectProperty<>();
- public VaultSettings(String id) {
- this.id = Objects.requireNonNull(id);
+ VaultSettings(VaultSettingsJson json) {
+ this.id = json.id;
+ this.path = new SimpleObjectProperty<>(this, "path", json.path == null ? null : Paths.get(json.path));
+ this.displayName = new SimpleStringProperty(this, "displayName", json.displayName);
+ this.unlockAfterStartup = new SimpleBooleanProperty(this, "unlockAfterStartup", json.unlockAfterStartup);
+ this.revealAfterMount = new SimpleBooleanProperty(this, "revealAfterMount", json.revealAfterMount);
+ this.usesReadOnlyMode = new SimpleBooleanProperty(this, "usesReadOnlyMode", json.usesReadOnlyMode);
+ this.mountFlags = new SimpleStringProperty(this, "mountFlags", json.mountFlags);
+ this.maxCleartextFilenameLength = new SimpleIntegerProperty(this, "maxCleartextFilenameLength", json.maxCleartextFilenameLength);
+ this.actionAfterUnlock = new SimpleObjectProperty<>(this, "actionAfterUnlock", json.actionAfterUnlock);
+ this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
+ this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
+ this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
+ // mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name;
if (displayName.isEmpty().get()) {
@@ -64,6 +79,18 @@ public class VaultSettings {
}
return normalizeDisplayName(name);
}, displayName, path));
+
+ migrateLegacySettings(json);
+ }
+
+ @SuppressWarnings("deprecation")
+ private void migrateLegacySettings(VaultSettingsJson json) {
+ // implicit migration of 1.6.x legacy setting "customMountPath" / "winDriveLetter":
+ if (json.useCustomMountPath && !Strings.isNullOrEmpty(json.customMountPath)) {
+ this.mountPoint.set(Path.of(json.customMountPath));
+ } else if (!Strings.isNullOrEmpty(json.winDriveLetter)) {
+ this.mountPoint.set(Path.of(json.winDriveLetter + ":\\"));
+ }
}
Observable[] observables() {
@@ -71,7 +98,9 @@ public class VaultSettings {
}
public static VaultSettings withRandomId() {
- return new VaultSettings(generateId());
+ var defaults = new VaultSettingsJson();
+ defaults.id = generateId();
+ return new VaultSettings(defaults);
}
private static String generateId() {
@@ -80,6 +109,23 @@ public class VaultSettings {
return BaseEncoding.base64Url().encode(randomBytes);
}
+ public VaultSettingsJson serialized() {
+ var json = new VaultSettingsJson();
+ json.id = id;
+ json.path = path.map(Path::toString).getValue();
+ json.displayName = displayName.get();
+ json.unlockAfterStartup = unlockAfterStartup.get();
+ json.revealAfterMount = revealAfterMount.get();
+ json.usesReadOnlyMode = usesReadOnlyMode.get();
+ json.mountFlags = mountFlags.get();
+ json.maxCleartextFilenameLength = maxCleartextFilenameLength.get();
+ json.actionAfterUnlock = actionAfterUnlock.get();
+ json.autoLockWhenIdle = autoLockWhenIdle.get();
+ json.autoLockIdleSeconds = autoLockIdleSeconds.get();
+ json.mountPoint = mountPoint.map(Path::toString).getValue();
+ return json;
+ }
+
//visible for testing
static String normalizeDisplayName(String original) {
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
@@ -94,6 +140,7 @@ public class VaultSettings {
}
/* Getter/Setter */
+ // TODO: remove accessors, make fields public
public String getId() {
return id;
diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java b/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java
new file mode 100644
index 000000000..2381203e5
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java
@@ -0,0 +1,62 @@
+package org.cryptomator.common.settings;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+class VaultSettingsJson {
+
+ @JsonProperty(value = "id", required = true)
+ String id;
+
+ @JsonProperty(value = "path")
+ String path;
+
+ @JsonProperty("displayName")
+ String displayName;
+
+ @JsonProperty("unlockAfterStartup")
+ boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
+
+ @JsonProperty("revealAfterMount")
+ boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
+
+ @JsonProperty("mountPoint")
+ String mountPoint;
+
+ @JsonProperty("usesReadOnlyMode")
+ boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
+
+ @JsonProperty("mountFlags")
+ String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
+
+ @JsonProperty("maxCleartextFilenameLength")
+ int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
+
+ @JsonProperty("actionAfterUnlock")
+ WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
+
+ @JsonProperty("autoLockWhenIdle")
+ boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
+
+ @JsonProperty("autoLockIdleSeconds")
+ int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
+
+ @Deprecated(since = "1.7.0")
+ @JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
+ String winDriveLetter;
+
+ @Deprecated(since = "1.7.0")
+ @JsonProperty(value = "useCustomMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
+ @JsonAlias("usesIndividualMountPath")
+ boolean useCustomMountPath;
+
+ @Deprecated(since = "1.7.0")
+ @JsonProperty(value = "customMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
+ @JsonAlias("individualMountPath")
+ String customMountPath;
+
+}
diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
deleted file mode 100644
index cffeb3aa7..000000000
--- a/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*******************************************************************************
- * 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.common.settings;
-
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
-import com.google.gson.stream.JsonWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-class VaultSettingsJsonAdapter {
-
- private static final Logger LOG = LoggerFactory.getLogger(VaultSettingsJsonAdapter.class);
-
- public void write(JsonWriter out, VaultSettings value) throws IOException {
- out.beginObject();
- out.name("id").value(value.getId());
- out.name("path").value(value.path().get().toString());
- out.name("displayName").value(value.displayName().get());
- out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
- out.name("revealAfterMount").value(value.revealAfterMount().get());
- var mountPoint = value.mountPoint().get();
- out.name("mountPoint").value(mountPoint != null ? mountPoint.toAbsolutePath().toString() : null);
- out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
- out.name("mountFlags").value(value.mountFlags().get());
- out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
- out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
- out.name("autoLockWhenIdle").value(value.autoLockWhenIdle().get());
- out.name("autoLockIdleSeconds").value(value.autoLockIdleSeconds().get());
- out.endObject();
- }
-
- public VaultSettings read(JsonReader in) throws IOException {
- String id = null;
- String path = null;
- String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
- String displayName = null;
- boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
- boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
- boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
- String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
- Path mountPoint = null;
- int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
- WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
- boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
- int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
-
- //legacy from 1.6.x
- boolean useCustomMountPath = false;
- String customMountPath = "";
- String winDriveLetter = "";
- //legacy end
-
- in.beginObject();
- while (in.hasNext()) {
- String name = in.nextName();
- switch (name) {
- case "id" -> id = in.nextString();
- case "path" -> path = in.nextString();
- case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
- case "displayName" -> displayName = in.nextString();
- case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
- case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
- case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
- case "mountFlags" -> mountFlags = in.nextString();
- case "mountPoint" -> {
- if (JsonToken.NULL == in.peek()) {
- in.nextNull();
- } else {
- mountPoint = parseMountPoint(in.nextString());
- }
- }
- case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
- case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
- case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
- case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
- //legacy from 1.6.x
- case "winDriveLetter" -> winDriveLetter = in.nextString();
- case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
- case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
- //legacy end
- default -> {
- LOG.warn("Unsupported vault setting found in JSON: {}", name);
- in.skipValue();
- }
- }
- }
- in.endObject();
-
- VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
- if (displayName != null) { //see https://github.com/cryptomator/cryptomator/pull/1318
- vaultSettings.displayName().set(displayName);
- } else {
- vaultSettings.displayName().set(mountName);
- }
- vaultSettings.path().set(Paths.get(path));
- vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
- vaultSettings.revealAfterMount().set(revealAfterMount);
- vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
- vaultSettings.mountFlags().set(mountFlags);
- vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
- vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
- vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
- vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
- vaultSettings.mountPoint().set(mountPoint);
- //legacy from 1.6.x
- if(useCustomMountPath && !customMountPath.isBlank()) {
- vaultSettings.mountPoint().set(parseMountPoint(customMountPath));
- } else if(!winDriveLetter.isBlank() ) {
- vaultSettings.mountPoint().set(parseMountPoint(winDriveLetter+":\\"));
- }
- //legacy end
- return vaultSettings;
- }
-
- private Path parseMountPoint(String mountPoint) {
- try {
- return Path.of(mountPoint);
- } catch (InvalidPathException e) {
- LOG.warn("Invalid string as mount point. Defaulting to null.");
- return null;
- }
- }
-
- private WhenUnlocked parseActionAfterUnlock(String actionAfterUnlockName) {
- try {
- return WhenUnlocked.valueOf(actionAfterUnlockName.toUpperCase());
- } catch (IllegalArgumentException e) {
- LOG.warn("Invalid action after unlock {}. Defaulting to {}.", actionAfterUnlockName, VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK);
- return VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
- }
- }
-
-}
diff --git a/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java b/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
index fe023222c..86aed19d5 100644
--- a/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
+++ b/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
@@ -1,9 +1,13 @@
package org.cryptomator.common.settings;
+import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum WhenUnlocked {
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
- ASK("vaultOptions.general.actionAfterUnlock.ask");
+ @JsonEnumDefaultValue ASK("vaultOptions.general.actionAfterUnlock.ask");
private String displayName;
diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java
index 0838c420e..2aa7c6abd 100644
--- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java
+++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java
@@ -34,7 +34,7 @@ public class SupportedLanguages {
var collator = Collator.getInstance(preferredLocale);
collator.setStrength(Collator.PRIMARY);
var sorted = new ArrayList();
- sorted.add(0, Settings.DEFAULT_LANGUAGE);
+ sorted.add(0, null);
sorted.add(1, ENGLISH);
LANGUAGE_TAGS.stream() //
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
diff --git a/src/main/java/org/cryptomator/ui/error/ErrorController.java b/src/main/java/org/cryptomator/ui/error/ErrorController.java
index 620a1f730..d84af9fde 100644
--- a/src/main/java/org/cryptomator/ui/error/ErrorController.java
+++ b/src/main/java/org/cryptomator/ui/error/ErrorController.java
@@ -1,11 +1,13 @@
package org.cryptomator.ui.error;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.cryptomator.common.Environment;
import org.cryptomator.common.ErrorCode;
import org.cryptomator.common.Nullable;
import org.cryptomator.ui.common.FxController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
@@ -21,8 +23,8 @@ import javafx.scene.Scene;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.stage.Stage;
+import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
@@ -38,6 +40,8 @@ import java.util.concurrent.TimeUnit;
public class ErrorController implements FxController {
+ private static final ObjectMapper JSON = new ObjectMapper();
+ private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class);
private static final String ERROR_CODES_URL = "https://gist.githubusercontent.com/cryptobot/accba9fb9555e7192271b85606f97230/raw/errorcodes.json";
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
@@ -137,11 +141,12 @@ public class ErrorController implements FxController {
}
private void loadHttpResponse(HttpResponse response) {
- if (response.statusCode() == 200) {
- Map errorDiscussionMap = new Gson().fromJson(//
- new InputStreamReader(response.body(), StandardCharsets.UTF_8),//
- new TypeToken