How do I do OS-specific notifications in Java?
Asked Answered
S

1

5

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."

Sage answered 3/12, 2019 at 22:8 Comment(5)
I know you asked for OS specific compilation, but you can do runtime detection like this: #228977 and since native libraries are probably loaded on run time, you might be fine to do it like thatStrangeness
Java tries to be platform independent. That code takes up a few bytes on disk if it is just sitting there. The correct Java way of doing things is to let it sit. Just put the functionality in different classes which you initiate depending on the OS. If you do that the code should not even enter memory. Probably best to only instantiate the OS specific ones on detection and fall back to the buggy shit if the OS is unknown.Hedgehop
@MaartenBodewes Perhaps I didn't phrase the question correctly. I don't mind if the Windows functionality is bundled into the final JAR file (i.e. our product). I just want Java to detect what system it's running on, and do the right thing.Sage
Yeah, alright, but in that case: doesn't runtime lookup as performed in the Q/A linked to by Tarmo answer the question?Hedgehop
@MaartenBodewes Yes it does, at least I think so.Sage
R
7

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);
}
Repairer answered 4/12, 2019 at 1:39 Comment(1)
On a lower level on Linux, you can use another CLI helper provided by libnotify on which zenity and many other notification packages depend: notify-send 'title' 'message' --icon=dialog-warningErie

© 2022 - 2024 — McMap. All rights reserved.