mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 00:31:27 +00:00
Feature: Replace JavaFX host services with RevealPathService impls (#4165)
when a OS path need to be shown in the OS file browser.
This commit is contained in:
@@ -13,6 +13,7 @@ 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.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
@@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||
import org.cryptomator.ui.fxapp.JfxRevealPathService;
|
||||
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
@@ -64,6 +66,7 @@ open module org.cryptomator.desktop {
|
||||
uses org.cryptomator.event.NotificationHandler;
|
||||
|
||||
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
||||
provides RevealPathService with JfxRevealPathService;
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||
|
||||
@@ -22,8 +22,6 @@ import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
@@ -76,8 +74,8 @@ public abstract class CommonsModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<RevealPathService> provideRevealPathService() {
|
||||
return RevealPathService.get().findFirst();
|
||||
static RevealPathService provideRevealPathService() {
|
||||
return RevealPathService.get().findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.mount.Mountpoint;
|
||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.HostServices;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
@@ -28,12 +27,12 @@ public class VaultService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
||||
|
||||
private final Lazy<Application> application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final ExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
public VaultService(Lazy<Application> application, ExecutorService executorService) {
|
||||
this.application = application;
|
||||
public VaultService(RevealPathService revealPathService, ExecutorService executorService) {
|
||||
this.revealPathService = revealPathService;
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
@@ -47,9 +46,9 @@ public class VaultService {
|
||||
* @param vault The vault to reveal
|
||||
*/
|
||||
public Task<Vault> createRevealTask(Vault vault) {
|
||||
Task<Vault> task = new RevealVaultTask(vault, application.get().getHostServices());
|
||||
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
|
||||
Task<Vault> task = new RevealVaultTask(vault, revealPathService);
|
||||
task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException()));
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -110,19 +109,18 @@ public class VaultService {
|
||||
private static class RevealVaultTask extends Task<Vault> {
|
||||
|
||||
private final Vault vault;
|
||||
private final HostServices hostServices;
|
||||
private final RevealPathService rs;
|
||||
|
||||
public RevealVaultTask(Vault vault, HostServices hostServices) {
|
||||
public RevealVaultTask(Vault vault, RevealPathService revealPathService) {
|
||||
this.vault = vault;
|
||||
this.hostServices = hostServices;
|
||||
setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException()));
|
||||
this.rs = revealPathService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vault call() {
|
||||
protected Vault call() throws RevealFailedException {
|
||||
switch (vault.getMountPoint()) {
|
||||
case null -> LOG.warn("Not currently mounted");
|
||||
case Mountpoint.WithPath m -> hostServices.showDocument(m.uri().toString());
|
||||
case Mountpoint.WithPath m -> rs.reveal(m.path());
|
||||
case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI?
|
||||
}
|
||||
return vault;
|
||||
|
||||
@@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.common.Nullable;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.cryptofs.CryptoPath;
|
||||
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
@@ -46,7 +45,6 @@ import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -57,7 +55,6 @@ public class EventListCellController implements FxController {
|
||||
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||
|
||||
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||
@Nullable
|
||||
private final RevealPathService revealService;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
|
||||
@@ -82,15 +79,17 @@ public class EventListCellController implements FxController {
|
||||
Button eventActionsButton;
|
||||
|
||||
@Inject
|
||||
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
|
||||
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator,
|
||||
RevealPathService revealService,
|
||||
ResourceBundle resourceBundle) {
|
||||
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||
this.revealService = revealService.orElseGet(() -> null);
|
||||
this.revealService = revealService;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.eventEntry = new SimpleObjectProperty<>(null);
|
||||
this.eventMessage = new SimpleStringProperty();
|
||||
this.eventDescription = new SimpleStringProperty();
|
||||
this.eventIcon = new SimpleObjectProperty<>();
|
||||
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
|
||||
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1 ? "" : "(" + e.getValue().count() + ")", "");
|
||||
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
|
||||
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||
@@ -136,13 +135,8 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
|
||||
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
||||
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||
|
||||
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||
var user = userAndDevice[0];
|
||||
@@ -156,11 +150,7 @@ public class EventListCellController implements FxController {
|
||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||
}
|
||||
|
||||
@@ -168,46 +158,29 @@ public class EventListCellController implements FxController {
|
||||
eventIcon.setValue(FontAwesome5Icon.CHECK);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
}
|
||||
|
||||
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
|
||||
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
}
|
||||
|
||||
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
|
||||
eventIcon.setValue(FontAwesome5Icon.BAN);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
}
|
||||
|
||||
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
|
||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
}
|
||||
|
||||
private void addLocalizedAction(String localizationKey, Runnable action) {
|
||||
@@ -270,7 +243,7 @@ public class EventListCellController implements FxController {
|
||||
}
|
||||
|
||||
var mountPoint = v.getMountPoint().uri().getPath();
|
||||
if(SystemUtils.IS_OS_WINDOWS) {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
|
||||
}
|
||||
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
||||
|
||||
@@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplication {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||
|
||||
static final AtomicReference<Application> INSTANCE = new AtomicReference<>();
|
||||
|
||||
private final long startupTime;
|
||||
private final Environment environment;
|
||||
private final Settings settings;
|
||||
@@ -33,7 +37,8 @@ public class FxApplication {
|
||||
private final FxNotificationManager notificationManager;
|
||||
|
||||
@Inject
|
||||
FxApplication(@Named("startupTime") long startupTime, //
|
||||
FxApplication(Application fxApp,
|
||||
@Named("startupTime") long startupTime, //
|
||||
Environment environment, //
|
||||
Settings settings, //
|
||||
AppLaunchEventHandler launchEventHandler, //
|
||||
@@ -55,6 +60,8 @@ public class FxApplication {
|
||||
this.autoUnlocker = autoUnlocker;
|
||||
this.fxFSEventList = fxFSEventList;
|
||||
this.notificationManager = notificationManager;
|
||||
|
||||
INSTANCE.set(fxApp);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.integrations.common.DisplayName;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
import org.cryptomator.integrations.common.Priority;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* A {@link RevealPathService} service implementation using the JavaFX {@link javafx.application.HostServices#showDocument(String)} to reveal documents.
|
||||
* <p>
|
||||
* Internally the HostServices class uses GTK on Linux.
|
||||
*
|
||||
* @implNote {@link #reveal(Path)} only succeeds when the class {@link FxApplication} is initialized.
|
||||
*/
|
||||
@DisplayName("JavaFX HostServices (GTK)")
|
||||
@OperatingSystem(OperatingSystem.Value.LINUX)
|
||||
@Priority(10)
|
||||
public class JfxRevealPathService implements RevealPathService {
|
||||
|
||||
@Override
|
||||
public void reveal(Path p) throws RevealFailedException {
|
||||
var fxApp = FxApplication.INSTANCE.get();
|
||||
if (fxApp != null) {
|
||||
fxApp.getHostServices().showDocument(p.toUri().toString());
|
||||
} else {
|
||||
throw new RevealFailedException("JavaFX Application not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ import com.google.common.base.Throwables;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
@@ -25,6 +28,7 @@ import java.util.stream.Collectors;
|
||||
@HealthCheckScoped
|
||||
public class ReportWriter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
|
||||
private static final String REPORT_HEADER = """
|
||||
*******************************************
|
||||
* Cryptomator Vault Health Report *
|
||||
@@ -43,14 +47,14 @@ public class ReportWriter {
|
||||
|
||||
private final Vault vault;
|
||||
private final VaultConfig vaultConfig;
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Path exportDestination;
|
||||
|
||||
@Inject
|
||||
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, Application application, Environment env) {
|
||||
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, RevealPathService revealPathService, Environment env) {
|
||||
this.vault = vault;
|
||||
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
|
||||
this.application = application;
|
||||
this.revealPathService = revealPathService;
|
||||
this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log");
|
||||
}
|
||||
|
||||
@@ -92,7 +96,11 @@ public class ReportWriter {
|
||||
}
|
||||
|
||||
private void reveal() {
|
||||
application.getHostServices().showDocument(exportDestination.getParent().toUri().toString());
|
||||
try {
|
||||
revealPathService.reveal(exportDestination.getParent());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal export destination location of report", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.AutoAnimator;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@@ -19,10 +22,12 @@ import javafx.fxml.FXML;
|
||||
@MainWindowScoped
|
||||
public class VaultDetailController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class);
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final Application application;
|
||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||
private final BooleanBinding anyVaultSelected;
|
||||
private final RevealPathService revealPathService;
|
||||
|
||||
private AutoAnimator spinAnimation;
|
||||
|
||||
@@ -31,11 +36,11 @@ public class VaultDetailController implements FxController {
|
||||
|
||||
|
||||
@Inject
|
||||
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
|
||||
VaultDetailController(ObjectProperty<Vault> vault, RevealPathService revealPathService) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
||||
this.anyVaultSelected = vault.isNotNull();
|
||||
this.revealPathService = revealPathService;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -61,7 +66,11 @@ public class VaultDetailController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void revealStorageLocation() {
|
||||
application.getHostServices().showDocument(vault.get().getPath().toUri().toString());
|
||||
try {
|
||||
revealPathService.reveal(vault.get().getPath());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal vault storage location", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Observable Properties */
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@@ -31,7 +30,6 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.stage.FileChooser;
|
||||
@@ -40,12 +38,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -60,7 +54,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
private final VaultService vaultService;
|
||||
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
||||
private final Stage mainWindow;
|
||||
private final Optional<RevealPathService> revealPathService;
|
||||
private final RevealPathService revealPathService;
|
||||
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||
@@ -84,7 +78,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
VaultStatisticsComponent.Builder vaultStatsBuilder, //
|
||||
WrongFileAlertComponent.Builder wrongFileAlert, //
|
||||
@MainWindow Stage mainWindow, //
|
||||
Optional<RevealPathService> revealPathService, //
|
||||
RevealPathService revealPathService, //
|
||||
DecryptNameComponent.Factory decryptNameWindowFactory, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.vault = vault;
|
||||
@@ -111,7 +105,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
|
||||
public void initialize() {
|
||||
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
|
||||
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths));
|
||||
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealPaths));
|
||||
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
|
||||
|
||||
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
|
||||
@@ -156,7 +150,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
if (cleartextFile != null) {
|
||||
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
|
||||
if (ciphertextPath != null) {
|
||||
revealOrCopyPaths(List.of(ciphertextPath));
|
||||
revealPaths(List.of(ciphertextPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,34 +182,18 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void revealOrCopyPaths(List<Path> paths) {
|
||||
revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> {
|
||||
LOG.warn("No service provider to reveal files found.");
|
||||
copyPathsToClipboard(paths);
|
||||
});
|
||||
}
|
||||
|
||||
private void revealPaths(RevealPathService service, List<Path> paths) {
|
||||
private void revealPaths(List<Path> paths) {
|
||||
paths.forEach(path -> {
|
||||
try {
|
||||
LOG.debug("Revealing {}", path);
|
||||
service.reveal(path);
|
||||
revealPathService.reveal(path);
|
||||
} catch (RevealFailedException e) {
|
||||
//TODO: show popup in ui
|
||||
LOG.error("Revealing ciphertext file failed.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void copyPathsToClipboard(List<Path> paths) {
|
||||
StringBuilder clipboardString = new StringBuilder();
|
||||
paths.forEach(p -> clipboardString.append(p.toString()).append("\n"));
|
||||
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, clipboardString.toString()));
|
||||
ciphertextPathsCopied.setValue(true);
|
||||
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
|
||||
ciphertextPathsCopied.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
private VaultStatisticsComponent buildVaultStats(Vault vault) {
|
||||
return vaultStatsBuilder.vault(vault).build();
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import org.cryptomator.integrations.common.NamedServiceProvider;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -41,7 +42,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
private final Settings settings;
|
||||
private final Optional<AutoStartProvider> autoStartProvider;
|
||||
private final List<QuickAccessService> quickAccessServices;
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Environment environment;
|
||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||
private final KeychainManager keychain;
|
||||
@@ -60,9 +61,15 @@ public class GeneralPreferencesController implements FxController {
|
||||
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
|
||||
|
||||
@Inject
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, //
|
||||
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
|
||||
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, //
|
||||
Settings settings, //
|
||||
Optional<AutoStartProvider> autoStartProvider, //
|
||||
List<KeychainAccessProvider> keychainAccessProviders, //
|
||||
KeychainManager keychain, //
|
||||
RevealPathService revealPathService, //
|
||||
Environment environment, //
|
||||
FxApplicationWindows appWindows, //
|
||||
ExecutorService backgroundExecutor) {
|
||||
this.window = window;
|
||||
this.settings = settings;
|
||||
this.autoStartProvider = autoStartProvider;
|
||||
@@ -70,7 +77,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
this.keychain = keychain;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.quickAccessServices = QuickAccessService.get().toList();
|
||||
this.application = application;
|
||||
this.revealPathService = revealPathService;
|
||||
this.environment = environment;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
@@ -148,7 +155,11 @@ public class GeneralPreferencesController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void showLogfileDirectory() {
|
||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||
try {
|
||||
revealPathService.reveal(environment.getLogDir().orElseThrow());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal log files directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper classes */
|
||||
@@ -196,4 +207,5 @@ public class GeneralPreferencesController implements FxController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.integrations.update.UpdateStep;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.updater.UpdateChecker;
|
||||
import org.cryptomator.updater.FallbackUpdateInfo;
|
||||
import org.cryptomator.updater.UpdateChecker;
|
||||
import org.cryptomator.updater.UpdateService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -49,7 +50,7 @@ public class UpdatesPreferencesController implements FxController {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
||||
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Environment environment;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Settings settings;
|
||||
@@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController {
|
||||
public CheckBox checkForUpdatesCheckbox;
|
||||
|
||||
@Inject
|
||||
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
||||
this.application = application;
|
||||
UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
||||
this.revealPathService = revealPathService;
|
||||
this.environment = environment;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.settings = settings;
|
||||
@@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController {
|
||||
updateService.setOnFailed(this::updateFailed);
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
public void showLogfileDirectory() {
|
||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||
try {
|
||||
revealPathService.reveal(environment.getLogDir().orElseThrow());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal log files directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -696,27 +696,19 @@ eventView.cell.actionsButton.tooltip=Event actions
|
||||
eventView.entry.vaultLocked.description=Unlock "%s" for details
|
||||
eventView.entry.conflictResolved.message=Resolved conflict
|
||||
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
|
||||
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.conflict.message=Conflict resolution failed
|
||||
eventView.entry.conflict.showDecrypted=Show decrypted, original file
|
||||
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
|
||||
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
|
||||
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
|
||||
eventView.entry.decryptionFailed.message=Decryption failed
|
||||
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
|
||||
eventView.entry.brokenDirFile.message=Broken directory link
|
||||
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
|
||||
eventView.entry.brokenFileNode.message=Broken filesystem node
|
||||
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.inUse.message=File in use
|
||||
eventView.entry.inUse.showDecrypted=Show decrypted file
|
||||
eventView.entry.inUse.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.inUse.showEncrypted=Show encrypted file
|
||||
eventView.entry.inUse.copyEncrypted=Copy encrypted path
|
||||
eventView.entry.inUse.copyUserAndDevice=Copy locking user and device name
|
||||
eventView.entry.inUse.ignoreLock=Ignore use status
|
||||
|
||||
|
||||
Reference in New Issue
Block a user