Getting to Know System Tray

by John Zukowski

The Java SE 6 release introduced several new capabilities to the Abstract Window Toolkit (AWT). Users are no longer limited to Swing and enhancing the graphical user interface (GUI) components. With Java SE 6, you have new access to things such as desktop applications through what used to be called the JDesktop Integration Components (JDIC), described in a previous tip: Communicating With Native Applications Using JDIC. Although its package changed to just java.awt, the API remains much as described in that tip. You also now have the ability to add applications to the Microsoft Windows taskbar, the Gnome notification area, or KDE's system tray. That is what this tip is all about.

Access to the system tray requires only two new classes: SystemTray to represent the desktop's system tray and TrayIcon for its icon. Why not use Image or Icon for that? TrayIcon has an attached PopupMenu and tool tip text. Here's how all the pieces fit together.

The SystemTray class builds on the concept of the platform having a single instance of the tray. Calling the getSystemTray() method of SystemTray will return that instance. But some platforms might not support a system tray, so it is best to check first through the isSupported() method. Otherwise, an UnsupportedOperationException will be thrown if the platform does not support a system tray. Here's the typical pattern you'll need for acquiring that system tray instance:

  if (SystemTray.isSupported()) {
    SystemTray tray = SystemTray.getSystemTray();
  }

Most of the common platforms -- Microsoft Windows, Solaris Operating System, and Linux -- will support a system, but some platforms may not.

After getting the SystemTray, TrayIcon comes into play. You get to add one or more of those to the tray, one for each menu and tool tip you wish to add, with of course an Image.

Getting started with a basic framework to use SystemTray gives you the following program. The program puts a single TrayIcon on the SystemTray with a single MenuItem on its PopupMenu. In the installed JDK directory, there is a demo subdirectory. You can get an icon from that subdirectory. Any supported format will do.

import javax.swing.\*;
import java.awt.\*;

public class BasicTray {
  public static void main(String args[]) {
    Runnable runner = new Runnable() {
      public void run() {
        if (SystemTray.isSupported()) {
          SystemTray tray = SystemTray.getSystemTray();
          Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif");
          PopupMenu popup = new PopupMenu();
          MenuItem item = new MenuItem("A MenuItem");
          popup.add(item);
          TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup);
          try {
            tray.add(trayIcon);
          } catch (AWTException e) {
            System.err.println("Can't add to tray");
          }
        } else {
          System.err.println("Tray unavailable");
        }
      }
    };
    EventQueue.invokeLater(runner);
  }
}

Resting your mouse over the icon shows the tool tip text. Clicking the icon pops the menu up, showing the menu item. Had there been an Action/ActionListener added to the MenuItem, it would have been activated upon selection.

Besides the ability to create a big frame for your application when you select the menu item, one other important method of TrayIcon is worth mentioning: displayMessage(). Calling this method will show a pop-up message near the icon on the system tray. The method takes three arguments: the title for the pop-up window, which can be null; the message itself; and a MessageType to indicate the type of message. Valid types are ERROR, WARNING, INFO, or NONE. All but NONE have their own icon added to the pop-up window. The icon is a smaller version of the one shown in a JOptionPane.

The following program adds a few more menu items to the earlier example to demonstrate the displayMessage() method and all its message types. The final menu item with Close uses the remove() method of SystemTray to remove the TrayIcon from the SystemTray and shuts down.

import javax.swing.\*;
import java.awt.\*;
import java.awt.event.\*;

public class FullTray {
  static class ShowMessageListener implements ActionListener {
    TrayIcon trayIcon;
    String title;
    String message;
    TrayIcon.MessageType messageType;
    ShowMessageListener(
        TrayIcon trayIcon,
        String title,
        String message,
        TrayIcon.MessageType messageType) {
      this.trayIcon = trayIcon;
      this.title = title;
      this.message = message;
      this.messageType = messageType;
    }
    public void actionPerformed(ActionEvent e) {
      trayIcon.displayMessage(title, message, messageType);
    }
  }
  public static void main(String args[]) {
    Runnable runner = new Runnable() {
      public void run() {
        if (SystemTray.isSupported()) {
          final SystemTray tray = SystemTray.getSystemTray();
          Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif");
          PopupMenu popup = new PopupMenu();
          final TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup);

          MenuItem item = new MenuItem("Error");
	  item.addActionListener(new ShowMessageListener(trayIcon,
            "Error Title", "Error", TrayIcon.MessageType.ERROR));
          popup.add(item);
          item = new MenuItem("Warning");
	  item.addActionListener(new ShowMessageListener(trayIcon,
            "Warning Title", "Warning", TrayIcon.MessageType.WARNING));
          popup.add(item);
          item = new MenuItem("Info");
	  item.addActionListener(new ShowMessageListener(trayIcon,
            "Info Title", "Info", TrayIcon.MessageType.INFO));
          popup.add(item);
          item = new MenuItem("None");
	  item.addActionListener(new ShowMessageListener(trayIcon,
            "None Title", "None", TrayIcon.MessageType.NONE));
          popup.add(item);
          item = new MenuItem("Close");
	  item.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
	      tray.remove(trayIcon);
            }
	  });
          popup.add(item);
          try {
            tray.add(trayIcon);
          } catch (AWTException e) {
            System.err.println("Can't add to tray");
          }
        } else {
          System.err.println("Tray unavailable");
        }
      }
    };
    EventQueue.invokeLater(runner);
  }
}

Compile and run the program to have the tray icon added to the system tray. On a Microsoft Windows box, you'll need to right click on the icon to see the pop-up menu. Select each icon to see each message displayed, and select Close when you are done. Removing the tray icon essentially causes the event dispatch thread to end, allowing the program to exit.

\*\*\*\*\*\*\*

jMaki is a lightweight framework that makes it easy to build Ajax-based web applications. Learn more in this Deep Dive with jMaki Project lead, Greg Murray.

\*\*\*\*\*\*\*

Sun Microsystems Developer Playground


Sun Microsystems Developer Playground
Join Dana Nourie November 29 at 9-10 AM PDT in Second Life at the Sun Microsystems Developer Playground to chat about how you can learn the Java platform.


Comments:

This is very helpful, however, how do you handle system shutdown?

If the system is shutdown, can an event be caught so the systemtray application can clean-up?

Posted by uuklanger on November 05, 2007 at 03:09 AM PST #

Well-written and very helpful, thank you. I overally appreciate the desktop integration in latest Java releases. However, one thing massively bugging me is that there is no way whatsoever to make standard Java SystemTray visually fit into Linux/Unix environments - where AWT looks more or less "native" on win32, it just looks crappy on anything else. Not that Swing wouldn't provide this sort of features, but unfortunately I am also unable to, say, attach a JPopupMenu to a TrayIcon. Hope that either one of these things is likely to change in near future.

Posted by kawazu on November 05, 2007 at 03:39 PM PST #

kawazu - search for "JPopupMenu TrayIcon" and click the first link.

http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html

Posted by Kirill Grouchnikov on November 06, 2007 at 01:03 AM PST #

Note:
there is a bug in SystemTray.isSupported() reporting on some window managers false. This was fixed in jdk 7. I hope this will move into the next JAVA 6 update release.

I agree with kawazu, a JPopupMenu would be an important feature. All workarounds are not stable enougth for cross platform deployment.

Posted by mbien on November 07, 2007 at 06:16 AM PST #

What if we had an alternate but simpler way to do it like this:

jframe1.minimizeToTray();

The frame icon becomes tray icon?
The frame menu becomes menu of the tray icon?

Posted by Siddiq on November 27, 2007 at 03:08 PM PST #

No message is displayed on my system.
Method: trayIcon.displayMessage(title, message, messageType);
Java: 1.6.0_01-b06 or 1.6.0_05-ea-b04
OS: Windows XP Pro SP 2

Posted by Sultan on November 28, 2007 at 07:58 PM PST #

Implementation of this jframe1.minimizeToTray(); is very simple. In body of this method You can put something like that:

public void minimizeToTry() {
if(this.isVisible()) {
if(SystemTry.isSuported()) {
SystemTray tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif");
PopupMenu popup = new PopupMenu();
MenuItem item = new MenuItem("A MenuItem");
popup.add(item);
TrayIcon trayIcon = new TrayIcon(image, "The Window", popup);
try {
tray.add(trayIcon);
jframe1.setVisible(false);
} catch (AWTException e) {
System.err.println("Unavailable to minimize this window");
}
} else {
System.err.println("Unavailable to minimize this window");
}
}
}

This is my suggestion and I'm sorry for all bugs if they are.

Posted by jabol on November 28, 2007 at 09:13 PM PST #

re: how do you handle system shutdown

There isn't an API in Java SE for this yet.

Consider makign a suggestion to Sun for them to include it.

Posted by John Zukowski on November 29, 2007 at 12:44 PM PST #

cool one... but when we double click on the system tray icon .. i guess it should run the application again .. or we must add this in the menuitem???????

Posted by Osama on November 30, 2007 at 01:48 AM PST #

Hi there,

I've been reading your work ever since "Teach Yourself Java in 21 Days" back in 1997 or 1998.

I've learned so much from you. Your examples are always well thought out and get the point across perfectly.

Thanks very much for the good work you do.

Patrick
--

Posted by Patrick Carroll on November 30, 2007 at 03:19 AM PST #

When we invoke minimizeToTry() method and window will be invisible how we can set window visible back? Answer is simple:

tryIcon.setActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(jframe1.isVisible()) {
//do nothing or we can put here somthing like this "jframe1.setVisible(false);" if we want to hide window when we double click on the tryIcon
} else if(jframe1.isVisible()) {
jframe1.setVisible(true);
}
}
});

Above solution I have used in my licence work.

Posted by jabol on November 30, 2007 at 06:09 AM PST #

GOOOOOOOOD!!!!!!!

Posted by usu on November 30, 2007 at 12:43 PM PST #

Comment to last example to scope " else if(jframe1.isVisible()) {
jframe1.setVisible(true);
} "
There must be mechanizm which remove icon from system try. After little modification all code should be like this:

tryIcon.setActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(jframe1.isVisible()) {
//do nothing or we can put here somthing like this "jframe1.setVisible(false);" if we want to hide window when we double click on the tryIcon
} else if(jframe1.isVisible()) {
jframe1.setVisible(true);
tray.remove(tryIcon);
}
}
});

Posted by jabol on November 30, 2007 at 09:55 PM PST #

ilove java

Posted by usu on December 23, 2007 at 09:58 PM PST #

Post a Comment:
Comments are closed for this entry.
About

John O'Conner

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today