Sebastian Stenzel 7 years ago
parent
commit
7436c398f8

+ 6 - 5
main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java

@@ -50,6 +50,7 @@ import javafx.beans.binding.Binding;
 import javafx.beans.binding.Bindings;
 import javafx.beans.binding.BooleanBinding;
 import javafx.beans.binding.BooleanExpression;
+import javafx.beans.binding.ObjectExpression;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.collections.FXCollections;
@@ -92,9 +93,9 @@ public class MainController implements ViewController {
 	private final ObservableList<Vault> vaults;
 	private final BooleanBinding areAllVaultsLocked;
 	private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
-	private final BooleanExpression isSelectedVaultUnlocked = BooleanExpression.booleanExpression(EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty).orElse(false));
+	private final ObjectExpression<Vault.State> selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty));
 	private final BooleanExpression isSelectedVaultValid = BooleanExpression.booleanExpression(EasyBind.monadic(selectedVault).map(Vault::isValidVaultDirectory).orElse(false));
-	private final BooleanExpression canEditSelectedVault = selectedVault.isNotNull().and(isSelectedVaultUnlocked.not());
+	private final BooleanExpression canEditSelectedVault = selectedVaultState.isEqualTo(Vault.State.LOCKED);
 	private final MonadicBinding<UpgradeStrategy> upgradeStrategyForSelectedVault;
 	private final BooleanBinding isShowingSettings;
 	private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
@@ -116,7 +117,7 @@ public class MainController implements ViewController {
 		// derived bindings:
 		this.isShowingSettings = Bindings.equal(SettingsController.class, EasyBind.monadic(activeController).map(ViewController::getClass));
 		this.upgradeStrategyForSelectedVault = EasyBind.monadic(selectedVault).map(upgradeStrategies::getUpgradeStrategy);
-		this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault::isUnlocked));
+		this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault.NOT_LOCKED));
 
 		EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit);
 		autoUnlocker.unlockAllSilently();
@@ -201,7 +202,7 @@ public class MainController implements ViewController {
 	}
 
 	private void gracefulShutdown() {
-		vaults.filtered(Vault::isUnlocked).forEach(Vault::prepareForShutdown);
+		vaults.filtered(Vault.NOT_LOCKED).forEach(Vault::prepareForShutdown);
 		Platform.runLater(Platform::exit);
 	}
 
@@ -361,7 +362,7 @@ public class MainController implements ViewController {
 		if (newValue == null) {
 			return;
 		}
-		if (newValue.isUnlocked()) {
+		if (newValue.getState() != Vault.State.LOCKED) {
 			this.showUnlockedView(newValue);
 		} else if (!newValue.doesVaultDirectoryExist()) {
 			this.showNotFoundView();

+ 6 - 6
main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java

@@ -30,7 +30,7 @@ import com.google.common.util.concurrent.Runnables;
 import javafx.animation.Animation;
 import javafx.animation.KeyFrame;
 import javafx.animation.Timeline;
-import javafx.beans.binding.BooleanExpression;
+import javafx.beans.binding.ObjectExpression;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.event.ActionEvent;
@@ -64,7 +64,7 @@ public class UnlockedController implements ViewController {
 	private final Localization localization;
 	private final AsyncTaskService asyncTaskService;
 	private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
-	private final BooleanExpression vaultMounted = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(Vault::mountedProperty).orElse(false));
+	private final ObjectExpression<Vault.State> vaultState = ObjectExpression.objectExpression(EasyBind.select(vault).selectObject(Vault::stateProperty));
 	private Optional<LockListener> listener = Optional.empty();
 	private Timeline ioAnimation;
 
@@ -103,9 +103,9 @@ public class UnlockedController implements ViewController {
 
 	@Override
 	public void initialize() {
-		mountVaultMenuItem.disableProperty().bind(vaultMounted);
-		unmountVaultMenuItem.disableProperty().bind(vaultMounted.not());
-		revealVaultMenuItem.disableProperty().bind(vaultMounted.not());
+		mountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.UNLOCKED).not()); // enable when unlocked
+		unmountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
+		revealVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
 
 		EasyBind.subscribe(vault, this::vaultChanged);
 		EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
@@ -121,7 +121,7 @@ public class UnlockedController implements ViewController {
 			return;
 		}
 
-		if (newVault.getVaultSettings().mountAfterUnlock().get()) {
+		if (newVault.getState() == Vault.State.UNLOCKED && newVault.getVaultSettings().mountAfterUnlock().get()) {
 			mountVault(newVault);
 		}
 

+ 38 - 13
main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java

@@ -10,8 +10,8 @@ package org.cryptomator.ui.controls;
 
 import org.cryptomator.ui.model.Vault;
 import org.fxmisc.easybind.EasyBind;
-import org.fxmisc.easybind.monadic.MonadicBinding;
 
+import javafx.beans.binding.ObjectExpression;
 import javafx.geometry.Pos;
 import javafx.scene.control.ContentDisplay;
 import javafx.scene.control.ContextMenu;
@@ -31,9 +31,13 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
 	private final Label pathText = new Label();
 	private final VBox vbox = new VBox(4.0, nameText, pathText);
 	private final HBox hbox = new HBox(6.0, statusText, vbox);
+	private final ObjectExpression<Vault.State> vaultState;
+
 	private ContextMenu vaultContextMenu;
 
 	public DirectoryListCell() {
+		vaultState = ObjectExpression.objectExpression(EasyBind.select(itemProperty()).selectObject(Vault::stateProperty));
+
 		hbox.setAlignment(Pos.CENTER_LEFT);
 		hbox.setPrefWidth(1);
 		vbox.setFillWidth(true);
@@ -46,38 +50,59 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
 		pathText.setTextOverrun(OverrunStyle.ELLIPSIS);
 		pathText.getStyleClass().add("detail-label");
 
-		MonadicBinding<Boolean> optionalItemIsUnlocked = EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty);
-		statusText.textProperty().bind(optionalItemIsUnlocked.map(this::getStatusIconText));
-		statusText.textFillProperty().bind(EasyBind.combine(optionalItemIsUnlocked, textFillProperty(), this::getStatusIconColor));
+		statusText.textProperty().bind(EasyBind.map(vaultState, this::getStatusIconText));
+		statusText.textFillProperty().bind(EasyBind.combine(vaultState, textFillProperty(), this::getStatusIconColor));
 		statusText.setMinSize(16.0, 16.0);
 		statusText.setAlignment(Pos.CENTER);
 		statusText.getStyleClass().add("fontawesome");
 
 		tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath).map(p -> new Tooltip(p.toString())));
-		contextMenuProperty().bind(optionalItemIsUnlocked.map(unlocked -> unlocked ? null : vaultContextMenu));
+		contextMenuProperty().bind(EasyBind.map(vaultState, this::getContextMenu));
 
 		setGraphic(hbox);
 		setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
 	}
 
-	private String getStatusIconText(Boolean unlockedOrNull) {
-		if (Boolean.TRUE.equals(unlockedOrNull)) {
+	private String getStatusIconText(Vault.State state) {
+		if (state == null) {
+			return "";
+		}
+		switch (state) {
+		case UNLOCKED:
+		case MOUNTED:
+		case MOUNTING:
+		case UNMOUNTING:
 			return "\uf09c";
-		} else if (Boolean.FALSE.equals(unlockedOrNull)) {
+		case LOCKED:
+		default:
 			return "\uf023";
-		} else {
-			return "";
 		}
 	}
 
-	private Paint getStatusIconColor(Boolean unlockedOrNull, Paint lockedValue) {
-		if (Boolean.TRUE.equals(unlockedOrNull)) {
+	private Paint getStatusIconColor(Vault.State state, Paint lockedValue) {
+		if (state == null) {
+			return lockedValue;
+		}
+		switch (state) {
+		case UNLOCKED:
+		case MOUNTED:
+		case MOUNTING:
+		case UNMOUNTING:
 			return UNLOCKED_ICON_COLOR;
-		} else {
+		case LOCKED:
+		default:
 			return lockedValue;
 		}
 	}
 
+	private ContextMenu getContextMenu(Vault.State state) {
+		if (state == Vault.State.LOCKED) {
+			return vaultContextMenu;
+		} else {
+			return null;
+		}
+	}
+
 	public void setVaultContextMenu(ContextMenu contextMenu) {
 		this.vaultContextMenu = contextMenu;
 	}

+ 36 - 26
main/ui/src/main/java/org/cryptomator/ui/model/Vault.java

@@ -20,6 +20,7 @@ import java.util.EnumSet;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.inject.Inject;
 
@@ -50,25 +51,30 @@ import org.slf4j.LoggerFactory;
 import javafx.application.Platform;
 import javafx.beans.Observable;
 import javafx.beans.binding.Binding;
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
 
 @PerVault
 public class Vault {
 
+	public static final Predicate<Vault> NOT_LOCKED = hasState(State.LOCKED).negate();
 	private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
 	private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
 
 	private final Settings settings;
 	private final VaultSettings vaultSettings;
 	private final WebDavServer server;
-	private final BooleanProperty unlocked = new SimpleBooleanProperty();
-	private final BooleanProperty mounted = new SimpleBooleanProperty();
 	private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
+	private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
 
 	private WebDavServletController servlet;
 	private Mount mount;
 
+	public enum State {
+		LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING
+	};
+
 	@Inject
 	Vault(Settings settings, VaultSettings vaultSettings, WebDavServer server) {
 		this.settings = settings;
@@ -121,7 +127,7 @@ public class Vault {
 			servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
 			servlet.start();
 			Platform.runLater(() -> {
-				unlocked.set(true);
+				state.set(State.UNLOCKED);
 			});
 		} catch (IOException e) {
 			LOG.error("Unable to provide filesystem", e);
@@ -138,9 +144,12 @@ public class Vault {
 				.withPreferredGvfsScheme(settings.preferredGvfsScheme().get()) //
 				.build();
 
-		mount = servlet.mount(mountParams);
 		Platform.runLater(() -> {
-			mounted.set(true);
+			state.set(State.MOUNTING);
+		});
+		mount = servlet.mount(mountParams); // might block this thread for a while
+		Platform.runLater(() -> {
+			state.set(State.MOUNTED);
 		});
 	}
 
@@ -153,11 +162,14 @@ public class Vault {
 	}
 
 	private synchronized void unmount(Function<Mount, ? extends UnmountOperation> unmountOperationChooser) throws CommandFailedException {
+		Platform.runLater(() -> {
+			state.set(State.UNMOUNTING);
+		});
 		if (mount != null) {
 			unmountOperationChooser.apply(mount).unmount();
 		}
 		Platform.runLater(() -> {
-			mounted.set(false);
+			state.set(State.UNLOCKED);
 		});
 	}
 
@@ -174,7 +186,7 @@ public class Vault {
 			fs.close();
 		}
 		Platform.runLater(() -> {
-			unlocked.set(false);
+			state.set(State.LOCKED);
 		});
 	}
 
@@ -212,8 +224,22 @@ public class Vault {
 	// Getter/Setter
 	// *******************************************************************************/
 
+	public State getState() {
+		return state.get();
+	}
+
+	public ReadOnlyObjectProperty<State> stateProperty() {
+		return state;
+	}
+
+	public static Predicate<Vault> hasState(State state) {
+		return vault -> {
+			return vault.getState() == state;
+		};
+	}
+
 	public Observable[] observables() {
-		return new Observable[] {unlocked, mounted};
+		return new Observable[] {state};
 	}
 
 	public VaultSettings getVaultSettings() {
@@ -256,22 +282,6 @@ public class Vault {
 		return CryptoFileSystemProvider.containsVault(getPath(), MASTERKEY_FILENAME);
 	}
 
-	public BooleanProperty unlockedProperty() {
-		return unlocked;
-	}
-
-	public BooleanProperty mountedProperty() {
-		return mounted;
-	}
-
-	public boolean isUnlocked() {
-		return unlocked.get();
-	}
-
-	public boolean isMounted() {
-		return mounted.get();
-	}
-
 	public long pollBytesRead() {
 		CryptoFileSystem fs = cryptoFileSystem.get();
 		if (fs != null) {