diff --git a/pom.xml b/pom.xml
index e6b8d527d..5a6b85ac4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,11 +158,18 @@
nimbus-jose-jwt
${nimbus-jose.version}
+
+
com.fasterxml.jackson.core
jackson-databind
${jackson.version}
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 4d956c180..368c4f8c2 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -38,6 +38,7 @@ open module org.cryptomator.desktop {
requires com.auth0.jwt;
requires com.google.common;
requires com.fasterxml.jackson.databind;
+ requires com.fasterxml.jackson.datatype.jsr310;
requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind;
diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java
index 4e0e0df97..a54e71a4f 100644
--- a/src/main/java/org/cryptomator/common/settings/Settings.java
+++ b/src/main/java/org/cryptomator/common/settings/Settings.java
@@ -25,6 +25,7 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
+import java.time.Instant;
import java.util.function.Consumer;
public class Settings {
@@ -44,8 +45,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 Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
public final ObservableList directories;
public final BooleanProperty askedForUpdateCheck;
public final BooleanProperty checkForUpdates;
@@ -67,7 +67,7 @@ public class Settings {
public final IntegerProperty windowHeight;
public final StringProperty language;
public final StringProperty mountService;
- public final StringProperty lastUpdateCheck;
+ public final ObjectProperty lastSuccessfulUpdateCheck;
private Consumer saveCmd;
@@ -104,7 +104,7 @@ public class Settings {
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
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.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -131,7 +131,7 @@ public class Settings {
windowHeight.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
- lastUpdateCheck.addListener(this::somethingChanged);
+ lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -185,7 +185,7 @@ public class Settings {
json.windowHeight = windowHeight.get();
json.language = language.get();
json.mountService = mountService.get();
- json.lastUpdateCheck = lastUpdateCheck.get();
+ json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.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..2ded82885 100644
--- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java
@@ -1,9 +1,11 @@
package org.cryptomator.common.settings;
+import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.time.Instant;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -80,7 +82,8 @@ class SettingsJson {
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String preferredVolumeImpl;
- @JsonProperty("lastUpdateCheck")
- String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;
+ @JsonProperty("lastSuccessfulUpdateCheck")
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
+ Instant lastSuccessfulUpdateCheck = Settings.DEFAULT_TIMESTAMP;
}
diff --git a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
index 586708ed1..a33b51027 100644
--- a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
@@ -10,6 +10,7 @@ package org.cryptomator.common.settings;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Suppliers;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
@@ -36,7 +37,7 @@ import java.util.stream.Stream;
@Singleton
public class SettingsProvider implements Supplier {
- private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
+ private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true).registerModule(new JavaTimeModule());
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final long SAVE_DELAY_MS = 1000;
diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java
index 709eb2fe7..b857adcae 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java
@@ -1,45 +1,57 @@
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 javax.inject.Named;
+import javafx.beans.binding.Bindings;
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.Instant;
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 latestVersion = new SimpleStringProperty();
private final ScheduledService updateCheckerService;
+ private final ObjectProperty state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
+ private final ObjectProperty lastSuccessfulUpdateCheck;
+ private final Comparator versionComparator = new SemVerComparator();
+ private final BooleanBinding updateAvailable;
+ private final BooleanBinding checkFailed;
@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;
+ this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
+ this.updateAvailable = Bindings.createBooleanBinding(this::isUpdateAvailable, latestVersion);
+ this.checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, state);
}
public void automaticallyCheckForUpdatesIfEnabled() {
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
- startCheckingForUpdates(AUTOCHECK_DELAY);
+ startCheckingForUpdates(AUTO_CHECK_DELAY);
}
}
@@ -59,36 +71,65 @@ 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);
- }
+ var latestVersionString = updateCheckerService.getValue();
+ LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersionString);
+ lastSuccessfulUpdateCheck.set(Instant.now());
+ latestVersion.set(latestVersionString);
+ state.set(UpdateCheckState.CHECK_SUCCESSFUL);
}
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);
}
public ReadOnlyStringProperty latestVersionProperty() {
- return latestVersionProperty;
+ return latestVersion;
+ }
+
+ public BooleanBinding updateAvailableProperty() {
+ return updateAvailable;
+ }
+
+ public BooleanBinding checkFailedProperty() {
+ return checkFailed;
+ }
+
+ public boolean isUpdateAvailable() {
+ String currentVersion = getCurrentVersion();
+ String latestVersionString = latestVersion.get();
+
+ if (currentVersion == null || latestVersionString == null) {
+ return false;
+ } else {
+ return versionComparator.compare(currentVersion, latestVersionString) < 0;
+ }
+ }
+
+ public ObjectProperty lastSuccessfulUpdateCheckProperty() {
+ return lastSuccessfulUpdateCheck;
+ }
+
+ public ObjectProperty updateCheckStateProperty() {
+ return state;
}
public String getCurrentVersion() {
return env.getAppVersion();
}
-
}
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/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
index 479a2d860..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.latestVersionProperty().isNotNull();
+ 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 630b82776..afa05cc8c 100644
--- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
+++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
@@ -1,18 +1,33 @@
package org.cryptomator.ui.preferences;
+import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
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;
import javafx.beans.binding.ObjectBinding;
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
@PreferencesScoped
public class UpdatesPreferencesController implements FxController {
@@ -20,29 +35,55 @@ 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 ResourceBundle resourceBundle;
private final Settings settings;
private final UpdateChecker updateChecker;
private final ObjectBinding checkForUpdatesButtonState;
private final ReadOnlyStringProperty latestVersion;
+ private final ObservableValue lastSuccessfulUpdateCheck;
+ private final StringBinding lastUpdateCheckMessage;
+ private final ObservableValue timeDifferenceMessage;
private final String currentVersion;
private final BooleanBinding updateAvailable;
+ private final BooleanBinding checkFailed;
+ private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
+ private final DateTimeFormatter formatter;
+ private final BooleanBinding upToDate;
/* FXML */
public CheckBox checkForUpdatesCheckbox;
@Inject
- UpdatesPreferencesController(Application application, Settings settings, UpdateChecker updateChecker) {
+ UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker) {
this.application = application;
+ this.environment = environment;
+ this.resourceBundle = resourceBundle;
this.settings = settings;
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.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
+ this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
this.currentVersion = updateChecker.getCurrentVersion();
+ this.updateAvailable = updateChecker.updateAvailableProperty();
+ this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
+ this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
+ this.checkFailed = updateChecker.checkFailedProperty();
+ this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
}
public void initialize() {
checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates);
+
+ upToDate.addListener((_, _, newVal) -> {
+ if (newVal) {
+ upToDateLabelVisible.set(true);
+ PauseTransition delay = new PauseTransition(javafx.util.Duration.seconds(5));
+ delay.setOnFinished(_ -> upToDateLabelVisible.set(false));
+ delay.play();
+ }
+ });
}
@FXML
@@ -55,6 +96,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() {
@@ -77,6 +123,46 @@ public class UpdatesPreferencesController implements FxController {
return currentVersion;
}
+ public StringBinding lastUpdateCheckMessageProperty() {
+ return lastUpdateCheckMessage;
+ }
+
+ public String getLastUpdateCheckMessage() {
+ Instant lastCheck = lastSuccessfulUpdateCheck.getValue();
+ if (lastCheck != null && !lastCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
+ return formatter.format(LocalDateTime.ofInstant(lastCheck, ZoneId.systemDefault()));
+ } else {
+ return "-";
+ }
+ }
+
+ public ObservableValue timeDifferenceMessageProperty() {
+ return timeDifferenceMessage;
+ }
+
+ public String getTimeDifferenceMessage() {
+ var lastSuccessCheck = lastSuccessfulUpdateCheck.getValue();
+ var duration = Duration.between(lastSuccessCheck, Instant.now());
+ var hours = duration.toHours();
+ if (lastSuccessCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
+ return resourceBundle.getString("preferences.updates.lastUpdateCheck.never");
+ } else if (hours < 1) {
+ return resourceBundle.getString("preferences.updates.lastUpdateCheck.recently");
+ } else if (hours < 24) {
+ return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.hoursAgo"), hours);
+ } else {
+ return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.daysAgo"), duration.toDays());
+ }
+ }
+
+ public BooleanProperty upToDateLabelVisibleProperty() {
+ return upToDateLabelVisible;
+ }
+
+ public boolean isUpToDateLabelVisible() {
+ return upToDateLabelVisible.get();
+ }
+
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
@@ -84,4 +170,13 @@ public class UpdatesPreferencesController implements FxController {
public boolean isUpdateAvailable() {
return updateAvailable.get();
}
+
+ public BooleanBinding checkFailedProperty() {
+ return checkFailed;
+ }
+
+ public boolean isCheckFailed() {
+ return checkFailed.getValue();
+ }
+
}
diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java
index aa13f30da..d2c10f8fd 100644
--- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java
+++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java
@@ -8,7 +8,8 @@ import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
-import java.time.LocalDate;
+import java.time.Duration;
+import java.time.Instant;
@UpdateReminderScoped
@Subcomponent(modules = {UpdateReminderModule.class})
@@ -23,7 +24,8 @@ public interface UpdateReminderComponent {
Settings settings();
default void checkAndShowUpdateReminderWindow() {
- if (LocalDate.parse(settings().lastUpdateCheck.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) {
+ var now = Instant.now();
+ if (!settings().checkForUpdates.getValue() && settings().lastSuccessfulUpdateCheck.get().isBefore(now.minus(Duration.ofDays(14)))) {
Stage stage = window();
stage.setScene(updateReminderScene().get());
stage.sizeToScene();
@@ -33,6 +35,7 @@ public interface UpdateReminderComponent {
@Subcomponent.Factory
interface Factory {
+
UpdateReminderComponent create();
}
}
\ No newline at end of file
diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java
index 28ae0b5c6..183298c44 100644
--- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java
+++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java
@@ -7,8 +7,6 @@ import org.cryptomator.ui.fxapp.UpdateChecker;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
@UpdateReminderScoped
public class UpdateReminderController implements FxController {
@@ -27,20 +25,17 @@ public class UpdateReminderController implements FxController {
@FXML
public void cancel() {
- settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
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();
diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml
index 3156d1c3c..d0910949b 100644
--- a/src/main/resources/fxml/preferences_updates.fxml
+++ b/src/main/resources/fxml/preferences_updates.fxml
@@ -1,13 +1,19 @@
+
+
+
+
-
+
+
+
-
-
+
-
+
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties
index 1585448e5..acdac613b 100644
--- a/src/main/resources/i18n/strings.properties
+++ b/src/main/resources/i18n/strings.properties
@@ -321,6 +321,14 @@ 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=Last check: %s
+preferences.updates.lastUpdateCheck.never=never
+preferences.updates.lastUpdateCheck.recently=recently
+preferences.updates.lastUpdateCheck.daysAgo=%s days ago
+preferences.updates.lastUpdateCheck.hoursAgo=%s hours ago
+preferences.updates.checkFailed=Looking for updates failed. Please check your internet connection or try again later.
+preferences.updates.upToDate=Cryptomator is up-to-date.
+
## Contribution
preferences.contribute=Support Us
preferences.contribute.registeredFor=Supporter certificate registered for %s
diff --git a/src/test/java/org/cryptomator/common/settings/SettingsJsonTest.java b/src/test/java/org/cryptomator/common/settings/SettingsJsonTest.java
index da78ee5bc..a73431681 100644
--- a/src/test/java/org/cryptomator/common/settings/SettingsJsonTest.java
+++ b/src/test/java/org/cryptomator/common/settings/SettingsJsonTest.java
@@ -3,6 +3,7 @@ package org.cryptomator.common.settings;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -68,7 +69,7 @@ public class SettingsJsonTest {
jsonObj.theme = UiTheme.DARK;
jsonObj.showTrayIcon = false;
- var jsonStr = new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(jsonObj);
+ var jsonStr = new ObjectMapper().registerModule(new JavaTimeModule()).writerWithDefaultPrettyPrinter().writeValueAsString(jsonObj);
MatcherAssert.assertThat(jsonStr, containsString("\"theme\" : \"DARK\""));
MatcherAssert.assertThat(jsonStr, containsString("\"showTrayIcon\" : false"));