Merge branch 'develop' into feature/dokany-info-dialog

This commit is contained in:
Jan-Peter Klein
2024-06-11 15:25:23 +02:00
38 changed files with 285 additions and 147 deletions

View File

@@ -11,7 +11,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: '21.0.2+13'
JAVA_VERSION: '22.0.1+8'
jobs:
get-version:
@@ -80,7 +80,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
--strip-native-commands
--no-header-files
--no-man-pages

View File

@@ -7,7 +7,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
defaults:
run:

View File

@@ -5,7 +5,7 @@ on:
- cron: '0 0 1 * *' # run once a month at the first day of month
env:
JDK_VERSION: '21.0.2+13'
JDK_VERSION: '22.0.1+8'
JDK_VENDOR: zulu
jobs:

View File

@@ -17,9 +17,9 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: '21.0.2+13'
COFFEELIBS_JDK: 21
COFFEELIBS_JDK_VERSION: '21.0.2+13-0ppa1'
JAVA_VERSION: '22.0.1+8'
COFFEELIBS_JDK: 22
COFFEELIBS_JDK_VERSION: '22.0.1+8-0ppa1'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'

View File

@@ -11,7 +11,7 @@ jobs:
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 21
java-version: 22
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -23,7 +23,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
jobs:
determine-version:

View File

@@ -16,7 +16,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: '21.0.2+13'
JAVA_VERSION: '22.0.1+8'
jobs:
get-version:
@@ -91,7 +91,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages

View File

@@ -5,7 +5,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
defaults:
run:

View File

@@ -12,7 +12,7 @@ defaults:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
jobs:
check-preconditions:

View File

@@ -16,7 +16,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: '21.0.2+13'
JAVA_VERSION: '22.0.1+8'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
@@ -89,7 +89,7 @@ jobs:
--verbose
--output runtime
--module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages

2
.idea/misc.xml generated
View File

@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" project-jdk-name="22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -1,4 +1,6 @@
# created during build
# downloaded/created during build
openjfx-jmods.zip
*.jmod
Cryptomator.AppDir
*.AppImage
*.AppImage.zsync

View File

@@ -56,7 +56,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \
--output runtime \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--strip-native-commands \
--no-header-files \
--no-man-pages \

View File

@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-21 (>= 21.0.2+12-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), coffeelibs-jdk-22 (>= 22.0.1+8-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git

View File

@@ -4,7 +4,7 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
JAVA_HOME = /usr/lib/jvm/java-22-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
@@ -28,7 +28,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \
--output runtime \
--module-path "${JMODS_PATH}" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--strip-native-commands \
--no-header-files \
--no-man-pages \

View File

@@ -1,6 +1,8 @@
# created during build
# downloaded/created during build
Cryptomator.app/
runtime/
dmg/
*.dmg
license.rtf
license.rtf
openjfx-jmods.zip
*.jmod

View File

@@ -71,7 +71,7 @@ cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
--strip-native-commands \
--no-header-files \
--no-man-pages \

1
dist/win/.gitignore vendored
View File

@@ -6,4 +6,5 @@ installer
*.msi
*.exe
*.jmod
resources/jfxJmods.zip
license.rtf

2
dist/win/build.ps1 vendored
View File

@@ -74,7 +74,7 @@ Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\ja
--verbose `
--output runtime `
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--strip-native-commands `
--no-header-files `
--no-man-pages `

View File

@@ -1,5 +0,0 @@
@echo off
:: see comments in file ./version170-migrate-settings.ps1
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1

View File

@@ -1,35 +0,0 @@
# This script migrates Cryptomator settings for all local users on Windows in case the users uses custom directories as mountpoint
# See also https://github.com/cryptomator/cryptomator/pull/2654.
#
# TODO: This script should be evaluated in a yearly interval if it is still needed and if not, should be removed
#
#Requires -RunAsAdministrator
#Get all active, local user profiles
$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
Get-ChildItem $profileList | ForEach-Object {
$profilePath = $_.GetValue("ProfileImagePath")
$settingsPath = "$profilePath\AppData\Roaming\Cryptomator\settings.json"
if(!(Test-Path -Path $settingsPath -PathType Leaf)) {
#No settings file, nothing to do.
return;
}
$settings = Get-Content -Path $settingsPath | ConvertFrom-Json
if($settings.preferredVolumeImpl -ne "FUSE") {
#Fuse not used, nothing to do
return;
}
#check if customMountPoints are used
$atLeastOneCustomPath = $false;
foreach ($vault in $settings.directories){
$atLeastOneCustomPath = $atLeastOneCustomPath -or ($vault.useCustomMountPath -eq "True")
}
#if so, use WinFsp Local Drive
if( $atLeastOneCustomPath ) {
Add-Member -Force -InputObject $settings -Name "mountService" -Value "org.cryptomator.frontend.fuse.mount.WinFspMountProvider" -MemberType NoteProperty
$newSettings = $settings | Select-Object * -ExcludeProperty "preferredVolumeImpl"
ConvertTo-Json $newSettings | Set-Content -Path $settingsPath
}
}

View File

@@ -139,11 +139,6 @@
Sequence="execute" Before="PatchWebDAV" />
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
<SetProperty Id="V170MigrateSettings" Value="&quot;[INSTALLDIR]version170-migrate-settings.bat&quot;"
Sequence="execute" Before="V170MigrateSettings" />
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
<util:CloseApplication
@@ -195,7 +190,6 @@
<RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to WixCloseApplications -->
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence>
<InstallUISequence>

34
pom.xml
View File

@@ -26,7 +26,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>21</project.jdk.version>
<project.jdk.version>22</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
@@ -37,23 +37,23 @@
<cryptomator.integrations.version>1.3.1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.5</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.4.4</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>4.0.0</cryptomator.fuse.version>
<cryptomator.integrations.linux.version>1.4.5</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.0.0</cryptomator.fuse.version>
<cryptomator.webdav.version>2.0.6</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<commons-lang3.version>3.14.0</commons-lang3.version>
<dagger.version>2.51</dagger.version>
<dagger.version>2.51.1</dagger.version>
<easybind.version>2.2</easybind.version>
<guava.version>33.0.0-jre</guava.version>
<jackson.version>2.16.2</jackson.version>
<guava.version>33.2.1-jre</guava.version>
<jackson.version>2.17.1</jackson.version>
<javafx.version>21.0.1</javafx.version>
<jwt.version>4.4.0</jwt.version>
<nimbus-jose.version>9.37.3</nimbus-jose.version>
<logback.version>1.5.3</logback.version>
<slf4j.version>2.0.12</slf4j.version>
<logback.version>1.5.6</logback.version>
<slf4j.version>2.0.13</slf4j.version>
<tinyoauth2.version>0.8.0</tinyoauth2.version>
<zxcvbn.version>1.8.2</zxcvbn.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.10.2</junit.jupiter.version>
@@ -62,7 +62,7 @@
<!-- build-time dependencies -->
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
<dependency-check.version>9.1.0</dependency-check.version>
<dependency-check.version>9.2.0</dependency-check.version>
<jacoco.version>0.8.12</jacoco.version>
<license-generator.version>2.4.0</license-generator.version>
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
@@ -70,11 +70,16 @@
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.6.1</mvn-dependency.version>
<mvn-surefire.version>3.2.5</mvn-surefire.version>
<mvn-jar.version>3.3.0</mvn-jar.version>
<mvn-jar.version>3.4.1</mvn-jar.version>
</properties>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
@@ -158,11 +163,18 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>

View File

@@ -31,13 +31,13 @@ open module org.cryptomator.desktop {
requires javafx.graphics;
requires javafx.controls;
requires javafx.fxml;
requires jdk.crypto.ec;
// 3rd party:
requires ch.qos.logback.classic;
requires ch.qos.logback.core;
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;

View File

@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SubstitutingProperties extends PropertiesDecorator {
@@ -58,7 +59,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
return "";
} else {
return val.replace("\\", "\\\\");
return Matcher.quoteReplacement(val);
}
}

View File

@@ -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<VaultSettings> 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<Instant> lastSuccessfulUpdateCheck;
private Consumer<Settings> 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;
}

View File

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

View File

@@ -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<Settings> {
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;

View File

@@ -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<String> semVerComparator;
private final StringProperty latestVersion = new SimpleStringProperty();
private final ScheduledService<String> updateCheckerService;
private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
private final Comparator<String> versionComparator = new SemVerComparator();
private final BooleanBinding updateAvailable;
private final BooleanBinding checkFailed;
@Inject
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
UpdateChecker(Settings settings, //
Environment env, //
ScheduledService<String> 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<Instant> lastSuccessfulUpdateCheckProperty() {
return lastSuccessfulUpdateCheck;
}
public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
return state;
}
public String getCurrentVersion() {
return env.getAppVersion();
}
}

View File

@@ -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<HttpClient> provideHttpClient() {

View File

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

View File

@@ -7,6 +7,7 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.slf4j.Logger;
@@ -58,6 +59,7 @@ public class VaultListController implements FxController {
private final Stage mainWindow;
private final ObservableList<Vault> vaults;
private final VaultService vaultService;
private final ObjectProperty<Vault> selectedVault;
private final VaultListCellFactory cellFactory;
private final AddVaultWizardComponent.Builder addVaultWizard;
@@ -79,6 +81,7 @@ public class VaultListController implements FxController {
ObservableList<Vault> vaults, //
ObjectProperty<Vault> selectedVault, //
VaultListCellFactory cellFactory, //
VaultService vaultService, //
AddVaultWizardComponent.Builder addVaultWizard, //
RemoveVaultComponent.Builder removeVaultDialogue, //
VaultListManager vaultListManager, //
@@ -88,6 +91,7 @@ public class VaultListController implements FxController {
this.vaults = vaults;
this.selectedVault = selectedVault;
this.cellFactory = cellFactory;
this.vaultService = vaultService;
this.addVaultWizard = addVaultWizard;
this.removeVaultDialogue = removeVaultDialogue;
this.vaultListManager = vaultListManager;
@@ -119,6 +123,9 @@ public class VaultListController implements FxController {
Optional.ofNullable(selectedVault.get())
.filter(Vault::isLocked)
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
Optional.ofNullable(selectedVault.get())
.filter(Vault::isUnlocked)
.ifPresent(vaultService::reveal);
}
});

View File

@@ -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<ContentDisplay> checkForUpdatesButtonState;
private final ReadOnlyStringProperty latestVersion;
private final ObservableValue<Instant> lastSuccessfulUpdateCheck;
private final StringBinding lastUpdateCheckMessage;
private final ObservableValue<String> 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<ContentDisplay> 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<String> 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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -1,13 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import org.cryptomator.ui.controls.FormattedString?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.text.TextFlow?>
<?import javafx.scene.text.Text?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
@@ -18,19 +24,34 @@
<padding>
<Insets topRightBottomLeft="24"/>
</padding>
<children>
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
<VBox alignment="CENTER" spacing="12">
<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
<graphic>
<FontAwesome5Spinner fx:id="spinner" glyphSize="12"/>
</graphic>
</Button>
<VBox alignment="CENTER" spacing="12">
<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
<graphic>
<FontAwesome5Spinner glyphSize="12"/>
</graphic>
</Button>
<Hyperlink text="${linkLabel.value}" onAction="#visitDownloadsPage" textAlignment="CENTER" wrapText="true" styleClass="hyperlink-underline" visible="${controller.updateAvailable}"/>
</VBox>
</children>
<TextFlow styleClass="text-flow" textAlignment="CENTER" visible="${controller.checkFailed}" managed="${controller.checkFailed}">
<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-orange" glyph="EXCLAMATION_TRIANGLE"/>
<Text text=" "/>
<Text text="%preferences.updates.checkFailed"/>
<Text text=" "/>
<Hyperlink styleClass="hyperlink-underline" text="%preferences.general.debugDirectory" onAction="#showLogfileDirectory"/>
</TextFlow>
<FormattedLabel format="%preferences.updates.lastUpdateCheck" arg1="${controller.timeDifferenceMessage}" textAlignment="CENTER" wrapText="true">
<tooltip>
<Tooltip text="${controller.lastUpdateCheckMessage}" showDelay="10ms"/>
</tooltip>
</FormattedLabel>
<Label text="%preferences.updates.upToDate" visible="${controller.upToDateLabelVisible}" managed="${controller.upToDateLabelVisible}">
<graphic>
<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-primary" glyph="CHECK"/>
</graphic>
</Label>
<Hyperlink text="${linkLabel.value}" onAction="#visitDownloadsPage" textAlignment="CENTER" wrapText="true" styleClass="hyperlink-underline" visible="${controller.updateAvailable}" managed="${controller.updateAvailable}"/>
</VBox>
</VBox>

View File

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

View File

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