mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 00:31:27 +00:00
Add AppindicatorTrayMenuController
This commit is contained in:
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,libappindicator.gtk3.java.minimal" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,libappindicator.gtk3.java.minimal" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
10
pom.xml
10
pom.xml
@@ -34,7 +34,7 @@
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.6.2</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.version>1.3.0-SNAPSHOT</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
|
||||
@@ -55,6 +55,7 @@
|
||||
<slf4j.version>2.0.6</slf4j.version>
|
||||
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||
<appindicator.version>1.1.0</appindicator.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.9.2</junit.jupiter.version>
|
||||
@@ -250,6 +251,13 @@
|
||||
<version>${jetbrains.annotations.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Java bindings for libappindicator -->
|
||||
<dependency>
|
||||
<groupId>org.purejava</groupId>
|
||||
<artifactId>libappindicator-gtk3-java-minimal</artifactId>
|
||||
<version>${appindicator.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ch.qos.logback.classic.spi.Configurator;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
import org.cryptomator.ui.traymenu.AppindicatorTrayMenuController;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
open module org.cryptomator.desktop {
|
||||
@@ -31,12 +32,13 @@ open module org.cryptomator.desktop {
|
||||
requires com.tobiasdiez.easybind;
|
||||
requires dagger;
|
||||
requires io.github.coffeelibs.tinyoauth2client;
|
||||
requires libappindicator.gtk3.java.minimal;
|
||||
requires org.slf4j;
|
||||
requires org.apache.commons.lang3;
|
||||
|
||||
/* TODO: filename-based modules: */
|
||||
requires static javax.inject; /* ugly dagger/guava crap */
|
||||
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides TrayMenuController with AwtTrayMenuController, AppindicatorTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.cryptomator.ui.traymenu;
|
||||
|
||||
import org.cryptomator.integrations.tray.ActionItem;
|
||||
import org.purejava.linux.GCallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ActionItemCallback implements GCallback {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActionItemCallback.class);
|
||||
private ActionItem actionItem;
|
||||
|
||||
public ActionItemCallback(ActionItem actionItem) {
|
||||
this.actionItem = actionItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
LOG.debug("Hit tray menu action '{}'", actionItem.title());
|
||||
actionItem.action().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.cryptomator.ui.traymenu;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.tray.ActionItem;
|
||||
import org.cryptomator.integrations.tray.SeparatorItem;
|
||||
import org.cryptomator.integrations.tray.SubMenuItem;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.integrations.tray.TrayMenuException;
|
||||
import org.cryptomator.integrations.tray.TrayMenuItem;
|
||||
import org.purejava.linux.MemoryAllocator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.foreign.MemoryAddress;
|
||||
import java.lang.foreign.MemorySession;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
import static org.purejava.linux.app_indicator_h.*;
|
||||
|
||||
@CheckAvailability
|
||||
public class AppindicatorTrayMenuController implements TrayMenuController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppindicatorTrayMenuController.class);
|
||||
|
||||
private final MemorySession session = MemorySession.openShared();
|
||||
private MemoryAddress indicator;
|
||||
private MemoryAddress menu = gtk_menu_new();
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isAvailable() {
|
||||
return SystemUtils.IS_OS_LINUX && MemoryAllocator.isLoadedNativeLib();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showTrayIcon(URI uri, Runnable runnable, String s) throws TrayMenuException {
|
||||
indicator = app_indicator_new(MemoryAllocator.ALLOCATE_FOR("org.cryptomator.Cryptomator"),
|
||||
MemoryAllocator.ALLOCATE_FOR(getAbsolutePath(getPathString(uri))),
|
||||
APP_INDICATOR_CATEGORY_APPLICATION_STATUS());
|
||||
gtk_widget_show_all(menu);
|
||||
app_indicator_set_menu(indicator, menu);
|
||||
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTrayIcon(URI uri) {
|
||||
app_indicator_set_icon(indicator, MemoryAllocator.ALLOCATE_FOR(getAbsolutePath(getPathString(uri))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTrayMenu(List<TrayMenuItem> items) throws TrayMenuException {
|
||||
menu = gtk_menu_new();
|
||||
addChildren(menu, items);
|
||||
gtk_widget_show_all(menu);
|
||||
app_indicator_set_menu(indicator, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeOpenMenu(Runnable runnable) {
|
||||
|
||||
}
|
||||
|
||||
private void addChildren(MemoryAddress menu, List<TrayMenuItem> items) {
|
||||
for (var item : items) {
|
||||
// TODO: use Pattern Matching for switch, once available
|
||||
if (item instanceof ActionItem a) {
|
||||
var gtkMenuItem = gtk_menu_item_new();
|
||||
gtk_menu_item_set_label(gtkMenuItem, MemoryAllocator.ALLOCATE_FOR(a.title()));
|
||||
g_signal_connect_object(gtkMenuItem,
|
||||
MemoryAllocator.ALLOCATE_FOR("activate"),
|
||||
MemoryAllocator.ALLOCATE_CALLBACK_FOR(new ActionItemCallback(a), session),
|
||||
menu,
|
||||
0);
|
||||
gtk_menu_shell_append(menu, gtkMenuItem);
|
||||
} else if (item instanceof SeparatorItem) {
|
||||
var gtkSeparator = gtk_menu_item_new();
|
||||
gtk_menu_shell_append(menu, gtkSeparator);
|
||||
} else if (item instanceof SubMenuItem s) {
|
||||
var gtkMenuItem = gtk_menu_item_new();
|
||||
var gtkSubmenu = gtk_menu_new();
|
||||
gtk_menu_item_set_label(gtkMenuItem, MemoryAllocator.ALLOCATE_FOR(s.title()));
|
||||
addChildren(gtkSubmenu, s.items());
|
||||
gtk_menu_item_set_submenu(gtkMenuItem, gtkSubmenu);
|
||||
gtk_menu_shell_append(menu, gtkMenuItem);
|
||||
}
|
||||
gtk_widget_show_all(menu);
|
||||
}
|
||||
}
|
||||
private String getAbsolutePath(String iconName) {
|
||||
var res = getClass().getClassLoader().getResource(iconName);
|
||||
if (null == res) {
|
||||
throw new IllegalArgumentException("Icon '" + iconName + "' cannot be found in resource folder");
|
||||
}
|
||||
File file = null;
|
||||
try {
|
||||
file = Paths.get(res.toURI()).toFile();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("Icon '" + iconName + "' cannot be converted to file", e);
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
private String getPathString(URI uri) {
|
||||
return uri.getPath().substring(1);
|
||||
}
|
||||
}
|
||||
@@ -22,25 +22,32 @@ import java.awt.Toolkit;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URI;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Responsible to manage the tray icon on macOS and Windows using AWT.
|
||||
* For Linux, we use {@link AppindicatorTrayMenuController}
|
||||
*/
|
||||
@CheckAvailability
|
||||
@Priority(Priority.FALLBACK)
|
||||
public class AwtTrayMenuController implements TrayMenuController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AwtTrayMenuController.class);
|
||||
|
||||
private static final String DATA_URI_SCHEME = "data:image/png;base64,";
|
||||
private final PopupMenu menu = new PopupMenu();
|
||||
private TrayIcon trayIcon;
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isAvailable() {
|
||||
return SystemTray.isSupported();
|
||||
return !SystemUtils.IS_OS_LINUX && SystemTray.isSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showTrayIcon(byte[] imageData, Runnable defaultAction, String tooltip) throws TrayMenuException {
|
||||
var image = Toolkit.getDefaultToolkit().createImage(imageData);
|
||||
public void showTrayIcon(URI uri, Runnable defaultAction, String tooltip) throws TrayMenuException {
|
||||
var image = Toolkit.getDefaultToolkit().createImage(getImageBytes(uri));
|
||||
trayIcon = new TrayIcon(image, tooltip, menu);
|
||||
|
||||
trayIcon.setImageAutoSize(true);
|
||||
@@ -57,11 +64,8 @@ public class AwtTrayMenuController implements TrayMenuController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTrayIcon(byte[] imageData) {
|
||||
if (trayIcon == null) {
|
||||
throw new IllegalStateException("Failed to update the icon as it has not yet been added");
|
||||
}
|
||||
var image = Toolkit.getDefaultToolkit().createImage(imageData);
|
||||
public void updateTrayIcon(URI uri) {
|
||||
var image = Toolkit.getDefaultToolkit().createImage(getImageBytes(uri));
|
||||
trayIcon.setImage(image);
|
||||
}
|
||||
|
||||
@@ -100,4 +104,8 @@ public class AwtTrayMenuController implements TrayMenuController {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getImageBytes(URI uri) {
|
||||
var data = uri.toString().split(DATA_URI_SCHEME)[1];
|
||||
return Base64.getDecoder().decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -36,6 +38,10 @@ public class TrayMenuBuilder {
|
||||
private static final String TRAY_ICON_UNLOCKED_MAC = "/img/tray_icon_unlocked_mac@2x.png";
|
||||
private static final String TRAY_ICON = "/img/tray_icon.png";
|
||||
private static final String TRAY_ICON_UNLOCKED = "/img/tray_icon_unlocked.png";
|
||||
private static final String TRAY_ICON_SVG = "tray_icon.svg";
|
||||
private static final String TRAY_ICON_UNLOCKED_SVG = "tray_icon_unlocked.svg";
|
||||
private static final String DATA_URI_SCHEME = "data:image/png;base64,";
|
||||
private static final String FILE_URI_SCHEME = "file:///";
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final VaultService vaultService;
|
||||
@@ -155,10 +161,16 @@ public class TrayMenuBuilder {
|
||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||
}
|
||||
|
||||
private byte[] getAppropriateTrayIconImage() {
|
||||
private URI getAppropriateTrayIconImage() {
|
||||
boolean isAnyVaultUnlocked = vaults.stream().anyMatch(Vault::isUnlocked);
|
||||
|
||||
String resourceName;
|
||||
|
||||
if (SystemUtils.IS_OS_LINUX) {
|
||||
resourceName = isAnyVaultUnlocked ? TRAY_ICON_UNLOCKED_SVG : TRAY_ICON_SVG;
|
||||
return URI.create(FILE_URI_SCHEME + resourceName);
|
||||
}
|
||||
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
resourceName = isAnyVaultUnlocked ? TRAY_ICON_UNLOCKED_MAC : TRAY_ICON_MAC;
|
||||
} else {
|
||||
@@ -167,10 +179,11 @@ public class TrayMenuBuilder {
|
||||
|
||||
try (var image = getClass().getResourceAsStream(resourceName)) {
|
||||
assert image != null;
|
||||
return image.readAllBytes();
|
||||
var imageBytes = image.readAllBytes();
|
||||
var data = Base64.getEncoder().encodeToString(imageBytes);
|
||||
return URI.create(DATA_URI_SCHEME + data);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to load tray icon image: " + resourceName, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
src/main/resources/tray_icon.svg
Normal file
1
src/main/resources/tray_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m32.66 29.319a1.432 1.432 0 0 0 -.66-.319h-1.5a8.125 8.125 0 0 0 1.5-5 11.027 11.027 0 0 0 -.5-3 2.519 2.519 0 0 1 0-1.5 12.987 12.987 0 0 0 .5-3.5c-6.681-1.955-11 0-11 0s-4.313-1.936-11 0a13.272 13.272 0 0 0 .5 3.5 2.519 2.519 0 0 1 0 1.5 11.1 11.1 0 0 0 -.5 3 8.035 8.035 0 0 0 1.5 5h-1.5a1.868 1.868 0 0 0 -.783.319 3.477 3.477 0 0 0 -1.636 2.157 7.058 7.058 0 0 0 -.234 3.18c.307 2.008 1.329 3.344 2.653 3.344h4a.379.379 0 0 0 .277-.187l1.793-1.483a3.263 3.263 0 0 0 .729-2.074 7.857 7.857 0 0 0 .027-.878 23.166 23.166 0 0 0 4.174 1.622 24.4 24.4 0 0 0 4.051-1.614 7.848 7.848 0 0 0 .027.869 3.263 3.263 0 0 0 .729 2.074l1.793 1.484a.61.61 0 0 0 .4.187h4c1.324 0 2.223-1.336 2.529-3.343a7.057 7.057 0 0 0 -.234-3.18 3.477 3.477 0 0 0 -1.635-2.158zm-17.069 6.5c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.048c.051 1.03-.161 2.644-.509 2.834zm6.409-6.819h-2l.5-5a2 2 0 1 1 1 0zm6.38 7.921a.418.418 0 0 1 -.627.111c-.522-.433-1.439-1.2-1.458-1.208-.348-.189-.56-1.8-.505-2.828a15.84 15.84 0 0 0 2.9-2.037 9.322 9.322 0 0 1 -.31 5.962zm-20.924-11.162.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm33.217 1.2a3.021 3.021 0 0 0 -2.658-1.525 1.574 1.574 0 0 0 -.107-1.283l-.745-1.367a1.779 1.779 0 0 0 -1.317-.891l-.7-1.278a2.409 2.409 0 0 0 .229-2.661 2.283 2.283 0 0 0 -2.375-1.454 7.039 7.039 0 0 1 0 3 3.272 3.272 0 0 0 0 1.5c.047.152-.047.3 0 .5.227.04-.069.156.165.14l.653 1.2a1.579 1.579 0 0 0 -.019 1.557l.745 1.367a1.753 1.753 0 0 0 1.045.832 2.66 2.66 0 0 0 -.238 2.916 2.989 2.989 0 0 0 2.326 1.509.79.79 0 0 0 .082 0 .707.707 0 0 0 .717-.6.688.688 0 0 0 -.636-.749 1.546 1.546 0 0 1 -1.2-.78 1.408 1.408 0 0 1 .672-1.95 1.628 1.628 0 0 1 1.179-.089 1.512 1.512 0 0 1 .9.719 1.37 1.37 0 0 1 -.023 1.361.656.656 0 0 0 .284.92.748.748 0 0 0 .981-.266 2.648 2.648 0 0 0 .04-2.633zm-19.673-11.959a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2z" fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
src/main/resources/tray_icon_unlocked.svg
Normal file
1
src/main/resources/tray_icon_unlocked.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1"><path d="m15.591 35.824c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.043c.051 1.03-.161 2.644-.509 2.834zm6.409-6.824h-2l.5-5a2 2 0 1 1 1 0zm-14.544-3.241.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm13.544-10.759a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm14.473 6a8.067 8.067 0 0 0 -8.08 8v2.141a3.891 3.891 0 0 0 -2.893 3.734v5.125a23.166 23.166 0 0 1 -4.174-1.623 7.857 7.857 0 0 1 -.027.878 3.263 3.263 0 0 1 -.729 2.074l-1.794 1.483a.379.379 0 0 1 -.276.188h-4c-1.324 0-2.346-1.336-2.653-3.343a7.058 7.058 0 0 1 .234-3.18 3.477 3.477 0 0 1 1.636-2.157 1.868 1.868 0 0 1 .783-.32h1.5a8.035 8.035 0 0 1 -1.5-5 11.1 11.1 0 0 1 .5-3 2.519 2.519 0 0 0 0-1.5 13.272 13.272 0 0 1 -.5-3.5c6.687-1.936 11 0 11 0s4.319-1.955 11 0"/><path d="m39 28h-10v-4a3.13 3.13 0 0 1 3-3 3.087 3.087 0 0 1 3 3v1a1.034 1.034 0 0 0 1 1h1a1.034 1.034 0 0 0 1-1v-1a6 6 0 0 0 -12 0v4h-1a2.073 2.073 0 0 0 -2 2v6a2.073 2.073 0 0 0 2 2h14a2.073 2.073 0 0 0 2-2v-6a2.073 2.073 0 0 0 -2-2zm-5.391 5.94a1.609 1.609 0 0 1 -3.217 0v-1.876a1.609 1.609 0 0 1 3.217 0z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Reference in New Issue
Block a user