[gnome-shell: 8/11] notificationDaemon: Implement the new GTK+ notifications API



commit e0b87f1e143447b375e321212a86b05bdb5375ea
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Sun Oct 13 13:04:02 2013 -0400

    notificationDaemon: Implement the new GTK+ notifications API
    
    The new API is designed to support features like persistence and uses
    the new org.freedesktop.Application specification for activating
    actions on notifications. While we won't add support for persistence
    yet, implement the new notification spec with parity of the old one.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=710137

 js/ui/notificationDaemon.js |  198 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 198 insertions(+), 0 deletions(-)
---
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index ac5b543..6a55509 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -710,10 +710,208 @@ const FdoNotificationDaemonSource = new Lang.Class({
     }
 });
 
+
+const GtkNotificationDaemonNotification = new Lang.Class({
+    Name: 'GtkNotificationDaemonNotification',
+    Extends: MessageTray.Notification,
+
+    _init: function(source, notification) {
+        this.parent(source);
+
+        let { "title": title,
+              "body": body,
+              "icon": gicon,
+              "urgent": urgent,
+              "buttons": buttons,
+              "default-action": defaultAction,
+              "default-action-target": defaultActionTarget } = notification;
+
+        this.setUrgency(urgent ? MessageTray.Urgency.CRITICAL : MessageTray.Urgency.NORMAL);
+
+        if (buttons) {
+            buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
+                this.addAction(button.label, Lang.bind(this, this._onButtonClicked, button));
+            }));
+        }
+
+        this._defaultAction = defaultAction ? defaultAction.unpack() : null;
+        this._defaultActionTarget = defaultActionTarget ? defaultActionTarget.unpack() : null;
+
+        this.update(title.unpack(), body ? body.unpack() : null, { gicon: gicon });
+    },
+
+    _activateAction: function(namespacedActionId, target) {
+        if (namespacedActionId) {
+            if (namespacedActionId.startsWith('app.')) {
+                let actionId = namespacedActionId.slice('app.'.length);
+                this.source.activateAction(actionId, target);
+            }
+        } else {
+            this.source.open();
+        }
+    },
+
+    _onButtonClicked: function(button) {
+        let { "action": action, "action-target": actionTarget } = button;
+        this._activateAction(action, actionTarget);
+    },
+
+    _onClicked: function() {
+        this._activateAction(this._defaultAction, this._defaultActionTarget);
+        this.parent();
+    },
+});
+
+const FdoApplicationIface = <interface name="org.freedesktop.Application">
+<method name="ActivateAction">
+    <arg type="s" direction="in" />
+    <arg type="av" direction="in" />
+    <arg type="a{sv}" direction="in" />
+</method>
+<method name="Activate">
+    <arg type="a{sv}" direction="in" />
+</method>
+</interface>;
+const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
+
+function objectPathFromAppId(appId) {
+    return '/' + appId.replace(/\./g, '/');
+}
+
+function getPlatformData() {
+    let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
+    return { "desktop-startup-id": startupId };
+}
+
+function InvalidAppError() {}
+
+const GtkNotificationDaemonAppSource = new Lang.Class({
+    Name: 'GtkNotificationDaemonAppSource',
+    Extends: MessageTray.Source,
+
+    _init: function(appId) {
+        this._appId = appId;
+        this._objectPath = objectPathFromAppId(appId);
+
+        this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
+        if (!this._app)
+            throw new InvalidAppError();
+
+        this._notifications = {};
+
+        this.parent(this._app.get_name());
+    },
+
+    createIcon: function(size) {
+        return this._app.create_icon_texture(size);
+    },
+
+    _createPolicy: function() {
+        return new MessageTray.NotificationApplicationPolicy(this._appId);
+    },
+
+    _createApp: function() {
+        return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
+    },
+
+    activateAction: function(actionId, target) {
+        let app = this._createApp();
+        app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
+    },
+
+    open: function() {
+        let app = this._createApp();
+        app.ActivateRemote(getPlatformData());
+    },
+
+    addNotification: function(notificationId, notificationParams) {
+        if (this._notifications[notificationId])
+            this._notifications[notificationId].destroy();
+
+        let notification = new GtkNotificationDaemonNotification(this, notificationParams);
+        notification.connect('destroy', Lang.bind(this, function() {
+            delete this._notifications[notificationId];
+        }));
+        this._notifications[notificationId] = notification;
+
+        this.notify(notification);
+    },
+
+    removeNotification: function(notificationId) {
+        if (this._notifications[notificationId])
+            
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
+    },
+});
+
+const GtkNotificationsIface = <interface name="org.gtk.Notifications">
+<method name="AddNotification">
+    <arg type="s" direction="in" />
+    <arg type="s" direction="in" />
+    <arg type="a{sv}" direction="in" />
+</method>
+<method name="RemoveNotification">
+    <arg type="s" direction="in" />
+    <arg type="s" direction="in" />
+</method>
+</interface>;
+
+const GtkNotificationDaemon = new Lang.Class({
+    Name: 'GtkNotificationDaemon',
+
+    _init: function() {
+        this._sources = {};
+
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
+
+        Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
+    },
+
+    _ensureAppSource: function(appId) {
+        if (this._sources[appId])
+            return this._sources[appId];
+
+        let source = new GtkNotificationDaemonAppSource(appId);
+
+        source.connect('destroy', Lang.bind(this, function() {
+            delete this._sources[appId];
+        }));
+        Main.messageTray.add(source);
+        this._sources[appId] = source;
+        return source;
+    },
+
+    AddNotificationAsync: function(params, invocation) {
+        let [appId, notificationId, notification] = params;
+
+        let source;
+        try {
+            source = this._ensureAppSource(appId);
+        } catch(e if e instanceof InvalidAppError) {
+            invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not 
be found'.format(appId));
+            return;
+        }
+
+        source.addNotification(notificationId, notification);
+
+        invocation.return_value(null);
+    },
+
+    RemoveNotificationAsync: function(params, invocation) {
+        let [appId, notificationId] = params;
+        let source = this._sources[appId];
+        if (source)
+            source.removeNotification(notificationId);
+
+        invocation.return_value(null);
+    },
+});
+
 const NotificationDaemon = new Lang.Class({
     Name: 'NotificationDaemon',
 
     _init: function() {
         this._fdoNotificationDaemon = new FdoNotificationDaemon();
+        this._gtkNotificationDaemon = new GtkNotificationDaemon();
     },
 });


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]