浏览代码

show progress during vault migration (ugly prototype)

Sebastian Stenzel 5 年之前
父节点
当前提交
cd4cb70896

+ 66 - 2
main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java

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

+ 3 - 0
main/ui/src/main/resources/fxml/migration_run.fxml

@@ -23,6 +23,9 @@
 			<NiceSecurePasswordField fx:id="passwordField"/>
 		</VBox>
 
+		<!-- TODO add a progress bar... -->
+		<FormattedLabel format="progress: %.3f" arg1="${controller.migrationProgress}"/>
+
 		<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
 			<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
 				<buttons>

+ 1 - 4
main/ui/src/main/resources/fxml/vault_detail_needsmigration.fxml

@@ -2,7 +2,6 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.layout.VBox?>
 <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
-<?import javafx.scene.control.Label?>
 <VBox xmlns="http://javafx.com/javafx"
 	  xmlns:fx="http://javafx.com/fxml"
 	  fx:controller="org.cryptomator.ui.mainwindow.VaultDetailNeedsMigrationController"
@@ -12,9 +11,7 @@
 		<Insets topRightBottomLeft="24"/>
 	</padding>
 	<children>
-		<!-- TODO -->
-		<Label text="Migration disabled during Alpha"/>
-		<Button styleClass="button-large" text="TODO Upgrade Vault" minWidth="120" onAction="#showVaultMigrator" disable="true">
+		<Button styleClass="button-large" text="TODO Upgrade Vault" minWidth="120" onAction="#showVaultMigrator">
 			<graphic>
 				<FontAwesome5IconView glyph="FILE_IMPORT" glyphSize="15"/>
 			</graphic>