From 4064b61cd7a72aea50cbb769bfcfbde24d00f723 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Thu, 29 Feb 2024 09:29:26 +0100 Subject: [PATCH] refactored updateChecker by incorporating state management and date tracking --- .../cryptomator/ui/fxapp/UpdateChecker.java | 49 ++++++++++++------- .../ui/fxapp/UpdateCheckerModule.java | 9 ---- .../UpdatesPreferencesController.java | 46 ++++++++++++++++- .../resources/fxml/preferences_updates.fxml | 27 +++++----- src/main/resources/i18n/strings.properties | 7 +++ 5 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index 709eb2fe7..f3ef0e5a9 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -2,44 +2,50 @@ package org.cryptomator.ui.fxapp; import org.cryptomator.common.Environment; import org.cryptomator.common.settings.Settings; +import org.cryptomator.ui.health.Check; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.concurrent.ScheduledService; import javafx.concurrent.Worker; import javafx.concurrent.WorkerStateEvent; import javafx.util.Duration; +import java.time.LocalDateTime; import java.util.Comparator; @FxApplicationScoped public class UpdateChecker { private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class); - private static final Duration AUTOCHECK_DELAY = Duration.seconds(5); + private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5); private final Environment env; private final Settings settings; - private final StringProperty latestVersionProperty; - private final Comparator semVerComparator; + private final StringProperty latestVersionProperty = new SimpleStringProperty(); private final ScheduledService updateCheckerService; + private final ObjectProperty state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED); + private final ObjectProperty updateCheckTimeProperty = new SimpleObjectProperty<>(); @Inject - UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator semVerComparator, ScheduledService updateCheckerService) { + UpdateChecker(Settings settings, // + Environment env, // + ScheduledService updateCheckerService) { this.env = env; this.settings = settings; - this.latestVersionProperty = latestVersionProperty; - this.semVerComparator = semVerComparator; this.updateCheckerService = updateCheckerService; } public void automaticallyCheckForUpdatesIfEnabled() { if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) { - startCheckingForUpdates(AUTOCHECK_DELAY); + startCheckingForUpdates(AUTO_CHECK_DELAY); } } @@ -59,26 +65,30 @@ public class UpdateChecker { private void checkStarted(WorkerStateEvent event) { LOG.debug("Checking for updates..."); + state.set(UpdateCheckState.IS_CHECKING); } private void checkSucceeded(WorkerStateEvent event) { String latestVersion = updateCheckerService.getValue(); - LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion); - - if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) { - // update is available - latestVersionProperty.set(latestVersion); - } else { - latestVersionProperty.set(null); - } + LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersion); + state.set(UpdateCheckState.CHECK_SUCCESSFUL); + updateCheckTimeProperty.set(LocalDateTime.now()); + latestVersionProperty.set(latestVersion); } private void checkFailed(WorkerStateEvent event) { LOG.warn("Error checking for updates", event.getSource().getException()); + state.set(UpdateCheckState.CHECK_FAILED); + } + + public enum UpdateCheckState { + NOT_CHECKED, + IS_CHECKING, + CHECK_SUCCESSFUL, + CHECK_FAILED; } /* Observable Properties */ - public BooleanBinding checkingForUpdatesProperty() { return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING); } @@ -88,7 +98,12 @@ public class UpdateChecker { } public String getCurrentVersion() { - return env.getAppVersion(); + return "1.12.3"; //env.getAppVersion(); } + public ObjectProperty updateCheckTimeProperty() { + return updateCheckTimeProperty; + } +public ObjectProperty updateCheckStateProperty() { return state;} + } diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerModule.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerModule.java index b5f06d7e5..585180662 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerModule.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerModule.java @@ -11,8 +11,6 @@ import org.slf4j.LoggerFactory; import javax.inject.Named; import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import javafx.util.Duration; @@ -32,13 +30,6 @@ public abstract class UpdateCheckerModule { private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3); private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows... - @Provides - @Named("latestVersion") - @FxApplicationScoped - static StringProperty provideLatestVersion() { - return new SimpleStringProperty(); - } - @Provides @FxApplicationScoped static Optional provideHttpClient() { diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 630b82776..93c58f9c0 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -1,7 +1,9 @@ package org.cryptomator.ui.preferences; +import org.cryptomator.common.SemVerComparator; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.controls.FormattedLabel; import org.cryptomator.ui.fxapp.UpdateChecker; import javax.inject.Inject; @@ -9,11 +11,19 @@ import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Comparator; +import java.util.Locale; +import java.util.ResourceBundle; + @PreferencesScoped public class UpdatesPreferencesController implements FxController { @@ -21,28 +31,60 @@ public class UpdatesPreferencesController implements FxController { private final Application application; private final Settings settings; + private final ResourceBundle resourceBundle; private final UpdateChecker updateChecker; private final ObjectBinding checkForUpdatesButtonState; private final ReadOnlyStringProperty latestVersion; private final String currentVersion; private final BooleanBinding updateAvailable; + private final ObjectProperty updateCheckDateProperty; + private final Comparator versionComparator = new SemVerComparator(); + private final ObjectProperty updateCheckStateProperty; /* FXML */ public CheckBox checkForUpdatesCheckbox; + public FormattedLabel updateCheckDateFormattedLabel; + public FormattedLabel statusFormattedLabel; @Inject - UpdatesPreferencesController(Application application, Settings settings, UpdateChecker updateChecker) { + UpdatesPreferencesController(Application application, Settings settings, ResourceBundle resourceBundle, UpdateChecker updateChecker) { this.application = application; this.settings = settings; + this.resourceBundle = resourceBundle; this.updateChecker = updateChecker; this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY); this.latestVersion = updateChecker.latestVersionProperty(); - this.updateAvailable = latestVersion.isNotNull(); this.currentVersion = updateChecker.getCurrentVersion(); + this.updateAvailable = Bindings.createBooleanBinding(() -> { + if (latestVersion.get() != null) { + return versionComparator.compare(currentVersion, latestVersion.get()) < 0; + } else { + return false; + } + }, latestVersion); + this.updateCheckDateProperty = updateChecker.updateCheckTimeProperty(); + this.updateCheckStateProperty = updateChecker.updateCheckStateProperty(); } public void initialize() { checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates); + + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); + updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> { + return (updateCheckDateProperty.get() != null) ? updateCheckDateProperty.get().format(formatter) : ""; + }, updateCheckDateProperty)); + updateCheckDateFormattedLabel.managedProperty().bind(updateCheckDateProperty.isNotNull()); + updateCheckDateFormattedLabel.visibleProperty().bind(updateCheckDateProperty.isNotNull()); + + statusFormattedLabel.arg1Property().bind(Bindings.createObjectBinding(() ->{ + return switch (updateCheckStateProperty.get()) { + case NOT_CHECKED -> resourceBundle.getString("preferences.updates.status.notChecked"); + case IS_CHECKING -> resourceBundle.getString("preferences.updates.status.isChecking"); + case CHECK_SUCCESSFUL -> resourceBundle.getString("preferences.updates.status.checkSuccessful"); + case CHECK_FAILED -> resourceBundle.getString("preferences.updates.status.checkFailed"); + }; + }, updateCheckStateProperty + )); } @FXML diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml index 3156d1c3c..6efb3700b 100644 --- a/src/main/resources/fxml/preferences_updates.fxml +++ b/src/main/resources/fxml/preferences_updates.fxml @@ -8,6 +8,7 @@ + - - + + - - - - - - - - + + + + + + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 1585448e5..b449c2224 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -321,6 +321,13 @@ preferences.updates.currentVersion=Current Version: %s preferences.updates.autoUpdateCheck=Check for updates automatically preferences.updates.checkNowBtn=Check Now preferences.updates.updateAvailable=Update to version %s available. +preferences.updates.lastUpdateCheck=The last update check was performed on: %s. +preferences.updates.status=Status: %s +preferences.updates.status.notChecked=Not checked +preferences.updates.status.isChecking=Is checking +preferences.updates.status.checkSuccessful=Check successful +preferences.updates.status.checkFailed=Check failed + ## Contribution preferences.contribute=Support Us preferences.contribute.registeredFor=Supporter certificate registered for %s