mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-20 09:36:55 -04:00
Refactored "file open events" to "app launch events", fixes #55
This commit is contained in:
@@ -4,10 +4,10 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.SettingsProvider;
|
||||
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
@@ -23,8 +23,8 @@ class CryptomatorModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("fileOpenRequests")
|
||||
BlockingQueue<Path> provideFileOpenRequests() {
|
||||
@Named("launchEventQueue")
|
||||
BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
||||
return new ArrayBlockingQueue<>(10);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,13 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -26,11 +31,11 @@ import javax.inject.Singleton;
|
||||
class FileOpenRequestHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class);
|
||||
private final BlockingQueue<Path> fileOpenRequests;
|
||||
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||
|
||||
@Inject
|
||||
public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests) {
|
||||
this.fileOpenRequests = fileOpenRequests;
|
||||
public FileOpenRequestHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
||||
this.launchEventQueue = launchEventQueue;
|
||||
try {
|
||||
Desktop.getDesktop().setOpenFileHandler(this::openFiles);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
@@ -39,7 +44,9 @@ class FileOpenRequestHandler {
|
||||
}
|
||||
|
||||
private void openFiles(final OpenFilesEvent evt) {
|
||||
evt.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add);
|
||||
Stream<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath);
|
||||
AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
|
||||
tryToEnqueueFileOpenRequest(launchEvent);
|
||||
}
|
||||
|
||||
public void handleLaunchArgs(String[] args) {
|
||||
@@ -48,19 +55,22 @@ class FileOpenRequestHandler {
|
||||
|
||||
// visible for testing
|
||||
void handleLaunchArgs(FileSystem fs, String[] args) {
|
||||
for (String arg : args) {
|
||||
Stream<Path> pathsToOpen = Arrays.stream(args).map(str -> {
|
||||
try {
|
||||
Path path = fs.getPath(arg);
|
||||
tryToEnqueueFileOpenRequest(path);
|
||||
return fs.getPath(str);
|
||||
} catch (InvalidPathException e) {
|
||||
LOG.trace("{} not a valid path", arg);
|
||||
LOG.trace("Argument not a valid path: {}", str);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}).filter(Objects::nonNull);
|
||||
AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
|
||||
tryToEnqueueFileOpenRequest(launchEvent);
|
||||
}
|
||||
|
||||
private void tryToEnqueueFileOpenRequest(Path path) {
|
||||
if (!fileOpenRequests.offer(path)) {
|
||||
LOG.warn("{} could not be enqueued for opening.", path);
|
||||
|
||||
private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) {
|
||||
if (!launchEventQueue.offer(launchEvent)) {
|
||||
LOG.warn("Could not enqueue application launch event.", launchEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@@ -13,64 +19,55 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileOpenRequestHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testOpenArgsWithCorrectPaths() throws IOException {
|
||||
Path p1 = Mockito.mock(Path.class);
|
||||
Path p2 = Mockito.mock(Path.class);
|
||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
||||
FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
|
||||
BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
|
||||
Mockito.when(p1.getFileSystem()).thenReturn(fs);
|
||||
Mockito.when(p2.getFileSystem()).thenReturn(fs);
|
||||
Mockito.when(fs.provider()).thenReturn(provider);
|
||||
Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p1, p2);
|
||||
Mockito.when(provider.readAttributes(Mockito.any(), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
|
||||
private FileOpenRequestHandler inTest;
|
||||
private BlockingQueue<AppLaunchEvent> queue;
|
||||
|
||||
BlockingQueue<Path> queue = new ArrayBlockingQueue<>(10);
|
||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
||||
handler.handleLaunchArgs(fs, new String[] {"foo", "bar"});
|
||||
|
||||
Assertions.assertEquals(p1, queue.poll());
|
||||
Assertions.assertEquals(p2, queue.poll());
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
queue = new ArrayBlockingQueue<>(1);
|
||||
inTest = new FileOpenRequestHandler(queue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("./cryptomator.exe foo bar")
|
||||
public void testOpenArgsWithCorrectPaths() throws IOException {
|
||||
inTest.handleLaunchArgs(new String[]{"foo", "bar"});
|
||||
|
||||
AppLaunchEvent evt = queue.poll();
|
||||
Assertions.assertNotNull(evt);
|
||||
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
||||
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)")
|
||||
public void testOpenArgsWithIncorrectPaths() throws IOException {
|
||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
||||
Mockito.when(fs.getPath(Mockito.anyString())).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
||||
Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
||||
inTest.handleLaunchArgs(fs, new String[]{"foo"});
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
|
||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
||||
handler.handleLaunchArgs(fs, new String[] {"foo"});
|
||||
|
||||
Mockito.verifyNoMoreInteractions(queue);
|
||||
AppLaunchEvent evt = queue.poll();
|
||||
Assertions.assertNotNull(evt);
|
||||
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
||||
Assertions.assertTrue(paths.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("./cryptomator.exe foo (with full event queue)")
|
||||
public void testOpenArgsWithFullQueue() throws IOException {
|
||||
Path p = Mockito.mock(Path.class);
|
||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
||||
FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
|
||||
BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
|
||||
Mockito.when(p.getFileSystem()).thenReturn(fs);
|
||||
Mockito.when(fs.provider()).thenReturn(provider);
|
||||
Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p);
|
||||
Mockito.when(provider.readAttributes(Mockito.eq(p), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
|
||||
Mockito.when(attrs.isRegularFile()).thenReturn(true);
|
||||
queue.add(new AppLaunchEvent(Stream.empty()));
|
||||
Assumptions.assumeTrue(queue.remainingCapacity() == 0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
|
||||
Mockito.when(queue.offer(Mockito.any())).thenReturn(false);
|
||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
||||
handler.handleLaunchArgs(fs, new String[] {"foo"});
|
||||
inTest.handleLaunchArgs(new String[]{"foo"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.ui.ExitUtil;
|
||||
import org.cryptomator.ui.controls.DirectoryListCell;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||
import org.cryptomator.ui.model.AutoUnlocker;
|
||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
||||
import org.cryptomator.ui.model.UpgradeStrategy;
|
||||
@@ -93,7 +94,7 @@ public class MainController implements ViewController {
|
||||
private final ExitUtil exitUtil;
|
||||
private final Localization localization;
|
||||
private final ExecutorService executorService;
|
||||
private final BlockingQueue<Path> fileOpenRequests;
|
||||
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||
private final VaultFactory vaultFactoy;
|
||||
private final ViewControllerLoader viewControllerLoader;
|
||||
private final ObjectProperty<ViewController> activeController = new SimpleObjectProperty<>();
|
||||
@@ -110,11 +111,11 @@ public class MainController implements ViewController {
|
||||
private Subscription subs = Subscription.EMPTY;
|
||||
|
||||
@Inject
|
||||
public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests, ExitUtil exitUtil, Localization localization,
|
||||
public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExitUtil exitUtil, Localization localization,
|
||||
VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.executorService = executorService;
|
||||
this.fileOpenRequests = fileOpenRequests;
|
||||
this.launchEventQueue = launchEventQueue;
|
||||
this.exitUtil = exitUtil;
|
||||
this.localization = localization;
|
||||
this.vaultFactoy = vaultFactoy;
|
||||
@@ -249,12 +250,12 @@ public class MainController implements ViewController {
|
||||
}
|
||||
|
||||
private void listenToFileOpenRequests(Stage stage) {
|
||||
Tasks.create(fileOpenRequests::take).onSuccess(path -> {
|
||||
addVault(path, true);
|
||||
Tasks.create(launchEventQueue::take).onSuccess(event -> {
|
||||
stage.setIconified(false);
|
||||
stage.show();
|
||||
stage.toFront();
|
||||
stage.requestFocus();
|
||||
event.getPathsToOpen().forEach(path -> addVault(path, true));
|
||||
}).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AppLaunchEvent {
|
||||
|
||||
private final Stream<Path> pathsToOpen;
|
||||
|
||||
public AppLaunchEvent(Stream<Path> pathsToOpen) {this.pathsToOpen = pathsToOpen;}
|
||||
|
||||
public Stream<Path> getPathsToOpen() {
|
||||
return pathsToOpen;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user