Simple library for desktop notifications from Java on Windows, Mac OS X and Linux.
All platforms support Growls GNTP protocol. If you have have Growl for Mac OS X, Linux or Windows installed, and it is listening on the default port, it will be used in preference to all platform specific notification systems.
All platforms can use one of the Java GUI toolkit specific implementations, such as JavaFX or SWT. If these toolkit libraries are available, those will be chosen if the experience is superior to the native implementation.
Additionally on all platforms, if no notifier implementation can be found, the last resort fallback will be to display the messages on System.out.
In all cases, you can override the chosen provider using ToasterSettings.setPreferredToasterClassName
, see below.
Windows support is currently provided in the following order :-
Mac OS X support will be provided in the following order :-
Linux support will be provided in the following order :-
dbus-java
is available, native DBus notifications will be used.The library is available in Maven Central, so configure your project according to the build system you use. For example, for Maven itself :-
<dependencies>
<dependency>
<groupId>com.sshtools</groupId>
<artifactId>two-slices</artifactId>
<version>0.9.4</version>
</dependency>
</dependencies>
Development builds may be available in the snapshots repository
<repositories>
<repository>
<id>oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
..
<dependencies>
<dependency>
<groupId>com.sshtools</groupId>
<artifactId>two-slices</artifactId>
<version>0.9.5-SNAPSHOT</version>
</dependency>
</dependencies>
At time of writing this, the SWT artifacts that are available on Maven Central are incompatible with having a cross-platform and fully JPMS compliant library or application. This is due to the module naming scheme chosen by the SWT packagers.
Two Slices is fully JPMS compliant otherwise, and including SWT as in the primary artifact would degrade this support.
For this reason, if you wish to use SWT you must also add the separate SWT artifact.
This is not modular, other than having an Automatic-Module-Name
. If you are using
SWT, you probably do not care about this as you'll have other problems with JPMS
anyway.
<dependencies>
<dependency>
<groupId>com.sshtools</groupId>
<artifactId>two-slices-swt</artifactId>
<version>0.9.4</version>
</dependency>
</dependencies>
For the simplest use, call Toast.toast() :-
Toast.toast(ToastType.INFO, "Information", "Here is some information you cannot do without.");
You can pass in the full path to an icon too (requires support for this in selected platform)
Toast.toast(ToastType.INFO, "/usr/share/pixmaps/mypic.png", "Boo!", "See my image?");
An alternative way to build more complex messages is using ToastBuilder
.
var builder = Toast.builder();
builder.title("My Title");
builder.content("Some content");
builder.toast();
You can prematurely close messages if the toaster implementation supports it.
var builder = Toast.builder();
builder.title("My Title");
builder.content("Some content");
var slice = builder.toast();
// ...
slice.close();
If you want to be notified when a message is closed, e.g. dismissed by the user. Set a listener on the builder.
var builder = Toast.builder();
builder.content("Some content");
builder.closed(() -> {
System.out.println("Message closed.");
});
builder.toast();
If the toaster implementation supports them, Actions may be added. An action would usually be represented as a button on a notification message.
There is a special action, the defaultAction()
. This is usually invoked when the whole notification message is clicked, or it may simply be presented as another button.
Be aware different implementations have different restrictions on how many actions can be presented.
var builder = Toast.builder();
builder.content("Some content");
builder.action("Action 1", () -> System.out.println("Do Action 1"));
builder.action("Action 2", () -> System.out.println("Do Action 2"));
builder.defaultAction(System.out.println("Do Default Action"));
builder.toast();
Some settings may be provided to alter the behaviour of the toasters. These are only hints, and specific toasters can ignore any and all of them.
ToasterFactory.setSettings(new ToasterSettings().setAppName("My App Name"));
Some toaster implementations have additional hints that can be passed. These hints are only generally only supported by individual toolkits. For example, to resize icons or images in the SWT implementation, you would do the following.
ToasterSettings settings = new ToasterSettings();
settings.getHints().put(BasicToastHint.ICON_SIZE, 64);
ToasterFactory.setSettings(settings);
BasicToastHint
contains a generic list of hints that a provider may or may not support. See
the class documentation for more detail.
Hints may also be set on individual toast, see ToastBuilder.hints()
.
Some implementations will require and/or show an icon in your system tray. This will be where the notification messages are anchored to. You can set a hint as to how to treat this icon via the configuration.
ToasterSettings settings = new ToasterSettings();
settings.setAppName("My App Name");
settings.setSystemTrayIconMode(SystemTrayIconMode.HIDDEN);
ToasterFactory.setSettings(settings);
The options for system tray icon mode are :-
ToasterSettings.setDefaultImage()
).When you are providing a 'parent' tray item, the icon mode above may be ignored.
If you have an SWT application that already has an icon on the tray, you can re-use this for your notification settings when the SWT notifier is used.
For SWT, you must already be running an event loop (see SWT toolkit documentation). At the moment it is not possible to automatically start a loop mainly due to restrictions on OS X where SWT must be on the main thread.
TrayItem myTrayItem = ..... // this is the reference to your org.eclipse.swt.widgets.TrayItem
ToasterFactory.setSettings(new ToasterSettings().setParent(myTrayItem));
Then, whenever the SWT notifier is used, the balloon message will be anchored to your tray item.
If you have a Swing/AWT application that already has an icon on the tray, you can re-use this for your notification settings when the AWT notifier is used.
TrayIcon myTrayIcon = ..... // this is the reference to your java.awt.TrayIcon
ToasterFactory.setSettings(new ToasterSettings().setParent(myTrayIcon));
Then, whenever the AWT notifier is used, the balloon message will be anchored to your tray item.
You can add your own notifier implementations and customise the factory if you have any special requirements.
Implement the Toaster
interface. An abstract implementation AbstractToaster
is provided for your convenience and should be used where possible. By convention, all Toasters take a ToasterSettings
in their constructor.
In the constructor of your implementation, you should test if this implementation is for the current enviroment. E.g. if you were writing an OS X specific notifier, then you would test for running on that platform and the availability of any libraries or external tools you might need. By convention, if the environment is not sufficient, an UnsupportedOperationException
should be thrown.
Before your new Toaster
implementation can be used, you must provide a ToasterService
implementation that creates it based on the given configuration. By convention, you place this as an inner class inside the Toaster implementation.
public class MyToaster extends AbstractToaster {
public static class Service implements ToasterService {
@Override
public Toaster create(ToasterSettings settings) {
return new MyToaster(settings);
}
}
public MyToaster(ToasterSettings configuration) {
super(configuration);
}
@Override
public Slice toast(ToastBuilder builder) {
// TODO
}
}
For Java to automatically find this service, you must add it's full class name, e.g. com.mypackage.MyToaster$Service
to a file in META-INF/services/com.sshtools.twoslices.ToasterService
, and/or add it to module-info.java
using the appropriate syntax for Java services.
If you do not wish to use Java's ServiceLoader
feature to locate toaster implementations, you can extend ToasterFactory
and provide your own toaster()
method. This will be registered as the default factory the first time you instantiate it (so make sure you do this before ever asking for toast) :-
new ToasterFactory() {
@Override
public Toaster toaster() {
return new MyToaster();
}
};
Just call Toast.toast
as you normally would.