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 f32faa3b5..cae0c9209 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 @@ -12,6 +12,7 @@ public enum FxmlFile { CHANGEPASSWORD("/fxml/changepassword.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), // MAIN_WINDOW("/fxml/main_window.fxml"), // + MIGRATION_GENERIC_ERROR("/fxml/migration_generic_error.fxml"), // MIGRATION_RUN("/fxml/migration_run.fxml"), // MIGRATION_START("/fxml/migration_start.fxml"), // MIGRATION_SUCCESS("/fxml/migration_success.fxml"), // diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/StackTraceController.java b/main/ui/src/main/java/org/cryptomator/ui/common/StackTraceController.java index 4dd6d91ba..f83614dc1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/StackTraceController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/StackTraceController.java @@ -12,7 +12,7 @@ public class StackTraceController implements FxController { this.stackTrace = provideStackTrace(cause); } - static String provideStackTrace(Throwable cause) { + private static String provideStackTrace(Throwable cause) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); cause.printStackTrace(new PrintStream(baos)); return baos.toString(StandardCharsets.UTF_8); diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationGenericErrorController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationGenericErrorController.java new file mode 100644 index 000000000..3b6121c79 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationGenericErrorController.java @@ -0,0 +1,29 @@ +package org.cryptomator.ui.migration; + +import dagger.Lazy; +import javafx.fxml.FXML; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; + +import javax.inject.Inject; + +@MigrationScoped +public class MigrationGenericErrorController implements FxController { + + private final Stage window; + private final Lazy startScene; + + @Inject + MigrationGenericErrorController(@MigrationWindow Stage window, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene) { + this.window = window; + this.startScene = startScene; + } + + @FXML + public void back() { + window.setScene(startScene.get()); + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationModule.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationModule.java index 2f5d4c18d..478bc91a6 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationModule.java @@ -4,6 +4,8 @@ import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Modality; @@ -14,6 +16,7 @@ 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.StackTraceController; import org.cryptomator.ui.mainwindow.MainWindow; import javax.inject.Named; @@ -45,6 +48,13 @@ abstract class MigrationModule { return stage; } + @Provides + @Named("genericErrorCause") + @MigrationScoped + static ObjectProperty provideGenericErrorCause() { + return new SimpleObjectProperty<>(); + } + @Provides @FxmlScene(FxmlFile.MIGRATION_START) @MigrationScoped @@ -66,6 +76,13 @@ abstract class MigrationModule { return fxmlLoaders.createScene("/fxml/migration_success.fxml"); } + @Provides + @FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR) + @MigrationScoped + static Scene provideMigrationGenericErrorScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene("/fxml/migration_generic_error.fxml"); + } + // ------------------ @Binds @@ -83,4 +100,16 @@ abstract class MigrationModule { @FxControllerKey(MigrationSuccessController.class) abstract FxController bindMigrationSuccessController(MigrationSuccessController controller); + @Binds + @IntoMap + @FxControllerKey(MigrationGenericErrorController.class) + abstract FxController bindMigrationGenericErrorController(MigrationGenericErrorController controller); + + @Provides + @IntoMap + @FxControllerKey(StackTraceController.class) + static FxController provideStackTraceController(@Named("genericErrorCause") ObjectProperty errorCause) { + return new StackTraceController(errorCause.get()); + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java index 181876d2f..18be66c5d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java @@ -4,10 +4,12 @@ import dagger.Lazy; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; @@ -21,6 +23,7 @@ import javafx.stage.Stage; import javafx.util.Duration; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.api.MigrationProgressListener; import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; @@ -36,44 +39,52 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.util.Arrays; import java.util.Optional; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; @MigrationScoped public class MigrationRunController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(MigrationRunController.class); private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes - private static final Duration MIGRATION_PROGRESS_UPDATE_INTERVAL = Duration.millis(25); + private static final long MIGRATION_PROGRESS_UPDATE_MILLIS = 50; private final Stage window; private final Vault vault; private final ExecutorService executor; + private final ScheduledExecutorService scheduler; private final Optional keychainAccess; + private final ObjectProperty errorCause; private final Lazy startScene; private final Lazy successScene; private final ObjectBinding migrateButtonContentDisplay; + private final Lazy genericErrorScene; private final BooleanProperty migrationButtonDisabled; private final DoubleProperty migrationProgress; - private final ScheduledService migrationProgressObservationService; private volatile double volatileMigrationProgress = -1.0; public NiceSecurePasswordField passwordField; @Inject - public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, Optional keychainAccess, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene) { + public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, Optional keychainAccess, @Named("genericErrorCause") ObjectProperty errorCause, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR) Lazy genericErrorScene) { this.window = window; this.vault = vault; this.executor = executor; + this.scheduler = scheduler; this.keychainAccess = keychainAccess; + this.errorCause = errorCause; this.startScene = startScene; this.successScene = successScene; this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty()); + this.genericErrorScene = genericErrorScene; this.migrationButtonDisabled = new SimpleBooleanProperty(); this.migrationProgress = new SimpleDoubleProperty(volatileMigrationProgress); - this.migrationProgressObservationService = new MigrationProgressObservationService(); - migrationProgressObservationService.setExecutor(executor); - migrationProgressObservationService.setPeriod(MIGRATION_PROGRESS_UPDATE_INTERVAL); } public void initialize() { @@ -93,7 +104,11 @@ public class MigrationRunController implements FxController { LOG.info("Migrating vault {}", vault.getPath()); CharSequence password = passwordField.getCharacters(); vault.setState(VaultState.PROCESSING); - migrationProgressObservationService.start(); + ScheduledFuture progressSyncTask = scheduler.scheduleAtFixedRate(() -> { + Platform.runLater(() -> { + migrationProgress.set(volatileMigrationProgress); + }); + }, 0, MIGRATION_PROGRESS_UPDATE_MILLIS, TimeUnit.MILLISECONDS); Tasks.create(() -> { Migrators migrators = Migrators.get(); migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password, this::migrationProgressChanged); @@ -118,11 +133,11 @@ public class MigrationRunController implements FxController { // TODO show specific error screen }).onError(Exception.class, e -> { // including RuntimeExceptions LOG.error("Migration failed for technical reasons.", e); - vault.setState(VaultState.ERROR); - // TODO show generic error screen + vault.setState(VaultState.NEEDS_MIGRATION); + errorCause.set(e); + window.setScene(genericErrorScene.get()); }).andFinally(() -> { - migrationProgressObservationService.cancel(); - migrationProgressObservationService.reset(); + progressSyncTask.cancel(true); }).runOnce(executor); } @@ -161,27 +176,6 @@ public class MigrationRunController implements FxController { } } - // Sets migrationProgress to volatileMigrationProgress at its configured interval - private class MigrationProgressObservationService extends ScheduledService { - - @Override - protected Task createTask() { - return new Task<>() { - @Override - protected Double call() { - return volatileMigrationProgress; - } - }; - } - - @Override - protected void succeeded() { - assert getValue() != null; - migrationProgress.set(getValue()); - super.succeeded(); - } - } - /* Animations */ private void shakeWindow() { diff --git a/main/ui/src/main/resources/fxml/migration_generic_error.fxml b/main/ui/src/main/resources/fxml/migration_generic_error.fxml new file mode 100644 index 000000000..6470505f1 --- /dev/null +++ b/main/ui/src/main/resources/fxml/migration_generic_error.fxml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + +