diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
index 3a7337fe3..83ec96635 100644
--- a/.github/CODE_OF_CONDUCT.md
+++ b/.github/CODE_OF_CONDUCT.md
@@ -22,7 +22,7 @@ include:
Examples of unacceptable behavior by participants include:
-* The use of sexualized language or imagery and unwelcome sexual attention or
+* The use of sexual language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index c7dc14547..f9ef8460a 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -14,5 +14,5 @@ For _everything else_, please visit our official [Cryptomator Community](https:/
- Discussions about the apps
- [Development discussions](https://community.cryptomator.org/c/development)
- General questions
- - Discussions regarding our design decissions
+ - Discussions regarding our design decisions
- Our roadmap
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3cf0cb845..21ef6aca2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -52,13 +52,14 @@ jobs:
with:
name: buildkit-win.zip
path: target/buildkit-win.zip
-
+
release:
name: Draft a Release on GitHub Releases
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'cryptomator/cryptomator'
steps:
+ - uses: actions/checkout@v2
- name: Download buildkit-linux.zip
uses: actions/download-artifact@v1
with:
@@ -74,6 +75,15 @@ jobs:
with:
name: buildkit-win.zip
path: .
+ - name: Create tarball
+ run: git archive --prefix="cryptomator-${{ github.ref }}/" -o "cryptomator-${{ github.ref }}.tar.gz" ${{ github.ref }}
+ - name: Sign tarball with key 615D449FE6E6A235
+ run: |
+ echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
+ echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a "cryptomator-${{ github.ref }}.tar.gz"
+ env:
+ GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
+ GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Create Release
id: create_release
uses: actions/create-release@v1
@@ -127,4 +137,13 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: buildkit-win.zip
asset_name: buildkit-win.zip
- asset_content_type: application/zip
\ No newline at end of file
+ asset_content_type: application/zip
+ - name: Upload tarball signature to GitHub Releases
+ uses: actions/upload-release-asset@v1.0.1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: "cryptomator-${{ github.ref }}.tar.gz.asc"
+ asset_name: "cryptomator-${{ github.ref }}.tar.gz.asc"
+ asset_content_type: application/octet-stream
diff --git a/.gitignore b/.gitignore
index 2bbe9355a..be67207df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,8 @@ pom.xml.versionsBackup
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/shelf
-.idea/dictionaries
+.idea/dictionaries/**
+!.idea/dictionaries/dict_*
.idea/compiler.xml
.idea/encodings.xml
.idea/jarRepositories.xml
diff --git a/.idea/dictionaries/dict_de.xml b/.idea/dictionaries/dict_de.xml
new file mode 100644
index 000000000..a6ecf3c80
--- /dev/null
+++ b/.idea/dictionaries/dict_de.xml
@@ -0,0 +1,15 @@
+
+
+
+ tresorinhalt
+ tresorkonfigurationsdatei
+ tresorlaufwerk
+ tresorliste
+ tresorname
+ tresoroptionen
+ tresorstatistik
+ ungespeicherten
+ ungespeicherter
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/dict_project.xml b/.idea/dictionaries/dict_project.xml
new file mode 100644
index 000000000..5649810c7
--- /dev/null
+++ b/.idea/dictionaries/dict_project.xml
@@ -0,0 +1,106 @@
+
+
+
+ addvault
+ addvaultwizard
+ adoptopenjdk
+ affero
+ aquafx
+ autolock
+ autolocked
+ autolocking
+ baos
+ bkup
+ buildkit
+ changepassword
+ checkerframework
+ crit
+ crowdin
+ cryptofs
+ cryptomator
+ cryptomator's
+ cryptor
+ csprng
+ dcryptomator
+ dfuse
+ dirid
+ djdk
+ dokany
+ dragboard
+ easybind
+ encr
+ errorprone
+ failureaccess
+ fbidis
+ fldinst
+ fldrslt
+ fontawesomefx
+ gdrive
+ gvfs
+ hmmss
+ httpcomponents
+ httpcore
+ iclouddrive
+ jensd
+ jffi
+ keyloading
+ kibs
+ listcell
+ listenablefuture
+ lopp
+ mainwindow
+ masterkey
+ masterkeyfile
+ mibs
+ mountpath
+ mpc's
+ needsmigration
+ noncommercially
+ noël
+ nulab
+ oatomic
+ oauto
+ objectgraph
+ odefault
+ ogid
+ onoappledouble
+ ordonly
+ ouid
+ ovolname
+ passthrough
+ patreon
+ pcloud
+ probot
+ recoverykey
+ relicensing
+ removevault
+ revealer
+ serceman
+ setolabs
+ skymatic
+ socio
+ spof
+ stenzel
+ styleclass
+ sublicenses
+ sublicensing
+ systemkeychain
+ tada
+ tidelift
+ tmpmountpoint
+ tobiasdiez
+ toggler
+ traymenu
+ unknownerror
+ unlockable
+ vaultconfig
+ vaultlist
+ vaultname
+ vaultoptions
+ wrongfilealert
+ xattr
+ zillmann
+ zxcvbn
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 146ab09b7..0634be039 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/README.md b/README.md
index ad821780e..5e847208c 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[](https://cryptomator.org/)
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
-[](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
+[](https://snyk.io/test/github/cryptomator/cryptomator)
[](https://www.codacy.com/gh/cryptomator/cryptomator/dashboard)
[](http://twitter.com/Cryptomator)
[](https://translate.cryptomator.org/)
@@ -56,7 +56,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
- File names get encrypted
- Folder structure gets obfuscated
- Use as many vaults in your Dropbox as you want, each having individual passwords
-- Two thousand commits for the security of your data!! :tada:
+- Three thousand commits for the security of your data!! :tada:
### Privacy
diff --git a/pom.xml b/pom.xml
index 61bae548b..cfe5c7e55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,29 +24,29 @@
16
- 2.1.0-beta6
+ 2.1.0-beta81.0.0-rc11.0.0-beta21.0.0-beta21.0.0-beta11.3.11.3.1
- 1.2.2
+ 1.2.416
- 3.11
- 3.15.0
- 2.1.0
+ 3.12.0
+ 3.17.0
+ 2.230.1.1-jre
- 2.35.1
+ 2.372.8.6
- 1.7.30
+ 1.7.311.2.3
- 5.7.1
- 3.9.0
+ 5.7.2
+ 3.11.22.2
diff --git a/src/main/java/org/cryptomator/common/CommonsModule.java b/src/main/java/org/cryptomator/common/CommonsModule.java
index 705f98b8f..eb7e6cd13 100644
--- a/src/main/java/org/cryptomator/common/CommonsModule.java
+++ b/src/main/java/org/cryptomator/common/CommonsModule.java
@@ -46,6 +46,7 @@ public abstract class CommonsModule {
private static final int NUM_CORE_BG_THREADS = 6;
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
+ @SuppressWarnings("SpellCheckingInspection")
@Provides
@Singleton
@Named("licensePublicKey")
diff --git a/src/main/java/org/cryptomator/common/Environment.java b/src/main/java/org/cryptomator/common/Environment.java
index e27ae9bde..a6c20c2d7 100644
--- a/src/main/java/org/cryptomator/common/Environment.java
+++ b/src/main/java/org/cryptomator/common/Environment.java
@@ -123,8 +123,8 @@ public class Environment {
return Stream.empty();
} else {
Iterable iter = Splitter.on(separator).split(value);
- Spliterator spliter = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
- return StreamSupport.stream(spliter, false);
+ Spliterator spliterator = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
+ return StreamSupport.stream(spliterator, false);
}
}
}
diff --git a/src/main/java/org/cryptomator/common/SemVerComparator.java b/src/main/java/org/cryptomator/common/SemVerComparator.java
index e0ff9ae25..0f9148bd5 100644
--- a/src/main/java/org/cryptomator/common/SemVerComparator.java
+++ b/src/main/java/org/cryptomator/common/SemVerComparator.java
@@ -60,17 +60,17 @@ public class SemVerComparator implements Comparator {
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
- int subversionComparisionResult = 0;
+ int subversionComparisonResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
- subversionComparisionResult = v1 - v2;
+ subversionComparisonResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
- subversionComparisionResult = vComps1[i].compareTo(vComps2[i]);
+ subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
}
- if (subversionComparisionResult != 0) {
- return subversionComparisionResult;
+ if (subversionComparisonResult != 0) {
+ return subversionComparisonResult;
}
}
diff --git a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
index 64db08018..57af6c685 100644
--- a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
+++ b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
@@ -59,8 +59,10 @@ public class KeychainManager implements KeychainAccessProvider {
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
- getKeychainOrFail().changePassphrase(key, passphrase);
- setPassphraseStored(key, true);
+ if (isPassphraseStored(key)) {
+ getKeychainOrFail().changePassphrase(key, passphrase);
+ setPassphraseStored(key, true);
+ }
}
@Override
diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java
index ee654db49..e4cb9b8f7 100644
--- a/src/main/java/org/cryptomator/common/settings/Settings.java
+++ b/src/main/java/org/cryptomator/common/settings/Settings.java
@@ -30,7 +30,7 @@ public class Settings {
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
- public static final boolean DEFAULT_CHECK_FOR_UDPATES = false;
+ public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
public static final boolean DEFAULT_START_HIDDEN = false;
public static final int DEFAULT_PORT = 42427;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
@@ -46,7 +46,7 @@ public class Settings {
private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
- private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
+ private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
diff --git a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
index 8310d0fa9..3be42f7ac 100644
--- a/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
@@ -101,7 +101,7 @@ public class SettingsProvider implements Supplier {
if (settings == null) {
return;
}
- final Optional settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
+ final Optional settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
settingsPath.ifPresent(path -> {
Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
index 10a023806..a7f366bfd 100644
--- a/src/main/java/org/cryptomator/common/settings/VaultSettings.java
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
@@ -31,7 +31,7 @@ import java.util.Random;
public class VaultSettings {
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
- public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
+ public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
public static final boolean DEFAULT_USES_READONLY_MODE = false;
public static final String DEFAULT_MOUNT_FLAGS = "";
@@ -47,7 +47,7 @@ public class VaultSettings {
private final StringProperty displayName = new SimpleStringProperty();
private final StringProperty winDriveLetter = new SimpleStringProperty();
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
- private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
+ private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
private final StringProperty customMountPath = new SimpleStringProperty();
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
index 0cf92a48b..15a081f0d 100644
--- a/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
@@ -44,7 +44,7 @@ class VaultSettingsJsonAdapter {
String customMountPath = null;
String winDriveLetter = null;
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
- boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
+ boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
diff --git a/src/main/java/org/cryptomator/common/vaults/VaultModule.java b/src/main/java/org/cryptomator/common/vaults/VaultModule.java
index 901ee7f42..4ee13a083 100644
--- a/src/main/java/org/cryptomator/common/vaults/VaultModule.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultModule.java
@@ -138,7 +138,7 @@ public class VaultModule {
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse_main.c#L53-L62 for syntax guide
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse.c#L295-L319 for options (-o <...>)
- // see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were choosen
+ // see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were chosen
private String getWindowsFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_WINDOWS;
StringBuilder flags = new StringBuilder();
diff --git a/src/main/java/org/cryptomator/common/vaults/VaultState.java b/src/main/java/org/cryptomator/common/vaults/VaultState.java
index 801ea7653..51365fbd2 100644
--- a/src/main/java/org/cryptomator/common/vaults/VaultState.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultState.java
@@ -46,7 +46,7 @@ public class VaultState extends ObservableValueBase implements
UNLOCKED,
/**
- * Unknown state due to preceeding unrecoverable exceptions.
+ * Unknown state due to preceding unrecoverable exceptions.
*/
ERROR;
}
diff --git a/src/main/java/org/cryptomator/common/vaults/VaultStats.java b/src/main/java/org/cryptomator/common/vaults/VaultStats.java
index 649be3a09..ac0b8df38 100644
--- a/src/main/java/org/cryptomator/common/vaults/VaultStats.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultStats.java
@@ -35,8 +35,8 @@ public class VaultStats {
private final LongProperty bytesPerSecondEncrypted = new SimpleLongProperty();
private final LongProperty bytesPerSecondDecrypted = new SimpleLongProperty();
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
- private final LongProperty toalBytesRead = new SimpleLongProperty();
- private final LongProperty toalBytesWritten = new SimpleLongProperty();
+ private final LongProperty totalBytesRead = new SimpleLongProperty();
+ private final LongProperty totalBytesWritten = new SimpleLongProperty();
private final LongProperty totalBytesEncrypted = new SimpleLongProperty();
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
@@ -75,8 +75,8 @@ public class VaultStats {
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
bytesPerSecondDecrypted.set(stats.map(CryptoFileSystemStats::pollBytesDecrypted).orElse(0L));
bytesPerSecondEncrypted.set(stats.map(CryptoFileSystemStats::pollBytesEncrypted).orElse(0L));
- toalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
- toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
+ totalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
+ totalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
var oldAccessCount = filesRead.get() + filesWritten.get();
@@ -146,7 +146,7 @@ public class VaultStats {
return bytesPerSecondEncrypted;
}
- public long getBytesPerSecondEnrypted() {
+ public long getBytesPerSecondEncrypted() {
return bytesPerSecondEncrypted.get();
}
@@ -164,13 +164,13 @@ public class VaultStats {
return cacheHitRate.get();
}
- public LongProperty toalBytesReadProperty() {return toalBytesRead;}
+ public LongProperty totalBytesReadProperty() {return totalBytesRead;}
- public long getTotalBytesRead() { return toalBytesRead.get();}
+ public long getTotalBytesRead() { return totalBytesRead.get();}
- public LongProperty toalBytesWrittenProperty() {return toalBytesWritten;}
+ public LongProperty totalBytesWrittenProperty() {return totalBytesWritten;}
- public long getTotalBytesWritten() { return toalBytesWritten.get();}
+ public long getTotalBytesWritten() { return totalBytesWritten.get();}
public LongProperty totalBytesEncryptedProperty() {return totalBytesEncrypted;}
diff --git a/src/main/java/org/cryptomator/common/vaults/Volume.java b/src/main/java/org/cryptomator/common/vaults/Volume.java
index f608122bf..5f434fa43 100644
--- a/src/main/java/org/cryptomator/common/vaults/Volume.java
+++ b/src/main/java/org/cryptomator/common/vaults/Volume.java
@@ -12,7 +12,7 @@ import java.util.function.Consumer;
import java.util.stream.Stream;
/**
- * Takes a Volume and usess it to mount an unlocked vault
+ * Takes a Volume and uses it to mount an unlocked vault
*/
public interface Volume {
@@ -24,7 +24,7 @@ public interface Volume {
boolean isSupported();
/**
- * Gets the coresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
+ * Gets the corresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
*
* @return the type of implementation as defined by the {@link VolumeImpl VolumeImpl enum}
*/
diff --git a/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java b/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
index 03c83377c..b1850bc1e 100644
--- a/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
+++ b/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
@@ -67,7 +67,7 @@ public class WebDavVolume implements Volume {
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
}
- //on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specifc one or there is no free.
+ //on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specific one or there is no free.
Supplier driveLetterSupplier;
if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
driveLetterSupplier = () -> windowsDriveLetters.getAvailableDriveLetter().orElse(null);
diff --git a/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java b/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
similarity index 86%
rename from src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java
rename to src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
index 3879fbdc6..25f5239ab 100644
--- a/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java
+++ b/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
@@ -11,12 +11,12 @@ import java.io.File;
*
* @param Event type the policy possibly reacts to
*/
-public class LaunchAndSizeBasedTriggerinPolicy extends TriggeringPolicyBase {
+public class LaunchAndSizeBasedTriggeringPolicy extends TriggeringPolicyBase {
LaunchBasedTriggeringPolicy launchBasedTriggeringPolicy;
SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy;
- public LaunchAndSizeBasedTriggerinPolicy(FileSize threshold) {
+ public LaunchAndSizeBasedTriggeringPolicy(FileSize threshold) {
this.launchBasedTriggeringPolicy = new LaunchBasedTriggeringPolicy<>();
this.sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy<>();
sizeBasedTriggeringPolicy.setMaxFileSize(threshold);
diff --git a/src/main/java/org/cryptomator/logging/LoggerModule.java b/src/main/java/org/cryptomator/logging/LoggerModule.java
index 0b24e0e24..4866655e3 100644
--- a/src/main/java/org/cryptomator/logging/LoggerModule.java
+++ b/src/main/java/org/cryptomator/logging/LoggerModule.java
@@ -85,7 +85,7 @@ public class LoggerModule {
appender.setContext(context);
appender.setFile(logDir.resolve(LOGFILE_NAME).toString());
appender.setEncoder(encoder);
- LaunchAndSizeBasedTriggerinPolicy triggeringPolicy = new LaunchAndSizeBasedTriggerinPolicy(FileSize.valueOf(LOG_MAX_SIZE));
+ LaunchAndSizeBasedTriggeringPolicy triggeringPolicy = new LaunchAndSizeBasedTriggeringPolicy(FileSize.valueOf(LOG_MAX_SIZE));
triggeringPolicy.setContext(context);
triggeringPolicy.start();
appender.setTriggeringPolicy(triggeringPolicy);
diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
index 4b4e02ed2..28b8384bb 100644
--- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
@@ -5,8 +5,8 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
-import org.cryptomator.cryptofs.VaultCipherCombo;
import org.cryptomator.cryptolib.api.CryptoException;
+import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
@@ -182,7 +182,7 @@ public class CreateNewVaultPasswordController implements FxController {
// 2. initialize vault:
try {
MasterkeyLoader loader = ignored -> masterkey.clone();
- CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(VaultCipherCombo.SIV_CTRMAC).withKeyLoader(loader).build();
+ CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC).withKeyLoader(loader).build();
CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID);
// 3. write vault-internal readme file:
diff --git a/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java b/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
index cf8940cc2..cbd023ce3 100644
--- a/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
+++ b/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
@@ -67,8 +67,8 @@ public class FxmlLoaderFactory {
}
Parent root = loader.getRoot();
// TODO: discuss if we can remove language-specific stylesheets
- // List addtionalStyleSheets = Splitter.on(',').omitEmptyStrings().splitToList(resourceBundle.getString("additionalStyleSheets"));
- // addtionalStyleSheets.forEach(styleSheet -> root.getStylesheets().add("/css/" + styleSheet));
+ // List additionalStyleSheets = Splitter.on(',').omitEmptyStrings().splitToList(resourceBundle.getString("additionalStyleSheets"));
+ // additionalStyleSheets.forEach(styleSheet -> root.getStylesheets().add("/css/" + styleSheet));
return sceneFactory.apply(root);
}
diff --git a/src/main/java/org/cryptomator/ui/common/WeakBindings.java b/src/main/java/org/cryptomator/ui/common/WeakBindings.java
index e6071df1d..6efa747c9 100644
--- a/src/main/java/org/cryptomator/ui/common/WeakBindings.java
+++ b/src/main/java/org/cryptomator/ui/common/WeakBindings.java
@@ -77,7 +77,7 @@ public final class WeakBindings {
* @param observable The observable
* @return a IntegerBinding weakly referenced from the given observable
*/
- public static IntegerBinding bindInterger(ObservableValue observable) {
+ public static IntegerBinding bindInteger(ObservableValue observable) {
return new IntegerBinding() {
{
bind(observable);
diff --git a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
index a273447bb..6ec3cddb4 100644
--- a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
+++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
@@ -39,7 +39,7 @@ public enum FontAwesome5Icon {
REDO("\uF01E"), //
SEARCH("\uF002"), //
SPINNER("\uF110"), //
- STOPWATCH("\uF2F2"), //
+ STETHOSCOPE("\uF0f1"), //
SYNC("\uF021"), //
TIMES("\uF00D"), //
TRASH("\uF1F8"), //
diff --git a/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
index 4d0797eaa..3bfa70c46 100644
--- a/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
+++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
@@ -21,8 +21,8 @@ public class FontAwesome5IconView extends Text {
private static final String FONT_PATH = "/css/fontawesome5-free-solid.otf";
private static final Font FONT;
- private ObjectProperty glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
- private DoubleProperty glyphSize = new SimpleDoubleProperty(this, "glyphSize", DEFAULT_GLYPH_SIZE);
+ private final ObjectProperty glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
+ private final DoubleProperty glyphSize = new SimpleDoubleProperty(this, "glyphSize", DEFAULT_GLYPH_SIZE);
static {
try {
@@ -42,7 +42,7 @@ public class FontAwesome5IconView extends Text {
}
private void glyphChanged(@SuppressWarnings("unused") ObservableValue extends FontAwesome5Icon> observable, @SuppressWarnings("unused") FontAwesome5Icon oldValue, FontAwesome5Icon newValue) {
- setText(newValue.unicode());
+ setText(newValue == null ? null : newValue.unicode());
}
private void glyphSizeChanged(@SuppressWarnings("unused") ObservableValue extends Number> observable, @SuppressWarnings("unused") Number oldValue, Number newValue) {
diff --git a/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java b/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
index 928cfc40e..4a4e43fff 100644
--- a/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
+++ b/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
@@ -12,8 +12,8 @@ import javafx.scene.layout.StackPane;
public class NiceSecurePasswordField extends StackPane {
private static final String STYLE_CLASS = "nice-secure-password-field";
- private static final String ICONS_STLYE_CLASS = "icons";
- private static final String REVEAL_BUTTON_STLYE_CLASS = "reveal-button";
+ private static final String ICONS_STYLE_CLASS = "icons";
+ private static final String REVEAL_BUTTON_STYLE_CLASS = "reveal-button";
private static final int ICON_SPACING = 6;
private static final double ICON_SIZE = 14.0;
@@ -30,7 +30,7 @@ public class NiceSecurePasswordField extends StackPane {
iconContainer.setAlignment(Pos.CENTER_RIGHT);
iconContainer.setMaxWidth(Double.NEGATIVE_INFINITY);
iconContainer.setPrefWidth(42); // TODO
- iconContainer.getStyleClass().add(ICONS_STLYE_CLASS);
+ iconContainer.getStyleClass().add(ICONS_STYLE_CLASS);
StackPane.setAlignment(iconContainer, Pos.CENTER_RIGHT);
capsLockedIcon.setGlyph(FontAwesome5Icon.ARROW_UP);
@@ -51,7 +51,7 @@ public class NiceSecurePasswordField extends StackPane {
revealPasswordButton.setFocusTraversable(false);
revealPasswordButton.visibleProperty().bind(passwordField.focusedProperty());
revealPasswordButton.managedProperty().bind(passwordField.focusedProperty());
- revealPasswordButton.getStyleClass().add(REVEAL_BUTTON_STLYE_CLASS);
+ revealPasswordButton.getStyleClass().add(REVEAL_BUTTON_STYLE_CLASS);
passwordField.revealPasswordProperty().bind(revealPasswordButton.selectedProperty());
diff --git a/src/main/java/org/cryptomator/ui/controls/ThrougputLabel.java b/src/main/java/org/cryptomator/ui/controls/ThroughputLabel.java
similarity index 96%
rename from src/main/java/org/cryptomator/ui/controls/ThrougputLabel.java
rename to src/main/java/org/cryptomator/ui/controls/ThroughputLabel.java
index a21e6d916..19999ea11 100644
--- a/src/main/java/org/cryptomator/ui/controls/ThrougputLabel.java
+++ b/src/main/java/org/cryptomator/ui/controls/ThroughputLabel.java
@@ -8,7 +8,7 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Label;
-public class ThrougputLabel extends Label {
+public class ThroughputLabel extends Label {
private static final long KIBS_THRESHOLD = 1l << 7; // 0.128 kiB/s
private static final long MIBS_THRESHOLD = 1l << 19; // 0.512 MiB/s
@@ -18,7 +18,7 @@ public class ThrougputLabel extends Label {
private final StringProperty mibsFormat = new SimpleStringProperty("%.3f");
private final LongProperty bytesPerSecond = new SimpleLongProperty();
- public ThrougputLabel() {
+ public ThroughputLabel() {
textProperty().bind(createStringBinding());
}
diff --git a/src/main/java/org/cryptomator/ui/health/BatchService.java b/src/main/java/org/cryptomator/ui/health/BatchService.java
index f3968c27d..40f4e173f 100644
--- a/src/main/java/org/cryptomator/ui/health/BatchService.java
+++ b/src/main/java/org/cryptomator/ui/health/BatchService.java
@@ -16,7 +16,6 @@ public class BatchService extends Service {
private final Iterator remainingTasks;
- @Inject
public BatchService(Iterable tasks) {
this.remainingTasks = tasks.iterator();
}
diff --git a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java
index 281e404cc..7ccc9e09c 100644
--- a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java
+++ b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java
@@ -15,17 +15,18 @@ import javafx.collections.FXCollections;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
+import java.time.Duration;
+import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Stream;
@HealthCheckScoped
public class CheckDetailController implements FxController {
- private final EasyObservableList results;
+ private final EasyObservableList results;
private final OptionalBinding taskState;
private final Binding taskName;
- private final Binding taskDuration;
- private final ResultListCellFactory resultListCellFactory;
+ private final Binding taskDuration;
private final Binding taskRunning;
private final Binding taskScheduled;
private final Binding taskFinished;
@@ -35,17 +36,20 @@ public class CheckDetailController implements FxController {
private final Binding taskCancelled;
private final Binding countOfWarnSeverity;
private final Binding countOfCritSeverity;
+ private final ResultListCellFactory resultListCellFactory;
+ private final ResourceBundle resourceBundle;
- public ListView resultsListView;
+ public ListView resultsListView;
private Subscription resultSubscription;
@Inject
- public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory) {
+ public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory, ResourceBundle resourceBundle) {
+ this.resultListCellFactory = resultListCellFactory;
+ this.resourceBundle = resourceBundle;
this.results = EasyBind.wrapList(FXCollections.observableArrayList());
this.taskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty);
this.taskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse("");
- this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L);
- this.resultListCellFactory = resultListCellFactory;
+ this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L).map(this::millisToReadAbleDuration);
this.taskRunning = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::runningProperty).orElse(false); //TODO: DOES NOT WORK
this.taskScheduled = taskState.map(Worker.State.SCHEDULED::equals).orElse(false);
this.taskNotStarted = taskState.map(Worker.State.READY::equals).orElse(false);
@@ -67,8 +71,8 @@ public class CheckDetailController implements FxController {
}
}
- private Function, Long> countSeverity(DiagnosticResult.Severity severity) {
- return stream -> stream.filter(item -> severity.equals(item.getSeverity())).count();
+ private Function, Long> countSeverity(DiagnosticResult.Severity severity) {
+ return stream -> stream.filter(item -> severity.equals(item.diagnosis().getSeverity())).count();
}
@FXML
@@ -87,11 +91,11 @@ public class CheckDetailController implements FxController {
return taskName;
}
- public Number getTaskDuration() {
+ public String getTaskDuration() {
return taskDuration.getValue();
}
- public Binding taskDurationProperty() {
+ public Binding taskDurationProperty() {
return taskDuration;
}
@@ -167,4 +171,21 @@ public class CheckDetailController implements FxController {
return taskCancelled;
}
+ private String millisToReadAbleDuration(Number millis) {
+ Duration tmp = Duration.ofMillis(millis.longValue());
+ long hours = tmp.toHoursPart();
+ long minutes = tmp.toMinutesPart();
+ long seconds = tmp.toSecondsPart();
+ if (hours != 0) {
+ String hms_format = resourceBundle.getString("health.check.detail.hmsFormat");
+ return String.format(hms_format, hours, minutes, seconds);
+ } else if (minutes != 0) {
+ String ms_format = resourceBundle.getString("health.check.detail.msFormat");
+ return String.format(ms_format, minutes, seconds);
+ } else {
+ String s_format = resourceBundle.getString("health.check.detail.sFormat");
+ return String.format(s_format, seconds);
+ }
+ }
+
}
diff --git a/src/main/java/org/cryptomator/ui/health/CheckListCell.java b/src/main/java/org/cryptomator/ui/health/CheckListCell.java
index 78f8b1b33..76a8f3c27 100644
--- a/src/main/java/org/cryptomator/ui/health/CheckListCell.java
+++ b/src/main/java/org/cryptomator/ui/health/CheckListCell.java
@@ -1,64 +1,71 @@
package org.cryptomator.ui.health;
+import org.cryptomator.cryptofs.health.api.DiagnosticResult;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
import javafx.beans.binding.Bindings;
-import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
+import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
+import java.util.function.Predicate;
class CheckListCell extends ListCell {
private final FontAwesome5IconView stateIcon = new FontAwesome5IconView();
+ private CheckBox checkBox = new CheckBox();
CheckListCell() {
setPadding(new Insets(6));
setAlignment(Pos.CENTER_LEFT);
setContentDisplay(ContentDisplay.LEFT);
+ getStyleClass().add("label");
}
@Override
protected void updateItem(HealthCheckTask item, boolean empty) {
super.updateItem(item, empty);
-
if (item != null) {
- textProperty().bind(item.titleProperty());
- item.stateProperty().addListener(this::stateChanged);
- graphicProperty().bind(Bindings.createObjectBinding(() -> graphicForState(item.getState()),item.stateProperty()));
- stateIcon.setGlyph(glyphForState(item.getState()));
+ setText(item.getTitle());
+ graphicProperty().bind(Bindings.createObjectBinding(() -> graphicForState(item.getState()), item.stateProperty()));
+ stateIcon.glyphProperty().bind(Bindings.createObjectBinding(() -> glyphForState(item), item.stateProperty()));
+ checkBox.selectedProperty().bindBidirectional(item.chosenForExecutionProperty());
} else {
- textProperty().unbind();
graphicProperty().unbind();
setGraphic(null);
setText(null);
+ checkBox.selectedProperty().unbind();
}
}
- private void stateChanged(ObservableValue extends Worker.State> observable, Worker.State oldState, Worker.State newState) {
- stateIcon.setGlyph(glyphForState(newState));
- stateIcon.setVisible(true);
- }
-
private Node graphicForState(Worker.State state) {
return switch (state) {
- case READY -> null;
+ case READY -> checkBox;
case SCHEDULED, RUNNING, FAILED, CANCELLED, SUCCEEDED -> stateIcon;
};
}
- private FontAwesome5Icon glyphForState(Worker.State state) {
- return switch (state) {
+ private FontAwesome5Icon glyphForState(HealthCheckTask item) {
+ return switch (item.getState()) {
case READY -> FontAwesome5Icon.COG; //just a placeholder
case SCHEDULED -> FontAwesome5Icon.CLOCK;
case RUNNING -> FontAwesome5Icon.SPINNER;
case FAILED -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
case CANCELLED -> FontAwesome5Icon.BAN;
- case SUCCEEDED -> FontAwesome5Icon.CHECK;
+ case SUCCEEDED -> checkFoundProblems(item) ? FontAwesome5Icon.EXCLAMATION_TRIANGLE : FontAwesome5Icon.CHECK;
};
}
+
+ private boolean checkFoundProblems(HealthCheckTask item) {
+ Predicate isProblem = severity -> switch (severity) {
+ case WARN, CRITICAL -> true;
+ case INFO, GOOD -> false;
+ };
+ return item.results().stream().map(Result::diagnosis).map(DiagnosticResult::getSeverity).anyMatch(isProblem);
+ }
+
}
diff --git a/src/main/java/org/cryptomator/ui/health/CheckListController.java b/src/main/java/org/cryptomator/ui/health/CheckListController.java
index ccb41d56b..7710eb14d 100644
--- a/src/main/java/org/cryptomator/ui/health/CheckListController.java
+++ b/src/main/java/org/cryptomator/ui/health/CheckListController.java
@@ -1,6 +1,7 @@
package org.cryptomator.ui.health;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
import com.tobiasdiez.easybind.EasyBind;
import dagger.Lazy;
import org.cryptomator.ui.common.ErrorComponent;
@@ -10,28 +11,24 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
+import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
-import javafx.scene.control.cell.CheckBoxListCell;
import javafx.stage.Stage;
-import javafx.util.StringConverter;
import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -43,40 +40,34 @@ public class CheckListController implements FxController {
private final Stage window;
private final ObservableList tasks;
+ private final FilteredList chosenTasks;
private final ReportWriter reportWriter;
private final ExecutorService executorService;
private final ObjectProperty selectedTask;
- private final Lazy errorComponenBuilder;
+ private final Lazy errorComponentBuilder;
private final SimpleObjectProperty> runningTask;
private final Binding running;
private final Binding finished;
- private final Map listPickIndicators;
- private final IntegerProperty numberOfPickedChecks;
+ private final IntegerBinding chosenTaskCount;
private final BooleanBinding anyCheckSelected;
private final BooleanProperty showResultScreen;
/* FXML */
public ListView checksListView;
-
@Inject
- public CheckListController(@HealthCheckWindow Stage window, Lazy> tasks, ReportWriter reportWriteTask, ObjectProperty selectedTask, ExecutorService executorService, Lazy errorComponenBuilder) {
+ public CheckListController(@HealthCheckWindow Stage window, Lazy> tasks, ReportWriter reportWriteTask, ObjectProperty selectedTask, ExecutorService executorService, Lazy errorComponentBuilder) {
this.window = window;
- this.tasks = FXCollections.observableArrayList(tasks.get());
+ this.tasks = FXCollections.observableList(tasks.get(), HealthCheckTask::observables);
+ this.chosenTasks = this.tasks.filtered(HealthCheckTask::isChosenForExecution);
this.reportWriter = reportWriteTask;
this.executorService = executorService;
this.selectedTask = selectedTask;
- this.errorComponenBuilder = errorComponenBuilder;
+ this.errorComponentBuilder = errorComponentBuilder;
this.runningTask = new SimpleObjectProperty<>();
this.running = EasyBind.wrapNullable(runningTask).mapObservable(Worker::runningProperty).orElse(false);
this.finished = EasyBind.wrapNullable(runningTask).mapObservable(Worker::stateProperty).map(END_STATES::contains).orElse(false);
- this.listPickIndicators = new HashMap<>();
- this.numberOfPickedChecks = new SimpleIntegerProperty(0);
- this.tasks.forEach(task -> {
- var entrySelectedProp = new SimpleBooleanProperty(false);
- entrySelectedProp.addListener((observable, oldValue, newValue) -> numberOfPickedChecks.set(numberOfPickedChecks.get() + (newValue ? 1 : -1)));
- listPickIndicators.put(task, entrySelectedProp);
- });
+ this.chosenTaskCount = Bindings.size(this.chosenTasks);
this.anyCheckSelected = selectedTask.isNotNull();
this.showResultScreen = new SimpleBooleanProperty(false);
}
@@ -84,38 +75,32 @@ public class CheckListController implements FxController {
@FXML
public void initialize() {
checksListView.setItems(tasks);
- checksListView.setCellFactory(CheckBoxListCell.forListView(listPickIndicators::get, new StringConverter() {
- @Override
- public String toString(HealthCheckTask object) {
- return object.getTitle();
- }
-
- @Override
- public HealthCheckTask fromString(String string) {
- return null;
- }
- }));
+ checksListView.setCellFactory(view -> new CheckListCell());
selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty());
}
@FXML
public void toggleSelectAll(ActionEvent event) {
if (event.getSource() instanceof CheckBox c) {
- listPickIndicators.forEach( (task, pickProperty) -> pickProperty.set(c.isSelected()));
+ tasks.forEach(t -> t.chosenForExecutionProperty().set(c.isSelected()));
}
}
@FXML
public void runSelectedChecks() {
Preconditions.checkState(runningTask.get() == null);
- var batch = checksListView.getItems().filtered(item -> listPickIndicators.get(item).get());
- var batchService = new BatchService(batch);
+
+ // prevent further interaction by cancelling non-chosen tasks:
+ tasks.filtered(Predicates.not(chosenTasks::contains)).forEach(HealthCheckTask::cancel);
+
+ // run chosen tasks:
+ var batchService = new BatchService(chosenTasks);
batchService.setExecutor(executorService);
batchService.start();
runningTask.set(batchService);
showResultScreen.set(true);
- checksListView.getSelectionModel().select(batch.get(0));
- checksListView.setCellFactory(view -> new CheckListCell());
+ checksListView.getSelectionModel().select(chosenTasks.get(0));
+ checksListView.refresh();
window.sizeToScene();
}
@@ -131,7 +116,7 @@ public class CheckListController implements FxController {
reportWriter.writeReport(tasks);
} catch (IOException e) {
LOG.error("Failed to write health check report.", e);
- errorComponenBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ errorComponentBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
}
@@ -168,13 +153,12 @@ public class CheckListController implements FxController {
return showResultScreen;
}
- public int getNumberOfPickedChecks() {
- return numberOfPickedChecks.get();
+ public int getChosenTaskCount() {
+ return chosenTaskCount.getValue();
}
- public IntegerProperty numberOfPickedChecksProperty() {
- return numberOfPickedChecks;
+ public IntegerBinding chosenTaskCountProperty() {
+ return chosenTaskCount;
}
-
}
diff --git a/src/main/java/org/cryptomator/ui/health/DummyHealthChecks.java b/src/main/java/org/cryptomator/ui/health/DummyHealthChecks.java
new file mode 100644
index 000000000..d2bccff37
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/health/DummyHealthChecks.java
@@ -0,0 +1,41 @@
+package org.cryptomator.ui.health;
+
+import org.cryptomator.cryptofs.VaultConfig;
+import org.cryptomator.cryptofs.health.api.DiagnosticResult;
+import org.cryptomator.cryptofs.health.api.HealthCheck;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.Masterkey;
+
+import java.nio.file.Path;
+import java.util.function.Consumer;
+
+/**
+ * FIXME: Remove in production release
+ */
+public class DummyHealthChecks {
+
+ public static class DummyCheck1 implements HealthCheck {
+
+ @Override
+ public void check(Path path, VaultConfig vaultConfig, Masterkey masterkey, Cryptor cryptor, Consumer consumer) {
+ // no-op
+ }
+ }
+
+ public static class DummyCheck2 implements HealthCheck {
+
+ @Override
+ public void check(Path path, VaultConfig vaultConfig, Masterkey masterkey, Cryptor cryptor, Consumer consumer) {
+ // no-op
+ }
+ }
+
+ public static class DummyCheck3 implements HealthCheck {
+
+ @Override
+ public void check(Path path, VaultConfig vaultConfig, Masterkey masterkey, Cryptor cryptor, Consumer consumer) {
+ // no-op
+ }
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java
index 48b16f694..365ab63df 100644
--- a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java
+++ b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java
@@ -7,6 +7,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import javax.inject.Named;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -17,6 +18,9 @@ public interface HealthCheckComponent {
@HealthCheckWindow
Stage window();
+ @Named("windowToClose")
+ Stage windowToClose();
+
@FxmlScene(FxmlFile.HEALTH_START)
Lazy scene();
@@ -24,6 +28,7 @@ public interface HealthCheckComponent {
Stage stage = window();
stage.setScene(scene().get());
stage.show();
+ windowToClose().close();
return stage;
}
@@ -33,6 +38,9 @@ public interface HealthCheckComponent {
@BindsInstance
Builder vault(@HealthCheckWindow Vault vault);
+ @BindsInstance
+ Builder windowToClose(@Named("windowToClose") Stage window);
+
HealthCheckComponent build();
}
diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java
index e33a9f2f1..78643f011 100644
--- a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java
+++ b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java
@@ -28,6 +28,7 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import java.security.SecureRandom;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
@@ -63,7 +64,7 @@ abstract class HealthCheckModule {
/* Only inject with Lazy-Wrapper!*/
@Provides
@HealthCheckScoped
- static Collection provideAvailableHealthCheckTasks(Collection availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, SecureRandom csprng, ResourceBundle resourceBundle) {
+ static List provideAvailableHealthCheckTasks(Collection availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, SecureRandom csprng, ResourceBundle resourceBundle) {
return availableHealthChecks.stream().map(check -> new HealthCheckTask(vault.getPath(), vaultConfigRef.get(), masterkeyRef.get(), csprng, check, resourceBundle)).toList();
}
@@ -86,10 +87,10 @@ abstract class HealthCheckModule {
@HealthCheckScoped
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle, ChangeListener showingListener) {
Stage stage = factory.create();
- stage.setTitle(resourceBundle.getString("health.title"));
- stage.setResizable(true);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(owner);
+ stage.setTitle(resourceBundle.getString("health.title"));
+ stage.setResizable(true);
stage.showingProperty().addListener(showingListener); // bind masterkey lifecycle to window
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java b/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java
index 7acbfc1c2..3efe333b3 100644
--- a/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java
+++ b/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java
@@ -3,12 +3,16 @@ package org.cryptomator.ui.health;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
import org.cryptomator.cryptofs.health.api.HealthCheck;
+import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
+import javafx.beans.Observable;
+import javafx.beans.property.BooleanProperty;
import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -31,8 +35,9 @@ class HealthCheckTask extends Task {
private final Masterkey masterkey;
private final SecureRandom csprng;
private final HealthCheck check;
- private final ObservableList results;
+ private final ObservableList results;
private final LongProperty durationInMillis;
+ private final BooleanProperty chosenForExecution;
public HealthCheckTask(Path vaultPath, VaultConfig vaultConfig, Masterkey masterkey, SecureRandom csprng, HealthCheck check, ResourceBundle resourceBundle) {
this.vaultPath = Objects.requireNonNull(vaultPath);
@@ -40,7 +45,7 @@ class HealthCheckTask extends Task {
this.masterkey = Objects.requireNonNull(masterkey);
this.csprng = Objects.requireNonNull(csprng);
this.check = Objects.requireNonNull(check);
- this.results = FXCollections.observableArrayList();
+ this.results = FXCollections.observableArrayList(Result::observables);
try {
updateTitle(resourceBundle.getString("health." + check.identifier()));
} catch (MissingResourceException e) {
@@ -48,32 +53,22 @@ class HealthCheckTask extends Task {
updateTitle(check.identifier());
}
this.durationInMillis = new SimpleLongProperty(-1);
+ this.chosenForExecution = new SimpleBooleanProperty();
}
@Override
protected Void call() {
Instant start = Instant.now();
try (var masterkeyClone = masterkey.clone(); //
- var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) {
- check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, result -> {
+ var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) {
+ check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, diagnosis -> {
if (isCancelled()) {
throw new CancellationException();
}
- // FIXME: slowdown for demonstration purposes only:
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- if (isCancelled()) {
- return;
- } else {
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
- }
- Platform.runLater(() -> results.add(result));
+ Platform.runLater(() -> results.add(Result.create(diagnosis)));
});
}
- Platform.runLater(() ->durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
+ Platform.runLater(() -> durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
return null;
}
@@ -89,7 +84,11 @@ class HealthCheckTask extends Task {
/* Getter */
- public ObservableList results() {
+ Observable[] observables() {
+ return new Observable[]{results, chosenForExecution};
+ }
+
+ public ObservableList results() {
return results;
}
@@ -105,4 +104,11 @@ class HealthCheckTask extends Task {
return durationInMillis.get();
}
+ public BooleanProperty chosenForExecutionProperty() {
+ return chosenForExecution;
+ }
+
+ public boolean isChosenForExecution() {
+ return chosenForExecution.get();
+ }
}
diff --git a/src/main/java/org/cryptomator/ui/health/ReportWriter.java b/src/main/java/org/cryptomator/ui/health/ReportWriter.java
index 2bc4c28d9..6039901ef 100644
--- a/src/main/java/org/cryptomator/ui/health/ReportWriter.java
+++ b/src/main/java/org/cryptomator/ui/health/ReportWriter.java
@@ -72,7 +72,7 @@ public class ReportWriter {
case SUCCEEDED -> {
writer.write("STATUS: SUCCESS\nRESULTS:\n");
for (var result : task.results()) {
- writer.write(REPORT_CHECK_RESULT.formatted(result.getSeverity(), result.toString()));
+ writer.write(REPORT_CHECK_RESULT.formatted(result.diagnosis().getSeverity(), result.getDescription()));
}
}
case CANCELLED -> writer.write("STATUS: CANCELED\n");
diff --git a/src/main/java/org/cryptomator/ui/health/Result.java b/src/main/java/org/cryptomator/ui/health/Result.java
new file mode 100644
index 000000000..23d812f50
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/health/Result.java
@@ -0,0 +1,43 @@
+package org.cryptomator.ui.health;
+
+import org.cryptomator.cryptofs.health.api.DiagnosticResult;
+
+import javafx.beans.Observable;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+record Result(DiagnosticResult diagnosis, ObjectProperty fixState) {
+
+ enum FixState {
+ NOT_FIXABLE,
+ FIXABLE,
+ FIXING,
+ FIXED,
+ FIX_FAILED
+ }
+
+ public static Result create(DiagnosticResult diagnosis) {
+ FixState initialState = switch (diagnosis.getSeverity()) {
+ case WARN -> FixState.FIXABLE;
+ default -> FixState.NOT_FIXABLE;
+ };
+ return new Result(diagnosis, new SimpleObjectProperty<>(initialState));
+ }
+
+ public Observable[] observables() {
+ return new Observable[]{fixState};
+ }
+
+ public String getDescription() {
+ return diagnosis.toString();
+ }
+
+ public FixState getState() {
+ return fixState.get();
+ }
+
+ public void setState(FixState state) {
+ this.fixState.set(state);
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java
index 3c5403ab7..841a8f5c4 100644
--- a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java
+++ b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java
@@ -4,25 +4,30 @@ import com.google.common.base.Preconditions;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
+import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
-import javafx.scene.control.Alert;
+import javafx.application.Platform;
import java.nio.file.Path;
import java.security.SecureRandom;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
@HealthCheckScoped
class ResultFixApplier {
- private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class);
-
private final Path vaultPath;
private final SecureRandom csprng;
private final Masterkey masterkey;
private final VaultConfig vaultConfig;
+ private final ExecutorService sequentialExecutor;
@Inject
public ResultFixApplier(@HealthCheckWindow Vault vault, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, SecureRandom csprng) {
@@ -30,18 +35,32 @@ class ResultFixApplier {
this.masterkey = masterkeyRef.get();
this.vaultConfig = vaultConfigRef.get();
this.csprng = csprng;
+ this.sequentialExecutor = Executors.newSingleThreadExecutor();
}
- public void fix(DiagnosticResult result) {
- Preconditions.checkArgument(result.getSeverity() == DiagnosticResult.Severity.WARN, "Unfixable result");
+ public CompletionStage fix(Result result) {
+ Preconditions.checkArgument(result.getState() == Result.FixState.FIXABLE);
+ result.setState(Result.FixState.FIXING);
+ return CompletableFuture.runAsync(() -> fix(result.diagnosis()), sequentialExecutor)
+ .whenCompleteAsync((unused, throwable) -> {
+ var fixed = throwable == null ? Result.FixState.FIXED : Result.FixState.FIX_FAILED;
+ result.setState(fixed);
+ }, Platform::runLater);
+ }
+
+ public void fix(DiagnosticResult diagnosis) {
+ Preconditions.checkArgument(diagnosis.getSeverity() == DiagnosticResult.Severity.WARN, "Unfixable result");
try (var masterkeyClone = masterkey.clone(); //
- var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) {
- result.fix(vaultPath, vaultConfig, masterkeyClone, cryptor);
+ var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) {
+ diagnosis.fix(vaultPath, vaultConfig, masterkeyClone, cryptor);
} catch (Exception e) {
- LOG.error("Failed to apply fix", e);
- Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
- alert.showAndWait();
- //TODO: real error/not supported handling
+ throw new FixFailedException(e);
+ }
+ }
+
+ public static class FixFailedException extends CompletionException {
+ private FixFailedException(Throwable cause) {
+ super(cause);
}
}
}
diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java
index 4506bc602..683d0fb93 100644
--- a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java
+++ b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java
@@ -1,84 +1,89 @@
package org.cryptomator.ui.health;
import com.tobiasdiez.easybind.EasyBind;
-import org.cryptomator.cryptofs.health.api.DiagnosticResult;
+import com.tobiasdiez.easybind.optional.OptionalBinding;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.inject.Inject;
+import javafx.application.Platform;
import javafx.beans.binding.Binding;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
// unscoped because each cell needs its own controller
public class ResultListCellController implements FxController {
- private final ResultFixApplier fixApplier;
- private final ObjectProperty result;
+ private final Logger LOG = LoggerFactory.getLogger(ResultListCellController.class);
+
+ private final ObjectProperty result;
private final Binding description;
+ private final ResultFixApplier fixApplier;
+ private final OptionalBinding fixState;
+ private final ObjectBinding glyph;
+ private final BooleanBinding fixable;
+ private final BooleanBinding fixing;
+ private final BooleanBinding fixed;
public FontAwesome5IconView iconView;
- public Button actionButton;
+ public Button fixButton;
@Inject
public ResultListCellController(ResultFixApplier fixApplier) {
this.result = new SimpleObjectProperty<>(null);
- this.description = EasyBind.wrapNullable(result).map(DiagnosticResult::toString).orElse("");
+ this.description = EasyBind.wrapNullable(result).map(Result::getDescription).orElse("");
this.fixApplier = fixApplier;
- result.addListener(this::updateCellContent);
- }
-
- private void updateCellContent(ObservableValue extends DiagnosticResult> observable, DiagnosticResult oldVal, DiagnosticResult newVal) {
- iconView.getStyleClass().clear();
- actionButton.setVisible(false);
- //TODO: see comment in case WARN
- actionButton.setManaged(false);
- switch (newVal.getSeverity()) {
- case INFO -> {
- iconView.setGlyph(FontAwesome5Icon.INFO_CIRCLE);
- iconView.getStyleClass().add("glyph-icon-muted");
- }
- case GOOD -> {
- iconView.setGlyph(FontAwesome5Icon.CHECK);
- iconView.getStyleClass().add("glyph-icon-primary");
- }
- case WARN -> {
- iconView.setGlyph(FontAwesome5Icon.EXCLAMATION_TRIANGLE);
- iconView.getStyleClass().add("glyph-icon-orange");
- //TODO: Neither is any fix implemented, nor it is ensured, that only fix is executed at a time with good ui indication
- // before both are not fix, do not show the button
- //actionButton.setVisible(true);
- }
- case CRITICAL -> {
- iconView.setGlyph(FontAwesome5Icon.TIMES);
- iconView.getStyleClass().add("glyph-icon-red");
- }
- }
+ this.fixState = EasyBind.wrapNullable(result).mapObservable(Result::fixState);
+ this.glyph = Bindings.createObjectBinding(this::getGlyph, result);
+ this.fixable = Bindings.createBooleanBinding(this::isFixable, fixState);
+ this.fixing = Bindings.createBooleanBinding(this::isFixing, fixState);
+ this.fixed = Bindings.createBooleanBinding(this::isFixed, fixState);
}
@FXML
- public void runResultAction() {
- final var realResult = result.get();
- if (realResult != null) {
- fixApplier.fix(realResult);
+ public void initialize() {
+ // see getGlyph() for relevant glyphs:
+ EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-muted", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.INFO_CIRCLE));
+ EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-primary", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.CHECK));
+ EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-orange", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.EXCLAMATION_TRIANGLE));
+ EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-red", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.TIMES));
+ }
+
+ @FXML
+ public void fix() {
+ Result r = result.get();
+ if (r != null) {
+ fixApplier.fix(r).whenCompleteAsync(this::fixFinished, Platform::runLater);
}
}
+
+ private void fixFinished(Void unused, Throwable exception) {
+ if (exception != null) {
+ LOG.error("Failed to apply fix", exception);
+ // TODO ...
+ }
+ }
+
+
/* Getter & Setter */
-
- public DiagnosticResult getResult() {
+ public Result getResult() {
return result.get();
}
- public void setResult(DiagnosticResult result) {
+ public void setResult(Result result) {
this.result.set(result);
}
- public ObjectProperty resultProperty() {
+ public ObjectProperty resultProperty() {
return result;
}
@@ -86,7 +91,49 @@ public class ResultListCellController implements FxController {
return description.getValue();
}
+ public ObjectBinding glyphProperty() {
+ return glyph;
+ }
+
+ public FontAwesome5Icon getGlyph() {
+ var r = result.get();
+ if (r == null) {
+ return null;
+ }
+ return switch (r.diagnosis().getSeverity()) {
+ case INFO -> FontAwesome5Icon.INFO_CIRCLE;
+ case GOOD -> FontAwesome5Icon.CHECK;
+ case WARN -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
+ case CRITICAL -> FontAwesome5Icon.TIMES;
+ };
+ }
+
public Binding descriptionProperty() {
return description;
}
+
+ public BooleanBinding fixableProperty() {
+ return fixable;
+ }
+
+ public boolean isFixable() {
+ return fixState.get().map(Result.FixState.FIXABLE::equals).orElse(false);
+ }
+
+ public BooleanBinding fixingProperty() {
+ return fixing;
+ }
+
+ public boolean isFixing() {
+ return fixState.get().map(Result.FixState.FIXING::equals).orElse(false);
+ }
+
+ public BooleanBinding fixedProperty() {
+ return fixed;
+ }
+
+ public boolean isFixed() {
+ return fixState.get().map(Result.FixState.FIXED::equals).orElse(false);
+ }
+
}
diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java b/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java
index 7acada487..86c793bf7 100644
--- a/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java
+++ b/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java
@@ -1,7 +1,6 @@
package org.cryptomator.ui.health;
-import org.cryptomator.cryptofs.health.api.DiagnosticResult;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import javax.inject.Inject;
@@ -15,7 +14,7 @@ import java.io.IOException;
import java.io.UncheckedIOException;
@HealthCheckScoped
-public class ResultListCellFactory implements Callback, ListCell> {
+public class ResultListCellFactory implements Callback, ListCell> {
private final FxmlLoaderFactory fxmlLoaders;
@@ -25,7 +24,7 @@ public class ResultListCellFactory implements Callback call(ListView param) {
+ public ListCell call(ListView param) {
try {
FXMLLoader fxmlLoader = fxmlLoaders.load("/fxml/health_result_listcell.fxml");
return new ResultListCellFactory.Cell(fxmlLoader.getRoot(), fxmlLoader.getController());
@@ -34,7 +33,7 @@ public class ResultListCellFactory implements Callback {
+ private static class Cell extends ListCell {
private final Parent node;
private final ResultListCellController controller;
@@ -45,7 +44,7 @@ public class ResultListCellFactory implements Callback unverifiedVaultConfig;
+ private final CompletableFuture unverifiedVaultConfig;
private final KeyLoadingStrategy keyLoadingStrategy;
private final ExecutorService executor;
private final AtomicReference masterkeyRef;
private final AtomicReference vaultConfigRef;
private final Lazy checkScene;
private final Lazy errorComponent;
+ private final ObjectProperty state = new SimpleObjectProperty<>(State.LOADING);
+ private final BooleanBinding loading = state.isEqualTo(State.LOADING);
+ private final BooleanBinding failed = state.isEqualTo(State.FAILED);
+ private final BooleanBinding loaded = state.isEqualTo(State.LOADED);
+
+ public enum State {
+ LOADING,
+ FAILED,
+ LOADED
+ }
/* FXML */
@Inject
public StartController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent) {
+ this.vault = vault;
this.window = window;
+ this.unverifiedVaultConfig = CompletableFuture.supplyAsync(this::loadConfig, executor);
this.keyLoadingStrategy = keyLoadingStrategy;
this.executor = executor;
this.masterkeyRef = masterkeyRef;
this.vaultConfigRef = vaultConfigRef;
this.checkScene = checkScene;
this.errorComponent = errorComponent;
-
- //TODO: this is ugly
- //idea: delay the loading of the vault config and show a spinner (something like "check/load config") and react to the result of the loading
- //or: load vault config in a previous step to see if it is loadable.
- VaultConfig.UnverifiedVaultConfig tmp;
- try {
- tmp = vault.getUnverifiedVaultConfig();
- } catch (IOException e) {
- e.printStackTrace();
- tmp = null;
- }
- this.unverifiedVaultConfig = Optional.ofNullable(tmp);
+ this.unverifiedVaultConfig.whenCompleteAsync(this::loadedConfig, Platform::runLater);
}
@FXML
@@ -74,41 +81,64 @@ public class StartController implements FxController {
@FXML
public void next() {
LOG.trace("StartController.next()");
- executor.submit(this::loadKey);
+ CompletableFuture.runAsync(this::loadKey, executor).whenCompleteAsync(this::loadedKey, Platform::runLater);
+ }
+
+ private VaultConfig.UnverifiedVaultConfig loadConfig() {
+ assert !Platform.isFxApplicationThread();
+ try {
+ return this.vault.getUnverifiedVaultConfig();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void loadedConfig(VaultConfig.UnverifiedVaultConfig cfg, Throwable exception) {
+ assert Platform.isFxApplicationThread();
+ if (exception != null) {
+ state.set(State.FAILED);
+ } else {
+ assert cfg != null;
+ state.set(State.LOADED);
+ }
}
private void loadKey() {
assert !Platform.isFxApplicationThread();
- assert unverifiedVaultConfig.isPresent();
- try (var masterkey = keyLoadingStrategy.loadKey(unverifiedVaultConfig.orElseThrow().getKeyId())) {
- var unverifiedCfg = unverifiedVaultConfig.get();
+ assert unverifiedVaultConfig.isDone();
+ var unverifiedCfg = unverifiedVaultConfig.join();
+ try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) {
var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion());
vaultConfigRef.set(verifiedCfg);
var old = masterkeyRef.getAndSet(masterkey.clone());
if (old != null) {
old.destroy();
}
- Platform.runLater(this::loadedKey);
} catch (MasterkeyLoadingFailedException e) {
if (keyLoadingStrategy.recoverFromException(e)) {
// retry
loadKey();
} else {
- Platform.runLater(() -> loadingKeyFailed(e));
+ throw new LoadingFailedException(e);
}
- } catch (VaultKeyInvalidException e) {
- Platform.runLater(() -> loadingKeyFailed(e));
} catch (VaultConfigLoadException e) {
- Platform.runLater(() -> loadingKeyFailed(e));
+ throw new LoadingFailedException(e);
}
}
- private void loadedKey() {
- LOG.debug("Loaded valid key");
- window.setScene(checkScene.get());
+ private void loadedKey(Void unused, Throwable exception) {
+ assert Platform.isFxApplicationThread();
+ if (exception instanceof LoadingFailedException) {
+ loadingKeyFailed(exception.getCause());
+ } else if (exception != null) {
+ loadingKeyFailed(exception);
+ } else {
+ LOG.debug("Loaded valid key");
+ window.setScene(checkScene.get());
+ }
}
- private void loadingKeyFailed(Exception e) {
+ private void loadingKeyFailed(Throwable e) {
if (e instanceof UnlockCancelledException) {
// ok
} else if (e instanceof VaultKeyInvalidException) {
@@ -120,8 +150,37 @@ public class StartController implements FxController {
}
}
- public boolean isInvalidConfig() {
- return unverifiedVaultConfig.isEmpty();
+ /* Getter */
+
+ public BooleanBinding loadingProperty() {
+ return loading;
}
+ public boolean isLoading() {
+ return loading.get();
+ }
+
+ public BooleanBinding failedProperty() {
+ return failed;
+ }
+
+ public boolean isFailed() {
+ return failed.get();
+ }
+
+ public BooleanBinding loadedProperty() {
+ return loaded;
+ }
+
+ public boolean isLoaded() {
+ return loaded.get();
+ }
+
+ /* internal types */
+
+ private static class LoadingFailedException extends CompletionException {
+ LoadingFailedException(Throwable cause) {
+ super(cause);
+ }
+ }
}
diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java
index ed8ca0540..eaef7480b 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java
@@ -35,7 +35,7 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
}
/**
- * Release any ressources or do follow-up tasks after loading a key.
+ * Release any resources or do follow-up tasks after loading a key.
*
* @param unlockedSuccessfully true if successfully unlocked a vault with the loaded key
* @implNote This method might be invoked multiple times, depending on whether multiple attempts to load a key are started.
@@ -47,7 +47,7 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
/**
* A key loading strategy that will always fail by throwing a {@link MasterkeyLoadingFailedException}.
*
- * @param exception The cause of the failure. If not alreay an {@link MasterkeyLoadingFailedException}, it will get wrapped.
+ * @param exception The cause of the failure. If not already an {@link MasterkeyLoadingFailedException}, it will get wrapped.
* @return A new KeyLoadingStrategy that will always fail with an {@link MasterkeyLoadingFailedException}.
*/
static KeyLoadingStrategy failed(Exception exception) {
diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java
index f8fbdd720..39db1cc04 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java
@@ -32,7 +32,7 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
public static final String SCHEME = "masterkeyfile";
private final Vault vault;
- private final MasterkeyFileAccess masterkeyFileAcccess;
+ private final MasterkeyFileAccess masterkeyFileAccess;
private final Stage window;
private final Lazy passphraseEntryScene;
private final Lazy selectMasterkeyFileScene;
@@ -45,9 +45,9 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
private boolean wrongPassword;
@Inject
- public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAcccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy selectMasterkeyFileScene, UserInteractionLock passwordEntryLock, UserInteractionLock masterkeyFileProvisionLock, AtomicReference password, AtomicReference filePath, MasterkeyFileLoadingFinisher finisher) {
+ public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy selectMasterkeyFileScene, UserInteractionLock passwordEntryLock, UserInteractionLock masterkeyFileProvisionLock, AtomicReference password, AtomicReference filePath, MasterkeyFileLoadingFinisher finisher) {
this.vault = vault;
- this.masterkeyFileAcccess = masterkeyFileAcccess;
+ this.masterkeyFileAccess = masterkeyFileAccess;
this.window = window;
this.passphraseEntryScene = passphraseEntryScene;
this.selectMasterkeyFileScene = selectMasterkeyFileScene;
@@ -68,7 +68,7 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
filePath = getAlternateMasterkeyFilePath();
}
CharSequence passphrase = getPassphrase();
- return masterkeyFileAcccess.load(filePath, passphrase);
+ return masterkeyFileAccess.load(filePath, passphrase);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new UnlockCancelledException("Unlock interrupted", e);
diff --git a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java b/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java
index 49ef79e83..f4e5bb790 100644
--- a/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java
+++ b/src/main/java/org/cryptomator/ui/launcher/AppLaunchEventHandler.java
@@ -64,7 +64,7 @@ class AppLaunchEventHandler {
}
}
- // TODO dedup MainWindowController...
+ // TODO deduplicate MainWindowController...
private void addOrRevealVault(Path potentialVaultPath) {
assert Platform.isFxApplicationThread();
try {
diff --git a/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java b/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
index 87fd486f2..73b4844b9 100644
--- a/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
+++ b/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
@@ -22,7 +22,7 @@ import javafx.stage.Window;
/**
* The sequence of actions performed and checked during lock of a vault.
*
- * This class implements the Task interface, sucht that it can run in the background with some possible forground operations/requests to the ui, without blocking the main app.
+ * This class implements the Task interface, sucht that it can run in the background with some possible foreground operations/requests to the ui, without blocking the main app.
* If the task state is
*
succeeded, the vault was successfully locked;
*
canceled, the lock was canceled;
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
index ea9311c88..ef050e799 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
@@ -129,7 +129,7 @@ public class MainWindowTitleController implements FxController {
}
public boolean isShowMinimizeButton() {
- // always show the minimize button if no tray icon is present OR it is explicitily enabled
+ // always show the minimize button if no tray icon is present OR it is explicitly enabled
return !trayMenuInitialized || settings.showMinimizeButton().get();
}
}
diff --git a/src/main/java/org/cryptomator/ui/recoverykey/AutoCompleter.java b/src/main/java/org/cryptomator/ui/recoverykey/AutoCompleter.java
index 5c747261b..daac7f459 100644
--- a/src/main/java/org/cryptomator/ui/recoverykey/AutoCompleter.java
+++ b/src/main/java/org/cryptomator/ui/recoverykey/AutoCompleter.java
@@ -26,7 +26,7 @@ public class AutoCompleter {
if (Strings.isNullOrEmpty(prefix)) {
return Optional.empty();
}
- int potentialMatchIdx = findIndexOfLexicographicallyPreceeding(0, dictionary.size(), prefix);
+ int potentialMatchIdx = findIndexOfLexicographicallyPreceding(0, dictionary.size(), prefix);
if (potentialMatchIdx < dictionary.size()) {
String potentialMatch = dictionary.get(potentialMatchIdx);
return potentialMatch.startsWith(prefix) ? Optional.of(potentialMatch) : Optional.empty();
@@ -48,21 +48,21 @@ public class AutoCompleter {
* @param prefix
* @return index between [0, dictLen], i.e. index can exceed the upper bounds of {@link #dictionary}.
*/
- private int findIndexOfLexicographicallyPreceeding(int begin, int end, String prefix) {
+ private int findIndexOfLexicographicallyPreceding(int begin, int end, String prefix) {
if (begin >= end) {
return begin; // this is usually where a binary search ends "unsuccessful"
}
int mid = (begin + end) / 2;
String word = dictionary.get(mid);
- if (prefix.compareTo(word) <= 0) { // prefix preceeds or matches word
+ if (prefix.compareTo(word) <= 0) { // prefix precedes or matches word
// proceed in left half
assert mid < end;
- return findIndexOfLexicographicallyPreceeding(0, mid, prefix);
+ return findIndexOfLexicographicallyPreceding(0, mid, prefix);
} else {
// proceed in right half
assert mid >= begin;
- return findIndexOfLexicographicallyPreceeding(mid + 1, end, prefix);
+ return findIndexOfLexicographicallyPreceding(mid + 1, end, prefix);
}
}
diff --git a/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java b/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
index ba4753879..7d31d660c 100644
--- a/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
+++ b/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
@@ -58,7 +58,7 @@ class WordEncoder {
* @throws IllegalArgumentException If input is not a multiple of three bytes
*/
public String encodePadded(byte[] input) {
- Preconditions.checkArgument(input.length % 3 == 0, "input needs to be padded to a multipe of three");
+ Preconditions.checkArgument(input.length % 3 == 0, "input needs to be padded to a multiple of three");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length; i += 3) {
byte b1 = input[i];
@@ -85,12 +85,12 @@ class WordEncoder {
* @throws IllegalArgumentException If the encoded string doesn't consist of a multiple of two words or one of the words is unknown to this encoder.
*/
public byte[] decode(String encoded) {
- List splitted = Splitter.on(DELIMITER).omitEmptyStrings().splitToList(Strings.nullToEmpty(encoded));
- Preconditions.checkArgument(splitted.size() % 2 == 0, "%s needs to be a multiple of two words", encoded);
- byte[] result = new byte[splitted.size() / 2 * 3];
- for (int i = 0; i < splitted.size(); i += 2) {
- String w1 = splitted.get(i);
- String w2 = splitted.get(i + 1);
+ List split = Splitter.on(DELIMITER).omitEmptyStrings().splitToList(Strings.nullToEmpty(encoded));
+ Preconditions.checkArgument(split.size() % 2 == 0, "%s needs to be a multiple of two words", encoded);
+ byte[] result = new byte[split.size() / 2 * 3];
+ for (int i = 0; i < split.size(); i += 2) {
+ String w1 = split.get(i);
+ String w2 = split.get(i + 1);
int firstWordIndex = indices.getOrDefault(w1, -1);
int secondWordIndex = indices.getOrDefault(w2, -1);
Preconditions.checkArgument(firstWordIndex != -1, "%s not in dictionary", w1);
diff --git a/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java b/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java
index f8937e90f..a3c430946 100644
--- a/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java
+++ b/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java
@@ -65,8 +65,8 @@ public class VaultStatisticsController implements FxController {
this.cacheHitRate = WeakBindings.bindDouble(stats.cacheHitRateProperty());
this.cacheHitDegrees = cacheHitRate.multiply(-270);
this.cacheHitPercentage = cacheHitRate.multiply(100);
- this.totalBytesRead = WeakBindings.bindLong(stats.toalBytesReadProperty());
- this.totalBytesWritten = WeakBindings.bindLong(stats.toalBytesWrittenProperty());
+ this.totalBytesRead = WeakBindings.bindLong(stats.totalBytesReadProperty());
+ this.totalBytesWritten = WeakBindings.bindLong(stats.totalBytesWrittenProperty());
this.totalBytesDecrypted = WeakBindings.bindLong(stats.totalBytesDecryptedProperty());
this.totalBytesEncrypted = WeakBindings.bindLong(stats.totalBytesEncryptedProperty());
this.filesRead = WeakBindings.bindLong(stats.filesRead());
@@ -102,7 +102,7 @@ public class VaultStatisticsController implements FxController {
this.decryptedBytesRead = readData;
this.encryptedBytesWrite = writeData;
- // initialize data once and change value of datapoints later:
+ // initialize data once and change value of data points later:
for (int i = 0; i < IO_SAMPLING_STEPS; i++) {
decryptedBytesRead.getData().add(new Data<>(i, 0));
encryptedBytesWrite.getData().add(new Data<>(i, 0));
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/AutoLockVaultOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/AutoLockVaultOptionsController.java
deleted file mode 100644
index 1bb74690f..000000000
--- a/src/main/java/org/cryptomator/ui/vaultoptions/AutoLockVaultOptionsController.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.cryptomator.ui.vaultoptions;
-
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.controls.NumericTextField;
-
-import javax.inject.Inject;
-import javafx.beans.binding.Bindings;
-import javafx.fxml.FXML;
-import javafx.scene.control.CheckBox;
-import javafx.util.StringConverter;
-
-@VaultOptionsScoped
-public class AutoLockVaultOptionsController implements FxController {
-
- private final Vault vault;
-
- public CheckBox lockAfterTimeCheckbox;
- public NumericTextField lockTimeInMinutesTextField;
-
- @Inject
- AutoLockVaultOptionsController(@VaultOptionsWindow Vault vault) {
- this.vault = vault;
- }
-
- @FXML
- public void initialize() {
- lockAfterTimeCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().autoLockWhenIdle());
- Bindings.bindBidirectional(lockTimeInMinutesTextField.textProperty(), vault.getVaultSettings().autoLockIdleSeconds(), new IdleTimeSecondsConverter());
- }
-
- private static class IdleTimeSecondsConverter extends StringConverter {
-
- @Override
- public String toString(Number seconds) {
- int minutes = seconds.intValue() / 60; // int-truncate
- return Integer.toString(minutes);
- }
-
- @Override
- public Number fromString(String string) {
- try {
- int minutes = Integer.valueOf(string);
- return minutes * 60;
- } catch (NumberFormatException e) {
- return 0;
- }
- }
- }
-
-}
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
index 79d3b53ad..0ccea096e 100644
--- a/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
@@ -3,10 +3,11 @@ package org.cryptomator.ui.vaultoptions;
import org.cryptomator.common.settings.WhenUnlocked;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.health.HealthCheckComponent;
+import org.cryptomator.ui.controls.NumericTextField;
import javax.inject.Inject;
import javafx.beans.Observable;
+import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
@@ -23,18 +24,18 @@ public class GeneralVaultOptionsController implements FxController {
private final Stage window;
private final Vault vault;
- private final HealthCheckComponent.Builder healthCheckWindow;
private final ResourceBundle resourceBundle;
public TextField vaultName;
public CheckBox unlockOnStartupCheckbox;
public ChoiceBox actionAfterUnlockChoiceBox;
+ public CheckBox lockAfterTimeCheckbox;
+ public NumericTextField lockTimeInMinutesTextField;
@Inject
- GeneralVaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, HealthCheckComponent.Builder healthCheckWindow, ResourceBundle resourceBundle) {
+ GeneralVaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
- this.healthCheckWindow = healthCheckWindow;
this.resourceBundle = resourceBundle;
}
@@ -47,6 +48,8 @@ public class GeneralVaultOptionsController implements FxController {
actionAfterUnlockChoiceBox.getItems().addAll(WhenUnlocked.values());
actionAfterUnlockChoiceBox.valueProperty().bindBidirectional(vault.getVaultSettings().actionAfterUnlock());
actionAfterUnlockChoiceBox.setConverter(new WhenUnlockedConverter(resourceBundle));
+ lockAfterTimeCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().autoLockWhenIdle());
+ Bindings.bindBidirectional(lockTimeInMinutesTextField.textProperty(), vault.getVaultSettings().autoLockIdleSeconds(), new IdleTimeSecondsConverter());
}
private void trimVaultNameOnFocusLoss(Observable observable, Boolean wasFocussed, Boolean isFocussed) {
@@ -64,12 +67,6 @@ public class GeneralVaultOptionsController implements FxController {
}
}
- @FXML
- public void showHealthCheck() {
- healthCheckWindow.vault(vault).build().showHealthCheckWindow();
- }
-
-
private static class WhenUnlockedConverter extends StringConverter {
private final ResourceBundle resourceBundle;
@@ -89,4 +86,22 @@ public class GeneralVaultOptionsController implements FxController {
}
}
+ private static class IdleTimeSecondsConverter extends StringConverter {
+
+ @Override
+ public String toString(Number seconds) {
+ int minutes = seconds.intValue() / 60; // int-truncate
+ return Integer.toString(minutes);
+ }
+
+ @Override
+ public Number fromString(String string) {
+ try {
+ int minutes = Integer.valueOf(string);
+ return minutes * 60;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+ }
}
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/HealthVaultOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/HealthVaultOptionsController.java
new file mode 100644
index 000000000..7b0842e97
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/HealthVaultOptionsController.java
@@ -0,0 +1,30 @@
+package org.cryptomator.ui.vaultoptions;
+
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.health.HealthCheckComponent;
+
+import javax.inject.Inject;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.stage.Stage;
+
+@VaultOptionsScoped
+public class HealthVaultOptionsController implements FxController {
+
+ private final Stage window;
+ private final Vault vault;
+ private final HealthCheckComponent.Builder healthCheckWindow;
+
+ @Inject
+ public HealthVaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, HealthCheckComponent.Builder healthCheckWindow) {
+ this.window = window;
+ this.vault = vault;
+ this.healthCheckWindow = healthCheckWindow;
+ }
+
+ @FXML
+ public void startHealthCheck(ActionEvent event) {
+ healthCheckWindow.vault(vault).windowToClose(window).build().showHealthCheckWindow();
+ }
+}
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/SelectedVaultOptionsTab.java b/src/main/java/org/cryptomator/ui/vaultoptions/SelectedVaultOptionsTab.java
index 03a4922d4..bfaff147a 100644
--- a/src/main/java/org/cryptomator/ui/vaultoptions/SelectedVaultOptionsTab.java
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/SelectedVaultOptionsTab.java
@@ -23,7 +23,11 @@ public enum SelectedVaultOptionsTab {
/**
* Show Auto-Lock tab
- *
*/
AUTOLOCK,
+
+ /**
+ * Show health tab
+ */
+ HEALTH;
}
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java
index 20dac7594..662232a49 100644
--- a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java
@@ -24,6 +24,7 @@ public class VaultOptionsController implements FxController {
public Tab mountTab;
public Tab keyTab;
public Tab autoLockTab;
+ public Tab healthTab;
@Inject
VaultOptionsController(@VaultOptionsWindow Stage window, ObjectProperty selectedTabProperty) {
@@ -49,6 +50,7 @@ public class VaultOptionsController implements FxController {
case MOUNT -> mountTab;
case KEY -> keyTab;
case AUTOLOCK -> autoLockTab;
+ case HEALTH -> healthTab;
};
}
diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
index 89cef234b..2de9421a0 100644
--- a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
+++ b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
@@ -86,7 +86,6 @@ abstract class VaultOptionsModule {
@Binds
@IntoMap
- @FxControllerKey(AutoLockVaultOptionsController.class)
- abstract FxController bindAutoLockVaultOptionsController(AutoLockVaultOptionsController controller);
-
+ @FxControllerKey(HealthVaultOptionsController.class)
+ abstract FxController bindHealthOptionsController(HealthVaultOptionsController controller);
}
diff --git a/src/main/resources/META-INF/services/org.cryptomator.cryptofs.health.api.HealthCheck b/src/main/resources/META-INF/services/org.cryptomator.cryptofs.health.api.HealthCheck
new file mode 100644
index 000000000..e78e709b4
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.cryptomator.cryptofs.health.api.HealthCheck
@@ -0,0 +1,3 @@
+org.cryptomator.ui.health.DummyHealthChecks$DummyCheck1
+org.cryptomator.ui.health.DummyHealthChecks$DummyCheck2
+org.cryptomator.ui.health.DummyHealthChecks$DummyCheck3
\ No newline at end of file
diff --git a/src/main/resources/fxml/health_check_list.fxml b/src/main/resources/fxml/health_check_list.fxml
index a3a9c2f93..30bf818d5 100644
--- a/src/main/resources/fxml/health_check_list.fxml
+++ b/src/main/resources/fxml/health_check_list.fxml
@@ -3,17 +3,18 @@
+
+
-
-
@@ -28,7 +29,7 @@
-
+
@@ -39,7 +40,7 @@
-
+
diff --git a/src/main/resources/fxml/health_result_listcell.fxml b/src/main/resources/fxml/health_result_listcell.fxml
index 6e30dd0ec..687842917 100644
--- a/src/main/resources/fxml/health_result_listcell.fxml
+++ b/src/main/resources/fxml/health_result_listcell.fxml
@@ -6,6 +6,10 @@
+
+
+
+
-
+
-
+
+
+
+
+
+
+
diff --git a/src/main/resources/fxml/health_start.fxml b/src/main/resources/fxml/health_start.fxml
index 79d946884..24b256fa8 100644
--- a/src/main/resources/fxml/health_start.fxml
+++ b/src/main/resources/fxml/health_start.fxml
@@ -17,23 +17,25 @@
-
-
-
-
diff --git a/src/main/resources/fxml/stats.fxml b/src/main/resources/fxml/stats.fxml
index a2ef920b9..b17dc67f6 100644
--- a/src/main/resources/fxml/stats.fxml
+++ b/src/main/resources/fxml/stats.fxml
@@ -2,7 +2,7 @@
-
+
@@ -37,7 +37,7 @@
-
+
@@ -56,7 +56,7 @@
-
+
diff --git a/src/main/resources/fxml/vault_detail_unlocked.fxml b/src/main/resources/fxml/vault_detail_unlocked.fxml
index 7a4d62952..29b11ed17 100644
--- a/src/main/resources/fxml/vault_detail_unlocked.fxml
+++ b/src/main/resources/fxml/vault_detail_unlocked.fxml
@@ -1,5 +1,5 @@
-
+
@@ -41,11 +41,11 @@
-
+
-
+
diff --git a/src/main/resources/fxml/vault_options.fxml b/src/main/resources/fxml/vault_options.fxml
index 4808209b2..42a924740 100644
--- a/src/main/resources/fxml/vault_options.fxml
+++ b/src/main/resources/fxml/vault_options.fxml
@@ -36,12 +36,12 @@
-
+
-
+
-
+
diff --git a/src/main/resources/fxml/vault_options_autolock.fxml b/src/main/resources/fxml/vault_options_autolock.fxml
deleted file mode 100644
index 8d63b792d..000000000
--- a/src/main/resources/fxml/vault_options_autolock.fxml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/fxml/vault_options_general.fxml b/src/main/resources/fxml/vault_options_general.fxml
index dce834c95..a9e18e852 100644
--- a/src/main/resources/fxml/vault_options_general.fxml
+++ b/src/main/resources/fxml/vault_options_general.fxml
@@ -8,6 +8,10 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/main/resources/fxml/vault_options_health.fxml b/src/main/resources/fxml/vault_options_health.fxml
new file mode 100644
index 000000000..aadfa43ff
--- /dev/null
+++ b/src/main/resources/fxml/vault_options_health.fxml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/fxml/vault_options_masterkey.fxml b/src/main/resources/fxml/vault_options_masterkey.fxml
index a8ba03f8f..ad7097924 100644
--- a/src/main/resources/fxml/vault_options_masterkey.fxml
+++ b/src/main/resources/fxml/vault_options_masterkey.fxml
@@ -29,7 +29,7 @@
-
+