From 02ae2e7ca0df50b367fc2c014fc4f8ed88f04c2c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 31 Jan 2017 22:30:44 +0100 Subject: [PATCH] Vastly refactored settings, integrated new webdav-nio-adapter snapshot version, allow reconfiguration of IPv6 and Port settings during runtime --- .../org/cryptomator/ui/CryptomatorModule.java | 19 ++- .../java/org/cryptomator/ui/DebugMode.java | 7 +- .../java/org/cryptomator/ui/ExitUtil.java | 6 +- .../LocalizedFXMLViewController.java | 5 + .../ui/controllers/MainController.java | 2 +- .../ui/controllers/NotFoundController.java | 5 + .../ui/controllers/SettingsController.java | 72 +++++----- .../ui/controllers/UpgradeController.java | 5 + .../ui/controllers/WelcomeController.java | 2 +- .../ui/model/UpgradeStrategies.java | 5 + .../cryptomator/ui/model/UpgradeStrategy.java | 5 + .../UpgradeVersion3DropBundleExtension.java | 7 +- .../ui/model/UpgradeVersion3to4.java | 5 + .../ui/model/UpgradeVersion4to5.java | 5 + .../java/org/cryptomator/ui/model/Vault.java | 23 ++- .../cryptomator/ui/model/VaultComponent.java | 5 + .../org/cryptomator/ui/model/VaultList.java | 15 +- .../org/cryptomator/ui/model/VaultModule.java | 5 + .../ui/model/WindowsDriveLetters.java | 5 + .../cryptomator/ui/settings/Localization.java | 5 + .../org/cryptomator/ui/settings/Settings.java | 132 ++++++------------ .../ui/settings/SettingsInstanceCreator.java | 21 --- .../ui/settings/SettingsJsonAdapter.java | 103 ++++++++++++++ .../ui/settings/SettingsProvider.java | 15 +- .../ui/settings/VaultSettings.java | 67 ++++----- .../ui/settings/VaultSettingsJsonAdapter.java | 17 ++- .../ui/util/ApplicationVersion.java | 5 + .../cryptomator/ui/util/AsyncTaskService.java | 5 + main/ui/src/main/resources/fxml/settings.fxml | 8 +- .../ui/src/main/resources/localization/en.txt | 3 +- .../ui/settings/SettingsJsonAdapterTest.java | 40 ++++++ .../VaultSettingsJsonAdapterTest.java | 27 ++++ 32 files changed, 416 insertions(+), 235 deletions(-) delete mode 100644 main/ui/src/main/java/org/cryptomator/ui/settings/SettingsInstanceCreator.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/settings/SettingsJsonAdapter.java create mode 100644 main/ui/src/test/java/org/cryptomator/ui/settings/SettingsJsonAdapterTest.java create mode 100644 main/ui/src/test/java/org/cryptomator/ui/settings/VaultSettingsJsonAdapterTest.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java index f0681c4a8..64f7399dc 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -8,6 +8,7 @@ *******************************************************************************/ package org.cryptomator.ui; +import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -22,12 +23,14 @@ import org.cryptomator.keychain.KeychainModule; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.settings.SettingsProvider; import org.cryptomator.ui.util.DeferredCloser; +import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dagger.Module; import dagger.Provides; import javafx.application.Application; +import javafx.beans.binding.Binding; import javafx.stage.Stage; @Module(includes = {CommonsModule.class, KeychainModule.class, JniModule.class, CryptoLibModule.class}) @@ -83,8 +86,20 @@ class CryptomatorModule { @Provides @Singleton - WebDavServer provideWebDavServer(Settings settings) { - return WebDavServer.create("localhost", settings.getPort()); + Binding provideServerSocketAddressBinding(Settings settings) { + return EasyBind.combine(settings.useIpv6(), settings.port(), (useIpv6, port) -> { + String host = useIpv6 ? "::1" : "localhost"; + return InetSocketAddress.createUnresolved(host, port.intValue()); + }); + } + + @Provides + @Singleton + WebDavServer provideWebDavServer(Binding serverSocketAddressBinding) { + WebDavServer server = WebDavServer.create(); + // no need to unsubscribe eventually, because server is a singleton + EasyBind.subscribe(serverSocketAddressBinding, server::bind); + return server; } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java b/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java index 037e68a42..55d79327b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java +++ b/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui; import static java.util.Arrays.asList; @@ -36,7 +41,7 @@ public class DebugMode { } public void initialize() { - if (settings.getDebugMode()) { + if (settings.debugMode().get()) { enable(); LOG.debug("Debug mode initialized"); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index 7352d149c..9126c8ff9 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -147,11 +147,11 @@ class ExitUtil { } private void showTrayNotification(TrayIcon trayIcon) { - if (settings.getNumTrayNotifications() <= 0) { + int remainingTrayNotification = settings.numTrayNotifications().get(); + if (remainingTrayNotification <= 0) { return; } else { - settings.setNumTrayNotifications(settings.getNumTrayNotifications() - 1); - settings.save(); + settings.numTrayNotifications().set(remainingTrayNotification - 1); } final Runnable notificationCmd; if (SystemUtils.IS_OS_MAC_OSX) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java index 9a480fb4e..de1496fbb 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.controllers; import org.cryptomator.ui.settings.Localization; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 04abc6900..40efea30f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -232,7 +232,7 @@ public class MainController extends LocalizedFXMLViewController { } final VaultSettings vaultSettings = VaultSettings.withRandomId(); - vaultSettings.setPath(vaultPath); + vaultSettings.path().set(vaultPath); final Vault vault = vaultFactoy.get(vaultSettings); if (!vaults.contains(vault)) { vaults.add(vault); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java index 20828b456..84388e51d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.controllers; import java.net.URL; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index 7861a5962..6e1bdf263 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -18,9 +18,11 @@ import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.settings.Localization; import org.cryptomator.ui.settings.Settings; -import org.fxmisc.easybind.EasyBind; +import javafx.beans.binding.Bindings; +import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; @@ -44,6 +46,9 @@ public class SettingsController extends LocalizedFXMLViewController { @FXML private TextField portField; + @FXML + private Button changePortButton; + @FXML private Label useIpv6Label; @@ -65,25 +70,26 @@ public class SettingsController extends LocalizedFXMLViewController { @Override public void initialize() { checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally()); - checkForUpdatesCheckbox.setSelected(settings.isCheckForUpdatesEnabled() && !areUpdatesManagedExternally()); - portField.setText(String.valueOf(settings.getPort())); + checkForUpdatesCheckbox.setSelected(settings.checkForUpdates().get() && !areUpdatesManagedExternally()); + portField.setText(String.valueOf(settings.port().intValue())); portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents); + changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty())); + changePortButton.disableProperty().bind(Bindings.createBooleanBinding(this::isPortValid, portField.textProperty()).not()); useIpv6Label.setVisible(SystemUtils.IS_OS_WINDOWS); useIpv6Checkbox.setVisible(SystemUtils.IS_OS_WINDOWS); - useIpv6Checkbox.setSelected(SystemUtils.IS_OS_WINDOWS && settings.shouldUseIpv6()); + useIpv6Checkbox.setSelected(SystemUtils.IS_OS_WINDOWS && settings.useIpv6().get()); versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion().orElse("SNAPSHOT"))); prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX); prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX); prefGvfsScheme.getItems().add("dav"); prefGvfsScheme.getItems().add("webdav"); - prefGvfsScheme.setValue(settings.getPreferredGvfsScheme()); - debugModeCheckbox.setSelected(settings.getDebugMode()); + prefGvfsScheme.setValue(settings.preferredGvfsScheme().get()); + debugModeCheckbox.setSelected(settings.debugMode().get()); - EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), this::checkForUpdateDidChange); - EasyBind.subscribe(portField.textProperty(), this::portDidChange); - EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), this::useIpv6DidChange); - EasyBind.subscribe(prefGvfsScheme.valueProperty(), this::prefGvfsSchemeDidChange); - EasyBind.subscribe(debugModeCheckbox.selectedProperty(), this::debugModeDidChange); + settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty()); + settings.useIpv6().bind(useIpv6Checkbox.selectedProperty()); + settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty()); + settings.debugMode().bind(debugModeCheckbox.selectedProperty()); } @Override @@ -95,38 +101,28 @@ public class SettingsController extends LocalizedFXMLViewController { return Optional.ofNullable(getClass().getPackage().getImplementationVersion()); } - private void checkForUpdateDidChange(Boolean newValue) { - settings.setCheckForUpdatesEnabled(newValue); - settings.save(); - } - - private void portDidChange(String newValue) { + @FXML + private void changePort(ActionEvent evt) { + assert isPortValid() : "Button must be disabled, if port is invalid."; try { - int port = Integer.parseInt(newValue); - if (!settings.isPortValid(port)) { - settings.setPort(Settings.DEFAULT_PORT); - } else { - settings.setPort(port); - settings.save(); - } + int port = Integer.parseInt(portField.getText()); + settings.port().set(port); } catch (NumberFormatException e) { - portField.setText(String.valueOf(Settings.DEFAULT_PORT)); + throw new IllegalStateException("Button must be disabled, if port is invalid.", e); } } - private void useIpv6DidChange(Boolean newValue) { - settings.setUseIpv6(newValue); - settings.save(); - } - - private void debugModeDidChange(Boolean newValue) { - settings.setDebugMode(newValue); - settings.save(); - } - - private void prefGvfsSchemeDidChange(String newValue) { - settings.setPreferredGvfsScheme(newValue); - settings.save(); + private boolean isPortValid() { + try { + int port = Integer.parseInt(portField.getText()); + if (port == 0 || port >= Settings.MIN_PORT && port <= Settings.MAX_PORT) { + return true; + } else { + return false; + } + } catch (NumberFormatException e) { + return false; + } } private void filterNumericKeyEvents(KeyEvent t) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java index 1890be6e2..2c1840481 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.controllers; import java.net.URL; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java index a163f02e0..a3c825830 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java @@ -82,7 +82,7 @@ public class WelcomeController extends LocalizedFXMLViewController { public void initialize() { if (areUpdatesManagedExternally()) { checkForUpdatesContainer.setVisible(false); - } else if (settings.isCheckForUpdatesEnabled()) { + } else if (settings.checkForUpdates().get()) { this.checkForUpdates(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java index d29b45ff2..abb918e5c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.util.Arrays; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java index eea0a8866..b2c1b2e8f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.io.IOException; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java index 58add9143..e8520686b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.io.IOException; @@ -60,7 +65,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy { LOG.info("Renaming {} to {}", path, newPath.getFileName()); Files.move(path, path.resolveSibling(newVaultName)); Platform.runLater(() -> { - vault.getVaultSettings().setPath(newPath); + vault.getVaultSettings().path().set(newPath); settings.save(); }); } catch (IOException e) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java index c7b8d2844..e28e2ff8b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java index 0fa7cd7b6..53296c3cd 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.io.IOException; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index d3b77d522..e933f1eef 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -114,7 +114,7 @@ public class Vault { if (!server.isRunning()) { server.start(); } - servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.getMountName()); + servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get()); servlet.start(); unlockSuccess = true; @@ -167,17 +167,16 @@ public class Vault { } public String getWebDavUrl() { - // TODO implement - return "http://localhost/not/implemented"; + return servlet.getServletRootUri().toString(); } public Path getPath() { - return vaultSettings.pathProperty().getValue(); + return vaultSettings.path().getValue(); } public Binding displayablePath() { Path homeDir = Paths.get(SystemUtils.USER_HOME); - return EasyBind.map(vaultSettings.pathProperty(), p -> { + return EasyBind.map(vaultSettings.path(), p -> { if (p.startsWith(homeDir)) { Path relativePath = homeDir.relativize(p); String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; @@ -192,7 +191,7 @@ public class Vault { * @return Directory name without preceeding path components and file extension */ public Binding name() { - return EasyBind.map(vaultSettings.pathProperty(), Path::getFileName).map(Path::toString); + return EasyBind.map(vaultSettings.path(), Path::getFileName).map(Path::toString); } public boolean doesVaultDirectoryExist() { @@ -238,30 +237,30 @@ public class Vault { } public String getMountName() { - return vaultSettings.getMountName(); + return vaultSettings.mountName().get(); } public void setMountName(String mountName) throws IllegalArgumentException { if (StringUtils.isBlank(mountName)) { throw new IllegalArgumentException("mount name is empty"); } else { - vaultSettings.setMountName(mountName); + vaultSettings.mountName().set(VaultSettings.normalizeMountName(mountName)); } } public Character getWinDriveLetter() { - if (vaultSettings.getWinDriveLetter() == null) { + if (vaultSettings.winDriveLetter().get() == null) { return null; } else { - return vaultSettings.getWinDriveLetter().charAt(0); + return vaultSettings.winDriveLetter().get().charAt(0); } } public void setWinDriveLetter(Character winDriveLetter) { if (winDriveLetter == null) { - vaultSettings.setWinDriveLetter(null); + vaultSettings.winDriveLetter().set(null); } else { - vaultSettings.setWinDriveLetter(String.valueOf(winDriveLetter)); + vaultSettings.winDriveLetter().set(String.valueOf(winDriveLetter)); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java index 9ecf3dcad..eebba2a05 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import org.cryptomator.ui.model.VaultModule.PerVault; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java index 5d1f7d904..fdf22d9d9 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.util.ArrayList; @@ -9,7 +14,6 @@ import javax.inject.Singleton; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.settings.VaultSettings; -import javafx.collections.FXCollections; import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import javafx.collections.transformation.TransformationList; @@ -22,14 +26,9 @@ public class VaultList extends TransformationList { @Inject public VaultList(Settings settings, VaultFactory vaultFactory) { - this(FXCollections.observableList(settings.getDirectories()), settings, vaultFactory); - } - - private VaultList(ObservableList source, Settings settings, VaultFactory vaultFactory) { - super(source); - this.source = source; + super(settings.getDirectories()); + this.source = settings.getDirectories(); this.vaultFactory = vaultFactory; - addListener((Change change) -> settings.save()); } @Override diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java index c9c5f391c..c24168378 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.lang.annotation.Documented; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java index f6646748b..41b6799ba 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.model; import java.nio.file.FileSystems; diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java index 7507bf2b7..99f4e7a55 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Localization.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.settings; import java.io.IOException; diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java index fca6860f1..0728a66d8 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java @@ -8,58 +8,55 @@ ******************************************************************************/ package org.cryptomator.ui.settings; -import java.util.ArrayList; -import java.util.List; import java.util.function.Consumer; -import com.google.gson.annotations.Expose; -import com.google.gson.annotations.SerializedName; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; public class Settings { public static final int MIN_PORT = 1024; public static final int MAX_PORT = 65535; + public static final boolean DEFAULT_CHECK_FOR_UDPATES = true; public static final int DEFAULT_PORT = 42427; public static final boolean DEFAULT_USE_IPV6 = false; - public static final Integer DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; + public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final String DEFAULT_GVFS_SCHEME = "dav"; public static final boolean DEFAULT_DEBUG_MODE = false; private final Consumer saveCmd; - - @Expose - @SerializedName("directories") - private List directories; - - @Expose - @SerializedName("checkForUpdatesEnabled") - private Boolean checkForUpdatesEnabled; - - @Expose - @SerializedName("port") - private Integer port; - - @Expose - @SerializedName("useIpv6") - private Boolean useIpv6; - - @Expose - @SerializedName("numTrayNotifications") - private Integer numTrayNotifications; - - @Expose - @SerializedName("preferredGvfsScheme") - private String preferredGvfsScheme; - - @Expose - @SerializedName("debugMode") - private Boolean debugMode; + private final ObservableList directories = FXCollections.observableArrayList(); + private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES); + private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT); + private final BooleanProperty useIpv6 = new SimpleBooleanProperty(DEFAULT_USE_IPV6); + private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS); + private final StringProperty preferredGvfsScheme = new SimpleStringProperty(DEFAULT_GVFS_SCHEME); + private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE); /** * Package-private constructor; use {@link SettingsProvider}. */ Settings(Consumer saveCmd) { this.saveCmd = saveCmd; + directories.addListener((ListChangeListener.Change change) -> this.save()); + checkForUpdates.addListener(this::somethingChanged); + port.addListener(this::somethingChanged); + useIpv6.addListener(this::somethingChanged); + numTrayNotifications.addListener(this::somethingChanged); + preferredGvfsScheme.addListener(this::somethingChanged); + debugMode.addListener(this::somethingChanged); + } + + private void somethingChanged(ObservableValue observable, Object oldValue, Object newValue) { + this.save(); } public void save() { @@ -70,75 +67,32 @@ public class Settings { /* Getter/Setter */ - public List getDirectories() { - if (directories == null) { - directories = new ArrayList<>(); - } + public ObservableList getDirectories() { return directories; } - public void setDirectories(List directories) { - this.directories = directories; + public BooleanProperty checkForUpdates() { + return checkForUpdates; } - public boolean isCheckForUpdatesEnabled() { - // not false meaning "null or true", so that true is the default value, if not setting exists yet. - return !Boolean.FALSE.equals(checkForUpdatesEnabled); + public IntegerProperty port() { + return port; } - public void setCheckForUpdatesEnabled(boolean checkForUpdatesEnabled) { - this.checkForUpdatesEnabled = checkForUpdatesEnabled; + public BooleanProperty useIpv6() { + return useIpv6; } - public void setPort(int port) { - if (!isPortValid(port)) { - throw new IllegalArgumentException("Invalid port"); - } - this.port = port; + public IntegerProperty numTrayNotifications() { + return numTrayNotifications; } - public int getPort() { - if (port == null || !isPortValid(port)) { - return DEFAULT_PORT; - } else { - return port; - } + public StringProperty preferredGvfsScheme() { + return preferredGvfsScheme; } - public boolean isPortValid(int port) { - return port == DEFAULT_PORT || port >= MIN_PORT && port <= MAX_PORT || port == 0; - } - - public boolean shouldUseIpv6() { - return useIpv6 == null ? DEFAULT_USE_IPV6 : useIpv6; - } - - public void setUseIpv6(boolean useIpv6) { - this.useIpv6 = useIpv6; - } - - public Integer getNumTrayNotifications() { - return numTrayNotifications == null ? DEFAULT_NUM_TRAY_NOTIFICATIONS : numTrayNotifications; - } - - public void setNumTrayNotifications(Integer numTrayNotifications) { - this.numTrayNotifications = numTrayNotifications; - } - - public String getPreferredGvfsScheme() { - return preferredGvfsScheme == null ? DEFAULT_GVFS_SCHEME : preferredGvfsScheme; - } - - public void setPreferredGvfsScheme(String preferredGvfsScheme) { - this.preferredGvfsScheme = preferredGvfsScheme; - } - - public boolean getDebugMode() { - return debugMode == null ? DEFAULT_DEBUG_MODE : debugMode; - } - - public void setDebugMode(boolean debugMode) { - this.debugMode = debugMode; + public BooleanProperty debugMode() { + return debugMode; } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsInstanceCreator.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsInstanceCreator.java deleted file mode 100644 index 66367ac12..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsInstanceCreator.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.cryptomator.ui.settings; - -import java.lang.reflect.Type; -import java.util.function.Consumer; - -import com.google.gson.InstanceCreator; - -class SettingsInstanceCreator implements InstanceCreator { - - private final Consumer saveCmd; - - public SettingsInstanceCreator(Consumer saveCmd) { - this.saveCmd = saveCmd; - } - - @Override - public Settings createInstance(Type type) { - return new Settings(saveCmd); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsJsonAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsJsonAdapter.java new file mode 100644 index 000000000..4d66a874a --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsJsonAdapter.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.ui.settings; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class SettingsJsonAdapter extends TypeAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class); + + private final Consumer saveCmd; + private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter(); + + public SettingsJsonAdapter(Consumer saveCmd) { + this.saveCmd = saveCmd; + } + + @Override + public void write(JsonWriter out, Settings value) throws IOException { + out.beginObject(); + out.name("directories"); + writeVaultSettingsArray(out, value.getDirectories()); + out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get()); + out.name("port").value(value.port().get()); + out.name("useIpv6").value(value.useIpv6().get()); + out.name("numTrayNotifications").value(value.numTrayNotifications().get()); + out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get()); + out.name("debugMode").value(value.debugMode().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(saveCmd); + + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); + switch (name) { + case "directories": + settings.getDirectories().addAll(readVaultSettingsArray(in)); + break; + case "checkForUpdatesEnabled": + settings.checkForUpdates().set(in.nextBoolean()); + break; + case "port": + settings.port().set(in.nextInt()); + break; + case "useIpv6": + settings.useIpv6().set(in.nextBoolean()); + break; + case "numTrayNotifications": + settings.numTrayNotifications().set(in.nextInt()); + break; + case "preferredGvfsScheme": + settings.preferredGvfsScheme().set(in.nextString()); + break; + case "debugMode": + settings.debugMode().set(in.nextBoolean()); + break; + default: + LOG.warn("Unsupported vault setting found in JSON: " + name); + in.skipValue(); + } + } + in.endObject(); + + return settings; + } + + 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/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java index 8a8c94b0d..d074f7f68 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java @@ -21,7 +21,6 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.Objects; import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -63,15 +62,15 @@ public class SettingsProvider implements Provider { private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor(); private final AtomicReference> scheduledSaveCmd = new AtomicReference<>(); private final AtomicReference settings = new AtomicReference<>(); - private final SettingsInstanceCreator settingsInstanceCreator = new SettingsInstanceCreator(this::scheduleSave); + private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter(this::scheduleSave); private final Gson gson; @Inject public SettingsProvider() { - GsonBuilder gsonBuilder = new GsonBuilder() // - .setPrettyPrinting().setLenient().disableHtmlEscaping().excludeFieldsWithoutExposeAnnotation(); - gsonBuilder.registerTypeAdapter(Settings.class, settingsInstanceCreator); - this.gson = gsonBuilder.create(); + this.gson = new GsonBuilder() // + .setPrettyPrinting().setLenient().disableHtmlEscaping() // + .registerTypeAdapter(Settings.class, settingsJsonAdapter) // + .create(); } private Path getSettingsPath() { @@ -101,7 +100,7 @@ public class SettingsProvider implements Provider { LOG.info("Settings loaded from " + settingsPath); } catch (IOException e) { LOG.info("Failed to load settings, creating new one."); - settings = settingsInstanceCreator.createInstance(Settings.class); + settings = new Settings(this::scheduleSave); } return settings; } @@ -120,7 +119,7 @@ public class SettingsProvider implements Provider { } private void save(Settings settings) { - Objects.requireNonNull(settings); + assert settings != null : "method should only be invoked by #scheduleSave, which checks for null"; final Path settingsPath = getSettingsPath(); try { Files.createDirectories(settingsPath.getParent()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettings.java index 168e6ef19..6bdf0c4a5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettings.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.settings; import java.nio.ByteBuffer; @@ -8,23 +13,31 @@ import java.util.Objects; import java.util.UUID; import org.apache.commons.lang3.StringUtils; +import org.fxmisc.easybind.EasyBind; -import com.google.gson.annotations.JsonAdapter; - -import javafx.beans.property.Property; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; -@JsonAdapter(VaultSettingsJsonAdapter.class) public class VaultSettings { private final String id; - private final Property path = new SimpleObjectProperty<>(); - private final Property mountName = new SimpleStringProperty(); - private final Property winDriveLetter = new SimpleStringProperty(); + private final ObjectProperty path = new SimpleObjectProperty<>(); + private final StringProperty mountName = new SimpleStringProperty(); + private final StringProperty winDriveLetter = new SimpleStringProperty(); public VaultSettings(String id) { this.id = Objects.requireNonNull(id); + + EasyBind.subscribe(path, this::deriveMountNameFromPath); + // TODO: automatically save settings, when chaning vaultSettings + } + + private void deriveMountNameFromPath(Path path) { + if (path != null && StringUtils.isBlank(mountName.get())) { + mountName.set(normalizeMountName(path.getFileName().toString())); + } } public static VaultSettings withRandomId() { @@ -48,10 +61,7 @@ public class VaultSettings { return uuidBuffer.array(); } - /* - * visible for testing - */ - static String normalizeMountName(String mountName) { + public static String normalizeMountName(String mountName) { String normalizedMountName = StringUtils.stripAccents(mountName); StringBuilder builder = new StringBuilder(); for (char c : normalizedMountName.toCharArray()) { @@ -76,43 +86,16 @@ public class VaultSettings { return id; } - public Property pathProperty() { + public ObjectProperty path() { return path; } - public Path getPath() { - return path.getValue(); - } - - public void setPath(Path path) { - this.path.setValue(path); - if (StringUtils.isBlank(getMountName())) { - setMountName(path.getFileName().toString()); - } - } - - public Property mountNameProperty() { + public StringProperty mountName() { return mountName; } - public String getMountName() { - return mountName.getValue(); - } - - public void setMountName(String mountName) { - this.mountName.setValue(normalizeMountName(mountName)); - } - - public Property winDriveLetterProperty() { - return mountName; - } - - public String getWinDriveLetter() { - return winDriveLetter.getValue(); - } - - public void setWinDriveLetter(String winDriveLetter) { - this.winDriveLetter.setValue(winDriveLetter); + public StringProperty winDriveLetter() { + return winDriveLetter; } /* Hashcode/Equals */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettingsJsonAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettingsJsonAdapter.java index bbccdc159..7dcceecd2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettingsJsonAdapter.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/VaultSettingsJsonAdapter.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.settings; import java.io.IOException; @@ -18,9 +23,9 @@ class VaultSettingsJsonAdapter extends TypeAdapter { public void write(JsonWriter out, VaultSettings value) throws IOException { out.beginObject(); out.name("id").value(value.getId()); - out.name("path").value(value.getPath().toString()); - out.name("mountName").value(value.getMountName()); - out.name("winDriveLetter").value(value.getWinDriveLetter()); + out.name("path").value(value.path().get().toString()); + out.name("mountName").value(value.mountName().get()); + out.name("winDriveLetter").value(value.winDriveLetter().get()); out.endObject(); } @@ -55,9 +60,9 @@ class VaultSettingsJsonAdapter extends TypeAdapter { in.endObject(); VaultSettings settings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id); - settings.setMountName(mountName); - settings.setPath(Paths.get(path)); - settings.setWinDriveLetter(winDriveLetter); + settings.mountName().set(mountName); + settings.path().set(Paths.get(path)); + settings.winDriveLetter().set(winDriveLetter); return settings; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java b/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java index beaaf46df..8b466b099 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.util; import java.util.Optional; diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/AsyncTaskService.java b/main/ui/src/main/java/org/cryptomator/ui/util/AsyncTaskService.java index e61a5b774..8eaf198d5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/AsyncTaskService.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/AsyncTaskService.java @@ -1,3 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ package org.cryptomator.ui.util; import java.util.ArrayList; diff --git a/main/ui/src/main/resources/fxml/settings.fxml b/main/ui/src/main/resources/fxml/settings.fxml index e18995898..5ce7db82f 100644 --- a/main/ui/src/main/resources/fxml/settings.fxml +++ b/main/ui/src/main/resources/fxml/settings.fxml @@ -16,6 +16,8 @@ + +