Skip to content

Commit de95ce2

Browse files
authored
Merge pull request #80 from cryptomator/feature/freedesktop-autostart
Feature: Autostart for DE following the freedesktop standard
2 parents 08a438f + 2c0f834 commit de95ce2

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ Linux-specific implemenations of the [integrations-api](https://github.com/crypt
55

66
This project uses the following JVM properties:
77
* `cryptomator.integrationsLinux.trayIconsDir` - specifies the directory from which svg images for the tray icon are loaded
8-
8+
* `cryptomator.integrationsLinux.autoStartCmd` - specifies the command used for starting Cryptomator

src/main/java/module-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import org.cryptomator.integrations.autostart.AutoStartProvider;
12
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
23
import org.cryptomator.integrations.quickaccess.QuickAccessService;
34
import org.cryptomator.integrations.revealpath.RevealPathService;
45
import org.cryptomator.integrations.tray.TrayMenuController;
6+
import org.cryptomator.linux.autostart.FreedesktopAutoStartService;
57
import org.cryptomator.linux.keychain.KDEWalletKeychainAccess;
68
import org.cryptomator.linux.keychain.SecretServiceKeychainAccess;
79
import org.cryptomator.linux.quickaccess.NautilusBookmarks;
@@ -16,6 +18,7 @@
1618
requires org.purejava.kwallet;
1719
requires de.swiesend.secretservice;
1820

21+
provides AutoStartProvider with FreedesktopAutoStartService;
1922
provides KeychainAccessProvider with SecretServiceKeychainAccess, KDEWalletKeychainAccess;
2023
provides RevealPathService with DBusSendRevealPathService;
2124
provides TrayMenuController with AppindicatorTrayMenuController;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.cryptomator.linux.autostart;
2+
3+
import org.cryptomator.integrations.autostart.AutoStartProvider;
4+
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
5+
import org.cryptomator.integrations.common.CheckAvailability;
6+
import org.cryptomator.integrations.common.OperatingSystem;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import java.io.IOException;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.nio.file.StandardOpenOption;
14+
import java.util.Objects;
15+
16+
/**
17+
* Enables autostart for Linux desktop environments following the freedesktop standard.
18+
* <p>
19+
* This service is based on <a href=https://specifications.freedesktop.org/autostart-spec/autostart-spec-0.5.html>version 0.5 of the freedesktop autostart-spec</a>.
20+
*/
21+
@CheckAvailability
22+
@OperatingSystem(OperatingSystem.Value.LINUX)
23+
public class FreedesktopAutoStartService implements AutoStartProvider {
24+
25+
private static final Logger LOG = LoggerFactory.getLogger(FreedesktopAutoStartService.class);
26+
private static final String CMD_PROPERTY = "cryptomator.integrationsLinux.autoStartCmd";
27+
private static final String AUTOSTART_FILENAME = "Cryptomator.desktop";
28+
private static final String CONTENT_TEMPLATE = """
29+
[Desktop Entry]
30+
Type=Application
31+
Exec=%s
32+
Hidden=false
33+
NoDisplay=false
34+
X-GNOME-Autostart-enabled=true
35+
Name=Cryptomator
36+
Comment=Created with %s
37+
""";
38+
39+
private final Path autostartFile;
40+
private final String content;
41+
private final boolean hasExecValue;
42+
43+
public FreedesktopAutoStartService() {
44+
var xdgConfigDirString = Objects.requireNonNullElse(System.getenv("XDG_CONFIG_HOME"), System.getProperty("user.home") + "/.config");
45+
this.autostartFile = Path.of(xdgConfigDirString, "autostart", AUTOSTART_FILENAME);
46+
47+
var execValue = System.getProperty(CMD_PROPERTY);
48+
if (execValue == null) {
49+
LOG.debug("JVM property {} not set, using command path", CMD_PROPERTY);
50+
execValue = ProcessHandle.current().info().command().orElse("");
51+
}
52+
this.hasExecValue = execValue.isBlank();
53+
this.content = CONTENT_TEMPLATE.formatted(execValue, this.getClass().getName());
54+
}
55+
56+
@Override
57+
public synchronized void enable() throws ToggleAutoStartFailedException {
58+
try {
59+
Files.writeString(autostartFile, content, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
60+
} catch (IOException e) {
61+
throw new ToggleAutoStartFailedException("Failed to activate Cryptomator autostart for GNOME desktop environment.", e);
62+
}
63+
}
64+
65+
@Override
66+
public synchronized void disable() throws ToggleAutoStartFailedException {
67+
try {
68+
Files.deleteIfExists(autostartFile);
69+
} catch (IOException e) {
70+
throw new ToggleAutoStartFailedException("Failed to deactivate Cryptomator autostart for GNOME desktop environment.", e);
71+
}
72+
}
73+
74+
@Override
75+
public synchronized boolean isEnabled() {
76+
return Files.exists(autostartFile);
77+
}
78+
79+
@CheckAvailability
80+
public boolean isSupported() {
81+
//TODO: might need to research which Desktop Environments support this
82+
return hasExecValue && Files.exists(autostartFile.getParent());
83+
}
84+
85+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.cryptomator.linux.autostart.FreedesktopAutoStartService
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.cryptomator.linux.autostart;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.MethodOrderer;
5+
import org.junit.jupiter.api.Order;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.TestMethodOrder;
8+
9+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
10+
public class FreedesktopAutoStartIT {
11+
12+
FreedesktopAutoStartService inTest = new FreedesktopAutoStartService();
13+
14+
@Test
15+
@Order(1)
16+
public void testAutostartEnable() {
17+
Assertions.assertDoesNotThrow(() -> inTest.enable());
18+
Assertions.assertTrue(inTest.isEnabled());
19+
}
20+
21+
22+
@Test
23+
@Order(2)
24+
public void testAutostartDisable() {
25+
Assertions.assertDoesNotThrow(() -> inTest.disable());
26+
Assertions.assertFalse(inTest.isEnabled());
27+
}
28+
}

0 commit comments

Comments
 (0)