|
@@ -7,8 +7,13 @@ import javafx.animation.Timeline;
|
|
|
import javafx.beans.binding.Bindings;
|
|
|
import javafx.beans.binding.ObjectBinding;
|
|
|
import javafx.beans.property.BooleanProperty;
|
|
|
+import javafx.beans.property.DoubleProperty;
|
|
|
+import javafx.beans.property.ReadOnlyDoubleProperty;
|
|
|
import javafx.beans.property.SimpleBooleanProperty;
|
|
|
+import javafx.beans.property.SimpleDoubleProperty;
|
|
|
import javafx.beans.value.WritableValue;
|
|
|
+import javafx.concurrent.ScheduledService;
|
|
|
+import javafx.concurrent.Task;
|
|
|
import javafx.fxml.FXML;
|
|
|
import javafx.scene.Scene;
|
|
|
import javafx.scene.control.ContentDisplay;
|
|
@@ -17,6 +22,7 @@ import javafx.util.Duration;
|
|
|
import org.cryptomator.common.vaults.Vault;
|
|
|
import org.cryptomator.common.vaults.VaultState;
|
|
|
import org.cryptomator.cryptofs.migration.Migrators;
|
|
|
+import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
|
|
|
import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
|
|
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
|
|
import org.cryptomator.keychain.KeychainAccess;
|
|
@@ -39,6 +45,7 @@ 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 final Stage window;
|
|
|
private final Vault vault;
|
|
@@ -48,6 +55,9 @@ public class MigrationRunController implements FxController {
|
|
|
private final Lazy<Scene> successScene;
|
|
|
private final ObjectBinding<ContentDisplay> migrateButtonContentDisplay;
|
|
|
private final BooleanProperty migrationButtonDisabled;
|
|
|
+ private final DoubleProperty migrationProgress;
|
|
|
+ private final ScheduledService<Double> migrationProgressObservationService;
|
|
|
+ private volatile double volatileMigrationProgress = -1.0;
|
|
|
public NiceSecurePasswordField passwordField;
|
|
|
|
|
|
@Inject
|
|
@@ -60,6 +70,10 @@ public class MigrationRunController implements FxController {
|
|
|
this.successScene = successScene;
|
|
|
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
|
|
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() {
|
|
@@ -79,17 +93,18 @@ public class MigrationRunController implements FxController {
|
|
|
LOG.info("Migrating vault {}", vault.getPath());
|
|
|
CharSequence password = passwordField.getCharacters();
|
|
|
vault.setState(VaultState.PROCESSING);
|
|
|
+ migrationProgressObservationService.start();
|
|
|
Tasks.create(() -> {
|
|
|
Migrators migrators = Migrators.get();
|
|
|
- migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password);
|
|
|
+ migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password, this::migrationProgressChanged);
|
|
|
return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME);
|
|
|
}).onSuccess(needsAnotherMigration -> {
|
|
|
+ LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
|
|
if (needsAnotherMigration) {
|
|
|
vault.setState(VaultState.NEEDS_MIGRATION);
|
|
|
} else {
|
|
|
vault.setState(VaultState.LOCKED);
|
|
|
passwordField.swipe();
|
|
|
- LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
|
|
window.setScene(successScene.get());
|
|
|
}
|
|
|
}).onError(InvalidPassphraseException.class, e -> {
|
|
@@ -105,9 +120,29 @@ public class MigrationRunController implements FxController {
|
|
|
LOG.error("Migration failed for technical reasons.", e);
|
|
|
vault.setState(VaultState.ERROR);
|
|
|
// TODO show generic error screen
|
|
|
+ }).andFinally(() -> {
|
|
|
+ migrationProgressObservationService.cancel();
|
|
|
+ migrationProgressObservationService.reset();
|
|
|
}).runOnce(executor);
|
|
|
}
|
|
|
|
|
|
+ // Called by a background task. We can not directly modify observable properties from here
|
|
|
+ private void migrationProgressChanged(MigrationProgressListener.ProgressState state, double progress) {
|
|
|
+ switch (state) {
|
|
|
+ case INITIALIZING:
|
|
|
+ volatileMigrationProgress = -1.0;
|
|
|
+ break;
|
|
|
+ case MIGRATING:
|
|
|
+ volatileMigrationProgress = progress;
|
|
|
+ break;
|
|
|
+ case FINALIZING:
|
|
|
+ volatileMigrationProgress = 1.0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalStateException("Unexpted state " + state);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void loadStoredPassword() {
|
|
|
assert keychainAccess.isPresent();
|
|
|
char[] storedPw = null;
|
|
@@ -126,6 +161,27 @@ public class MigrationRunController implements FxController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Sets migrationProgress to volatileMigrationProgress at its configured interval
|
|
|
+ private class MigrationProgressObservationService extends ScheduledService<Double> {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Task<Double> 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() {
|
|
@@ -180,4 +236,12 @@ public class MigrationRunController implements FxController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public ReadOnlyDoubleProperty migrationProgressProperty() {
|
|
|
+ return migrationProgress;
|
|
|
+ }
|
|
|
+
|
|
|
+ public double getMigrationProgress() {
|
|
|
+ return migrationProgress.get();
|
|
|
+ }
|
|
|
+
|
|
|
}
|