Skip to content

Commit eef050b

Browse files
authored
Merge pull request #18 from purejava/libappindicator
appindicator support
2 parents e5928a0 + 78a3e42 commit eef050b

File tree

11 files changed

+185
-14
lines changed

11 files changed

+185
-14
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ jobs:
1010
- uses: actions/checkout@v3
1111
- uses: actions/setup-java@v3
1212
with:
13-
distribution: 'temurin'
14-
java-version: 19
13+
distribution: 'zulu'
14+
java-version: 20
1515
cache: 'maven'
1616
- name: Ensure to use tagged version
1717
if: startsWith(github.ref, 'refs/tags/')

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ jobs:
2020
fetch-depth: 2
2121
- uses: actions/setup-java@v3
2222
with:
23-
distribution: 'temurin'
24-
java-version: 19
23+
distribution: 'zulu'
24+
java-version: 20
2525
cache: 'maven'
2626
- name: Initialize CodeQL
2727
uses: github/codeql-action/init@v2

.github/workflows/publish-central.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ jobs:
1515
ref: "refs/tags/${{ github.event.inputs.tag }}"
1616
- uses: actions/setup-java@v3
1717
with:
18-
distribution: 'temurin'
19-
java-version: 19
18+
distribution: 'zulu'
19+
java-version: 20
2020
cache: 'maven'
2121
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
2222
server-username: MAVEN_USERNAME # env variable for username in deploy

.github/workflows/publish-github.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ jobs:
1010
- uses: actions/checkout@v3
1111
- uses: actions/setup-java@v3
1212
with:
13-
distribution: 'temurin'
14-
java-version: 19
13+
distribution: 'zulu'
14+
java-version: 20
1515
cache: 'maven'
1616
gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
1717
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pom.xml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@
3636

3737
<properties>
3838
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
39-
<project.jdk.version>19</project.jdk.version>
39+
<project.jdk.version>20</project.jdk.version>
4040

4141
<!-- runtime dependencies -->
4242

43-
<api.version>1.2.0</api.version>
43+
<api.version>1.3.0-beta1</api.version>
4444
<secret-service.version>1.8.1-jdk17</secret-service.version>
45-
<kdewallet.version>1.2.8</kdewallet.version>
45+
<kdewallet.version>1.3.0</kdewallet.version>
46+
<appindicator.version>1.3.0</appindicator.version>
4647
<guava.version>31.1-jre</guava.version>
4748
<slf4j.version>1.7.36</slf4j.version>
49+
<commons-lang3.version>3.12.0</commons-lang3.version>
4850

4951
<!-- test dependencies -->
5052
<junit.version>5.8.2</junit.version>
@@ -80,6 +82,19 @@
8082
<artifactId>kdewallet</artifactId>
8183
<version>${kdewallet.version}</version>
8284
</dependency>
85+
<!-- Apache Commons -->
86+
<dependency>
87+
<groupId>org.apache.commons</groupId>
88+
<artifactId>commons-lang3</artifactId>
89+
<version>${commons-lang3.version}</version>
90+
</dependency>
91+
<!-- Java bindings for appindicator -->
92+
<dependency>
93+
<groupId>org.purejava</groupId>
94+
<artifactId>appindicator-gtk3-java</artifactId>
95+
<classifier>libayatana-appindicator-libappindicator-minimal</classifier>
96+
<version>${appindicator.version}</version>
97+
</dependency>
8398
<dependency>
8499
<groupId>org.junit.jupiter</groupId>
85100
<artifactId>junit-jupiter</artifactId>
@@ -96,6 +111,9 @@
96111
<version>3.9.0</version>
97112
<configuration>
98113
<release>${project.jdk.version}</release>
114+
<compilerArgs>
115+
<arg>--enable-preview</arg>
116+
</compilerArgs>
99117
</configuration>
100118
</plugin>
101119
<plugin>
@@ -190,6 +208,7 @@
190208
<name>see</name>
191209
</tag>
192210
</tags>
211+
<additionalOptions>--enable-preview</additionalOptions>
193212
</configuration>
194213
</plugin>
195214
</plugins>

src/main/java/module-info.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
2+
import org.cryptomator.integrations.revealpath.RevealPathService;
3+
import org.cryptomator.integrations.tray.TrayMenuController;
4+
import org.cryptomator.linux.keychain.SecretServiceKeychainAccess;
5+
import org.cryptomator.linux.revealpath.DBusSendRevealPathService;
6+
import org.cryptomator.linux.tray.AppindicatorTrayMenuController;
7+
8+
module org.cryptomator.integrations.linux {
9+
requires org.cryptomator.integrations.api;
10+
requires org.slf4j;
11+
requires com.google.common;
12+
requires org.apache.commons.lang3;
13+
requires org.freedesktop.dbus;
14+
requires org.purejava.appindicator;
15+
requires org.purejava.kwallet;
16+
requires secret.service;
17+
18+
provides KeychainAccessProvider with SecretServiceKeychainAccess;
19+
provides RevealPathService with DBusSendRevealPathService;
20+
provides TrayMenuController with AppindicatorTrayMenuController;
21+
22+
opens org.cryptomator.linux.tray to org.cryptomator.integrations.api;
23+
}

src/main/java/org/cryptomator/linux/keychain/KDEWalletKeychainAccess.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import org.freedesktop.dbus.exceptions.DBusException;
1212
import org.freedesktop.dbus.exceptions.DBusExecutionException;
1313
import org.kde.KWallet;
14-
import org.kde.Static;
15-
import org.purejava.KDEWallet;
14+
import org.purejava.kwallet.KDEWallet;
15+
import org.purejava.kwallet.Static;
1616
import org.slf4j.Logger;
1717
import org.slf4j.LoggerFactory;
1818

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.cryptomator.linux.tray;
2+
3+
import org.cryptomator.integrations.tray.ActionItem;
4+
import org.purejava.appindicator.GCallback;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
record ActionItemCallback (ActionItem actionItem) implements GCallback {
9+
private static final Logger LOG = LoggerFactory.getLogger(ActionItemCallback.class);
10+
11+
@Override
12+
public void apply() {
13+
LOG.trace("Hit tray menu action '{}'", actionItem.title());
14+
actionItem.action().run();
15+
}
16+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.cryptomator.linux.tray;
2+
3+
import org.cryptomator.integrations.common.CheckAvailability;
4+
import org.cryptomator.integrations.common.OperatingSystem;
5+
import org.cryptomator.integrations.common.Priority;
6+
import org.cryptomator.integrations.tray.ActionItem;
7+
import org.cryptomator.integrations.tray.SeparatorItem;
8+
import org.cryptomator.integrations.tray.SubMenuItem;
9+
import org.cryptomator.integrations.tray.TrayIconLoader;
10+
import org.cryptomator.integrations.tray.TrayMenuController;
11+
import org.cryptomator.integrations.tray.TrayMenuException;
12+
import org.cryptomator.integrations.tray.TrayMenuItem;
13+
import org.purejava.appindicator.GCallback;
14+
import org.purejava.appindicator.MemoryAllocator;
15+
16+
import java.lang.foreign.Arena;
17+
import java.lang.foreign.MemorySegment;
18+
import java.lang.foreign.SegmentScope;
19+
import java.util.List;
20+
import java.util.function.Consumer;
21+
22+
import static org.purejava.appindicator.app_indicator_h.*;
23+
24+
@Priority(1000)
25+
@OperatingSystem(OperatingSystem.Value.LINUX)
26+
public class AppindicatorTrayMenuController implements TrayMenuController {
27+
28+
private static final String APP_INDICATOR_ID = "org.cryptomator.Cryptomator";
29+
30+
private static final SegmentScope SCOPE = SegmentScope.global();
31+
private MemorySegment indicator;
32+
private MemorySegment menu = gtk_menu_new();
33+
34+
@CheckAvailability
35+
public static boolean isAvailable() {
36+
return MemoryAllocator.isLoadedNativeLib();
37+
}
38+
39+
@Override
40+
public void showTrayIcon(Consumer<TrayIconLoader> iconLoader, Runnable runnable, String s) throws TrayMenuException {
41+
TrayIconLoader.FreedesktopIconName callback = this::showTrayIconWithSVG;
42+
iconLoader.accept(callback);
43+
gtk_widget_show_all(menu);
44+
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE());
45+
}
46+
47+
private void showTrayIconWithSVG(String s) {
48+
try (var arena = Arena.openConfined()) {
49+
indicator = app_indicator_new(arena.allocateUtf8String(APP_INDICATOR_ID),
50+
arena.allocateUtf8String(s),
51+
APP_INDICATOR_CATEGORY_APPLICATION_STATUS());
52+
}
53+
}
54+
55+
@Override
56+
public void updateTrayIcon(Consumer<TrayIconLoader> iconLoader) {
57+
TrayIconLoader.FreedesktopIconName callback = this::updateTrayIconWithSVG;
58+
iconLoader.accept(callback);
59+
}
60+
61+
private void updateTrayIconWithSVG(String s) {
62+
try (var arena = Arena.openConfined()) {
63+
app_indicator_set_icon(indicator, arena.allocateUtf8String(s));
64+
}
65+
}
66+
67+
@Override
68+
public void updateTrayMenu(List<TrayMenuItem> items) throws TrayMenuException {
69+
menu = gtk_menu_new();
70+
addChildren(menu, items);
71+
gtk_widget_show_all(menu);
72+
app_indicator_set_menu(indicator, menu);
73+
}
74+
75+
@Override
76+
public void onBeforeOpenMenu(Runnable runnable) {
77+
78+
}
79+
80+
private void addChildren(MemorySegment menu, List<TrayMenuItem> items) {
81+
for (var item : items) {
82+
switch (item) {
83+
case ActionItem a -> {
84+
var gtkMenuItem = gtk_menu_item_new();
85+
try (var arena = Arena.openConfined()) {
86+
gtk_menu_item_set_label(gtkMenuItem, arena.allocateUtf8String(a.title()));
87+
g_signal_connect_object(gtkMenuItem,
88+
arena.allocateUtf8String("activate"),
89+
GCallback.allocate(new ActionItemCallback(a), SCOPE),
90+
menu,
91+
0);
92+
}
93+
gtk_menu_shell_append(menu, gtkMenuItem);
94+
}
95+
case SeparatorItem separatorItem -> {
96+
var gtkSeparator = gtk_menu_item_new();
97+
gtk_menu_shell_append(menu, gtkSeparator);
98+
}
99+
case SubMenuItem s -> {
100+
var gtkMenuItem = gtk_menu_item_new();
101+
var gtkSubmenu = gtk_menu_new();
102+
try (var arena = Arena.openConfined()) {
103+
gtk_menu_item_set_label(gtkMenuItem, arena.allocateUtf8String(s.title()));
104+
}
105+
addChildren(gtkSubmenu, s.items());
106+
gtk_menu_item_set_submenu(gtkMenuItem, gtkSubmenu);
107+
gtk_menu_shell_append(menu, gtkMenuItem);
108+
}
109+
}
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)