Parcourir la source

hooked up in UI

Sebastian Stenzel il y a 1 mois
Parent
commit
02186ca17a

+ 62 - 0
src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java

@@ -4,10 +4,15 @@ import org.cryptomator.common.Environment;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.fxapp.UpdateChecker;
+import org.cryptomator.updater.UpdateMechanism;
+import org.cryptomator.updater.UpdateProcess;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import javafx.animation.PauseTransition;
 import javafx.application.Application;
+import javafx.application.Platform;
 import javafx.beans.binding.Bindings;
 import javafx.beans.binding.BooleanBinding;
 import javafx.beans.binding.ObjectBinding;
@@ -16,9 +21,11 @@ import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ReadOnlyStringProperty;
 import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.value.ObservableValue;
+import javafx.concurrent.Task;
 import javafx.fxml.FXML;
 import javafx.scene.control.CheckBox;
 import javafx.scene.control.ContentDisplay;
+import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
@@ -29,11 +36,14 @@ import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
 import java.util.Locale;
 import java.util.ResourceBundle;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 
 @PreferencesScoped
 public class UpdatesPreferencesController implements FxController {
 
+	private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
 	private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" //
 			+ "?utm_source=cryptomator-desktop" //
 			+ "&utm_medium=update-notification&" //
@@ -56,6 +66,9 @@ public class UpdatesPreferencesController implements FxController {
 	private final DateTimeFormatter formatter;
 	private final BooleanBinding upToDate;
 	private final String downloadsUri;
+	private final UpdateMechanism updateMechanism;
+	public final Task<UpdateProcess> updatePreparationTask;
+	private final StringBinding updateButtonTitle;
 
 	/* FXML */
 	public CheckBox checkForUpdatesCheckbox;
@@ -78,6 +91,18 @@ public class UpdatesPreferencesController implements FxController {
 		this.checkFailed = updateChecker.checkFailedProperty();
 		this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
 		this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII));
+		this.updateMechanism = UpdateMechanism.get();
+		this.updatePreparationTask = new Task<>() { // TODO custom class?
+			@Override
+			protected UpdateProcess call() throws IOException, InterruptedException {
+				var updateProcess = updateMechanism.prepareUpdate();
+				do {
+					updateProgress(updateProcess.preparationProgress(), 1.0);
+				} while (!updateProcess.await(100, TimeUnit.MILLISECONDS));
+				return updateProcess;
+			}
+		};
+		this.updateButtonTitle = Bindings.createStringBinding(this::getUpdateButtonTitle, updatePreparationTask.stateProperty());
 	}
 
 	public void initialize() {
@@ -108,6 +133,26 @@ public class UpdatesPreferencesController implements FxController {
 		environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
 	}
 
+	@FXML
+	public void prepareUpdate() {
+		if (updatePreparationTask.isDone()) {
+			try {
+				// TODO: check if all vaults closed?
+				var restartProcess = updatePreparationTask.get().applyUpdate();
+				assert restartProcess.isAlive();
+				Platform.exit(); // TODO: prompt?
+			} catch (IOException | InterruptedException | ExecutionException e) {
+				LOG.error("Oh no", e); // TODO: Show error dialog
+			}
+		} else if (updatePreparationTask.isRunning()) {
+			throw new IllegalStateException("Update already in progress");
+		} else if (updatePreparationTask.isCancelled()) {
+			throw new IllegalStateException("Update preparation task was cancelled");
+		} else {
+			Thread.startVirtualThread(updatePreparationTask);
+		}
+	}
+
 	/* Observable Properties */
 
 	public ObjectBinding<ContentDisplay> checkForUpdatesButtonStateProperty() {
@@ -186,4 +231,21 @@ public class UpdatesPreferencesController implements FxController {
 		return checkFailed.getValue();
 	}
 
+	public Task<UpdateProcess> getUpdatePreparationTask() {
+		return updatePreparationTask;
+	}
+
+	public StringBinding updateButtonTitleProperty() {
+		return updateButtonTitle;
+	}
+
+	public String getUpdateButtonTitle() {
+		return switch (updatePreparationTask.getState()) {
+			case READY -> "Prepare Update"; // TODO: resourceBundle.getString("preferences.updates.preparingUpdate")...
+			case SCHEDULED, RUNNING -> "Preparing Update...";
+			case SUCCEEDED -> "Restart to Update";
+			case FAILED, CANCELLED -> "failed";
+		};
+	}
+
 }

+ 3 - 5
src/main/java/org/cryptomator/updater/UpdateMechanism.java

@@ -1,17 +1,15 @@
 package org.cryptomator.updater;
 
-import org.cryptomator.integrations.common.IntegrationsLoader;
 import org.cryptomator.integrations.common.NamedServiceProvider;
 import org.jetbrains.annotations.Blocking;
 
 import javafx.concurrent.Task;
 import java.io.IOException;
-import java.util.stream.Stream;
 
 public interface UpdateMechanism extends NamedServiceProvider {
 
-	static Stream<UpdateMechanism> get() {
-		return IntegrationsLoader.loadAll(UpdateMechanism.class);
+	static UpdateMechanism get() {
+		return new MacOsDmgUpdateMechanism(); // TODO: IntegrationsLoader.load(UpdateMechanism.class).orElseThrow();
 	}
 
 	/**
@@ -23,7 +21,7 @@ public interface UpdateMechanism extends NamedServiceProvider {
 
 	/**
 	 * Performs as much as possible to prepare the update. This may include downloading the update, checking signatures, etc.
-	 * @return a {@link Task} that can be used to monitor the progress of the update preparation. The task will complete when the preparation is done.
+	 * @return a new {@link Task} that can be used to monitor the progress of the update preparation. The task will complete when the preparation is done.
 	 * @throws IOException I/O error during preparation, such as network issues or file access problems.
 	 */
 	UpdateProcess prepareUpdate() throws IOException; // TODO: exception types?

+ 8 - 0
src/main/resources/fxml/preferences_updates.fxml

@@ -14,6 +14,7 @@
 <?import javafx.scene.control.Tooltip?>
 <?import javafx.scene.text.TextFlow?>
 <?import javafx.scene.text.Text?>
+<?import javafx.scene.control.ProgressBar?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
@@ -53,5 +54,12 @@
 			</graphic>
 		</Label>
 		<Hyperlink text="${linkLabel.value}" onAction="#visitDownloadsPage" textAlignment="CENTER" wrapText="true" styleClass="hyperlink-underline" visible="${controller.updateAvailable}" managed="${controller.updateAvailable}"/>
+
+		<Button text="${controller.updateButtonTitle}" onAction="#prepareUpdate" disable="${controller.updatePreparationTask.running}">
+			<graphic>
+				<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-primary" glyph="FILE_DOWNLOAD"/>
+			</graphic>
+		</Button>
+		<ProgressBar progress="${controller.updatePreparationTask.progress}" visible="${controller.updatePreparationTask.running}"/>
 	</VBox>
 </VBox>