This commit is contained in:
Sebastian Stenzel
2017-06-07 13:10:50 +02:00
parent 3c4494a14f
commit 7436c398f8
4 changed files with 88 additions and 52 deletions

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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,36 +50,57 @@ 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)) {
return "\uf09c";
} else if (Boolean.FALSE.equals(unlockedOrNull)) {
return "\uf023";
} else {
private String getStatusIconText(Vault.State state) {
if (state == null) {
return "";
}
switch (state) {
case UNLOCKED:
case MOUNTED:
case MOUNTING:
case UNMOUNTING:
return "\uf09c";
case LOCKED:
default:
return "\uf023";
}
}
private Paint getStatusIconColor(Boolean unlockedOrNull, Paint lockedValue) {
if (Boolean.TRUE.equals(unlockedOrNull)) {
return UNLOCKED_ICON_COLOR;
} else {
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;
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) {

View File

@@ -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) {