Skip to content

Commit 5568025

Browse files
committed
implement sidebar integration for GNOME Nautilus
1 parent 7b72518 commit 5568025

File tree

5 files changed

+103
-1
lines changed

5 files changed

+103
-1
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
<!-- runtime dependencies -->
4242

43-
<api.version>1.3.1</api.version>
43+
<api.version>1.4.0-sidebar</api.version>
4444
<secret-service.version>2.0.1-alpha</secret-service.version>
4545
<kdewallet.version>1.4.0</kdewallet.version>
4646
<slf4j.version>2.0.13</slf4j.version>

src/main/java/module-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import org.cryptomator.integrations.sidebar.SidebarService;
12
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
23
import org.cryptomator.integrations.revealpath.RevealPathService;
34
import org.cryptomator.integrations.tray.TrayMenuController;
5+
import org.cryptomator.linux.sidebar.NautilusSidebarService;
46
import org.cryptomator.linux.keychain.KDEWalletKeychainAccess;
57
import org.cryptomator.linux.keychain.SecretServiceKeychainAccess;
68
import org.cryptomator.linux.revealpath.DBusSendRevealPathService;
@@ -17,6 +19,7 @@
1719
provides KeychainAccessProvider with SecretServiceKeychainAccess, KDEWalletKeychainAccess;
1820
provides RevealPathService with DBusSendRevealPathService;
1921
provides TrayMenuController with AppindicatorTrayMenuController;
22+
provides SidebarService with NautilusSidebarService;
2023

2124
opens org.cryptomator.linux.tray to org.cryptomator.integrations.api;
2225
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.cryptomator.linux.sidebar;
2+
3+
import org.cryptomator.integrations.sidebar.SidebarService;
4+
import org.cryptomator.integrations.sidebar.SidebarServiceException;
5+
6+
import java.io.IOException;
7+
import java.nio.charset.StandardCharsets;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.StandardCopyOption;
11+
import java.nio.file.StandardOpenOption;
12+
import java.util.concurrent.locks.Lock;
13+
import java.util.concurrent.locks.ReentrantReadWriteLock;
14+
15+
public class NautilusSidebarService implements SidebarService {
16+
17+
private static final int MAX_FILE_SIZE = 4096;
18+
private static final Path BOOKMARKS_FILE = Path.of(System.getProperty("user.home"), ".config/gtk-3.0/bookmarks");
19+
private static final Path TMP_FILE = BOOKMARKS_FILE.resolveSibling("bookmarks.cryptomator.tmp");
20+
private static final Lock BOOKMARKS_LOCK = new ReentrantReadWriteLock().writeLock();
21+
22+
@Override
23+
public SidebarEntry add(Path target, String displayName) throws SidebarServiceException {
24+
String entryLine = "file://" + target.toAbsolutePath() + " " + displayName;
25+
try {
26+
BOOKMARKS_LOCK.lock();
27+
if (Files.size(BOOKMARKS_FILE) > MAX_FILE_SIZE) {
28+
throw new IOException("File %s exceeds size of %d bytes".formatted(BOOKMARKS_FILE, MAX_FILE_SIZE));
29+
}
30+
//by reading all lines, we ensure that each line is terminated with EOL
31+
var entries = Files.readAllLines(BOOKMARKS_FILE, StandardCharsets.UTF_8);
32+
entries.add(entryLine);
33+
Files.write(TMP_FILE, entries, StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
34+
Files.move(TMP_FILE, BOOKMARKS_FILE, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
35+
return new NautilusSidebarEntry(entryLine);
36+
} catch (IOException e) {
37+
throw new SidebarServiceException("Adding entry to Nautilus bookmarks file failed.", e);
38+
} finally {
39+
BOOKMARKS_LOCK.unlock();
40+
}
41+
}
42+
43+
static class NautilusSidebarEntry implements SidebarEntry {
44+
45+
private final String line;
46+
private volatile boolean isRemoved = false;
47+
48+
NautilusSidebarEntry(String line) {
49+
this.line = line;
50+
}
51+
52+
@Override
53+
public void remove() throws SidebarServiceException {
54+
try {
55+
BOOKMARKS_LOCK.lock();
56+
if (isRemoved) {
57+
return;
58+
}
59+
if (Files.size(BOOKMARKS_FILE) > MAX_FILE_SIZE) {
60+
throw new IOException("File %s exceeds size of %d bytes".formatted(BOOKMARKS_FILE, MAX_FILE_SIZE));
61+
}
62+
var entries = Files.readAllLines(BOOKMARKS_FILE);
63+
if (entries.remove(line)) {
64+
Files.write(TMP_FILE, entries, StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
65+
Files.move(TMP_FILE, BOOKMARKS_FILE, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
66+
}
67+
isRemoved = true;
68+
} catch (IOException e) {
69+
throw new SidebarServiceException("Removing entry from Nautilus bookmarks faile failed", e);
70+
} finally {
71+
BOOKMARKS_LOCK.unlock();
72+
}
73+
}
74+
}
75+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.cryptomator.linux.sidebar.NautilusSidebarService
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.cryptomator.linux.filemanagersidebar;
2+
3+
import org.cryptomator.integrations.sidebar.SidebarServiceException;
4+
import org.cryptomator.linux.sidebar.NautilusSidebarService;
5+
import org.junit.jupiter.api.Disabled;
6+
import org.junit.jupiter.api.DisplayName;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.io.TempDir;
9+
10+
import java.nio.file.Path;
11+
import java.time.Duration;
12+
13+
public class NautilusSidebarServiceIT {
14+
15+
@Test
16+
@DisplayName("Adds for 20s an entryto the Nautilus sidebar")
17+
@Disabled
18+
public void testSidebarIntegration(@TempDir Path tmpdir) throws SidebarServiceException, InterruptedException {
19+
var entry = new NautilusSidebarService().add(tmpdir, "integrations-linux");
20+
Thread.sleep(Duration.ofSeconds(20));
21+
entry.remove();
22+
}
23+
}

0 commit comments

Comments
 (0)