forked from dorkbox/SystemTray
Converted SystemTray to use a singleton pattern for ease of use. Icon *must* be set to see it (obviously). Updated Readme.md example.
This commit is contained in:
parent
801baad635
commit
8b109f6d1c
22
README.md
22
README.md
@ -44,24 +44,22 @@ GnomeShellExtension.SHELL_RESTART_COMMAND (type String, default value 'gnome-s
|
||||
SystemTray.TRAY_SIZE (type int, default value '24')
|
||||
- Size of the tray, so that the icon can properly scale based on OS. (if it's not exact). This only applies for Swing tray icons.
|
||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
||||
|
||||
|
||||
SystemTray.ICON_PATH (type String, default value '')
|
||||
- Location of the icon (to make it easier when specifying icons)
|
||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
||||
of the library.
|
||||
```
|
||||
|
||||
|
||||
|
||||
The test application is [on GitHub](https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java), and a *simple* example is as follows:
|
||||
```
|
||||
// if using provided JNA jars. Not necessary if
|
||||
//using JNA from https://github.com/twall/jna
|
||||
System.load("Path to OS specific JNA jar");
|
||||
this.systemTray = SystemTray.getSystemTray();
|
||||
if (systemTray == null) {
|
||||
throw new RuntimeException("Unable to load SystemTray!");
|
||||
}
|
||||
|
||||
|
||||
this.systemTray = SystemTray.create("grey_icon.png");
|
||||
try {
|
||||
this.systemTray.setIcon("grey_icon.png");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.systemTray.setStatus("Not Running");
|
||||
|
||||
@ -135,7 +133,7 @@ This project is **kept in sync** with the utilities library, so "jar hell" is no
|
||||
<dependency>
|
||||
<groupId>com.dorkbox</groupId>
|
||||
<artifactId>SystemTray</artifactId>
|
||||
<version>1.15</version>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -17,6 +17,7 @@ package dorkbox.systemTray;
|
||||
|
||||
import dorkbox.systemTray.linux.AppIndicatorTray;
|
||||
import dorkbox.systemTray.linux.GnomeShellExtension;
|
||||
import dorkbox.systemTray.linux.GtkSystemTray;
|
||||
import dorkbox.systemTray.swing.SwingSystemTray;
|
||||
import dorkbox.util.OS;
|
||||
import dorkbox.util.Property;
|
||||
@ -24,7 +25,6 @@ import dorkbox.util.jna.linux.AppIndicator;
|
||||
import dorkbox.util.jna.linux.AppIndicatorQuery;
|
||||
import dorkbox.util.jna.linux.GtkSupport;
|
||||
import dorkbox.util.process.ShellProcessBuilder;
|
||||
import dorkbox.systemTray.linux.GtkSystemTray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -42,7 +42,7 @@ import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for system tray implementations.
|
||||
* Factory and base-class for system tray implementations.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract
|
||||
@ -53,11 +53,13 @@ class SystemTray {
|
||||
/** Size of the tray, so that the icon can properly scale based on OS. (if it's not exact) */
|
||||
public static int TRAY_SIZE = 22;
|
||||
|
||||
private static Class<? extends SystemTray> trayType;
|
||||
private static final SystemTray systemTray;
|
||||
|
||||
static boolean isKDE = false;
|
||||
|
||||
static {
|
||||
Class<? extends SystemTray> trayType = null;
|
||||
|
||||
// Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on
|
||||
// mouseover or something, but I don't know how to do that. It seems that tooltips for app-indicators are a custom job, as
|
||||
// all examined ones sometimes have it (and it's more than just text), or they don't have it at all.
|
||||
@ -215,9 +217,6 @@ class SystemTray {
|
||||
// fallback...
|
||||
if (trayType == null) {
|
||||
trayType = GtkSystemTray.class;
|
||||
}
|
||||
|
||||
if (trayType == null) {
|
||||
logger.error("Unable to load the system tray native library. Please write an issue and include your OS type and " +
|
||||
"configuration");
|
||||
}
|
||||
@ -226,20 +225,30 @@ class SystemTray {
|
||||
|
||||
// this is windows OR mac
|
||||
if (trayType == null && java.awt.SystemTray.isSupported()) {
|
||||
try {
|
||||
java.awt.SystemTray.getSystemTray();
|
||||
trayType = SwingSystemTray.class;
|
||||
} catch (Throwable ignored) {
|
||||
logger.error("Maybe you should grant the AWTPermission `accessSystemTray` in the SecurityManager.");
|
||||
}
|
||||
}
|
||||
|
||||
if (trayType == null) {
|
||||
// unsupported tray
|
||||
logger.error("Unsupported tray type!");
|
||||
systemTray = null;
|
||||
}
|
||||
else {
|
||||
SystemTray systemTray_ = null;
|
||||
try {
|
||||
ImageUtil.init();
|
||||
systemTray_ = (SystemTray) trayType.getConstructors()[0].newInstance();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("Unsupported hashing algorithm!");
|
||||
trayType = null;
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'");
|
||||
}
|
||||
systemTray = systemTray_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,112 +257,20 @@ class SystemTray {
|
||||
*/
|
||||
public static
|
||||
String getVersion() {
|
||||
return "1.15";
|
||||
return "2.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will directly use the
|
||||
* contents of the specified file.
|
||||
* This always returns the same instance per JVM (it's a singleton), and on some platforms the system tray may not be
|
||||
* supported, in which case this will return NULL.
|
||||
*
|
||||
* @param iconPath the full path for an icon to use
|
||||
*
|
||||
* @return a new SystemTray instance with the specified path for the icon
|
||||
* <p>If this is using the Swing SystemTray and a SecurityManager is installed, the AWTPermission {@code accessSystemTray} must
|
||||
* be granted in order to get the {@code SystemTray} instance. Otherwise this will return null.
|
||||
*/
|
||||
public static
|
||||
SystemTray create(String iconPath) {
|
||||
if (trayType != null) {
|
||||
try {
|
||||
iconPath = ImageUtil.iconPath(iconPath);
|
||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
||||
return (SystemTray) o;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
SystemTray getSystemTray() {
|
||||
return systemTray;
|
||||
}
|
||||
}
|
||||
|
||||
// unsupported
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the URL to a temporary location on disk, based on the path specified by the URL.
|
||||
*
|
||||
* @param iconUrl the URL for the icon to use
|
||||
*
|
||||
* @return a new SystemTray instance with the specified URL for the icon
|
||||
*/
|
||||
public static
|
||||
SystemTray create(final URL iconUrl) {
|
||||
if (trayType != null) {
|
||||
try {
|
||||
String iconPath = ImageUtil.iconPath(iconUrl);
|
||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
||||
return (SystemTray) o;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// unsupported
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the iconStream to a temporary location on disk, based on the `cacheName` specified.
|
||||
*
|
||||
* @param cacheName the name to use for the cache lookup for the iconStream. This can be anything you want, but should be
|
||||
* consistently unique
|
||||
* @param iconStream the InputStream to load the icon from
|
||||
*
|
||||
* @return a new SystemTray instance with the specified InputStream for the icon
|
||||
*/
|
||||
public static
|
||||
SystemTray create(final String cacheName, final InputStream iconStream) {
|
||||
if (trayType != null) {
|
||||
try {
|
||||
String iconPath = ImageUtil.iconPath(cacheName, iconStream);
|
||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
||||
return (SystemTray) o;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// unsupported
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the iconStream to a temporary location on disk.
|
||||
*
|
||||
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
||||
* also NOT RECOMMENDED, but is provided for simplicity.
|
||||
*
|
||||
* @param iconStream the InputStream to load the icon from
|
||||
*
|
||||
* @return a new SystemTray instance with the specified InputStream for the icon
|
||||
*/
|
||||
@Deprecated
|
||||
public static
|
||||
SystemTray create(final InputStream iconStream) {
|
||||
if (trayType != null) {
|
||||
try {
|
||||
String iconPath = ImageUtil.iconPathNoCache(iconStream);
|
||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
||||
return (SystemTray) o;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// unsupported
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected final java.util.List<MenuEntry> menuEntries = new ArrayList<MenuEntry>();
|
||||
|
||||
@ -391,6 +308,9 @@ class SystemTray {
|
||||
/**
|
||||
* Changes the tray icon used.
|
||||
*
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray,
|
||||
* this will directly use the contents of the specified file.
|
||||
*
|
||||
* @param imagePath the path of the icon to use
|
||||
*/
|
||||
public
|
||||
@ -402,6 +322,9 @@ class SystemTray {
|
||||
/**
|
||||
* Changes the tray icon used.
|
||||
*
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the URL to a temporary location on disk, based on the path specified by the URL.
|
||||
*
|
||||
* @param imageUrl the URL of the icon to use
|
||||
*/
|
||||
public
|
||||
@ -413,6 +336,9 @@ class SystemTray {
|
||||
/**
|
||||
* Changes the tray icon used.
|
||||
*
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the imageStream to a temporary location on disk, based on the `cacheName` specified.
|
||||
*
|
||||
* @param cacheName the name to use for lookup in the cache for the iconStream
|
||||
* @param imageStream the InputStream of the icon to use
|
||||
*/
|
||||
@ -425,6 +351,9 @@ class SystemTray {
|
||||
/**
|
||||
* Changes the tray icon used.
|
||||
*
|
||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||
* the imageStream to a temporary location on disk.
|
||||
*
|
||||
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
||||
* also NOT RECOMMENDED, but is provided for simplicity.
|
||||
*
|
||||
|
@ -33,15 +33,14 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
||||
private static final AppIndicator appindicator = AppIndicator.INSTANCE;
|
||||
|
||||
private AppIndicator.AppIndicatorInstanceStruct appIndicator;
|
||||
private volatile boolean isActive = false;
|
||||
|
||||
public
|
||||
AppIndicatorTray(String iconPath) {
|
||||
AppIndicatorTray() {
|
||||
gtk.gdk_threads_enter();
|
||||
|
||||
this.appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", iconPath,
|
||||
this.appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", "",
|
||||
AppIndicator.CATEGORY_APPLICATION_STATUS);
|
||||
appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE);
|
||||
|
||||
gtk.gdk_threads_leave();
|
||||
|
||||
GtkSupport.startGui();
|
||||
@ -69,6 +68,13 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
||||
void setIcon_(final String iconPath) {
|
||||
gtk.gdk_threads_enter();
|
||||
appindicator.app_indicator_set_icon(this.appIndicator, iconPath);
|
||||
|
||||
if (!isActive) {
|
||||
isActive = true;
|
||||
|
||||
appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
gtk.gdk_threads_leave();
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,11 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private NativeLong button_press_event;
|
||||
|
||||
|
||||
private volatile boolean isActive = false;
|
||||
private volatile Pointer menu;
|
||||
|
||||
public
|
||||
GtkSystemTray(String iconPath) {
|
||||
GtkSystemTray() {
|
||||
super();
|
||||
|
||||
gtk.gdk_threads_enter();
|
||||
@ -51,8 +51,6 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
||||
|
||||
this.trayIcon = trayIcon;
|
||||
|
||||
gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
|
||||
|
||||
this.gtkCallback = new Gobject.GEventCallback() {
|
||||
@Override
|
||||
public
|
||||
@ -65,8 +63,6 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
||||
};
|
||||
button_press_event = gobject.g_signal_connect_data(trayIcon, "button_press_event", gtkCallback, null, null, 0);
|
||||
|
||||
gtk.gtk_status_icon_set_visible(trayIcon, true);
|
||||
|
||||
gtk.gdk_threads_leave();
|
||||
|
||||
GtkSupport.startGui();
|
||||
@ -101,7 +97,13 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
||||
protected synchronized
|
||||
void setIcon_(final String iconPath) {
|
||||
gtk.gdk_threads_enter();
|
||||
|
||||
gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
|
||||
|
||||
if (!isActive) {
|
||||
isActive = true;
|
||||
gtk.gtk_status_icon_set_visible(trayIcon, true);
|
||||
}
|
||||
gtk.gdk_threads_leave();
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,11 @@
|
||||
package dorkbox.systemTray.swing;
|
||||
|
||||
import dorkbox.systemTray.ImageUtil;
|
||||
import dorkbox.util.ScreenUtil;
|
||||
import dorkbox.util.SwingUtil;
|
||||
import dorkbox.systemTray.MenuEntry;
|
||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||
import dorkbox.systemTray.SystemTrayMenuPopup;
|
||||
import dorkbox.util.ScreenUtil;
|
||||
import dorkbox.util.SwingUtil;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JMenuItem;
|
||||
@ -49,11 +49,13 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
||||
volatile SystemTray tray;
|
||||
volatile TrayIcon trayIcon;
|
||||
|
||||
volatile boolean isActive = false;
|
||||
|
||||
/**
|
||||
* Creates a new system tray handler class.
|
||||
*/
|
||||
public
|
||||
SwingSystemTray(final String iconPath) {
|
||||
SwingSystemTray() {
|
||||
super();
|
||||
SwingUtil.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
@ -63,63 +65,6 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
||||
if (SwingSystemTray.this.tray == null) {
|
||||
logger.error("The system tray is not available");
|
||||
}
|
||||
else {
|
||||
SwingSystemTray.this.menu = new SystemTrayMenuPopup();
|
||||
|
||||
Image trayImage = new ImageIcon(iconPath).getImage()
|
||||
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
||||
trayImage.flush();
|
||||
final TrayIcon trayIcon = new TrayIcon(trayImage);
|
||||
SwingSystemTray.this.trayIcon = trayIcon;
|
||||
|
||||
// appindicators don't support this, so we cater to the lowest common denominator
|
||||
// trayIcon.setToolTip(SwingSystemTray.this.appName);
|
||||
|
||||
trayIcon.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public
|
||||
void mousePressed(MouseEvent e) {
|
||||
final SystemTrayMenuPopup menu = SwingSystemTray.this.menu;
|
||||
Dimension size = menu.getPreferredSize();
|
||||
|
||||
Point point = e.getPoint();
|
||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||
|
||||
int x = point.x;
|
||||
int y = point.y;
|
||||
|
||||
if (y < bounds.y) {
|
||||
y = bounds.y;
|
||||
}
|
||||
else if (y + size.height > bounds.y + bounds.height) {
|
||||
// our menu cannot have the top-edge snap to the mouse
|
||||
// so we make the bottom-edge snap to the mouse
|
||||
y -= size.height; // snap to edge of mouse
|
||||
}
|
||||
|
||||
if (x < bounds.x) {
|
||||
x = bounds.x;
|
||||
}
|
||||
else if (x + size.width > bounds.x + bounds.width) {
|
||||
// our menu cannot have the left-edge snap to the mouse
|
||||
// so we make the right-edge snap to the mouse
|
||||
x -= size.width; // snap to edge of mouse
|
||||
}
|
||||
|
||||
// weird voodoo to get this to popup with the correct parent
|
||||
menu.setInvoker(menu);
|
||||
menu.setLocation(x, y);
|
||||
menu.setVisible(true);
|
||||
menu.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
SwingSystemTray.this.tray.add(trayIcon);
|
||||
} catch (AWTException e) {
|
||||
logger.error("TrayIcon could not be added.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -184,12 +129,72 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
||||
void run() {
|
||||
SwingSystemTray tray = SwingSystemTray.this;
|
||||
synchronized (tray) {
|
||||
if (!isActive) {
|
||||
isActive = true;
|
||||
|
||||
SwingSystemTray.this.menu = new SystemTrayMenuPopup();
|
||||
|
||||
Image trayImage = new ImageIcon(iconPath).getImage()
|
||||
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
||||
trayImage.flush();
|
||||
final TrayIcon trayIcon = new TrayIcon(trayImage);
|
||||
SwingSystemTray.this.trayIcon = trayIcon;
|
||||
|
||||
// appindicators don't support this, so we cater to the lowest common denominator
|
||||
// trayIcon.setToolTip(SwingSystemTray.this.appName);
|
||||
|
||||
trayIcon.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public
|
||||
void mousePressed(MouseEvent e) {
|
||||
final SystemTrayMenuPopup menu = SwingSystemTray.this.menu;
|
||||
Dimension size = menu.getPreferredSize();
|
||||
|
||||
Point point = e.getPoint();
|
||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||
|
||||
int x = point.x;
|
||||
int y = point.y;
|
||||
|
||||
if (y < bounds.y) {
|
||||
y = bounds.y;
|
||||
}
|
||||
else if (y + size.height > bounds.y + bounds.height) {
|
||||
// our menu cannot have the top-edge snap to the mouse
|
||||
// so we make the bottom-edge snap to the mouse
|
||||
y -= size.height; // snap to edge of mouse
|
||||
}
|
||||
|
||||
if (x < bounds.x) {
|
||||
x = bounds.x;
|
||||
}
|
||||
else if (x + size.width > bounds.x + bounds.width) {
|
||||
// our menu cannot have the left-edge snap to the mouse
|
||||
// so we make the right-edge snap to the mouse
|
||||
x -= size.width; // snap to edge of mouse
|
||||
}
|
||||
|
||||
// weird voodoo to get this to popup with the correct parent
|
||||
menu.setInvoker(menu);
|
||||
menu.setLocation(x, y);
|
||||
menu.setVisible(true);
|
||||
menu.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
SwingSystemTray.this.tray.add(trayIcon);
|
||||
} catch (AWTException e) {
|
||||
logger.error("TrayIcon could not be added.", e);
|
||||
}
|
||||
} else {
|
||||
Image trayImage = new ImageIcon(iconPath).getImage()
|
||||
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
||||
trayImage.flush();
|
||||
tray.trayIcon.setImage(trayImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import dorkbox.systemTray.MenuEntry;
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
@ -36,11 +37,11 @@ class TestTray {
|
||||
|
||||
public static
|
||||
void main(String[] args) {
|
||||
// ONLY if manually loading JNA jars (which is how i do it).
|
||||
// ONLY if manually loading JNA jars.
|
||||
//
|
||||
// Not necessary if using the official JNA downloaded from https://github.com/twall/jna AND THAT JAR is on the classpath
|
||||
//
|
||||
// System.load(new File("../../resources/Dependencies/jna/linux_64/libjna.so").getAbsolutePath()); //64bit linux library
|
||||
System.load(new File("../../resources/Dependencies/jna/linux_64/libjna.so").getAbsolutePath()); //64bit linux library
|
||||
|
||||
new TestTray();
|
||||
}
|
||||
@ -51,11 +52,17 @@ class TestTray {
|
||||
|
||||
public
|
||||
TestTray() {
|
||||
this.systemTray = SystemTray.create(LT_GRAY_MAIL);
|
||||
this.systemTray = SystemTray.getSystemTray();
|
||||
if (systemTray == null) {
|
||||
throw new RuntimeException("Unable to load SystemTray!");
|
||||
}
|
||||
|
||||
try {
|
||||
this.systemTray.setIcon(LT_GRAY_MAIL);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
systemTray.setStatus("No Mail");
|
||||
|
||||
callbackGreen = new SystemTrayMenuAction() {
|
||||
|
Loading…
Reference in New Issue
Block a user