diff --git a/README.md b/README.md
index 9f0a0e0a1..d2bf39f29 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
[](https://cryptomator.org/)
-[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
+[](https://github.com/cryptomator/cryptomator/actions/workflows/build.yml?query=branch%3Adevelop)
[](https://snyk.io/test/github/cryptomator/cryptomator)
[](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
[](https://mastodon.online/@cryptomator)
diff --git a/pom.xml b/pom.xml
index e6f1e96e4..1735b728f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,7 +61,7 @@
26.0.1
- 12.0.1
+ 12.1.0
0.8.12
2.5.0
1.4.0
diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
index 719071ed2..65bc080ca 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
@@ -43,8 +43,8 @@ public class AutoUnlocker {
private CompletionStage unlockSequentially(Stream vaultStream) {
// this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow.
return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null),
- (prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)),
- (prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere
+ (prevUnlock, nextVault) -> prevUnlock.thenCompose(_ -> appWindows.startUnlockWorkflow(nextVault, null)),
+ (_, nextUnlock) -> nextUnlock.exceptionally(_ -> null) // we don't care here about the exception, logged elsewhere
);
}
diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
index 162874dce..c8a870fd8 100644
--- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
+++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
@@ -6,6 +6,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.dialogs.Dialogs;
+import org.cryptomator.ui.dialogs.SimpleDialog;
import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.lock.LockComponent;
@@ -97,17 +98,17 @@ public class FxApplicationWindows {
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
- desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
+ desktop.setPreferencesHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ANY));
}
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
- desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
+ desktop.setAboutHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
}
// register app reopen listener
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
- desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
+ desktop.addAppEventListener((AppReopenedListener) _ -> showMainWindow());
}
// observe visible windows
@@ -139,11 +140,12 @@ public class FxApplicationWindows {
}
public CompletionStage showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
- return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
+ return showMainWindow().thenApplyAsync(_ -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater) //
+ .whenComplete(this::reportErrors);
}
public void showQuitWindow(QuitResponse response, boolean forced) {
- CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
+ CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response, forced), Platform::runLater);
}
public void showUpdateReminderWindow() {
@@ -151,13 +153,14 @@ public class FxApplicationWindows {
}
public void showDokanySupportEndWindow() {
- CompletableFuture.runAsync(() -> dialogs.prepareDokanySupportEndDialog(
- mainWindow.get().window(),
- stage -> {
- showPreferencesWindow(SelectedPreferencesTab.VOLUME);
- stage.close();
- }
- ).build().showAndWait(), Platform::runLater);
+ CompletableFuture.runAsync(() -> createDokanySupportEndDialog().showAndWait(), Platform::runLater);
+ }
+
+ private SimpleDialog createDokanySupportEndDialog() {
+ return dialogs.prepareDokanySupportEndDialog(mainWindow.get().window(), stage -> {
+ showPreferencesWindow(SelectedPreferencesTab.VOLUME);
+ stage.close();
+ }).build();
}
public CompletionStage startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
@@ -166,8 +169,7 @@ public class FxApplicationWindows {
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
}, Platform::runLater) //
- .thenAcceptAsync(UnlockWorkflow::run, executor)
- .exceptionally(e -> {
+ .thenAcceptAsync(UnlockWorkflow::run, executor).exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null;
});
diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
index b80804f2e..fa1b441d9 100644
--- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
+++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java
@@ -1,6 +1,7 @@
package org.cryptomator.ui.mainwindow;
import dagger.Binds;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
@@ -14,9 +15,11 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.common.StageInitializer;
import org.cryptomator.ui.error.ErrorComponent;
+import org.cryptomator.ui.fxapp.FxApplicationTerminator;
import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
+import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
import javax.inject.Named;
@@ -35,11 +38,19 @@ abstract class MainWindowModule {
@Provides
@MainWindow
@MainWindowScoped
- static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
+ static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer, FxApplicationTerminator terminator, Lazy trayMenu) {
initializer.accept(stage);
stage.setTitle("Cryptomator");
stage.setMinWidth(650);
stage.setMinHeight(498);
+ stage.setOnCloseRequest(e -> {
+ if (!trayMenu.get().isInitialized()) {
+ terminator.terminate();
+ e.consume();
+ } else {
+ stage.close();
+ }
+ });
return stage;
}
diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
index afa05cc8c..f5a72290f 100644
--- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
+++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
@@ -19,6 +19,8 @@ import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -32,7 +34,10 @@ import java.util.ResourceBundle;
@PreferencesScoped
public class UpdatesPreferencesController implements FxController {
- private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
+ private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" //
+ + "?utm_source=cryptomator-desktop" //
+ + "&utm_medium=update-notification&" //
+ + "utm_campaign=app-update-%s";
private final Application application;
private final Environment environment;
@@ -50,6 +55,7 @@ public class UpdatesPreferencesController implements FxController {
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
private final DateTimeFormatter formatter;
private final BooleanBinding upToDate;
+ private final String downloadsUri;
/* FXML */
public CheckBox checkForUpdatesCheckbox;
@@ -65,12 +71,13 @@ public class UpdatesPreferencesController implements FxController {
this.latestVersion = updateChecker.latestVersionProperty();
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
- this.currentVersion = updateChecker.getCurrentVersion();
+ this.currentVersion = environment.getAppVersion();
this.updateAvailable = updateChecker.updateAvailableProperty();
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
this.checkFailed = updateChecker.checkFailedProperty();
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
+ this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII));
}
public void initialize() {
@@ -93,7 +100,7 @@ public class UpdatesPreferencesController implements FxController {
@FXML
public void visitDownloadsPage() {
- application.getHostServices().showDocument(DOWNLOADS_URI);
+ application.getHostServices().showDocument(downloadsUri);
}
@FXML