javanotificationssystrayplatform-specific

How do I do OS-specific notifications in Java?


I am not a programmer by trade (any Java knowledge I have comes from the School of Hard Knocks). Please forgive me for the stupid question I'm about to ask, and answer appropriately.

A Java app I'm working on uses very buggy platform-agnostic notifications (such as when a file has been successfully downloaded). I want to use platform-aware notifications. The code to raise a notification on Linux is quite simple:

import org.gnome.gtk.Gtk;
import org.gnome.notify.Notify;
import org.gnome.notify.Notification;

public class HelloWorld
{
    public static void main(String[] args) {
        Gtk.init(args);
        Notify.init("Hello world");
        Notification Hello = new Notification("Hello world!", "This is an example notification.", "dialog-information");
        Hello.show();
    }
}

On Mac it's a bit more complicated but still doable:

interface NsUserNotificationsBridge extends Library {
    NsUserNotificationsBridge instance = (NsUserNotificationsBridge)
            Native.loadLibrary("/usr/local/lib/NsUserNotificationsBridge.dylib", NsUserNotificationsBridge.class);

    public int sendNotification(String title, String subtitle, String text, int timeoffset);
}

It requires a dylib obtainable from this github repository: https://github.com/petesh/OSxNotificationCenter

Windows way is like this:

import java.awt.*;
import java.awt.TrayIcon.MessageType;

public class TrayIconDemo {

    public static void main(String[] args) throws AWTException {
        if (SystemTray.isSupported()) {
            TrayIconDemo td = new TrayIconDemo();
            td.displayTray();
        } else {
            System.err.println("System tray not supported!");
        }
    }

    public void displayTray() throws AWTException {
        //Obtain only one instance of the SystemTray object
        SystemTray tray = SystemTray.getSystemTray();

        //If the icon is a file
        Image image = Toolkit.getDefaultToolkit().createImage("icon.png");
        //Alternative (if the icon is on the classpath):
        //Image image = Toolkit.getDefaultToolkit().createImage(getClass().getResource("icon.png"));

        TrayIcon trayIcon = new TrayIcon(image, "Tray Demo");
        //Let the system resize the image if needed
        trayIcon.setImageAutoSize(true);
        //Set tooltip text for the tray icon
        trayIcon.setToolTip("System tray icon demo");
        tray.add(trayIcon);

        trayIcon.displayMessage("Hello, World", "notification demo", MessageType.INFO);
    }
}

The point is, I want these snippets to execute only on the appropriate platform; I don't want Java to compile, say, the GTK method on Windows, because the dependency for it doesn't exist.

How do I make it so that Java recognises it, like "Hey, I'm compiling for a Mac system, so I'm using the Mac version of the code."


Solution

  • In the interest of having something simple and clean with no additional dependencies, I would forego all the native libraries, and instead rely on native programs that I’m pretty sure are guaranteed (or at least likely) to be available on each respective system:

    String title = "Hello world!";
    String message = "This is an example notification.";
    Image image = ImageIO.read(getClass().getResource("icon.png"));
    
    String os = System.getProperty("os.name");
    if (os.contains("Linux")) {
        ProcessBuilder builder = new ProcessBuilder(
            "zenity",
            "--notification",
            "--text=" + title + "\\n" + message);
        builder.inheritIO().start();
    } else if (os.contains("Mac")) {
        ProcessBuilder builder = new ProcessBuilder(
            "osascript", "-e",
            "display notification \"" + message + "\""
                + " with title \"" + title + "\"");
        builder.inheritIO().start();
    } else if (SystemTray.isSupported()) {
        SystemTray tray = SystemTray.getSystemTray();
    
        TrayIcon trayIcon = new TrayIcon(image, "Tray Demo");
        trayIcon.setImageAutoSize(true);
        tray.add(trayIcon);
    
        trayIcon.displayMessage(title, message, TrayIcon.MessageType.INFO);
    }