From 4064b61cd7a72aea50cbb769bfcfbde24d00f723 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Thu, 29 Feb 2024 09:29:26 +0100 Subject: [PATCH 01/31] 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 From 8064d75102c100174254f292f991023ac56ee6cf Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Tue, 5 Mar 2024 10:15:11 +0100 Subject: [PATCH 02/31] removed state label and reorganized ui elements --- .../cryptomator/ui/fxapp/UpdateChecker.java | 10 ++--- .../UpdatesPreferencesController.java | 38 +++++++++---------- .../resources/fxml/preferences_updates.fxml | 20 ++++++++-- src/main/resources/i18n/strings.properties | 9 ++--- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index f3ef0e5a9..a29a06e59 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -2,12 +2,10 @@ 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; @@ -19,7 +17,6 @@ import javafx.concurrent.Worker; import javafx.concurrent.WorkerStateEvent; import javafx.util.Duration; import java.time.LocalDateTime; -import java.util.Comparator; @FxApplicationScoped public class UpdateChecker { @@ -98,12 +95,15 @@ public class UpdateChecker { } public String getCurrentVersion() { - return "1.12.3"; //env.getAppVersion(); + return env.getAppVersion(); } public ObjectProperty updateCheckTimeProperty() { return updateCheckTimeProperty; } -public ObjectProperty updateCheckStateProperty() { return state;} + + public ObjectProperty updateCheckStateProperty() { + return state; + } } diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 93c58f9c0..fb5fd964c 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.preferences; +import org.cryptomator.common.Environment; import org.cryptomator.common.SemVerComparator; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxController; @@ -16,13 +17,12 @@ import javafx.beans.property.ReadOnlyStringProperty; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; - +import javafx.scene.layout.HBox; 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 { @@ -30,8 +30,8 @@ public class UpdatesPreferencesController implements FxController { private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads"; private final Application application; + private final Environment environment; private final Settings settings; - private final ResourceBundle resourceBundle; private final UpdateChecker updateChecker; private final ObjectBinding checkForUpdatesButtonState; private final ReadOnlyStringProperty latestVersion; @@ -44,13 +44,14 @@ public class UpdatesPreferencesController implements FxController { /* FXML */ public CheckBox checkForUpdatesCheckbox; public FormattedLabel updateCheckDateFormattedLabel; - public FormattedLabel statusFormattedLabel; + public HBox checkFailedHBox; + public FormattedLabel latestVersionFormattedLabel; @Inject - UpdatesPreferencesController(Application application, Settings settings, ResourceBundle resourceBundle, UpdateChecker updateChecker) { + UpdatesPreferencesController(Application application, Environment environment, Settings settings, UpdateChecker updateChecker) { this.application = application; + this.environment = environment; 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(); @@ -70,21 +71,13 @@ public class UpdatesPreferencesController implements FxController { 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()); + updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (updateCheckDateProperty.get() != null) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty)); + + checkFailedHBox.managedProperty().bind(updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED)); + checkFailedHBox.visibleProperty().bind(updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED)); + + latestVersionFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (latestVersion.get() != null) ? latestVersion.get() : "-", latestVersion)); - 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 @@ -97,6 +90,11 @@ public class UpdatesPreferencesController implements FxController { application.getHostServices().showDocument(DOWNLOADS_URI); } + @FXML + public void showLogfileDirectory() { + environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString())); + } + /* Observable Properties */ public ObjectBinding checkForUpdatesButtonStateProperty() { diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml index 6efb3700b..ef037580f 100644 --- a/src/main/resources/fxml/preferences_updates.fxml +++ b/src/main/resources/fxml/preferences_updates.fxml @@ -1,14 +1,16 @@ + + + + - - + + + @@ -28,8 +33,15 @@ - - + + + + + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index b449c2224..0a4c795a0 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -318,15 +318,12 @@ preferences.volume.feature.readOnly=Read-only mount ## Updates preferences.updates=Updates preferences.updates.currentVersion=Current Version: %s +preferences.updates.latestVersion=Latest 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 +preferences.updates.lastUpdateCheck=The last update check was performed on: %s +preferences.updates.checkFailed=Check failed ## Contribution preferences.contribute=Support Us From b3789700e1b82fe54e8f56e8741cd85f4589919e Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Tue, 5 Mar 2024 12:56:38 +0100 Subject: [PATCH 03/31] add upToDateLabel and fix update indicator issue --- .../cryptomator/ui/fxapp/UpdateChecker.java | 2 +- .../mainwindow/MainWindowTitleController.java | 2 +- .../UpdatesPreferencesController.java | 27 +++++++++++++++++-- .../resources/fxml/preferences_updates.fxml | 5 ++++ src/main/resources/i18n/strings.properties | 1 + 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index a29a06e59..e2e423f84 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -68,9 +68,9 @@ public class UpdateChecker { private void checkSucceeded(WorkerStateEvent event) { String latestVersion = updateCheckerService.getValue(); LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersion); - state.set(UpdateCheckState.CHECK_SUCCESSFUL); updateCheckTimeProperty.set(LocalDateTime.now()); latestVersionProperty.set(latestVersion); + state.set(UpdateCheckState.CHECK_SUCCESSFUL); } private void checkFailed(WorkerStateEvent event) { diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java index 479a2d860..2dcbd6095 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java @@ -46,7 +46,7 @@ public class MainWindowTitleController implements FxController { this.appWindows = appWindows; this.trayMenuInitialized = trayMenu.isInitialized(); this.updateChecker = updateChecker; - this.updateAvailable = updateChecker.latestVersionProperty().isNotNull(); + this.updateAvailable = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(updateChecker.latestVersionProperty().isNotEqualTo(updateChecker.getCurrentVersion())); this.licenseHolder = licenseHolder; this.settings = settings; this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton, settings.showTrayIcon); diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index fb5fd964c..57ebc9706 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -8,6 +8,7 @@ import org.cryptomator.ui.controls.FormattedLabel; import org.cryptomator.ui.fxapp.UpdateChecker; import javax.inject.Inject; +import javafx.animation.PauseTransition; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -17,13 +18,17 @@ import javafx.beans.property.ReadOnlyStringProperty; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; import javafx.scene.layout.HBox; +import javafx.util.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.Comparator; import java.util.Locale; + + @PreferencesScoped public class UpdatesPreferencesController implements FxController { @@ -46,6 +51,7 @@ public class UpdatesPreferencesController implements FxController { public FormattedLabel updateCheckDateFormattedLabel; public HBox checkFailedHBox; public FormattedLabel latestVersionFormattedLabel; + public Label upToDateLabel; @Inject UpdatesPreferencesController(Application application, Environment environment, Settings settings, UpdateChecker updateChecker) { @@ -73,11 +79,28 @@ public class UpdatesPreferencesController implements FxController { DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (updateCheckDateProperty.get() != null) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty)); - checkFailedHBox.managedProperty().bind(updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED)); - checkFailedHBox.visibleProperty().bind(updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED)); + BooleanBinding isUpdateCheckFailed = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED); + checkFailedHBox.managedProperty().bind(isUpdateCheckFailed); + checkFailedHBox.visibleProperty().bind(isUpdateCheckFailed); latestVersionFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (latestVersion.get() != null) ? latestVersion.get() : "-", latestVersion)); + BooleanBinding isUpdateSuccessfulAndCurrent = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL) + .and(latestVersion.isEqualTo(currentVersion)); + + updateCheckStateProperty.addListener((_, _, _) -> { + if (isUpdateSuccessfulAndCurrent.get()) { + upToDateLabel.setVisible(true); + upToDateLabel.setManaged(true); + + PauseTransition delay = new PauseTransition(Duration.seconds(5)); + delay.setOnFinished(_ -> { + upToDateLabel.setVisible(false); + upToDateLabel.setManaged(false); + }); + delay.play(); + } + }); } @FXML diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml index ef037580f..3db4824f5 100644 --- a/src/main/resources/fxml/preferences_updates.fxml +++ b/src/main/resources/fxml/preferences_updates.fxml @@ -42,6 +42,11 @@ + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 0a4c795a0..96b49d119 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -324,6 +324,7 @@ 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.checkFailed=Check failed +preferences.updates.upToDate=Cryptomator is up-to-date. ## Contribution preferences.contribute=Support Us From db224e9e5c3c1d943c45e78b66dd1f9e2143b31c Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Fri, 8 Mar 2024 09:58:59 +0100 Subject: [PATCH 04/31] added latestVersion and lastUpdateCheck to settings and integrated with update checker --- .../cryptomator/common/settings/Settings.java | 6 +++++- .../common/settings/SettingsJson.java | 3 +++ .../cryptomator/ui/fxapp/UpdateChecker.java | 21 ++++++++++++++++++- .../UpdatesPreferencesController.java | 2 +- .../UpdateReminderComponent.java | 6 ++++-- .../UpdateReminderController.java | 5 ++--- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index 4e0e0df97..6c46654c1 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -44,7 +44,7 @@ public class Settings { static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess"; static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name(); static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false; - static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01"; + public static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01T10:00:00"; public final ObservableList directories; public final BooleanProperty askedForUpdateCheck; @@ -68,6 +68,7 @@ public class Settings { public final StringProperty language; public final StringProperty mountService; public final StringProperty lastUpdateCheck; + public final StringProperty latestVersion; private Consumer saveCmd; @@ -105,6 +106,7 @@ public class Settings { this.language = new SimpleStringProperty(this, "language", json.language); this.mountService = new SimpleStringProperty(this, "mountService", json.mountService); this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck); + this.latestVersion = new SimpleStringProperty(this, "latestVersion", json.latestVersion); this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList()); @@ -132,6 +134,7 @@ public class Settings { language.addListener(this::somethingChanged); mountService.addListener(this::somethingChanged); lastUpdateCheck.addListener(this::somethingChanged); + latestVersion.addListener(this::somethingChanged); } @SuppressWarnings("deprecation") @@ -186,6 +189,7 @@ public class Settings { json.language = language.get(); json.mountService = mountService.get(); json.lastUpdateCheck = lastUpdateCheck.get(); + json.latestVersion = latestVersion.get(); return json; } diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java index 2c7c963da..2dbb514c0 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java @@ -83,4 +83,7 @@ class SettingsJson { @JsonProperty("lastUpdateCheck") String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK; + @JsonProperty("latestVersion") + String latestVersion; + } diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index e2e423f84..369ab168a 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -16,7 +16,10 @@ import javafx.concurrent.ScheduledService; import javafx.concurrent.Worker; import javafx.concurrent.WorkerStateEvent; import javafx.util.Duration; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; @FxApplicationScoped public class UpdateChecker { @@ -38,6 +41,21 @@ public class UpdateChecker { this.env = env; this.settings = settings; this.updateCheckerService = updateCheckerService; + this.latestVersionProperty.set(settings.latestVersion.get()); + var dateTimeString = !settings.lastUpdateCheck.get().isEmpty() ? settings.lastUpdateCheck.get() : Settings.DEFAULT_LAST_UPDATE_CHECK; + try { + LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME); + this.updateCheckTimeProperty.set(dateTime); + } catch (DateTimeParseException e) { + try { + LocalDate date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE); + this.updateCheckTimeProperty.set(LocalDateTime.of(date, LocalDate.MIN.atStartOfDay().toLocalTime())); + } catch (DateTimeParseException ex) { + LOG.error("The date/time format is invalid:" + dateTimeString, ex); + } + } + this.latestVersionProperty().addListener((_, _, newValue) -> settings.latestVersion.set(newValue)); + this.updateCheckTimeProperty().addListener((_,_,newValue) -> settings.lastUpdateCheck.set(newValue.toString())); } public void automaticallyCheckForUpdatesIfEnabled() { @@ -69,6 +87,7 @@ public class UpdateChecker { String latestVersion = updateCheckerService.getValue(); LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersion); updateCheckTimeProperty.set(LocalDateTime.now()); + //settings.lastUpdateCheck.set(updateCheckTimeProperty.get().toString()); latestVersionProperty.set(latestVersion); state.set(UpdateCheckState.CHECK_SUCCESSFUL); } @@ -95,7 +114,7 @@ public class UpdateChecker { } public String getCurrentVersion() { - return env.getAppVersion(); + return "1.12.3";//env.getAppVersion(); } public ObjectProperty updateCheckTimeProperty() { diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 57ebc9706..f168b0a5c 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -77,7 +77,7 @@ public class UpdatesPreferencesController implements FxController { checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates); DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); - updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (updateCheckDateProperty.get() != null) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty)); + updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (!updateCheckDateProperty.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) && latestVersionProperty().isNotNull().get() ) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty, latestVersionProperty())); BooleanBinding isUpdateCheckFailed = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED); checkFailedHBox.managedProperty().bind(isUpdateCheckFailed); diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java index aa13f30da..d51d8df82 100644 --- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java @@ -5,10 +5,11 @@ import dagger.Subcomponent; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.fxapp.UpdateChecker; import javafx.scene.Scene; import javafx.stage.Stage; -import java.time.LocalDate; +import java.time.LocalDateTime; @UpdateReminderScoped @Subcomponent(modules = {UpdateReminderModule.class}) @@ -21,9 +22,10 @@ public interface UpdateReminderComponent { Lazy updateReminderScene(); Settings settings(); + UpdateChecker updateChecker(); default void checkAndShowUpdateReminderWindow() { - if (LocalDate.parse(settings().lastUpdateCheck.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) { + if (updateChecker().updateCheckTimeProperty().get().isBefore(LocalDateTime.now().minusDays(14)) && !settings().checkForUpdates.getValue()) { Stage stage = window(); stage.setScene(updateReminderScene().get()); stage.sizeToScene(); diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java index 28ae0b5c6..bd70115e3 100644 --- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java @@ -8,6 +8,7 @@ import javax.inject.Inject; import javafx.fxml.FXML; import javafx.stage.Stage; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @UpdateReminderScoped @@ -27,20 +28,18 @@ public class UpdateReminderController implements FxController { @FXML public void cancel() { - settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); + updateChecker.updateCheckTimeProperty().set(LocalDateTime.now()); window.close(); } @FXML public void once() { - settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); updateChecker.checkForUpdatesNow(); window.close(); } @FXML public void automatically() { - settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); updateChecker.checkForUpdatesNow(); settings.checkForUpdates.set(true); window.close(); From 1ed77ebcc05f97b44d77a543b9d8764885cf6d21 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Fri, 8 Mar 2024 10:27:35 +0100 Subject: [PATCH 05/31] code cleanup --- src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java | 3 +-- .../ui/preferences/UpdatesPreferencesController.java | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index 369ab168a..2cbf9bb31 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -87,7 +87,6 @@ public class UpdateChecker { String latestVersion = updateCheckerService.getValue(); LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersion); updateCheckTimeProperty.set(LocalDateTime.now()); - //settings.lastUpdateCheck.set(updateCheckTimeProperty.get().toString()); latestVersionProperty.set(latestVersion); state.set(UpdateCheckState.CHECK_SUCCESSFUL); } @@ -114,7 +113,7 @@ public class UpdateChecker { } public String getCurrentVersion() { - return "1.12.3";//env.getAppVersion(); + return env.getAppVersion(); } public ObjectProperty updateCheckTimeProperty() { diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index f168b0a5c..49f2a2250 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -28,7 +28,6 @@ import java.util.Comparator; import java.util.Locale; - @PreferencesScoped public class UpdatesPreferencesController implements FxController { @@ -77,7 +76,7 @@ public class UpdatesPreferencesController implements FxController { checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates); DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); - updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (!updateCheckDateProperty.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) && latestVersionProperty().isNotNull().get() ) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty, latestVersionProperty())); + updateCheckDateFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (!updateCheckDateProperty.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) && latestVersionProperty().isNotNull().get()) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty, latestVersionProperty())); BooleanBinding isUpdateCheckFailed = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED); checkFailedHBox.managedProperty().bind(isUpdateCheckFailed); @@ -85,8 +84,7 @@ public class UpdatesPreferencesController implements FxController { latestVersionFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (latestVersion.get() != null) ? latestVersion.get() : "-", latestVersion)); - BooleanBinding isUpdateSuccessfulAndCurrent = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL) - .and(latestVersion.isEqualTo(currentVersion)); + BooleanBinding isUpdateSuccessfulAndCurrent = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion)); updateCheckStateProperty.addListener((_, _, _) -> { if (isUpdateSuccessfulAndCurrent.get()) { From 43d0dd99eca4e9f8ddf0582a365565b7fe5ae6cd Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Thu, 4 Apr 2024 12:37:56 +0200 Subject: [PATCH 06/31] adjusted code as per PR suggestions --- .../cryptomator/common/settings/Settings.java | 18 +++++ .../cryptomator/ui/fxapp/UpdateChecker.java | 41 +++++------ .../mainwindow/MainWindowTitleController.java | 2 +- .../UpdatesPreferencesController.java | 68 ++++++++++--------- .../UpdateReminderComponent.java | 24 +++++-- .../resources/fxml/preferences_updates.fxml | 8 +-- src/main/resources/i18n/strings.properties | 2 +- 7 files changed, 99 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index 6c46654c1..019f87b2a 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -25,6 +25,10 @@ import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.NodeOrientation; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.function.Consumer; public class Settings { @@ -163,6 +167,20 @@ public class Settings { } }); } + + var dateTimeString = !lastUpdateCheck.get().isEmpty() ? lastUpdateCheck.get() : DEFAULT_LAST_UPDATE_CHECK; + try { + LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME); + lastUpdateCheck.set(dateTime.toString()); + } catch (DateTimeParseException e) { + try { + LocalDate date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE); + lastUpdateCheck.set(LocalDateTime.of(date, LocalDate.MIN.atStartOfDay().toLocalTime()).toString()); + } catch (DateTimeParseException ex) { + LOG.error("The date/time format is invalid:" + dateTimeString, ex); + } + } + } SettingsJson serialized() { diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index 2cbf9bb31..a4af88b93 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -1,11 +1,13 @@ package org.cryptomator.ui.fxapp; import org.cryptomator.common.Environment; +import org.cryptomator.common.SemVerComparator; import org.cryptomator.common.settings.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; @@ -16,10 +18,8 @@ import javafx.concurrent.ScheduledService; import javafx.concurrent.Worker; import javafx.concurrent.WorkerStateEvent; import javafx.util.Duration; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; +import java.util.Comparator; @FxApplicationScoped public class UpdateChecker { @@ -33,6 +33,8 @@ public class UpdateChecker { private final ScheduledService updateCheckerService; private final ObjectProperty state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED); private final ObjectProperty updateCheckTimeProperty = new SimpleObjectProperty<>(); + private final Comparator versionComparator = new SemVerComparator(); + private final BooleanBinding updateAvailable; @Inject UpdateChecker(Settings settings, // @@ -42,20 +44,14 @@ public class UpdateChecker { this.settings = settings; this.updateCheckerService = updateCheckerService; this.latestVersionProperty.set(settings.latestVersion.get()); - var dateTimeString = !settings.lastUpdateCheck.get().isEmpty() ? settings.lastUpdateCheck.get() : Settings.DEFAULT_LAST_UPDATE_CHECK; - try { - LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME); - this.updateCheckTimeProperty.set(dateTime); - } catch (DateTimeParseException e) { - try { - LocalDate date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE); - this.updateCheckTimeProperty.set(LocalDateTime.of(date, LocalDate.MIN.atStartOfDay().toLocalTime())); - } catch (DateTimeParseException ex) { - LOG.error("The date/time format is invalid:" + dateTimeString, ex); - } - } - this.latestVersionProperty().addListener((_, _, newValue) -> settings.latestVersion.set(newValue)); - this.updateCheckTimeProperty().addListener((_,_,newValue) -> settings.lastUpdateCheck.set(newValue.toString())); + this.updateCheckTimeProperty.set(LocalDateTime.parse(settings.lastUpdateCheck.get())); + this.updateAvailable = Bindings.createBooleanBinding(() -> { + var latestVersion = latestVersionProperty.get(); + return latestVersion != null && versionComparator.compare(getCurrentVersion(), latestVersion) < 0; + }, latestVersionProperty); + + this.latestVersionProperty.addListener((_, _, newValue) -> settings.latestVersion.set(newValue)); + this.updateCheckTimeProperty.addListener((_, _, newValue) -> settings.lastUpdateCheck.set(newValue.toString())); } public void automaticallyCheckForUpdatesIfEnabled() { @@ -112,8 +108,12 @@ public class UpdateChecker { return latestVersionProperty; } - public String getCurrentVersion() { - return env.getAppVersion(); + + public BooleanBinding updateAvailableProperty(){ + return updateAvailable; + } + public boolean isUpdateAvailable(){ + return updateAvailable.get(); } public ObjectProperty updateCheckTimeProperty() { @@ -124,4 +124,7 @@ public class UpdateChecker { return state; } + public String getCurrentVersion() { + return env.getAppVersion(); + } } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java index 2dcbd6095..f3c92790d 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java @@ -46,7 +46,7 @@ public class MainWindowTitleController implements FxController { this.appWindows = appWindows; this.trayMenuInitialized = trayMenu.isInitialized(); this.updateChecker = updateChecker; - this.updateAvailable = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(updateChecker.latestVersionProperty().isNotEqualTo(updateChecker.getCurrentVersion())); + this.updateAvailable = updateChecker.updateAvailableProperty(); this.licenseHolder = licenseHolder; this.settings = settings; this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton, settings.showTrayIcon); diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 49f2a2250..4c1a0d315 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -1,10 +1,8 @@ package org.cryptomator.ui.preferences; import org.cryptomator.common.Environment; -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; @@ -13,8 +11,10 @@ import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; @@ -24,7 +24,6 @@ import javafx.util.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.Comparator; import java.util.Locale; @@ -39,17 +38,15 @@ public class UpdatesPreferencesController implements FxController { private final UpdateChecker updateChecker; private final ObjectBinding checkForUpdatesButtonState; private final ReadOnlyStringProperty latestVersion; + private final ObjectProperty updateCheckDate; private final String currentVersion; private final BooleanBinding updateAvailable; - private final ObjectProperty updateCheckDateProperty; - private final Comparator versionComparator = new SemVerComparator(); + private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false); private final ObjectProperty updateCheckStateProperty; /* FXML */ public CheckBox checkForUpdatesCheckbox; - public FormattedLabel updateCheckDateFormattedLabel; public HBox checkFailedHBox; - public FormattedLabel latestVersionFormattedLabel; public Label upToDateLabel; @Inject @@ -60,42 +57,23 @@ public class UpdatesPreferencesController implements FxController { this.updateChecker = updateChecker; this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY); this.latestVersion = updateChecker.latestVersionProperty(); + this.updateCheckDate = updateChecker.updateCheckTimeProperty(); 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.updateAvailable = updateChecker.updateAvailableProperty(); 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(() -> (!updateCheckDateProperty.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) && latestVersionProperty().isNotNull().get()) ? updateCheckDateProperty.get().format(formatter) : "-", updateCheckDateProperty, latestVersionProperty())); - - BooleanBinding isUpdateCheckFailed = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED); - checkFailedHBox.managedProperty().bind(isUpdateCheckFailed); - checkFailedHBox.visibleProperty().bind(isUpdateCheckFailed); - - latestVersionFormattedLabel.arg1Property().bind(Bindings.createStringBinding(() -> (latestVersion.get() != null) ? latestVersion.get() : "-", latestVersion)); - BooleanBinding isUpdateSuccessfulAndCurrent = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion)); + updateCheckStateProperty.addListener((_, _, _) -> { if (isUpdateSuccessfulAndCurrent.get()) { - upToDateLabel.setVisible(true); - upToDateLabel.setManaged(true); - + upToDateLabelVisibleProperty().set(true); PauseTransition delay = new PauseTransition(Duration.seconds(5)); - delay.setOnFinished(_ -> { - upToDateLabel.setVisible(false); - upToDateLabel.setManaged(false); - }); + delay.setOnFinished(_ -> upToDateLabelVisibleProperty().set(false)); delay.play(); } }); @@ -131,13 +109,30 @@ public class UpdatesPreferencesController implements FxController { } public String getLatestVersion() { - return latestVersion.get(); + return latestVersion.isNotNull().get() ? latestVersion.get() : "-"; } public String getCurrentVersion() { return currentVersion; } + public ObjectProperty updateCheckDateProperty() { + return updateCheckDate; + } + + public String getUpdateCheckDate() { + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); + return !updateCheckDate.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) ? updateCheckDate.get().format(formatter) : "-"; + } + + public BooleanProperty upToDateLabelVisibleProperty() { + return upToDateLabelVisible; + } + + public final boolean isUpToDateLabelVisible() { + return upToDateLabelVisibleProperty().get(); + } + public BooleanBinding updateAvailableProperty() { return updateAvailable; } @@ -145,4 +140,13 @@ public class UpdatesPreferencesController implements FxController { public boolean isUpdateAvailable() { return updateAvailable.get(); } + + public BooleanBinding checkFailedProperty() { + return updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_FAILED); + } + + public boolean isCheckFailed() { + return checkFailedProperty().get(); + } + } diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java index d51d8df82..7a3d3077f 100644 --- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java @@ -5,16 +5,21 @@ import dagger.Subcomponent; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javafx.scene.Scene; import javafx.stage.Stage; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; @UpdateReminderScoped @Subcomponent(modules = {UpdateReminderModule.class}) public interface UpdateReminderComponent { + Logger LOG = LoggerFactory.getLogger(UpdateReminderComponent.class); + @UpdateReminderWindow Stage window(); @@ -22,19 +27,24 @@ public interface UpdateReminderComponent { Lazy updateReminderScene(); Settings settings(); - UpdateChecker updateChecker(); default void checkAndShowUpdateReminderWindow() { - if (updateChecker().updateCheckTimeProperty().get().isBefore(LocalDateTime.now().minusDays(14)) && !settings().checkForUpdates.getValue()) { - Stage stage = window(); - stage.setScene(updateReminderScene().get()); - stage.sizeToScene(); - stage.show(); + try { + var dateTime = LocalDateTime.parse(settings().lastUpdateCheck.get(), DateTimeFormatter.ISO_DATE_TIME); + if (dateTime.isBefore(LocalDateTime.now().minusDays(14)) && !settings().checkForUpdates.getValue()) { + Stage stage = window(); + stage.setScene(updateReminderScene().get()); + stage.sizeToScene(); + stage.show(); + } + } catch (DateTimeParseException e) { + LOG.error("The date/time format is invalid:" + settings().lastUpdateCheck.get(), e); } } @Subcomponent.Factory interface Factory { + UpdateReminderComponent create(); } } \ No newline at end of file diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml index 3db4824f5..f7b5383e0 100644 --- a/src/main/resources/fxml/preferences_updates.fxml +++ b/src/main/resources/fxml/preferences_updates.fxml @@ -22,8 +22,8 @@ - - + + @@ -34,7 +34,7 @@ - + - + + + + +