Browse Source

Merge branch 'develop' into feature/update-reminder

Jan-Peter Klein 1 year ago
parent
commit
c18f489e9d
23 changed files with 365 additions and 36 deletions
  1. 1 0
      .github/workflows/appimage.yml
  2. 1 0
      .github/workflows/mac-dmg.yml
  3. 1 0
      .github/workflows/win-exe.yml
  4. 1 0
      dist/linux/appimage/build.sh
  5. 1 0
      dist/linux/debian/rules
  6. 1 0
      dist/mac/dmg/build.sh
  7. 1 0
      dist/win/build.ps1
  8. 23 2
      src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
  9. 118 0
      src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java
  10. 9 4
      src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
  11. 29 8
      src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
  12. 1 0
      src/main/java/org/cryptomator/ui/common/FxmlFile.java
  13. 1 0
      src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
  14. 16 3
      src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
  15. 3 1
      src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java
  16. 9 0
      src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
  17. 1 2
      src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
  18. 56 11
      src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java
  19. 2 1
      src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsComponent.java
  20. 79 0
      src/main/resources/fxml/addvault_new_expert_settings.fxml
  21. 3 3
      src/main/resources/fxml/addvault_new_location.fxml
  22. 2 1
      src/main/resources/fxml/unlock_invalid_mount_point.fxml
  23. 6 0
      src/main/resources/i18n/strings.properties

+ 1 - 0
.github/workflows/appimage.yml

@@ -85,6 +85,7 @@ jobs:
           --java-options "-Xmx256m"
           --java-options "-Dcryptomator.appVersion=\"${{  needs.get-version.outputs.semVerStr }}\""
           --java-options "-Dfile.encoding=\"utf-8\""
+          --java-options "-Djava.net.useSystemProxies=true"
           --java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
           --java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
           --java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""

+ 1 - 0
.github/workflows/mac-dmg.yml

@@ -95,6 +95,7 @@ jobs:
           --java-options "-Xss5m"
           --java-options "-Xmx256m"
           --java-options "-Dfile.encoding=\"utf-8\""
+          --java-options "-Djava.net.useSystemProxies=true"
           --java-options "-Dapple.awt.enableTemplateImages=true"
           --java-options "-Dsun.java2d.metal=true"
           --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""

+ 1 - 0
.github/workflows/win-exe.yml

@@ -112,6 +112,7 @@ jobs:
           --java-options "-Xmx256m"
           --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
           --java-options "-Dfile.encoding=\"utf-8\""
+          --java-options "-Djava.net.useSystemProxies=true"
           --java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
           --java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
           --java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""

+ 1 - 0
dist/linux/appimage/build.sh

@@ -50,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
     --java-options "-Xmx256m" \
     --app-version "${VERSION}.${REVISION_NO}" \
     --java-options "-Dfile.encoding=\"utf-8\"" \
+    --java-options "-Djava.net.useSystemProxies=true" \
     --java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
     --java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
     --java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \

+ 1 - 0
dist/linux/debian/rules

@@ -48,6 +48,7 @@ override_dh_auto_build:
 		--java-options "-Xss5m" \
 		--java-options "-Xmx256m" \
 		--java-options "-Dfile.encoding=\"utf-8\"" \
+		--java-options "-Djava.net.useSystemProxies=true" \
 		--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
 		--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
 		--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \

+ 1 - 0
dist/mac/dmg/build.sh

@@ -71,6 +71,7 @@ ${JAVA_HOME}/bin/jpackage \
     --java-options "-Xss5m" \
     --java-options "-Xmx256m" \
     --java-options "-Dfile.encoding=\"utf-8\"" \
+    --java-options "-Djava.net.useSystemProxies=true" \
     --java-options "-Dapple.awt.enableTemplateImages=true" \
     --java-options "-Dsun.java2d.metal=true" \
     --java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \

+ 1 - 0
dist/win/build.ps1

@@ -102,6 +102,7 @@ if ($clean -and (Test-Path -Path $appPath)) {
 	--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
 	--app-version "$semVerNo.$revisionNo" `
 	--java-options "-Dfile.encoding=`"utf-8`"" `
+	--java-options "-Djava.net.useSystemProxies=true" `
 	--java-options "-Dcryptomator.logDir=`"@{localappdata}/$AppName`"" `
 	--java-options "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`"" `
 	--java-options "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`"" `

+ 23 - 2
src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java

@@ -5,21 +5,23 @@ import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoMap;
 import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.ui.changepassword.NewPasswordController;
+import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
 import org.cryptomator.ui.common.DefaultSceneFactory;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.common.FxControllerKey;
 import org.cryptomator.ui.common.FxmlFile;
 import org.cryptomator.ui.common.FxmlLoaderFactory;
 import org.cryptomator.ui.common.FxmlScene;
-import org.cryptomator.ui.changepassword.NewPasswordController;
-import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
 import org.cryptomator.ui.common.StageFactory;
 import org.cryptomator.ui.fxapp.PrimaryStage;
 import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
 
 import javax.inject.Named;
 import javax.inject.Provider;
+import javafx.beans.property.IntegerProperty;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleIntegerProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.property.SimpleStringProperty;
 import javafx.beans.property.StringProperty;
@@ -65,6 +67,13 @@ public abstract class AddVaultModule {
 		return new SimpleStringProperty("");
 	}
 
+	@Provides
+	@Named("shorteningThreshold")
+	@AddVaultWizardScoped
+	static IntegerProperty provideShorteningThreshold() {
+		return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
+	}
+
 	@Provides
 	@AddVaultWizardWindow
 	@AddVaultWizardScoped
@@ -130,6 +139,13 @@ public abstract class AddVaultModule {
 		return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS);
 	}
 
+	@Provides
+	@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS)
+	@AddVaultWizardScoped
+	static Scene provideCreateNewVaultExpertSettingsScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS);
+	}
+
 	// ------------------
 
 	@Binds
@@ -181,4 +197,9 @@ public abstract class AddVaultModule {
 	@FxControllerKey(AddVaultSuccessController.class)
 	abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller);
 
+	@Binds
+	@IntoMap
+	@FxControllerKey(CreateNewVaultExpertSettingsController.class)
+	abstract FxController bindCreateNewVaultExpertSettingsController(CreateNewVaultExpertSettingsController controller);
+
 }

+ 118 - 0
src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java

@@ -0,0 +1,118 @@
+package org.cryptomator.ui.addvaultwizard;
+
+import dagger.Lazy;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.controls.NumericTextField;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javafx.application.Application;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.StringProperty;
+import javafx.fxml.FXML;
+import javafx.scene.Scene;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+import java.nio.file.Path;
+
+@AddVaultWizardScoped
+public class CreateNewVaultExpertSettingsController implements FxController {
+
+	public static final int MAX_SHORTENING_THRESHOLD = 220;
+	public static final int MIN_SHORTENING_THRESHOLD = 36;
+	private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/en/1.7/security/architecture/#name-shortening";
+
+	private final Stage window;
+	private final Lazy<Application> application;
+	private final Lazy<Scene> chooseLocationScene;
+	private final Lazy<Scene> choosePasswordScene;
+	private final StringProperty vaultNameProperty;
+	private final ObjectProperty<Path> vaultPathProperty;
+	private final IntegerProperty shorteningThreshold;
+
+	private final BooleanBinding validShorteningThreshold;
+
+	//FXML
+	public Label vaultNameLabel;
+	public Label vaultPathLabel;
+	public CheckBox expertSettingsCheckBox;
+	public NumericTextField shorteningThresholdTextField;
+
+	@Inject
+	CreateNewVaultExpertSettingsController(@AddVaultWizardWindow Stage window, //
+										   Lazy<Application> application, //
+										   @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
+										   @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, //
+										   @Named("vaultName") StringProperty vaultName, //
+										   ObjectProperty<Path> vaultPath, //
+										   @Named("shorteningThreshold") IntegerProperty shorteningThreshold) {
+		this.window = window;
+		this.application = application;
+		this.chooseLocationScene = chooseLocationScene;
+		this.choosePasswordScene = choosePasswordScene;
+		this.vaultNameProperty = vaultName;
+		this.vaultPathProperty = vaultPath;
+		this.shorteningThreshold = shorteningThreshold;
+		this.validShorteningThreshold = Bindings.createBooleanBinding(this::isValidShorteningThreshold, shorteningThreshold);
+	}
+
+	@FXML
+	public void initialize() {
+		vaultNameLabel.textProperty().bind(vaultNameProperty);
+		vaultPathLabel.textProperty().bind(vaultPathProperty.asString());
+		shorteningThresholdTextField.setPromptText(MIN_SHORTENING_THRESHOLD + "-" + MAX_SHORTENING_THRESHOLD);
+		shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
+		shorteningThresholdTextField.textProperty().addListener((observable, oldValue, newValue) -> {
+			try {
+				int intValue = Integer.parseInt(newValue);
+				shorteningThreshold.set(intValue);
+			} catch (NumberFormatException e) {
+				shorteningThreshold.set(0); //the value is set to 0 to ensure that an invalid value assignment is detected during a NumberFormatException
+			}
+		});
+	}
+
+	@FXML
+	public void toggleUseExpertSettings() {
+		if (!expertSettingsCheckBox.isSelected()) {
+			shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
+		}
+	}
+
+	@FXML
+	public void back() {
+		window.setScene(chooseLocationScene.get());
+	}
+
+	@FXML
+	public void next() {
+		window.setScene(choosePasswordScene.get());
+	}
+
+	public BooleanBinding validShorteningThresholdProperty() {
+		return validShorteningThreshold;
+	}
+
+	public boolean isValidShorteningThreshold() {
+		var value = shorteningThreshold.get();
+		return value >= MIN_SHORTENING_THRESHOLD && value <= MAX_SHORTENING_THRESHOLD;
+	}
+
+	public void openDocs() {
+		application.get().getHostServices().showDocument(DOCS_NAME_SHORTENING_URL);
+	}
+
+	public Path getVaultPath() {
+		return vaultPathProperty.get();
+	}
+
+	public String getVaultName() {
+		return vaultNameProperty.get();
+	}
+}

+ 9 - 4
src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java

@@ -48,7 +48,7 @@ public class CreateNewVaultLocationController implements FxController {
 
 	private final Stage window;
 	private final Lazy<Scene> chooseNameScene;
-	private final Lazy<Scene> choosePasswordScene;
+	private final Lazy<Scene> chooseExpertSettingsScene;
 	private final List<RadioButton> locationPresetBtns;
 	private final ObjectProperty<Path> vaultPath;
 	private final StringProperty vaultName;
@@ -68,10 +68,15 @@ public class CreateNewVaultLocationController implements FxController {
 	public FontAwesome5IconView badLocation;
 
 	@Inject
-	CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
+	CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, //
+									 @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, //
+									 @FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
+									 ObjectProperty<Path> vaultPath, //
+									 @Named("vaultName") StringProperty vaultName, //
+									 ResourceBundle resourceBundle) {
 		this.window = window;
 		this.chooseNameScene = chooseNameScene;
-		this.choosePasswordScene = choosePasswordScene;
+		this.chooseExpertSettingsScene = chooseExpertSettingsScene;
 		this.vaultPath = vaultPath;
 		this.vaultName = vaultName;
 		this.resourceBundle = resourceBundle;
@@ -151,7 +156,7 @@ public class CreateNewVaultLocationController implements FxController {
 	@FXML
 	public void next() {
 		if (validVaultPath.getValue()) {
-			window.setScene(choosePasswordScene.get());
+			window.setScene(chooseExpertSettingsScene.get());
 		}
 	}
 

File diff suppressed because it is too large
+ 29 - 8
src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java


+ 1 - 0
src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -4,6 +4,7 @@ public enum FxmlFile {
 	ADDVAULT_EXISTING("/fxml/addvault_existing.fxml"), //
 	ADDVAULT_NEW_NAME("/fxml/addvault_new_name.fxml"), //
 	ADDVAULT_NEW_LOCATION("/fxml/addvault_new_location.fxml"), //
+	ADDVAULT_NEW_EXPERT_SETTINGS("/fxml/addvault_new_expert_settings.fxml"), //
 	ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
 	ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
 	ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //

+ 1 - 0
src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java

@@ -40,6 +40,7 @@ public enum FontAwesome5Icon {
 	LOCK("\uF023"), //
 	LOCK_OPEN("\uF3C1"), //
 	MAGIC("\uF0D0"), //
+	PENCIL("\uF303"), //
 	PLUS("\uF067"), //
 	PRINT("\uF02F"), //
 	QUESTION("\uF128"), //

+ 16 - 3
src/main/java/org/cryptomator/ui/controls/FormattedLabel.java

@@ -13,18 +13,19 @@ public class FormattedLabel extends Label {
 	private final StringProperty format = new SimpleStringProperty("");
 	private final ObjectProperty<Object> arg1 = new SimpleObjectProperty<>();
 	private final ObjectProperty<Object> arg2 = new SimpleObjectProperty<>();
-	// add arg2, arg3, ... on demand
+	private final ObjectProperty<Object> arg3 = new SimpleObjectProperty<>();
+	// add arg4, arg5, ... on demand
 
 	public FormattedLabel() {
 		textProperty().bind(createStringBinding());
 	}
 
 	protected StringBinding createStringBinding() {
-		return Bindings.createStringBinding(this::updateText, format, arg1, arg2);
+		return Bindings.createStringBinding(this::updateText, format, arg1, arg2, arg3);
 	}
 
 	private String updateText() {
-		return String.format(format.get(), arg1.get(), arg2.get());
+		return String.format(format.get(), arg1.get(), arg2.get(), arg3.get());
 	}
 
 	/* Observables */
@@ -64,4 +65,16 @@ public class FormattedLabel extends Label {
 	public void setArg2(Object arg2) {
 		this.arg2.set(arg2);
 	}
+
+	public ObjectProperty<Object> arg3Property() {
+		return arg3;
+	}
+
+	public Object getArg3() {
+		return arg3.get();
+	}
+
+	public void setArg3(Object arg3) {
+		this.arg3.set(arg3);
+	}
 }

+ 3 - 1
src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java

@@ -8,6 +8,7 @@ package org.cryptomator.ui.fxapp;
 import dagger.Module;
 import dagger.Provides;
 import org.cryptomator.ui.error.ErrorComponent;
+import org.cryptomator.ui.health.HealthCheckComponent;
 import org.cryptomator.ui.lock.LockComponent;
 import org.cryptomator.ui.mainwindow.MainWindowComponent;
 import org.cryptomator.ui.preferences.PreferencesComponent;
@@ -15,12 +16,13 @@ import org.cryptomator.ui.quit.QuitComponent;
 import org.cryptomator.ui.traymenu.TrayMenuComponent;
 import org.cryptomator.ui.unlock.UnlockComponent;
 import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
+import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
 
 import javafx.scene.image.Image;
 import java.io.IOException;
 import java.io.InputStream;
 
-@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, UpdateReminderComponent.class})
+@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class, UpdateReminderComponent.class})
 abstract class FxApplicationModule {
 
 	private static Image createImageFromResource(String resourceName) throws IOException {

+ 9 - 0
src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java

@@ -14,6 +14,8 @@ import org.cryptomator.ui.quit.QuitComponent;
 import org.cryptomator.ui.unlock.UnlockComponent;
 import org.cryptomator.ui.unlock.UnlockWorkflow;
 import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
+import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
+import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,6 +50,7 @@ public class FxApplicationWindows {
 	private final LockComponent.Factory lockWorkflowFactory;
 	private final ErrorComponent.Factory errorWindowFactory;
 	private final ExecutorService executor;
+	private final VaultOptionsComponent.Factory vaultOptionsWindow;
 	private final FilteredList<Window> visibleWindows;
 
 	@Inject
@@ -60,6 +63,7 @@ public class FxApplicationWindows {
 								UpdateReminderComponent.Builder updateReminderWindowBuilder, //
 								LockComponent.Factory lockWorkflowFactory, //
 								ErrorComponent.Factory errorWindowFactory, //
+								VaultOptionsComponent.Factory vaultOptionsWindow, //
 								ExecutorService executor) {
 		this.primaryStage = primaryStage;
 		this.trayIntegration = trayIntegration;
@@ -71,6 +75,7 @@ public class FxApplicationWindows {
 		this.lockWorkflowFactory = lockWorkflowFactory;
 		this.errorWindowFactory = errorWindowFactory;
 		this.executor = executor;
+		this.vaultOptionsWindow = vaultOptionsWindow;
 		this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
 	}
 
@@ -117,6 +122,10 @@ public class FxApplicationWindows {
 		return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
 	}
 
+	public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
+		return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
+	}
+
 	public void showQuitWindow(QuitResponse response, boolean forced) {
 			CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
 	}

+ 1 - 2
src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java

@@ -19,7 +19,6 @@ import org.cryptomator.ui.health.HealthCheckComponent;
 import org.cryptomator.ui.migration.MigrationComponent;
 import org.cryptomator.ui.removevault.RemoveVaultComponent;
 import org.cryptomator.ui.stats.VaultStatisticsComponent;
-import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
 import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
 
 import javax.inject.Named;
@@ -33,7 +32,7 @@ import javafx.stage.StageStyle;
 import java.util.Map;
 import java.util.ResourceBundle;
 
-@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
+@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
 abstract class MainWindowModule {
 
 	@Provides

+ 56 - 11
src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java

@@ -8,6 +8,7 @@ import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.controls.FormattedLabel;
 import org.cryptomator.ui.fxapp.FxApplicationWindows;
 import org.cryptomator.ui.preferences.SelectedPreferencesTab;
+import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
 
 import javax.inject.Inject;
 import javafx.fxml.FXML;
@@ -21,9 +22,10 @@ public class UnlockInvalidMountPointController implements FxController {
 
 	private final Stage window;
 	private final Vault vault;
-	private final AtomicReference<Throwable> unlockException;
 	private final FxApplicationWindows appWindows;
 	private final ResourceBundle resourceBundle;
+	private final ExceptionType exceptionType;
+	private final String exceptionMessage;
 
 	public FormattedLabel dialogDescription;
 
@@ -31,22 +33,18 @@ public class UnlockInvalidMountPointController implements FxController {
 	UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault, @UnlockWindow AtomicReference<Throwable> unlockException, FxApplicationWindows appWindows, ResourceBundle resourceBundle) {
 		this.window = window;
 		this.vault = vault;
-		this.unlockException = unlockException;
 		this.appWindows = appWindows;
 		this.resourceBundle = resourceBundle;
+
+		var exc = unlockException.get();
+		this.exceptionType = getExceptionType(exc);
+		this.exceptionMessage = exc.getMessage();
 	}
 
 	@FXML
 	public void initialize() {
-		var e = unlockException.get();
-		var translationKey = switch (e) {
-			case MountPointNotSupportedException x -> "unlock.error.customPath.description.notSupported";
-			case MountPointNotExistsException x -> "unlock.error.customPath.description.notExists";
-			case MountPointInUseException x -> "unlock.error.customPath.description.inUse";
-			default -> "unlock.error.customPath.description.generic";
-		};
-		dialogDescription.setFormat(resourceBundle.getString(translationKey));
-		dialogDescription.setArg1(e.getMessage());
+		dialogDescription.setFormat(resourceBundle.getString(exceptionType.translationKey));
+		dialogDescription.setArg1(exceptionMessage);
 	}
 
 	@FXML
@@ -60,4 +58,51 @@ public class UnlockInvalidMountPointController implements FxController {
 		window.close();
 	}
 
+	@FXML
+	public void closeAndOpenVaultOptions() {
+		appWindows.showVaultOptionsWindow(vault, SelectedVaultOptionsTab.MOUNT);
+		window.close();
+	}
+
+	private ExceptionType getExceptionType(Throwable unlockException) {
+		return switch (unlockException) {
+			case MountPointNotSupportedException x -> ExceptionType.NOT_SUPPORTED;
+			case MountPointNotExistsException x -> ExceptionType.NOT_EXISTING;
+			case MountPointInUseException x -> ExceptionType.IN_USE;
+			default -> ExceptionType.GENERIC;
+		};
+	}
+
+	private enum ExceptionType {
+
+		NOT_SUPPORTED("unlock.error.customPath.description.notSupported", ButtonAction.SHOW_PREFERENCES),
+		NOT_EXISTING("unlock.error.customPath.description.notExists", ButtonAction.SHOW_VAULT_OPTIONS),
+		IN_USE("unlock.error.customPath.description.inUse", ButtonAction.SHOW_VAULT_OPTIONS),
+		GENERIC("unlock.error.customPath.description.generic", ButtonAction.SHOW_PREFERENCES);
+
+		private final String translationKey;
+		private final ButtonAction action;
+
+		ExceptionType(String translationKey, ButtonAction action) {
+			this.translationKey = translationKey;
+			this.action = action;
+		}
+	}
+
+	private enum ButtonAction {
+
+		SHOW_PREFERENCES,
+		SHOW_VAULT_OPTIONS;
+
+	}
+
+	/* Getter */
+
+	public boolean isShowPreferences() {
+		return exceptionType.action == ButtonAction.SHOW_PREFERENCES;
+	}
+
+	public boolean isShowVaultOptions() {
+		return exceptionType.action == ButtonAction.SHOW_VAULT_OPTIONS;
+	}
 }

+ 2 - 1
src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsComponent.java

@@ -28,12 +28,13 @@ public interface VaultOptionsComponent {
 
 	ObjectProperty<SelectedVaultOptionsTab> selectedTabProperty();
 
-	default void showVaultOptionsWindow(SelectedVaultOptionsTab selectedTab) {
+	default Stage showVaultOptionsWindow(SelectedVaultOptionsTab selectedTab) {
 		selectedTabProperty().set(selectedTab);
 		Stage stage = window();
 		stage.setScene(scene().get());
 		stage.show();
 		stage.requestFocus();
+		return stage;
 	}
 
 	@Subcomponent.Factory

+ 79 - 0
src/main/resources/fxml/addvault_new_expert_settings.fxml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.NumericTextField?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.CheckBox?>
+<?import javafx.scene.control.Hyperlink?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.Tooltip?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.Region?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.layout.VBox?>
+<VBox xmlns:fx="http://javafx.com/fxml"
+	  xmlns="http://javafx.com/javafx"
+	  fx:controller="org.cryptomator.ui.addvaultwizard.CreateNewVaultExpertSettingsController"
+	  prefWidth="450"
+	  prefHeight="450"
+	  spacing="12"
+	  alignment="CENTER_LEFT">
+	<padding>
+		<Insets topRightBottomLeft="24"/>
+	</padding>
+	<children>
+		<Region prefHeight="12" VBox.vgrow="NEVER"/>
+		<Label fx:id="vaultNameLabel" alignment="CENTER_RIGHT" graphicTextGap="6" wrapText="true">
+			<graphic>
+				<FontAwesome5IconView styleClass="glyph-icon-muted" wrappingWidth="12" glyph="PENCIL"/>
+			</graphic>
+		</Label>
+		<Label fx:id="vaultPathLabel" alignment="CENTER_RIGHT" graphicTextGap="6" wrapText="true">
+			<graphic>
+				<FontAwesome5IconView styleClass="glyph-icon-muted" wrappingWidth="12" glyph="HDD"/>
+			</graphic>
+		</Label>
+		<Region prefHeight="12" VBox.vgrow="NEVER"/>
+		<CheckBox fx:id="expertSettingsCheckBox" text="%addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox" onAction="#toggleUseExpertSettings"/>
+		<VBox spacing="6" visible="${expertSettingsCheckBox.selected}">
+			<HBox spacing="2" HBox.hgrow="NEVER">
+				<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title"/>
+				<Region prefWidth="2"/>
+				<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
+					<graphic>
+						<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
+					</graphic>
+					<tooltip>
+						<Tooltip text="%addvaultwizard.new.expertSettings.shorteningThreshold.tooltip" showDelay="10ms"/>
+					</tooltip>
+				</Hyperlink>
+			</HBox>
+			<NumericTextField fx:id="shorteningThresholdTextField"/>
+			<HBox alignment="TOP_RIGHT">
+				<Region minWidth="4" prefWidth="4" HBox.hgrow="NEVER"/>
+				<StackPane>
+					<Label styleClass="label-muted" text="%addvaultwizard.new.expertSettings.shorteningThreshold.invalid" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${!controller.validShorteningThreshold}" managed="${!controller.validShorteningThreshold}" graphicTextGap="6">
+						<graphic>
+							<FontAwesome5IconView styleClass="glyph-icon-red" glyph="TIMES"/>
+						</graphic>
+					</Label>
+					<Label styleClass="label-muted" text="%addvaultwizard.new.expertSettings.shorteningThreshold.valid" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.validShorteningThreshold}" managed="${controller.validShorteningThreshold}" graphicTextGap="6">
+						<graphic>
+							<FontAwesome5IconView styleClass="glyph-icon-primary" glyph="CHECK"/>
+						</graphic>
+					</Label>
+				</StackPane>
+			</HBox>
+		</VBox>
+		<Region VBox.vgrow="ALWAYS"/>
+
+		<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
+			<buttons>
+				<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>
+				<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" onAction="#next" defaultButton="true" disable="${!controller.validShorteningThreshold}"/>
+			</buttons>
+		</ButtonBar>
+	</children>
+</VBox>

+ 3 - 3
src/main/resources/fxml/addvault_new_location.fxml

@@ -20,8 +20,8 @@
 	  alignment="CENTER_LEFT">
 	<fx:define>
 		<ToggleGroup fx:id="locationPresetsToggler"/>
-		<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES" />
-		<FontAwesome5IconView fx:id="goodLocation" styleClass="glyph-icon-primary" glyph="CHECK" />
+		<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES"/>
+		<FontAwesome5IconView fx:id="goodLocation" styleClass="glyph-icon-primary" glyph="CHECK"/>
 	</fx:define>
 	<padding>
 		<Insets topRightBottomLeft="24"/>
@@ -47,7 +47,7 @@
 		<VBox spacing="6">
 			<Label text="%addvaultwizard.new.locationLabel" labelFor="$locationTextField"/>
 			<TextField fx:id="locationTextField" promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" editable="false" disable="${!controller.anyRadioButtonSelected}" HBox.hgrow="ALWAYS"/>
-			<Label fx:id="locationStatusLabel" alignment="CENTER_RIGHT" wrapText="true" visible="${controller.anyRadioButtonSelected}" maxWidth="Infinity" graphicTextGap="6" />
+			<Label fx:id="locationStatusLabel" alignment="CENTER_RIGHT" wrapText="true" visible="${controller.anyRadioButtonSelected}" maxWidth="Infinity" graphicTextGap="6"/>
 		</VBox>
 
 		<Region VBox.vgrow="ALWAYS"/>

+ 2 - 1
src/main/resources/fxml/unlock_invalid_mount_point.fxml

@@ -46,7 +46,8 @@
 			<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
 				<buttons>
 					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
-					<Button text="%hub.noKeychain.openBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenPreferences"/>
+					<Button text="%hub.noKeychain.openBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenPreferences" visible="${controller.showPreferences}" managed="${controller.showPreferences}"/>
+					<Button text="%main.vaultDetail.optionsBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenVaultOptions" visible="${controller.showVaultOptions}" managed="${controller.showVaultOptions}"/>
 				</buttons>
 			</ButtonBar>
 		</VBox>

+ 6 - 0
src/main/resources/i18n/strings.properties

@@ -63,6 +63,12 @@ addvaultwizard.new.validCharacters.message=The vault name may contain the follow
 addvaultwizard.new.validCharacters.chars=Word characters (e.g. a, ж or 수)
 addvaultwizard.new.validCharacters.numbers=Numbers
 addvaultwizard.new.validCharacters.dashes=Hyphen (%s) or underscore (%s)
+### Expert Settings
+addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Enable expert settings
+addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Enter a value between 36 and 220 (default 220)
+addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Open the documentation to learn more.
+addvaultwizard.new.expertSettings.shorteningThreshold.title=Maximum length of encrypted file names
+addvaultwizard.new.expertSettings.shorteningThreshold.valid=Valid
 ### Password
 addvaultwizard.new.createVaultBtn=Create Vault
 addvaultwizard.new.generateRecoveryKeyChoice=You won't be able to access your data without your password. Do you want a recovery key for the case you lose your password?