Notifications on Linux
Send freedesktop desktop notifications over D-Bus from Kotlin, with urgency, hints, sounds, and action callbacks.
notification-linux sends desktop notifications through the org.freedesktop.Notifications
D-Bus service, which GNOME, KDE, Cinnamon, Xfce, and other desktops implement. It maps the
freedesktop Desktop Notifications specification to Kotlin over GIO/GDBus, with no JNA or
Java D-Bus dependency.
Add the dependency
dependencies {
implementation("dev.nucleusframework:nucleus.notification-linux:2.0.7")
}This pulls in freedesktop-icons transitively for typesafe icon names.
Send a notification
Build a Notification and pass it to LinuxNotificationCenter.notify, which returns the
server-assigned ID:
import dev.nucleusframework.notification.linux.LinuxNotificationCenter
import dev.nucleusframework.notification.linux.Notification
import dev.nucleusframework.notification.linux.NotificationAction
import dev.nucleusframework.notification.linux.NotificationHints
import dev.nucleusframework.notification.linux.Urgency
import dev.nucleusframework.freedesktop.icons.FreedesktopIcon
val id: Int = LinuxNotificationCenter.notify(
Notification(
appName = "MyApp",
summary = "Build finished",
body = "myapp-2.0.0.zip is ready",
appIcon = FreedesktopIcon.Status.DIALOG_INFORMATION,
hints = NotificationHints(urgency = Urgency.NORMAL),
actions = listOf(NotificationAction("open", "Open")),
),
)notify returns an ID greater than 0 on success, or 0 on failure.
Check LinuxNotificationCenter.isAvailable before sending. It returns false on non-Linux
platforms or when the native library could not be loaded.
How it works
LinuxNotificationCenter talks to org.freedesktop.Notifications over D-Bus through JNI.
Each call to notify invokes the Notify method and returns the ID the server assigns.
Passing a non-zero replacesId atomically replaces an existing notification with that ID.
Action clicks and close events arrive later as D-Bus signals. Register a
LinuxNotificationListener to receive them; signal monitoring starts when the first listener is
added and stops when the last is removed. Callbacks are dispatched on the Swing EDT.
The specification is uniform, but servers differ in what they render. GNOME Shell in particular
is unreliable about appIcon; set hints.imagePath for consistent iconography. Call
getServerInformation() at startup to log the active server, and getCapabilities() to see
what the server supports.
API reference
Set urgency
hints = NotificationHints(urgency = Urgency.CRITICAL)Urgency is LOW, NORMAL, or CRITICAL. Critical notifications typically bypass
do-not-disturb and do not auto-expire.
Add action buttons
Each NotificationAction has a key and a label. Use NotificationAction.DEFAULT_KEY
for the action bound to clicking the notification body.
val notification = Notification(
appName = "MyApp",
summary = "New message",
actions = listOf(
NotificationAction("reply", "Reply"),
NotificationAction("archive", "Archive"),
),
)Receive callbacks
import dev.nucleusframework.notification.linux.CloseReason
import dev.nucleusframework.notification.linux.LinuxNotificationListener
LinuxNotificationCenter.addListener(object : LinuxNotificationListener {
override fun onActionInvoked(notificationId: Int, actionKey: String) { /* … */ }
override fun onClosed(notificationId: Int, reason: CloseReason) { /* … */ }
})CloseReason is EXPIRED, DISMISSED, CLOSED, or UNDEFINED. Remove a listener with
removeListener.
Embed inline image data
Use ImageData when you have raw pixels rather than a file path — for example, a generated
avatar. The bytes must be in RGB or RGBA order:
import dev.nucleusframework.notification.linux.ImageData
hints = NotificationHints(
imageData = ImageData(
width = width,
height = height,
rowstride = width * 4,
hasAlpha = true,
data = bytes,
),
)channels defaults to 4 when hasAlpha is true and 3 otherwise, and bitsPerSample
defaults to 8.
Play a sound
import dev.nucleusframework.notification.linux.NotificationSound
hints = NotificationHints(
soundName = NotificationSound.Notification.DIALOG_INFORMATION,
)Sound names are grouped by category (Alert, Notification, Action, InputFeedback,
Game); use NotificationSound.Custom("x-myapp-…") for names outside the specification.
Close a notification
LinuxNotificationCenter.closeNotification(id)Notes
- Under
Xvfbor a headless CI,notifyreturns without error, but no daemon displays the notification. - If notifications work in development but not under Flatpak, check the
org.freedesktop.Notificationsportal permission in your manifest.
What's next
- freedesktop-icons — typesafe icon names for
appIconandimagePath. - Notifications overview — the cross-platform notification API.
- Notifications on macOS and Windows.