[skip ci] drop interface approach and just add fx window

This commit is contained in:
Armin Schrenk
2025-11-29 18:03:32 +01:00
parent 3d679c73e7
commit b4528f825e
19 changed files with 264 additions and 129 deletions

View File

@@ -34,7 +34,7 @@
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.8.0-SNAPSHOT</cryptomator.integrations.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta1</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta1</cryptomator.integrations.linux.version>

View File

@@ -1,7 +1,4 @@
import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.integrations.notify.NotifyService2;
import org.cryptomator.integrationsbase.JavaFXNotifyService;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
@@ -16,11 +13,12 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
open module org.cryptomator.desktop {
@@ -66,7 +64,6 @@ open module org.cryptomator.desktop {
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
provides NotifyService2 with JavaFXNotifyService;
provides LocationPresetsProvider with //
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //

View File

@@ -1,30 +0,0 @@
package org.cryptomator.integrationsbase;
import org.cryptomator.integrations.common.DisplayName;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.notify.NotifyService;
import org.cryptomator.integrations.notify.NotifyService2;
import org.cryptomator.integrations.notify.NotifyServiceException;
import java.util.ArrayList;
@Priority(Priority.FALLBACK)
//@LocalizedDisplayName(bundle = "strings", key ="")
@DisplayName("Cryptomator App Window")
public class JavaFXNotifyService implements NotifyService2 {
//TODO: ipcMessageFile!
@Override
public void sendNotification(String header, String description, NotifyService2.Action... actions) throws NotifyServiceException {
var argList = new ArrayList<String>();
argList.add(header);
argList.add(description);
for(var action: actions) {
argList.add(action.label());
argList.add(action.returnMessage());
}
NotificationApp.main(argList.toArray(new String[] {}));
}
}

View File

@@ -1,52 +0,0 @@
package org.cryptomator.integrationsbase;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.BuilderFactory;
import java.io.IOException;
import java.util.ResourceBundle;
public class NotificationApp extends Application {
private static final KeyCodeCombination ALT_F4 = new KeyCodeCombination(KeyCode.F4, KeyCombination.ALT_DOWN);
private static final KeyCodeCombination SHORTCUT_W = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
@Override
public void start(Stage stage) throws IOException {
var args = getParameters();
String javaVersion = System.getProperty("java.version");
var url = getClass().getResource("/fxml/notification_window.fxml");
var root = FXMLLoader.<AnchorPane>load(url, ResourceBundle.getBundle("i18n.strings"));
var scene = new Scene(root);
stage.setScene(scene);
setupDefaultAccelerators(scene, stage);
stage.show();
stage.requestFocus();
}
public static void main(String[] args) {
//assert args.length >= 2;
launch(args);
}
private void setupDefaultAccelerators(Scene scene, Stage stage) {
if (SystemUtils.IS_OS_WINDOWS) {
scene.getAccelerators().put(ALT_F4, stage::close);
} else {
scene.getAccelerators().put(SHORTCUT_W, stage::close);
}
}
}

View File

@@ -1,20 +0,0 @@
package org.cryptomator.integrationsbase;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
public class NotificationWindowController {
public NotificationWindowController() {
}
@FXML
public void initialize() {
System.out.println("Hello");
}
public void handleButtonAction(ActionEvent actionEvent) {
System.out.println("OKAY");
}
}

View File

@@ -57,7 +57,8 @@ public enum FxmlFile {
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
VAULT_OPTIONS("/fxml/vault_options.fxml"), //
VAULT_STATISTICS("/fxml/stats.fxml"), //
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
WRONGFILEALERT("/fxml/wrongfilealert.fxml"),
NOTIFICATION("/fxml/notification.fxml");
private final String ressourcePathString;

View File

@@ -89,6 +89,7 @@ public class FxApplication {
launchEventHandler.startHandlingLaunchEvents();
fxFSEventList.schedulePollForUpdates();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
//TODO: init the NotificationListener
}
private void migrateAndInformDokanyRemoval() {

View File

@@ -13,6 +13,7 @@ import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
@@ -39,7 +40,8 @@ import java.io.InputStream;
UpdateReminderComponent.class, //
ShareVaultComponent.class, //
EventViewComponent.class, //
RecoveryKeyComponent.class})
RecoveryKeyComponent.class, //
NotificationComponent.class })
abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException {

View File

@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.notify.NotifyService;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.dialogs.SimpleDialog;
@@ -11,6 +12,7 @@ import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
@@ -54,6 +56,7 @@ public class FxApplicationWindows {
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final Lazy<EventViewComponent> eventViewWindow;
private final NotificationComponent.Factory notificationWindow;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
@@ -73,6 +76,7 @@ public class FxApplicationWindows {
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, //
NotificationComponent.Factory notificationWindow,
ExecutorService executor, //
Dialogs dialogs) {
this.primaryStage = primaryStage;
@@ -85,6 +89,7 @@ public class FxApplicationWindows {
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
@@ -193,6 +198,10 @@ public class FxApplicationWindows {
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showNotification(Runnable action) {
return CompletableFuture.supplyAsync(() -> notificationWindow.create(message, description, action).showNotification(), Platform::runLater).whenComplete(this::reportErrors);
}
/**
* Displays the generic error scene in the given window.
*
@@ -210,4 +219,5 @@ public class FxApplicationWindows {
LOG.error("Failed to display stage", error);
}
}
}

View File

@@ -0,0 +1,21 @@
package org.cryptomator.ui.fxapp;
import javax.inject.Inject;
/**
* Scans the event list for events requiring a notification
*/
@FxApplicationScoped
public class FxNotificationRadar {
private final FxFSEventList eventList;
@Inject
FxNotificationRadar(FxFSEventList eventList) {
this.eventList = eventList;
eventList.getObservableList()
}
}

View File

@@ -11,6 +11,8 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.integrations.notify.NotifyService;
import org.cryptomator.integrations.notify.NotifyServiceException;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
@@ -205,11 +207,22 @@ public class VaultListController implements FxController {
@FXML
private void toggleMenu() {
/*
if (addVaultContextMenu.isShowing()) {
addVaultContextMenu.hide();
} else {
addVaultContextMenu.show(addVaultButton, Side.BOTTOM, 0.0, 0.0);
}
*/
NotifyService.loadAll().findFirst().ifPresent(
s -> {
try {
s.sendNotification("Hello", "Lindsay");
} catch (NotifyServiceException e) {
throw new RuntimeException(e);
}
}
);
}
private void deselect(MouseEvent released) {

View File

@@ -0,0 +1,26 @@
package org.cryptomator.ui.notification;
import dagger.BindsInstance;
import dagger.Subcomponent;
import javafx.stage.Stage;
@NotificationScoped
@Subcomponent(modules = {NotificationModule.class})
public interface NotificationComponent {
@NotificationWindow
Stage notificationWindow();
default Stage showNotification(){
var window = notificationWindow();
window.show();
return window;
}
@Subcomponent.Factory
interface Factory {
NotificationComponent create(@BindsInstance Runnable action);
}
}

View File

@@ -0,0 +1,56 @@
package org.cryptomator.ui.notification;
import org.cryptomator.common.Nullable;
import org.cryptomator.integrations.notify.NotifyService;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import java.util.concurrent.ExecutorService;
@NotificationScoped
public class NotificationController implements FxController {
private final String message;
private final String description;
private final NotifyAction callback;
private final ExecutorService executorService;
@FXML
Button button;
@Inject
public NotificationController(@Named("Message") String message, @Named("Description") String description, @Nullable NotifyAction callback, ExecutorService executorService) {
this.message = message;
this.description = description;
this.callback = callback;
this.executorService = executorService;
}
//FXML bindings
public String getMessage() {
return message;
}
public String getDescription() {
return description;
}
public String getButtonText() {
return callback == null? "" : callback.label();
}
public boolean isButtonVisible() {
return callback != null;
}
@FXML
public void handleButtonAction(ActionEvent actionEvent) {
executorService.submit(callback.action());
}
}

View File

@@ -0,0 +1,62 @@
package org.cryptomator.ui.notification;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageInitializer;
import org.cryptomator.ui.quit.QuitForcedController;
import javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class NotificationModule {
@Provides
@NotificationWindow
@NotificationScoped
static Stage provideStage(StageInitializer initializer, @FxmlScene(FxmlFile.NOTIFICATION) Scene notificationScene) {
Stage stage = new Stage(StageStyle.UTILITY);
stage.setTitle("Filesystem notification"); //TODO: translate
stage.setResizable(false);
stage.initModality(Modality.NONE);
initializer.accept(stage);
stage.setScene(notificationScene);
stage.sizeToScene();
return stage;
}
// javafx setup
@Provides
@FxmlScene(FxmlFile.NOTIFICATION)
@NotificationScoped
static Scene provideNotificationScene(FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.NOTIFICATION);
}
@Provides
@NotificationScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Binds
@IntoMap
@FxControllerKey(NotificationController.class)
abstract FxController bindNotificationController(NotificationController controller);
}

View File

@@ -0,0 +1,11 @@
package org.cryptomator.ui.notification;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface NotificationScoped {}

View File

@@ -0,0 +1,12 @@
package org.cryptomator.ui.notification;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface NotificationWindow {}

View File

@@ -0,0 +1,3 @@
package org.cryptomator.ui.notification;
public record NotifyAction(String label, Runnable action) {}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ButtonBar?>
<HBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.notification.NotificationController"
prefHeight="100.0" prefWidth="200.0">
<children>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<ImageView fitHeight="64" preserveRatio="true" cache="true">
<Image url="@../img/logo64.png"/>
</ImageView>
</StackPane>
<VBox HBox.hgrow="ALWAYS">
<Label text="${controller.message}" styleClass="label-large" wrapText="true">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="${controller.description}" wrapText="true"/>
<Button text="${controller.buttonText}" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#handleButtonAction" visible="${controller.buttonVisible}" managed="${controller.buttonVisible}"/>
</VBox>
</children>
</HBox>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.integrationsbase.NotificationWindowController"
prefHeight="400.0" prefWidth="600.0">
<children>
<Button layoutX="126" layoutY="90" onAction="#handleButtonAction" text="Click Me!" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
</children>
</AnchorPane>