hooked up in UI

This commit is contained in:
Sebastian Stenzel
2025-07-06 20:02:41 +02:00
parent b0ed133e05
commit 02186ca17a
3 changed files with 73 additions and 5 deletions

View File

@@ -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";
};
}
}

View File

@@ -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?

View File

@@ -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>