diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 43074a605..310e11747 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -11,6 +11,8 @@ public enum FxmlFile { CHANGEPASSWORD("/fxml/changepassword.fxml"), // ERROR("/fxml/error.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), // + LOCK_FORCED("/fxml/lock_forced.fxml"), // + LOCK_FAILED("/fxml/lock_failed.fxml"), // MAIN_WINDOW("/fxml/main_window.fxml"), // MIGRATION_CAPABILITY_ERROR("/fxml/migration_capability_error.fxml"), // MIGRATION_IMPOSSIBLE("/fxml/migration_impossible.fxml"), diff --git a/main/ui/src/main/java/org/cryptomator/ui/lock/LockFailedController.java b/main/ui/src/main/java/org/cryptomator/ui/lock/LockFailedController.java new file mode 100644 index 000000000..66dd203ea --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/lock/LockFailedController.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.lock; + +import org.cryptomator.ui.common.FxController; + +import javax.inject.Inject; + +public class LockFailedController implements FxController { + + @Inject + public LockFailedController() { + + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/lock/LockForcedController.java b/main/ui/src/main/java/org/cryptomator/ui/lock/LockForcedController.java index a5746082d..a66af9902 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/lock/LockForcedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/lock/LockForcedController.java @@ -1,5 +1,48 @@ package org.cryptomator.ui.lock; -public class LockForcedController { +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.UserInteractionLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javafx.event.ActionEvent; +import javafx.stage.Stage; +import javafx.stage.WindowEvent; + +public class LockForcedController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(LockForcedController.class); + + private final Stage window; + private final Vault vault; + private final UserInteractionLock forceLockDecisionLock; + + @Inject + public LockForcedController(@LockWindow Stage window, @LockWindow Vault vault, UserInteractionLock forceLockDecisionLock) { + this.window = window; + this.vault = vault; + this.forceLockDecisionLock = forceLockDecisionLock; + this.window.setOnHiding(this::windowClosed); + } + + public void cancel() { + forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL); + window.close(); + } + + public void confirmForcedLock(ActionEvent actionEvent) { + forceLockDecisionLock.interacted(LockModule.ForceLockDecision.FORCE); + window.close(); + } + + private void windowClosed(WindowEvent windowEvent) { + // if not already interacted, mark this workflow as cancelled: + if (forceLockDecisionLock.awaitingInteraction().get()) { + LOG.debug("Lock canceled in force-lock-phase by user."); + forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL); + } + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/lock/LockModule.java b/main/ui/src/main/java/org/cryptomator/ui/lock/LockModule.java index a4ccc97cd..bbc8c2209 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/lock/LockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/lock/LockModule.java @@ -1,8 +1,89 @@ package org.cryptomator.ui.lock; +import dagger.Binds; import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoMap; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.ui.common.DefaultSceneFactory; +import org.cryptomator.ui.common.FXMLLoaderFactory; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.StageFactory; +import org.cryptomator.ui.common.UserInteractionLock; + +import javax.inject.Named; +import javax.inject.Provider; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; @Module -public class LockModule { +abstract class LockModule { + + enum ForceLockDecision { + CANCEL, + FORCE; + } + + @Provides + @LockScoped + static UserInteractionLock provideForceLockDecisionLock() { + return new UserInteractionLock<>(null); + } + + @Provides + @LockWindow + @LockScoped + static FXMLLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { + return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle); + } + + @Provides + @LockWindow + @LockScoped + static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional owner) { + Stage stage = factory.create(); + stage.setTitle(vault.getDisplayName()); + stage.setResizable(false); + if (owner.isPresent()) { + stage.initOwner(owner.get()); + stage.initModality(Modality.WINDOW_MODAL); + } else { + stage.initModality(Modality.APPLICATION_MODAL); + } + return stage; + } + + @Provides + @FxmlScene(FxmlFile.LOCK_FORCED) + @LockScoped + static Scene provideForceLockScene(@LockWindow FXMLLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene("/fxml/lock_forced.fxml"); + } + + @Provides + @FxmlScene(FxmlFile.LOCK_FAILED) + @LockScoped + static Scene provideLockFailedScene(@LockWindow FXMLLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene("/fxml/lock_failed.fxml"); + } + + // ------------------ + + @Binds + @IntoMap + @FxControllerKey(LockForcedController.class) + abstract FxController bindLockForcedController(LockForcedController controller); + + @Binds + @IntoMap + @FxControllerKey(LockFailedController.class) + abstract FxController bindLockFailedController(LockFailedController controller); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java index 381c6fdb9..d63661e02 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java +++ b/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java @@ -1,27 +1,43 @@ package org.cryptomator.ui.lock; +import dagger.Lazy; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.UserInteractionLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javafx.application.Platform; import javafx.concurrent.Task; +import javafx.scene.Scene; +import javafx.stage.Stage; +import javafx.stage.Window; public class LockWorkflow extends Task { private static final Logger LOG = LoggerFactory.getLogger(LockWorkflow.class); + private final Stage lockWindow; private final Vault vault; + private final UserInteractionLock forceLockDecisionLock; + private final Lazy lockForcedScene; + private final Lazy lockFailedScene; @Inject - public LockWorkflow(@LockWindow Vault vault) { + public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy lockFailedScene) { + this.lockWindow = lockWindow; this.vault = vault; + this.forceLockDecisionLock = forceLockDecisionLock; + this.lockForcedScene = lockForcedScene; + this.lockFailedScene = lockFailedScene; } @Override - protected Boolean call() throws Exception { + protected Boolean call() throws Volume.VolumeException, InterruptedException { // change vault state to processing -- done by overriding scheduled method of Task if (attemptLock() || attemptForcedLock()) { handleSuccess(); @@ -42,19 +58,34 @@ public class LockWorkflow extends Task { } } - private boolean attemptForcedLock() { - // show forcedLock dialogue - // wait for answer - // depening on answer do one of two things - // a) force Lock -> needs to throw exception on failure - // b) cancel - // if lock was performed over main window, show it again - return false; + private boolean attemptForcedLock() throws Volume.VolumeException, InterruptedException { + // show forcedLock dialogue ... + Platform.runLater(() -> { + lockWindow.setScene(lockForcedScene.get()); + lockWindow.show(); + Window owner = lockWindow.getOwner(); + if (owner != null) { + lockWindow.setX(owner.getX() + (owner.getWidth() - lockWindow.getWidth()) / 2); + lockWindow.setY(owner.getY() + (owner.getHeight() - lockWindow.getHeight()) / 2); + } else { + lockWindow.centerOnScreen(); + } + }); + // ... and wait for answer + switch (forceLockDecisionLock.awaitInteraction()) { + case FORCE: + vault.lock(true); + return true; + case CANCEL: + // if lock was performed over main window, show it again + return false; + default: + return false; + } } private void handleSuccess() { - LOG.info("Lock of {} succeeded.", vault.getDisplayName()); - // set vault state to locked + LOG.info("Lock of {} succeeded.", vault.getDisplayName()); } @Override diff --git a/main/ui/src/main/resources/fxml/lock_failed.fxml b/main/ui/src/main/resources/fxml/lock_failed.fxml new file mode 100644 index 000000000..9b4e0a2b9 --- /dev/null +++ b/main/ui/src/main/resources/fxml/lock_failed.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/main/ui/src/main/resources/fxml/lock_forced.fxml b/main/ui/src/main/resources/fxml/lock_forced.fxml new file mode 100644 index 000000000..be311261d --- /dev/null +++ b/main/ui/src/main/resources/fxml/lock_forced.fxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +