Nucleus
OS integration

Notifications sous Linux

Envoyez des notifications de bureau freedesktop via D-Bus depuis Kotlin, avec urgence, hints, sons et callbacks d'action.

notification-linux envoie des notifications de bureau via le service D-Bus org.freedesktop.Notifications, que GNOME, KDE, Cinnamon, Xfce et d'autres environnements implémentent. Le module traduit la spécification freedesktop Desktop Notifications en Kotlin au-dessus de GIO/GDBus, sans dépendance JNA ni bibliothèque D-Bus Java.

Ajouter la dépendance

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.notification-linux:2.0.7")
}

Cela tire freedesktop-icons de façon transitive pour les noms d'icônes typés.

Envoyer une notification

Construisez une Notification et passez-la à LinuxNotificationCenter.notify, qui renvoie l'ID assigné par le serveur :

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 terminé",
        body = "myapp-2.0.0.zip est prêt",
        appIcon = FreedesktopIcon.Status.DIALOG_INFORMATION,
        hints = NotificationHints(urgency = Urgency.NORMAL),
        actions = listOf(NotificationAction("open", "Ouvrir")),
    ),
)

notify renvoie un ID supérieur à 0 en cas de succès, ou 0 en cas d'échec.

Vérifiez LinuxNotificationCenter.isAvailable avant d'envoyer. Il renvoie false sur les plateformes non-Linux ou quand la bibliothèque native n'a pas pu être chargée.

Fonctionnement

LinuxNotificationCenter dialogue avec org.freedesktop.Notifications via D-Bus à travers JNI. Chaque appel à notify invoque la méthode Notify et renvoie l'ID que le serveur assigne. Un replacesId non nul remplace atomiquement la notification existante portant cet ID.

Les clics d'action et les événements de fermeture arrivent ensuite sous forme de signaux D-Bus. Enregistrez un LinuxNotificationListener pour les recevoir ; la surveillance des signaux démarre dès l'ajout du premier listener et s'arrête au retrait du dernier. Les callbacks sont dispatchés sur l'EDT Swing.

La spécification est uniforme, mais les serveurs diffèrent dans ce qu'ils affichent. GNOME Shell en particulier gère mal appIcon ; renseignez hints.imagePath pour un rendu d'icône cohérent. Appelez getServerInformation() au démarrage pour logger le serveur actif, et getCapabilities() pour connaître ce qu'il prend en charge.

Référence de l'API

Définir l'urgence

hints = NotificationHints(urgency = Urgency.CRITICAL)

Urgency vaut LOW, NORMAL ou CRITICAL. Les notifications critiques outrepassent généralement le mode ne-pas-déranger et n'expirent pas automatiquement.

Ajouter des boutons d'action

Chaque NotificationAction a une key et un label. Utilisez NotificationAction.DEFAULT_KEY pour l'action liée au clic sur le corps de la notification.

val notification = Notification(
    appName = "MyApp",
    summary = "Nouveau message",
    actions = listOf(
        NotificationAction("reply", "Répondre"),
        NotificationAction("archive", "Archiver"),
    ),
)

Recevoir les 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 vaut EXPIRED, DISMISSED, CLOSED ou UNDEFINED. Retirez un listener avec removeListener.

Intégrer une image inline

Utilisez ImageData quand vous disposez des pixels bruts plutôt que d'un chemin de fichier — par exemple un avatar généré. Les octets doivent être en ordre RGB ou RGBA :

import dev.nucleusframework.notification.linux.ImageData

hints = NotificationHints(
    imageData = ImageData(
        width = width,
        height = height,
        rowstride = width * 4,
        hasAlpha = true,
        data = bytes,
    ),
)

channels vaut 4 par défaut quand hasAlpha est true, sinon 3, et bitsPerSample vaut 8 par défaut.

Jouer un son

import dev.nucleusframework.notification.linux.NotificationSound

hints = NotificationHints(
    soundName = NotificationSound.Notification.DIALOG_INFORMATION,
)

Les noms de sons sont regroupés par catégorie (Alert, Notification, Action, InputFeedback, Game) ; utilisez NotificationSound.Custom("x-myapp-…") pour un nom hors spécification.

Fermer une notification

LinuxNotificationCenter.closeNotification(id)

Notes

  • Sous Xvfb ou en CI headless, notify rend la main sans erreur, mais aucun daemon n'affiche la notification.
  • Si les notifications fonctionnent en développement mais pas sous Flatpak, vérifiez la permission portail org.freedesktop.Notifications dans votre manifeste.

Et ensuite