From ba627d0d6021217ece6eddb6ab889a5ee3c936d8 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Sun, 19 Feb 2023 16:29:06 +0100 Subject: [PATCH 01/39] Add a scheduled service to auto unlock vaults that were missing at startup --- .../cryptomator/ui/fxapp/AutoUnlocker.java | 67 +++++++++++++++++-- .../cryptomator/ui/fxapp/FxApplication.java | 4 +- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 973d919fc..410910e47 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -1,30 +1,83 @@ package org.cryptomator.ui.fxapp; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultListManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.collections.ObservableList; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; @FxApplicationScoped public class AutoUnlocker { + private static final Logger LOG = LoggerFactory.getLogger(AutoUnlocker.class); + private final ObservableList vaults; private final FxApplicationWindows appWindows; + private final ScheduledExecutorService scheduler; + private ScheduledFuture future; + private boolean isPeriodicCheckActive = false; @Inject - public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows) { + public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows, ScheduledExecutorService scheduler) { this.vaults = vaults; this.appWindows = appWindows; + this.scheduler = scheduler; } - public void unlock() { - vaults.stream().filter(Vault::isLocked) // - .filter(v -> v.getVaultSettings().unlockAfterStartup().get()) // - .>reduce(CompletableFuture.completedFuture(null), // - (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), //we don't care here about the exception, logged elsewhere - (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); + public void unlockAll() { + unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); } + public void unlock(Stream vaultStream) { + vaultStream.filter(Vault::isLocked) + .>reduce(CompletableFuture.completedFuture(null), + (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), // we don't care here about the exception, logged elsewhere + (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); + } + + public void startMissingVaultsChecker() { + if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { + LOG.info("Found MISSING vaults, starting periodic check"); + future = scheduler.scheduleWithFixedDelay(this::tick, 0, 1, TimeUnit.SECONDS); + isPeriodicCheckActive = true; + } + } + + private void tick() { + // Find the vaults that are missing but have an existing directory + Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); + if (vaultArray.length > 0) { + + // Redetermine vault states + for (Vault v : vaultArray) { + LOG.info("Found vault directory for '{}'", v.getDisplayName()); + VaultListManager.redetermineVaultState(v); + } + + // Unlock the vaults that were previously missing + unlock(Arrays.stream(vaultArray)); + } + + // Stop checking if there are no more missing vaults + if (getMissingAutoUnlockVaults().count() == 0) { + LOG.info("No more MISSING vaults, stopping periodic check"); + isPeriodicCheckActive = false; + future.cancel(false); + } + } + + private Stream getMissingAutoUnlockVaults() { + return vaults.stream() + .filter(Vault::isMissing) + .filter(v -> v.getVaultSettings().unlockAfterStartup().get()); + } } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 3ddb7cba6..850d2a91e 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -68,7 +68,7 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.unlock(); + autoUnlocker.unlockAll(); + autoUnlocker.startMissingVaultsChecker(); } - } From 72fd38baf15808537e3ef2931d626fef9950019c Mon Sep 17 00:00:00 2001 From: Rexbas Date: Sat, 6 May 2023 15:40:24 +0200 Subject: [PATCH 02/39] Add timeout to periodic missing vaults check --- .../org/cryptomator/ui/fxapp/AutoUnlocker.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 410910e47..79b440782 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -23,7 +23,8 @@ public class AutoUnlocker { private final ObservableList vaults; private final FxApplicationWindows appWindows; private final ScheduledExecutorService scheduler; - private ScheduledFuture future; + private ScheduledFuture checkFuture; + private ScheduledFuture timeoutFuture; private boolean isPeriodicCheckActive = false; @Inject @@ -47,12 +48,13 @@ public class AutoUnlocker { public void startMissingVaultsChecker() { if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { LOG.info("Found MISSING vaults, starting periodic check"); - future = scheduler.scheduleWithFixedDelay(this::tick, 0, 1, TimeUnit.SECONDS); + checkFuture = scheduler.scheduleWithFixedDelay(this::check, 0, 1, TimeUnit.SECONDS); + timeoutFuture = scheduler.schedule(this::timeout, 2, TimeUnit.MINUTES); isPeriodicCheckActive = true; } } - private void tick() { + private void check() { // Find the vaults that are missing but have an existing directory Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); if (vaultArray.length > 0) { @@ -71,10 +73,17 @@ public class AutoUnlocker { if (getMissingAutoUnlockVaults().count() == 0) { LOG.info("No more MISSING vaults, stopping periodic check"); isPeriodicCheckActive = false; - future.cancel(false); + checkFuture.cancel(false); + timeoutFuture.cancel(false); } } + private void timeout() { + LOG.info("MISSING vaults periodic check timed out"); + isPeriodicCheckActive = false; + checkFuture.cancel(false); + } + private Stream getMissingAutoUnlockVaults() { return vaults.stream() .filter(Vault::isMissing) From 151ef6c7b26dd21709df98f9b63bbae19291e49f Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 8 May 2023 19:12:35 +0200 Subject: [PATCH 03/39] upgrade to jdk20 * use pattern matching preview feature * bump fuse-nio-adapter --- .idea/misc.xml | 2 +- pom.xml | 9 ++++--- .../cryptomator/ipc/IpcMessageListener.java | 8 +++--- .../ui/health/StartController.java | 18 +++++++------ .../ui/traymenu/AwtTrayMenuController.java | 25 ++++++++++--------- .../UnlockInvalidMountPointController.java | 13 +++++----- 6 files changed, 40 insertions(+), 35 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index e5d629592..891096945 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 084ceeb7e..433a89f2b 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ UTF-8 - 19 + 20 @@ -38,7 +38,7 @@ 1.2.0 1.2.0 1.2.0 - 2.0.5 + 3.0.0 2.0.0 2.0.2 @@ -64,7 +64,7 @@ 23.0.0 8.1.0 - 0.8.8 + 0.8.9 @@ -332,6 +332,9 @@ org.apache.maven.plugins maven-surefire-plugin + + --enable-preview + org.apache.maven.plugins diff --git a/src/main/java/org/cryptomator/ipc/IpcMessageListener.java b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java index f49275824..f2e76dc2a 100644 --- a/src/main/java/org/cryptomator/ipc/IpcMessageListener.java +++ b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java @@ -5,10 +5,10 @@ import java.util.List; public interface IpcMessageListener { default void handleMessage(IpcMessage message) { - if (message instanceof RevealRunningAppMessage) { - revealRunningApp(); - } else if (message instanceof HandleLaunchArgsMessage m) { - handleLaunchArgs(m.args()); + switch (message) { + case RevealRunningAppMessage x -> revealRunningApp(); + case HandleLaunchArgsMessage hlam -> handleLaunchArgs(hlam.args()); + default -> {} } } diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java index fa41a7fdc..4e95b6b0f 100644 --- a/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/src/main/java/org/cryptomator/ui/health/StartController.java @@ -102,14 +102,16 @@ public class StartController implements FxController { } private void loadingKeyFailed(Throwable e) { - if (e instanceof UnlockCancelledException) { - // ok - } else if (e instanceof VaultKeyInvalidException) { - LOG.error("Invalid key"); //TODO: specific error screen - appWindows.showErrorWindow(e, window, null); - } else { - LOG.error("Failed to load key.", e); - appWindows.showErrorWindow(e, window, null); + switch (e) { + case UnlockCancelledException uce -> {} //ok + case VaultKeyInvalidException vkie -> { + LOG.error("Invalid key"); //TODO: specific error screen + appWindows.showErrorWindow(e, window, null); + } + default -> { + LOG.error("Failed to load key.", e); + appWindows.showErrorWindow(e, window, null); + } } } diff --git a/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java b/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java index 704ffa9ab..2f1d4e620 100644 --- a/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java +++ b/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java @@ -84,18 +84,19 @@ public class AwtTrayMenuController implements TrayMenuController { private void addChildren(Menu menu, List items) { for (var item : items) { - // TODO: use Pattern Matching for switch, once available - if (item instanceof ActionItem a) { - var menuItem = new MenuItem(a.title()); - menuItem.addActionListener(evt -> a.action().run()); - menuItem.setEnabled(a.enabled()); - menu.add(menuItem); - } else if (item instanceof SeparatorItem) { - menu.addSeparator(); - } else if (item instanceof SubMenuItem s) { - var submenu = new Menu(s.title()); - addChildren(submenu, s.items()); - menu.add(submenu); + switch (item) { + case ActionItem a -> { + var menuItem = new MenuItem(a.title()); + menuItem.addActionListener(evt -> a.action().run()); + menuItem.setEnabled(a.enabled()); + menu.add(menuItem); + } + case SeparatorItem si -> menu.addSeparator(); + case SubMenuItem smi -> { + var submenu = new Menu(smi.title()); + addChildren(submenu, smi.items()); + menu.add(submenu); + } } } } diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java index aeb4a9166..22a1b1eec 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java @@ -38,13 +38,12 @@ public class UnlockInvalidMountPointController implements FxController { @FXML public void initialize() { var e = unlockException.get(); - String translationKey = "unlock.error.customPath.description.generic"; - if (e instanceof MountPointNotSupportedException) { - translationKey = "unlock.error.customPath.description.notSupported"; - } else if (e instanceof MountPointNotExistsException) { - translationKey = "unlock.error.customPath.description.notExists"; - } - dialogDescription.setFormat(resourceBundle.getString(translationKey)); + var translationKeySuffix = switch (e) { + case MountPointNotSupportedException x -> "notSupported"; + case MountPointNotExistsException x -> "notExists"; + default -> "generic"; + }; + dialogDescription.setFormat(resourceBundle.getString("unlock.error.customPath.description." + translationKeySuffix)); dialogDescription.setArg1(e.getMessage()); } From a3f05db189a2b92d483f7b86b0fb8df4c856cbc6 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 8 May 2023 19:41:02 +0200 Subject: [PATCH 04/39] bump javafx --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 433a89f2b..37f45a940 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 2.2 31.1-jre 2.10.1 - 19.0.2.1 + 20.0.1 4.3.0 9.31 1.4.5 From 6386dd3d500f2b08f2784c004626d3fac649ce18 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 8 May 2023 19:41:58 +0200 Subject: [PATCH 05/39] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/debian.yml | 18 +++++++++++++++--- .github/workflows/get-version.yml | 2 +- .github/workflows/mac-dmg.yml | 2 +- .github/workflows/pullrequest.yml | 2 +- .github/workflows/release-check.yml | 2 +- .github/workflows/win-exe.yml | 8 ++++---- 8 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 4e81a4295..f966f6b22 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -10,7 +10,7 @@ on: required: false env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 jobs: get-version: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 681ba1f94..bf21af27c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: types: [labeled] env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 defaults: run: diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 700322c06..b2d2bf4c8 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -19,9 +19,11 @@ on: type: boolean env: - JAVA_VERSION: 19 - OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-x64_bin-jmods.zip' - OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-aarch64_bin-jmods.zip' + JAVA_VERSION: 20 + OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-x64_bin-jmods.zip' + JMODS_AMD64_HASH: '0b244b08354dc5e1b639765fd0858cbc154774e9cb05467c4111b7c63d3b3721' + OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-aarch64_bin-jmods.zip' + JMODS_AARCH64_HASH: '4b0dd30282c067aa07fe840374115b5ec5dcfd8dc479ebd2231dfdb6a518b3b3' jobs: build: @@ -58,9 +60,19 @@ jobs: id: download-jmods run: | curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip + echo "${{ env.JMODS_AMD64_HASH }} openjfx-amd64.zip" | sha256sum -c + if [ $? != 0 ]; then + echo "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}."; + exit 1 + fi mkdir -p jmods/amd64 unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64 curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip + echo "${{ env.JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | sha256sum -c + if [ $? != 0 ]; then + echo "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AARCH64 }}."; + exit 1 + fi mkdir -p jmods/aarch64 unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64 - name: Ensure major jfx version in pom and in jmods is the same diff --git a/.github/workflows/get-version.yml b/.github/workflows/get-version.yml index 5cffb56fb..d8684dc20 100644 --- a/.github/workflows/get-version.yml +++ b/.github/workflows/get-version.yml @@ -22,7 +22,7 @@ on: value: ${{ jobs.determine-version.outputs.type }} env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 JAVA_DIST: 'temurin' JAVA_CACHE: 'maven' diff --git a/.github/workflows/mac-dmg.yml b/.github/workflows/mac-dmg.yml index 27320dba8..bad80138b 100644 --- a/.github/workflows/mac-dmg.yml +++ b/.github/workflows/mac-dmg.yml @@ -10,7 +10,7 @@ on: required: false env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 jobs: get-version: diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 48f7bd185..73a21edde 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -4,7 +4,7 @@ on: pull_request: env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 defaults: run: diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml index 7309cb852..481861540 100644 --- a/.github/workflows/release-check.yml +++ b/.github/workflows/release-check.yml @@ -7,7 +7,7 @@ on: - 'hotfix/**' env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 defaults: run: diff --git a/.github/workflows/win-exe.yml b/.github/workflows/win-exe.yml index 45ce36c2e..df67c1dcb 100644 --- a/.github/workflows/win-exe.yml +++ b/.github/workflows/win-exe.yml @@ -14,11 +14,11 @@ on: env: - JAVA_VERSION: 19 + JAVA_VERSION: 20 JAVA_DIST: 'temurin' JAVA_CACHE: 'maven' - JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_windows-x64_bin-jmods.zip' - JFX_JMODS_HASH: 'B7CF2CAD2468842B3B78D99F6C0555771499A36FA1F1EE3DD1B9A4597F1FAB86' + JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip' + JFX_JMODS_HASH: 'D00767334C43B8832B5CF10267D34CA8F563D187C4655B73EB6020DD79C054B5' defaults: run: @@ -51,7 +51,7 @@ jobs: run: | curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}" if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) { - exit 1; + throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}."; } Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent} From ff80f634d201b8c5025c03939cc34d9019bc226d Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 09:42:01 +0200 Subject: [PATCH 06/39] Apply suggestions from code review Co-authored-by: Sebastian Stenzel --- .../java/org/cryptomator/ipc/IpcMessageListener.java | 5 ++--- .../cryptomator/ui/traymenu/AwtTrayMenuController.java | 8 ++++---- .../ui/unlock/UnlockInvalidMountPointController.java | 10 +++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/cryptomator/ipc/IpcMessageListener.java b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java index f2e76dc2a..756305cbe 100644 --- a/src/main/java/org/cryptomator/ipc/IpcMessageListener.java +++ b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java @@ -6,9 +6,8 @@ public interface IpcMessageListener { default void handleMessage(IpcMessage message) { switch (message) { - case RevealRunningAppMessage x -> revealRunningApp(); - case HandleLaunchArgsMessage hlam -> handleLaunchArgs(hlam.args()); - default -> {} + case RevealRunningAppMessage m -> revealRunningApp(); // TODO: rename to _ with JEP 443 + case HandleLaunchArgsMessage m -> handleLaunchArgs(m.args()); } } diff --git a/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java b/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java index 2f1d4e620..5cb1af7a3 100644 --- a/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java +++ b/src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java @@ -91,10 +91,10 @@ public class AwtTrayMenuController implements TrayMenuController { menuItem.setEnabled(a.enabled()); menu.add(menuItem); } - case SeparatorItem si -> menu.addSeparator(); - case SubMenuItem smi -> { - var submenu = new Menu(smi.title()); - addChildren(submenu, smi.items()); + case SeparatorItem s -> menu.addSeparator(); // TODO: rename to _ with JEP 443 + case SubMenuItem s -> { + var submenu = new Menu(s.title()); + addChildren(submenu, s.items()); menu.add(submenu); } } diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java index 22a1b1eec..7bc0cbccd 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java @@ -38,12 +38,12 @@ public class UnlockInvalidMountPointController implements FxController { @FXML public void initialize() { var e = unlockException.get(); - var translationKeySuffix = switch (e) { - case MountPointNotSupportedException x -> "notSupported"; - case MountPointNotExistsException x -> "notExists"; - default -> "generic"; + var translationKey = switch (e) { + case MountPointNotSupportedException x -> "unlock.error.customPath.description.notSupported"; + case MountPointNotExistsException x -> "unlock.error.customPath.description.notExists"; + default -> "unlock.error.customPath.description.generic"; }; - dialogDescription.setFormat(resourceBundle.getString("unlock.error.customPath.description." + translationKeySuffix)); + dialogDescription.setFormat(resourceBundle.getString(translationKey)); dialogDescription.setArg1(e.getMessage()); } From d4cba2fd6ed56900c4b5245c0a6bf5d9355f4fd6 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 09:55:56 +0200 Subject: [PATCH 07/39] fix errors --- dist/linux/debian/control | 2 +- src/main/java/org/cryptomator/ipc/IpcMessage.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/linux/debian/control b/dist/linux/debian/control index edf9dd3ce..b04812fbb 100644 --- a/dist/linux/debian/control +++ b/dist/linux/debian/control @@ -2,7 +2,7 @@ Source: cryptomator Maintainer: Cryptobot Section: utils Priority: optional -Build-Depends: debhelper (>=10), coffeelibs-jdk-19, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1 +Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1 Standards-Version: 4.5.0 Homepage: https://cryptomator.org Vcs-Git: https://github.com/cryptomator/cryptomator.git diff --git a/src/main/java/org/cryptomator/ipc/IpcMessage.java b/src/main/java/org/cryptomator/ipc/IpcMessage.java index 9d1c8d3de..7f76da295 100644 --- a/src/main/java/org/cryptomator/ipc/IpcMessage.java +++ b/src/main/java/org/cryptomator/ipc/IpcMessage.java @@ -9,8 +9,8 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.function.Function; -// TODO make sealed, remove enum -interface IpcMessage { +//TODO can the enum be removed? +sealed interface IpcMessage permits HandleLaunchArgsMessage, RevealRunningAppMessage { enum MessageType { REVEAL_RUNNING_APP(RevealRunningAppMessage::decode), From 82de8b6994aec8e7fdaa62b303713e0adb99a421 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 09:57:23 +0200 Subject: [PATCH 08/39] remove unrelated change --- .github/workflows/debian.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index b2d2bf4c8..c61563c63 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -21,9 +21,7 @@ on: env: JAVA_VERSION: 20 OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-x64_bin-jmods.zip' - JMODS_AMD64_HASH: '0b244b08354dc5e1b639765fd0858cbc154774e9cb05467c4111b7c63d3b3721' OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-aarch64_bin-jmods.zip' - JMODS_AARCH64_HASH: '4b0dd30282c067aa07fe840374115b5ec5dcfd8dc479ebd2231dfdb6a518b3b3' jobs: build: @@ -60,19 +58,9 @@ jobs: id: download-jmods run: | curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip - echo "${{ env.JMODS_AMD64_HASH }} openjfx-amd64.zip" | sha256sum -c - if [ $? != 0 ]; then - echo "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}."; - exit 1 - fi mkdir -p jmods/amd64 unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64 curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip - echo "${{ env.JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | sha256sum -c - if [ $? != 0 ]; then - echo "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AARCH64 }}."; - exit 1 - fi mkdir -p jmods/aarch64 unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64 - name: Ensure major jfx version in pom and in jmods is the same From 2ff71ed7b003769a0d3de0023d95f02025972733 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 14:51:44 +0200 Subject: [PATCH 09/39] remove langauges with insufficient translation --- .../java/org/cryptomator/launcher/SupportedLanguages.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java index 1d2f22da7..e57184af8 100644 --- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -18,9 +18,9 @@ public class SupportedLanguages { private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_". // "en" is not part of this list - it is always inserted at the top. - public static final List LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fil", "fa", "fr", "gl", "he", // - "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "si", "sk", "sr", "sr-Latn", "sv", "sw", // - "ta", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW"); + public static final List LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", // + "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", // + "ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW"); public static final String ENGLISH = "en"; private final List sortedLanguageTags; From 4429d57b5ed4bca20bf47e2f6bac32147ead86b2 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 14:52:15 +0200 Subject: [PATCH 10/39] ensure mutability of temporary collection --- .../org/cryptomator/launcher/SupportedLanguages.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java index e57184af8..0838c420e 100644 --- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -7,10 +7,10 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import java.text.Collator; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.stream.Collectors; @Singleton public class SupportedLanguages { @@ -33,11 +33,12 @@ public class SupportedLanguages { preferredLocale = preferredLanguage == null ? Locale.getDefault() : Locale.forLanguageTag(preferredLanguage); var collator = Collator.getInstance(preferredLocale); collator.setStrength(Collator.PRIMARY); - var sorted = LANGUAGE_TAGS.stream() // - .sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) // - .collect(Collectors.toList()); + var sorted = new ArrayList(); sorted.add(0, Settings.DEFAULT_LANGUAGE); sorted.add(1, ENGLISH); + LANGUAGE_TAGS.stream() // + .sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) // + .forEach(sorted::add); sortedLanguageTags = Collections.unmodifiableList(sorted); } From d23bd2865a13e33a7a8352492c260fa938ad992a Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 14:56:44 +0200 Subject: [PATCH 11/39] update location preset for Dropbox --- src/main/java/org/cryptomator/common/LocationPreset.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/common/LocationPreset.java b/src/main/java/org/cryptomator/common/LocationPreset.java index 17d276bf8..7861c6266 100644 --- a/src/main/java/org/cryptomator/common/LocationPreset.java +++ b/src/main/java/org/cryptomator/common/LocationPreset.java @@ -10,7 +10,7 @@ import java.util.List; */ public enum LocationPreset { - DROPBOX("Dropbox", "~/Dropbox"), + DROPBOX("Dropbox", "~/Library/CloudStorage/Dropbox", "~/Dropbox"), ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"), GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"), MEGA("MEGA", "~/MEGA"), From 7dd1c3576f756cd6784bb28ed4ae7bca34073044 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 15:02:15 +0200 Subject: [PATCH 12/39] always use the same JDK version in debian workflow --- .github/workflows/debian.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index c61563c63..bd353a434 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -45,7 +45,7 @@ jobs: run: | sudo add-apt-repository ppa:coffeelibs/openjdk sudo apt-get update - sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0 + sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0 - name: Setup Java uses: actions/setup-java@v3 with: From 085f762a350646e307b24d4fcfe9fcf53645b8ed Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 15:48:06 +0200 Subject: [PATCH 13/39] further fixing debian --- dist/linux/debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/linux/debian/rules b/dist/linux/debian/rules index be1014ea9..e0f077cc9 100755 --- a/dist/linux/debian/rules +++ b/dist/linux/debian/rules @@ -4,7 +4,7 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs +JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) ifeq ($(DEB_BUILD_ARCH),amd64) JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods From a9ec76a344b8b1600514413d51b9f18af071c7c7 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 9 May 2023 17:40:13 +0200 Subject: [PATCH 14/39] update wix main file due to updated jpackage installer template --- dist/win/resources/main.wxs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dist/win/resources/main.wxs b/dist/win/resources/main.wxs index 50edd6371..df73b195f 100644 --- a/dist/win/resources/main.wxs +++ b/dist/win/resources/main.wxs @@ -70,6 +70,9 @@ + + + @@ -172,11 +175,12 @@ - JP_UPGRADABLE_FOUND + JP_UPGRADABLE_FOUND - JP_DOWNGRADABLE_FOUND + JP_DOWNGRADABLE_FOUND + @@ -188,6 +192,10 @@ NOT Installed OR REINSTALL + + + + From 1253b7db2b3056427a70a448ca5abcebc35740d7 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Thu, 11 May 2023 20:43:23 +0200 Subject: [PATCH 15/39] Make unlock method private and simplify missing vaults unlocker --- .../cryptomator/ui/fxapp/AutoUnlocker.java | 52 +++++++------------ .../cryptomator/ui/fxapp/FxApplication.java | 3 +- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 79b440782..a4754fd29 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -7,7 +7,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.collections.ObservableList; -import java.util.Arrays; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ScheduledExecutorService; @@ -23,9 +23,8 @@ public class AutoUnlocker { private final ObservableList vaults; private final FxApplicationWindows appWindows; private final ScheduledExecutorService scheduler; - private ScheduledFuture checkFuture; + private ScheduledFuture unlockMissingFuture; private ScheduledFuture timeoutFuture; - private boolean isPeriodicCheckActive = false; @Inject public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows, ScheduledExecutorService scheduler) { @@ -34,54 +33,41 @@ public class AutoUnlocker { this.scheduler = scheduler; } - public void unlockAll() { + public void tryUnlockForTimespan(int timespan) { + // Unlock all available auto unlock vaults unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); + + // Start a temporary service if there are missing auto unlock vaults + if (getMissingAutoUnlockVaults().findAny().isPresent()) { + LOG.info("Found MISSING vaults, starting periodic check"); + unlockMissingFuture = scheduler.scheduleWithFixedDelay(this::unlockMissing, 0, 1, TimeUnit.SECONDS); + timeoutFuture = scheduler.schedule(this::timeout, timespan, TimeUnit.MINUTES); + } } - public void unlock(Stream vaultStream) { + private void unlock(Stream vaultStream) { vaultStream.filter(Vault::isLocked) .>reduce(CompletableFuture.completedFuture(null), (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), // we don't care here about the exception, logged elsewhere (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); } - public void startMissingVaultsChecker() { - if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { - LOG.info("Found MISSING vaults, starting periodic check"); - checkFuture = scheduler.scheduleWithFixedDelay(this::check, 0, 1, TimeUnit.SECONDS); - timeoutFuture = scheduler.schedule(this::timeout, 2, TimeUnit.MINUTES); - isPeriodicCheckActive = true; - } - } - - private void check() { - // Find the vaults that are missing but have an existing directory - Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); - if (vaultArray.length > 0) { - - // Redetermine vault states - for (Vault v : vaultArray) { - LOG.info("Found vault directory for '{}'", v.getDisplayName()); - VaultListManager.redetermineVaultState(v); - } - - // Unlock the vaults that were previously missing - unlock(Arrays.stream(vaultArray)); - } + private void unlockMissing() { + List missingAutoUnlockVaults = getMissingAutoUnlockVaults().toList(); + missingAutoUnlockVaults.forEach(VaultListManager::redetermineVaultState); + unlock(missingAutoUnlockVaults.stream()); // Stop checking if there are no more missing vaults - if (getMissingAutoUnlockVaults().count() == 0) { + if (getMissingAutoUnlockVaults().findAny().isEmpty()) { LOG.info("No more MISSING vaults, stopping periodic check"); - isPeriodicCheckActive = false; - checkFuture.cancel(false); + unlockMissingFuture.cancel(false); timeoutFuture.cancel(false); } } private void timeout() { LOG.info("MISSING vaults periodic check timed out"); - isPeriodicCheckActive = false; - checkFuture.cancel(false); + unlockMissingFuture.cancel(false); } private Stream getMissingAutoUnlockVaults() { diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 850d2a91e..736b793d2 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -68,7 +68,6 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.unlockAll(); - autoUnlocker.startMissingVaultsChecker(); + autoUnlocker.tryUnlockForTimespan(2); } } From 32d7189a12817a72b9bade58bd79484197852ddc Mon Sep 17 00:00:00 2001 From: Rexbas Date: Fri, 12 May 2023 21:52:30 +0200 Subject: [PATCH 16/39] Add time unit parameter --- src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java | 4 ++-- src/main/java/org/cryptomator/ui/fxapp/FxApplication.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index a4754fd29..bf40971c6 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -33,7 +33,7 @@ public class AutoUnlocker { this.scheduler = scheduler; } - public void tryUnlockForTimespan(int timespan) { + public void tryUnlockForTimespan(int timespan, TimeUnit timeUnit) { // Unlock all available auto unlock vaults unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); @@ -41,7 +41,7 @@ public class AutoUnlocker { if (getMissingAutoUnlockVaults().findAny().isPresent()) { LOG.info("Found MISSING vaults, starting periodic check"); unlockMissingFuture = scheduler.scheduleWithFixedDelay(this::unlockMissing, 0, 1, TimeUnit.SECONDS); - timeoutFuture = scheduler.schedule(this::timeout, timespan, TimeUnit.MINUTES); + timeoutFuture = scheduler.schedule(this::timeout, timespan, timeUnit); } } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 736b793d2..cfc7ba62b 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Platform; +import java.util.concurrent.TimeUnit; @FxApplicationScoped public class FxApplication { @@ -68,6 +69,6 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.tryUnlockForTimespan(2); + autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); } } From 5b6d09308b5a1ff4e513aeaf086096bc43f9f058 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 16 May 2023 17:05:54 +0200 Subject: [PATCH 17/39] Create SPI for cloud location presets --- src/main/java/module-info.java | 16 +++ .../DropboxMacLocationPresetsProvider.java | 27 +++++ ...DropboxWindowsLocationPresetsProvider.java | 27 +++++ ...GoogleDriveMacLocationPresetsProvider.java | 27 +++++ ...leDriveWindowsLocationPresetsProvider.java | 27 +++++ .../ICloudMacLocationPresetsProvider.java | 26 +++++ .../ICloudWindowsLocationPresetsProvider.java | 26 +++++ .../locationpresets/LocationPreset.java | 9 ++ .../LocationPresetsProvider.java | 20 ++++ .../MegaLocationPresetsProvider.java | 28 +++++ .../OneDriveMacLocationPresetsProvider.java | 27 +++++ ...neDriveWindowsLocationPresetsProvider.java | 107 ++++++++++++++++++ .../PCloudLocationPresetsProvider.java | 29 +++++ 13 files changed, 396 insertions(+) create mode 100644 src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/LocationPreset.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java create mode 100644 src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index a4a71a2cc..b24f2b841 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,15 @@ import ch.qos.logback.classic.spi.Configurator; +import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider; +import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider; +import org.cryptomator.common.locationpresets.GoogleDriveMacLocationPresetsProvider; +import org.cryptomator.common.locationpresets.GoogleDriveWindowsLocationPresetsProvider; +import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider; +import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider; +import org.cryptomator.common.locationpresets.LocationPresetsProvider; +import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider; +import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider; +import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider; +import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider; import org.cryptomator.integrations.tray.TrayMenuController; import org.cryptomator.logging.LogbackConfiguratorFactory; import org.cryptomator.ui.traymenu.AwtTrayMenuController; @@ -39,4 +50,9 @@ open module org.cryptomator.desktop { provides TrayMenuController with AwtTrayMenuController; provides Configurator with LogbackConfiguratorFactory; + provides LocationPresetsProvider with DropboxMacLocationPresetsProvider, // + DropboxWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, // + ICloudWindowsLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, // + GoogleDriveMacLocationPresetsProvider, PCloudLocationPresetsProvider, // + MegaLocationPresetsProvider, OneDriveMacLocationPresetsProvider, OneDriveWindowsLocationPresetsProvider; } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java new file mode 100644 index 000000000..6da4ccbea --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java @@ -0,0 +1,27 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; + +@OperatingSystem(MAC) +public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox"); + + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("Dropbox", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java new file mode 100644 index 000000000..132cf008e --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java @@ -0,0 +1,27 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox"); + + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("Dropbox", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java new file mode 100644 index 000000000..1e1b7763a --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java @@ -0,0 +1,27 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; + +@OperatingSystem(MAC) +public final class GoogleDriveMacLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive"); + + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("Google Drive", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java new file mode 100644 index 000000000..b99e4383a --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java @@ -0,0 +1,27 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +public final class GoogleDriveWindowsLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"); + + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("Google Drive", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java new file mode 100644 index 000000000..2d94c1bde --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java @@ -0,0 +1,26 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; + +@OperatingSystem(MAC) +public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs"); + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("iCloud Drive", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java new file mode 100644 index 000000000..2a786fad7 --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java @@ -0,0 +1,26 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive"); + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("iCloud Drive", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/LocationPreset.java b/src/main/java/org/cryptomator/common/locationpresets/LocationPreset.java new file mode 100644 index 000000000..5ba49eb9b --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/LocationPreset.java @@ -0,0 +1,9 @@ +package org.cryptomator.common.locationpresets; + +import java.nio.file.Path; + +public record LocationPreset(String name, Path path) { + + + +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java new file mode 100644 index 000000000..c48a45655 --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java @@ -0,0 +1,20 @@ +package org.cryptomator.common.locationpresets; + +import java.nio.file.Path; +import java.util.stream.Stream; + +public interface LocationPresetsProvider { + + String USER_HOME = System.getProperty("user.home"); + + Stream getLocations(); + + static Path resolveLocation(String p) { + if (p.startsWith("~/")) { + return Path.of(USER_HOME, p.substring(2)); + } else { + return Path.of(p); + } + } + +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java new file mode 100644 index 000000000..bba1f42da --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java @@ -0,0 +1,28 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +@OperatingSystem(MAC) +public final class MegaLocationPresetsProvider implements LocationPresetsProvider { + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA"); + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("MEGA", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java new file mode 100644 index 000000000..e6f73a39e --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java @@ -0,0 +1,27 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; + +@OperatingSystem(MAC) +public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider { + + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive"); + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("OneDrive", LOCATION)); + } +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java new file mode 100644 index 000000000..07e253fb0 --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java @@ -0,0 +1,107 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.OperatingSystem; +import org.jetbrains.annotations.Blocking; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider { + + private static final String REGSTR_TOKEN = "REG_SZ"; + private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\"; + + @Override + public Stream getLocations() { + try { + + var accounts = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList(); + var cloudLocations = new ArrayList(); + for (var account : accounts) { + var path = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // + .map(Path::of) // + .findFirst().orElseThrow(); + var name = "OneDrive"; //we assume personal oneDrive account by default + if (!account.equals("Personal")) { + name = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // + .map("OneDrive - "::concat).findFirst().orElseThrow(); + } + cloudLocations.add(new LocationPreset(name, path)); + } + return cloudLocations.stream(); + } catch (RuntimeException e) { + return Stream.of(); + } + } + + private Stream queryRegistry(String keyname, List moreArgs, Predicate outputFilter) { + var args = new ArrayList(); + args.add("reg"); + args.add("query"); + args.add(keyname); + args.addAll(moreArgs); + try { + ProcessBuilder command = new ProcessBuilder(args); + Process p = command.start(); + waitForSuccess(p, 3, "`reg query`"); + return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter); + } catch (TimeoutException | IOException | CommandFailedException e) { + throw new RuntimeException("FAIL"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("FAIL"); + } + } + + + /** + * Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}. + * + * @param process The process to wait for + * @param timeoutSeconds How long to wait (in seconds) + * @param cmdDescription A short description of the process used to generate log and exception messages + * @throws TimeoutException Thrown when the process doesn't finish in time + * @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish + * @throws CommandFailedException Thrown when the process exit code is non-zero + */ + @Blocking + static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException { + boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS); + if (!exited) { + throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s"); + } + if (process.exitValue() != 0) { + @SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n")); + @SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n")); + throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr); + } + } + + static class CommandFailedException extends Exception { + + int exitCode; + String stdout; + String stderr; + + private CommandFailedException(String cmdDescription, int exitCode, String stdout, String stderr) { + super(cmdDescription + " returned with non-zero exit code " + exitCode); + this.exitCode = exitCode; + this.stdout = stdout; + this.stderr = stderr; + } + + } + + +} diff --git a/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java new file mode 100644 index 000000000..969385b5a --- /dev/null +++ b/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java @@ -0,0 +1,29 @@ +package org.cryptomator.common.locationpresets; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; +import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; + +@OperatingSystem(WINDOWS) +@OperatingSystem(MAC) +public final class PCloudLocationPresetsProvider implements LocationPresetsProvider { + + + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/pCloudDrive"); + + @CheckAvailability + public static boolean isPresent() { + return Files.isDirectory(LOCATION); + } + + @Override + public Stream getLocations() { + return Stream.of(new LocationPreset("pCloud", LOCATION)); + } +} From e345e6415fde27d3c7f27256af333782ba65326f Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 17 May 2023 14:17:28 +0200 Subject: [PATCH 18/39] use @CheckAvailability annotation correctly --- .../locationpresets/DropboxMacLocationPresetsProvider.java | 1 + .../locationpresets/DropboxWindowsLocationPresetsProvider.java | 1 + .../locationpresets/GoogleDriveMacLocationPresetsProvider.java | 3 ++- .../GoogleDriveWindowsLocationPresetsProvider.java | 3 ++- .../locationpresets/ICloudMacLocationPresetsProvider.java | 1 + .../locationpresets/ICloudWindowsLocationPresetsProvider.java | 1 + .../common/locationpresets/MegaLocationPresetsProvider.java | 1 + .../locationpresets/OneDriveMacLocationPresetsProvider.java | 1 + .../common/locationpresets/PCloudLocationPresetsProvider.java | 1 + 9 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java index 6da4ccbea..9a8bb683d 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; @OperatingSystem(MAC) +@CheckAvailability public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider { private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox"); diff --git a/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java index 132cf008e..4e0365275 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) +@CheckAvailability public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider { private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox"); diff --git a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java index 1e1b7763a..6c0bee8fc 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java @@ -10,9 +10,10 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; @OperatingSystem(MAC) +@CheckAvailability public final class GoogleDriveMacLocationPresetsProvider implements LocationPresetsProvider { - private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive"); + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"); @CheckAvailability diff --git a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java index b99e4383a..bc84bdee3 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java @@ -10,9 +10,10 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) +@CheckAvailability public final class GoogleDriveWindowsLocationPresetsProvider implements LocationPresetsProvider { - private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"); + private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive"); @CheckAvailability diff --git a/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java index 2d94c1bde..9138610b5 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; @OperatingSystem(MAC) +@CheckAvailability public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider { private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs"); diff --git a/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java index 2a786fad7..f19333066 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) +@CheckAvailability public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider { private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive"); diff --git a/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java index bba1f42da..f11b3d2bb 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java @@ -12,6 +12,7 @@ import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) @OperatingSystem(MAC) +@CheckAvailability public final class MegaLocationPresetsProvider implements LocationPresetsProvider { private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA"); diff --git a/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java index e6f73a39e..62ecae18d 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC; @OperatingSystem(MAC) +@CheckAvailability public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider { diff --git a/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java index 969385b5a..05e2867ce 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java @@ -12,6 +12,7 @@ import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) @OperatingSystem(MAC) +@CheckAvailability public final class PCloudLocationPresetsProvider implements LocationPresetsProvider { From a3492b9ea3e3d701c02e4d370ad54138c108cd8e Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 17 May 2023 14:25:16 +0200 Subject: [PATCH 19/39] use correct registry keys for onedrive detection --- .../OneDriveWindowsLocationPresetsProvider.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java index 07e253fb0..ab14a483e 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java @@ -1,7 +1,11 @@ package org.cryptomator.common.locationpresets; import org.cryptomator.integrations.common.OperatingSystem; +import org.cryptomator.integrations.locationpresets.LocationPreset; +import org.cryptomator.integrations.locationpresets.LocationPresetsProvider; import org.jetbrains.annotations.Blocking; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -19,22 +23,22 @@ import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS; @OperatingSystem(WINDOWS) public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider { + private static final Logger LOG = LoggerFactory.getLogger(OneDriveWindowsLocationPresetsProvider.class); private static final String REGSTR_TOKEN = "REG_SZ"; private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\"; @Override public Stream getLocations() { try { - - var accounts = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList(); + var accountRegKeys = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList(); var cloudLocations = new ArrayList(); - for (var account : accounts) { - var path = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // + for (var accountRegKey : accountRegKeys) { + var path = queryRegistry(accountRegKey, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // .map(Path::of) // .findFirst().orElseThrow(); var name = "OneDrive"; //we assume personal oneDrive account by default - if (!account.equals("Personal")) { - name = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // + if (!accountRegKey.endsWith("Personal")) { + name = queryRegistry(accountRegKey, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) // .map("OneDrive - "::concat).findFirst().orElseThrow(); } cloudLocations.add(new LocationPreset(name, path)); From 0989c735c02e9589a8cb0f3f06e451901f574c95 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 17 May 2023 14:25:46 +0200 Subject: [PATCH 20/39] improve error handling when querying registry --- ...neDriveWindowsLocationPresetsProvider.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java index ab14a483e..809803118 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java @@ -44,28 +44,26 @@ public final class OneDriveWindowsLocationPresetsProvider implements LocationPre cloudLocations.add(new LocationPreset(name, path)); } return cloudLocations.stream(); - } catch (RuntimeException e) { + } catch (IOException | CommandFailedException | TimeoutException e) { + LOG.error("Unable to determine OneDrive location", e); + return Stream.of(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOG.error("Determination of OneDrive location interrupted", e); return Stream.of(); } } - private Stream queryRegistry(String keyname, List moreArgs, Predicate outputFilter) { + private Stream queryRegistry(String keyname, List moreArgs, Predicate outputFilter) throws InterruptedException, CommandFailedException, TimeoutException, IOException { var args = new ArrayList(); args.add("reg"); args.add("query"); args.add(keyname); args.addAll(moreArgs); - try { - ProcessBuilder command = new ProcessBuilder(args); - Process p = command.start(); - waitForSuccess(p, 3, "`reg query`"); - return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter); - } catch (TimeoutException | IOException | CommandFailedException e) { - throw new RuntimeException("FAIL"); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("FAIL"); - } + ProcessBuilder command = new ProcessBuilder(args); + Process p = command.start(); + waitForSuccess(p, 3, "`reg query`"); + return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter); } From 0af0a9e4400143ee944775ea8ed939d498ba2a5a Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 17 May 2023 14:51:43 +0200 Subject: [PATCH 21/39] refactor location ui in addVault workflow to new locationPreset framework --- .../cryptomator/common/LocationPreset.java | 64 -------- .../LocationPresetsProvider.java | 73 +++++++++ .../CreateNewVaultLocationController.java | 58 ++++--- .../ObservedLocationPresets.java | 141 ------------------ .../resources/fxml/addvault_new_location.fxml | 15 +- 5 files changed, 104 insertions(+), 247 deletions(-) delete mode 100644 src/main/java/org/cryptomator/common/LocationPreset.java delete mode 100644 src/main/java/org/cryptomator/ui/addvaultwizard/ObservedLocationPresets.java diff --git a/src/main/java/org/cryptomator/common/LocationPreset.java b/src/main/java/org/cryptomator/common/LocationPreset.java deleted file mode 100644 index 7861c6266..000000000 --- a/src/main/java/org/cryptomator/common/LocationPreset.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.cryptomator.common; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; - -/** - * Enum of common cloud providers and their default local storage location path. - */ -public enum LocationPreset { - - DROPBOX("Dropbox", "~/Library/CloudStorage/Dropbox", "~/Dropbox"), - ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"), - GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"), - MEGA("MEGA", "~/MEGA"), - ONEDRIVE("OneDrive", "~/OneDrive"), - PCLOUD("pCloud", "~/pCloudDrive"), - - LOCAL("local"); - - private final String name; - private final List candidates; - - LocationPreset(String name, String... candidates) { - this.name = name; - this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList(); - } - - /** - * Checks for this LocationPreset if any of the associated paths exist. - * - * @return the first existing path or null, if none exists. - */ - public Path existingPath() { - return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null); - } - - public String getDisplayName() { - return name; - } - - @Override - public String toString() { - return getDisplayName(); - } - - //this contruct is needed, since static members are initialized after every enum member is initialized - //TODO: refactor this to normal class and use this also in different parts of the project - private static class UserHome { - - private static final String USER_HOME = System.getProperty("user.home"); - - private static String resolve(String path) { - if (path.startsWith("~/")) { - return UserHome.USER_HOME + path.substring(1); - } else { - return path; - } - } - } - -} - diff --git a/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java b/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java index c48a45655..cf527a749 100644 --- a/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java +++ b/src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java @@ -1,10 +1,22 @@ package org.cryptomator.common.locationpresets; +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.IntegrationsLoader; +import org.cryptomator.integrations.common.OperatingSystem; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.nio.file.Path; +import java.util.Arrays; +import java.util.ServiceLoader; import java.util.stream.Stream; public interface LocationPresetsProvider { + Logger LOG = LoggerFactory.getLogger(LocationPresetsProvider.class); String USER_HOME = System.getProperty("user.home"); Stream getLocations(); @@ -17,4 +29,65 @@ public interface LocationPresetsProvider { } } + //copied from org.cryptomator.integrations.common.IntegrationsLoader + //TODO: delete, once migrated to integrations-api + static Stream loadAll(Class clazz) { + return ServiceLoader.load(clazz) + .stream() + .filter(LocationPresetsProvider::isSupportedOperatingSystem) + .filter(LocationPresetsProvider::passesStaticAvailabilityCheck) + .map(ServiceLoader.Provider::get) + .peek(impl -> logServiceIsAvailable(clazz, impl.getClass())); + } + + + private static boolean isSupportedOperatingSystem(ServiceLoader.Provider provider) { + var annotations = provider.type().getAnnotationsByType(OperatingSystem.class); + return annotations.length == 0 || Arrays.stream(annotations).anyMatch(OperatingSystem.Value::isCurrent); + } + + private static boolean passesStaticAvailabilityCheck(ServiceLoader.Provider provider) { + return passesStaticAvailabilityCheck(provider.type()); + } + + static boolean passesStaticAvailabilityCheck(Class type) { + return passesAvailabilityCheck(type, null); + } + + private static void logServiceIsAvailable(Class apiType, Class implType) { + if (LOG.isDebugEnabled()) { + LOG.debug("{}: Implementation is available: {}", apiType.getSimpleName(), implType.getName()); + } + } + + private static boolean passesAvailabilityCheck(Class type, @Nullable T instance) { + if (!type.isAnnotationPresent(CheckAvailability.class)) { + return true; // if type is not annotated, skip tests + } + if (!type.getModule().isExported(type.getPackageName(), IntegrationsLoader.class.getModule())) { + LOG.error("Can't run @CheckAvailability tests for class {}. Make sure to export {} to {}!", type.getName(), type.getPackageName(), IntegrationsLoader.class.getPackageName()); + return false; + } + return Arrays.stream(type.getMethods()) + .filter(m -> isAvailabilityCheck(m, instance == null)) + .allMatch(m -> passesAvailabilityCheck(m, instance)); + } + + private static boolean passesAvailabilityCheck(Method m, @Nullable Object instance) { + assert Boolean.TYPE.equals(m.getReturnType()); + try { + return (boolean) m.invoke(instance); + } catch (ReflectiveOperationException e) { + LOG.warn("Failed to invoke @CheckAvailability test {}#{}", m.getDeclaringClass(), m.getName(), e); + return false; + } + } + + private static boolean isAvailabilityCheck(Method m, boolean isStatic) { + return m.isAnnotationPresent(CheckAvailability.class) + && Boolean.TYPE.equals(m.getReturnType()) + && m.getParameterCount() == 0 + && Modifier.isStatic(m.getModifiers()) == isStatic; + } + } diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java index cadccc091..5a36552b0 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java @@ -1,6 +1,8 @@ package org.cryptomator.ui.addvaultwizard; import dagger.Lazy; +import org.cryptomator.common.locationpresets.LocationPresetsProvider; +import org.cryptomator.common.locationpresets.LocationPreset; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; @@ -26,6 +28,7 @@ import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.VBox; import javafx.stage.DirectoryChooser; import javafx.stage.Stage; import java.io.File; @@ -34,6 +37,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; import java.util.ResourceBundle; @AddVaultWizardScoped @@ -46,7 +52,7 @@ public class CreateNewVaultLocationController implements FxController { private final Stage window; private final Lazy chooseNameScene; private final Lazy choosePasswordScene; - private final ObservedLocationPresets locationPresets; + private final List locationPresetBtns; private final ObjectProperty vaultPath; private final StringProperty vaultName; private final ResourceBundle resourceBundle; @@ -58,24 +64,18 @@ public class CreateNewVaultLocationController implements FxController { private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH; //FXML - public ToggleGroup predefinedLocationToggler; - public RadioButton iclouddriveRadioButton; - public RadioButton dropboxRadioButton; - public RadioButton gdriveRadioButton; - public RadioButton onedriveRadioButton; - public RadioButton megaRadioButton; - public RadioButton pcloudRadioButton; + public ToggleGroup locationPresetsToggler; + public VBox radioButtonVBox; public RadioButton customRadioButton; public Label vaultPathStatus; public FontAwesome5IconView goodLocation; public FontAwesome5IconView badLocation; @Inject - CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) { + CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy choosePasswordScene, ObjectProperty vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) { this.window = window; this.chooseNameScene = chooseNameScene; this.choosePasswordScene = choosePasswordScene; - this.locationPresets = locationPresets; this.vaultPath = vaultPath; this.vaultName = vaultName; this.resourceBundle = resourceBundle; @@ -83,6 +83,14 @@ public class CreateNewVaultLocationController implements FxController { this.usePresetPath = new SimpleBooleanProperty(); this.statusText = new SimpleStringProperty(); this.statusGraphic = new SimpleObjectProperty<>(); + this.locationPresetBtns = LocationPresetsProvider.loadAll(LocationPresetsProvider.class) // + .flatMap(LocationPresetsProvider::getLocations) // + .sorted(Comparator.comparing(LocationPreset::name)) // + .map(preset -> { // + var btn = new RadioButton(preset.name()); + btn.setUserData(preset.path()); + return btn; + }).toList(); } private boolean validateVaultPathAndSetStatus() { @@ -127,26 +135,15 @@ public class CreateNewVaultLocationController implements FxController { @FXML public void initialize() { - predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation); - usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton)); + radioButtonVBox.getChildren().addAll(1, locationPresetBtns); //first item is the list header + locationPresetsToggler.getToggles().addAll(locationPresetBtns); + locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation); + usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton)); } private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) { - if (iclouddriveRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getIclouddriveLocation().resolve(vaultName.get())); - } else if (dropboxRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getDropboxLocation().resolve(vaultName.get())); - } else if (gdriveRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get())); - } else if (onedriveRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get())); - } else if (megaRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get())); - } else if (pcloudRadioButton.equals(newValue)) { - vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get())); - } else if (customRadioButton.equals(newValue)) { - vaultPath.set(customVaultPath.resolve(vaultName.get())); - } + var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath); + vaultPath.set(storagePath.resolve(vaultName.get())); } @FXML @@ -197,10 +194,6 @@ public class CreateNewVaultLocationController implements FxController { return validVaultPath.get(); } - public ObservedLocationPresets getObservedLocationPresets() { - return locationPresets; - } - public BooleanProperty usePresetPathProperty() { return usePresetPath; } @@ -210,7 +203,7 @@ public class CreateNewVaultLocationController implements FxController { } public BooleanBinding anyRadioButtonSelectedProperty() { - return predefinedLocationToggler.selectedToggleProperty().isNotNull(); + return locationPresetsToggler.selectedToggleProperty().isNotNull(); } public boolean isAnyRadioButtonSelected() { @@ -232,4 +225,5 @@ public class CreateNewVaultLocationController implements FxController { public Node getStatusGraphic() { return statusGraphic.get(); } + } diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ObservedLocationPresets.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ObservedLocationPresets.java deleted file mode 100644 index 1c988dc04..000000000 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ObservedLocationPresets.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.cryptomator.ui.addvaultwizard; - -import org.cryptomator.common.LocationPreset; - -import javax.inject.Inject; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import java.nio.file.Path; - -@AddVaultWizardScoped -public class ObservedLocationPresets { - - private final ReadOnlyObjectProperty iclouddriveLocation; - private final ReadOnlyObjectProperty dropboxLocation; - private final ReadOnlyObjectProperty gdriveLocation; - private final ReadOnlyObjectProperty onedriveLocation; - private final ReadOnlyObjectProperty megaLocation; - private final ReadOnlyObjectProperty pcloudLocation; - private final BooleanBinding foundIclouddrive; - private final BooleanBinding foundDropbox; - private final BooleanBinding foundGdrive; - private final BooleanBinding foundOnedrive; - private final BooleanBinding foundMega; - private final BooleanBinding foundPcloud; - - @Inject - public ObservedLocationPresets() { - this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath()); - this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath()); - this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath()); - this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath()); - this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath()); - this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath()); - this.foundIclouddrive = iclouddriveLocation.isNotNull(); - this.foundDropbox = dropboxLocation.isNotNull(); - this.foundGdrive = gdriveLocation.isNotNull(); - this.foundOnedrive = onedriveLocation.isNotNull(); - this.foundMega = megaLocation.isNotNull(); - this.foundPcloud = pcloudLocation.isNotNull(); - } - - /* Observables */ - - public ReadOnlyObjectProperty iclouddriveLocationProperty() { - return iclouddriveLocation; - } - - public Path getIclouddriveLocation() { - return iclouddriveLocation.get(); - } - - public BooleanBinding foundIclouddriveProperty() { - return foundIclouddrive; - } - - public boolean isFoundIclouddrive() { - return foundIclouddrive.get(); - } - - public ReadOnlyObjectProperty dropboxLocationProperty() { - return dropboxLocation; - } - - public Path getDropboxLocation() { - return dropboxLocation.get(); - } - - public BooleanBinding foundDropboxProperty() { - return foundDropbox; - } - - public boolean isFoundDropbox() { - return foundDropbox.get(); - } - - public ReadOnlyObjectProperty gdriveLocationProperty() { - return gdriveLocation; - } - - public Path getGdriveLocation() { - return gdriveLocation.get(); - } - - public BooleanBinding foundGdriveProperty() { - return foundGdrive; - } - - public boolean isFoundGdrive() { - return foundGdrive.get(); - } - - public ReadOnlyObjectProperty onedriveLocationProperty() { - return onedriveLocation; - } - - public Path getOnedriveLocation() { - return onedriveLocation.get(); - } - - public BooleanBinding foundOnedriveProperty() { - return foundOnedrive; - } - - public boolean isFoundOnedrive() { - return foundOnedrive.get(); - } - - public ReadOnlyObjectProperty megaLocationProperty() { - return megaLocation; - } - - public Path getMegaLocation() { - return megaLocation.get(); - } - - public BooleanBinding foundMegaProperty() { - return foundMega; - } - - public boolean isFoundMega() { - return foundMega.get(); - } - - public ReadOnlyObjectProperty pcloudLocationProperty() { - return pcloudLocation; - } - - public Path getPcloudLocation() { - return pcloudLocation.get(); - } - - public BooleanBinding foundPcloudProperty() { - return foundPcloud; - } - - public boolean isFoundPcloud() { - return foundPcloud.get(); - } - -} diff --git a/src/main/resources/fxml/addvault_new_location.fxml b/src/main/resources/fxml/addvault_new_location.fxml index 343a2d580..7920b6aac 100644 --- a/src/main/resources/fxml/addvault_new_location.fxml +++ b/src/main/resources/fxml/addvault_new_location.fxml @@ -19,7 +19,7 @@ spacing="12" alignment="CENTER_LEFT"> - + @@ -29,16 +29,11 @@ - +