[NotificationDaemon] always resolve a notification's ShellApp first
Resolve a notification's ShellApp before posting the notification, so that notification sources now always have an app associated (assuming they came from an app as opposed to the command line). Index sources by PID rather than by appName (so that, eg, multiple calls to notify-send now show up as separate sources). For Sources that have an associated app, use the app's icon for the source, rather than tracking the notification icon. This change also lets us get rid of appNameMap, since we can just use shell_app_get_name() now. https://bugzilla.gnome.org/show_bug.cgi?id=629090
This commit is contained in:
parent
ad8a97dfe0
commit
c9a7c9d491
@ -81,14 +81,6 @@ const rewriteRules = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// The notification spec stipulates using formal names for the appName the applications
|
|
||||||
// pass in. However, not all applications do that. Here is a list of the offenders we
|
|
||||||
// encountered so far.
|
|
||||||
const appNameMap = {
|
|
||||||
'evolution-mail-notification': 'Evolution Mail',
|
|
||||||
'rhythmbox': 'Rhythmbox'
|
|
||||||
};
|
|
||||||
|
|
||||||
function NotificationDaemon() {
|
function NotificationDaemon() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
@ -108,7 +100,9 @@ NotificationDaemon.prototype = {
|
|||||||
Lang.bind(this, this._lostName));
|
Lang.bind(this, this._lostName));
|
||||||
|
|
||||||
this._sources = {};
|
this._sources = {};
|
||||||
this._currentNotifications = {};
|
this._senderToPid = {};
|
||||||
|
this._notifications = {};
|
||||||
|
this._busProxy = new Bus();
|
||||||
|
|
||||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
@ -174,12 +168,13 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
Notify: function(appName, replacesId, icon, summary, body,
|
Notify: function(appName, replacesId, icon, summary, body,
|
||||||
actions, hints, timeout) {
|
actions, hints, timeout) {
|
||||||
let source = this._sources[appName];
|
let id;
|
||||||
let id = null;
|
|
||||||
|
|
||||||
// Filter out notifications from Empathy, since we
|
// Filter out notifications from Empathy, since we
|
||||||
// handle that information from telepathyClient.js
|
// handle that information from telepathyClient.js
|
||||||
if (appName == 'Empathy') {
|
if (appName == 'Empathy') {
|
||||||
|
// Ignore replacesId since we already sent back a
|
||||||
|
// NotificationClosed for that id.
|
||||||
id = nextNotificationId++;
|
id = nextNotificationId++;
|
||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
@ -188,35 +183,6 @@ NotificationDaemon.prototype = {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
|
||||||
|
|
||||||
// Source may be null if we have never received a notification
|
|
||||||
// from this app or if all notifications from this app have
|
|
||||||
// been acknowledged.
|
|
||||||
if (source == null) {
|
|
||||||
let title = appNameMap[appName] || appName;
|
|
||||||
source = new Source(title);
|
|
||||||
Main.messageTray.add(source);
|
|
||||||
this._sources[appName] = source;
|
|
||||||
|
|
||||||
source.connect('clicked', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
source.destroy();
|
|
||||||
}));
|
|
||||||
source.connect('destroy', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
delete this._sources[appName];
|
|
||||||
}));
|
|
||||||
|
|
||||||
let sender = DBus.getCurrentMessageContext().sender;
|
|
||||||
let busProxy = new Bus();
|
|
||||||
busProxy.GetConnectionUnixProcessIDRemote(sender, function (result, excp) {
|
|
||||||
let app = Shell.WindowTracker.get_default().get_app_from_pid(result);
|
|
||||||
if (app)
|
|
||||||
source.setApp(app);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = GLib.markup_escape_text(summary, -1);
|
summary = GLib.markup_escape_text(summary, -1);
|
||||||
|
|
||||||
let rewrites = rewriteRules[appName];
|
let rewrites = rewriteRules[appName];
|
||||||
@ -228,25 +194,89 @@ NotificationDaemon.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let notification;
|
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||||
if (replacesId != 0) {
|
|
||||||
id = replacesId;
|
let ndata = { appName: appName,
|
||||||
notification = this._currentNotifications[id];
|
icon: icon,
|
||||||
|
summary: summary,
|
||||||
|
body: body,
|
||||||
|
actions: actions,
|
||||||
|
hints: hints,
|
||||||
|
timeout: timeout };
|
||||||
|
if (replacesId != 0 && this._notifications[replacesId]) {
|
||||||
|
ndata.id = id = replacesId;
|
||||||
|
ndata.notification = this._notifications[replacesId].notification;
|
||||||
|
} else {
|
||||||
|
replacesId = 0;
|
||||||
|
ndata.id = id = nextNotificationId++;
|
||||||
|
}
|
||||||
|
this._notifications[id] = ndata;
|
||||||
|
|
||||||
|
let sender = DBus.getCurrentMessageContext().sender;
|
||||||
|
let pid = this._senderToPid[sender];
|
||||||
|
let source = pid ? this._sources[pid] : null;
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
this._notifyForSource(source, ndata);
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replacesId) {
|
||||||
|
// There's already a pending call to GetConnectionUnixProcessID,
|
||||||
|
// which will see the new notification data when it finishes,
|
||||||
|
// so we don't have to do anything.
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._busProxy.GetConnectionUnixProcessIDRemote(sender, Lang.bind(this,
|
||||||
|
function (pid, ex) {
|
||||||
|
// The app may have updated or removed the notification
|
||||||
|
ndata = this._notifications[id];
|
||||||
|
if (!ndata)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._senderToPid[sender] = pid;
|
||||||
|
source = this._sources[pid];
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
source = new Source(appName, pid);
|
||||||
|
source.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
source.destroy();
|
||||||
|
}));
|
||||||
|
source.connect('destroy', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
delete this._sources[pid];
|
||||||
|
delete this._senderToPid[sender];
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._sources[pid] = source;
|
||||||
|
Main.messageTray.add(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._notifyForSource(source, ndata);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
|
||||||
|
_notifyForSource: function(source, ndata) {
|
||||||
|
let [id, icon, summary, body, actions, hints, notification] =
|
||||||
|
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||||
|
ndata.actions, ndata.hints, ndata.notification];
|
||||||
|
|
||||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
id = nextNotificationId++;
|
notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
|
||||||
notification = new MessageTray.Notification(source, summary, body,
|
ndata.notification = notification;
|
||||||
{ icon: iconActor });
|
|
||||||
this._currentNotifications[id] = notification;
|
|
||||||
notification.connect('dismissed', Lang.bind(this,
|
notification.connect('dismissed', Lang.bind(this,
|
||||||
function(n) {
|
function(n) {
|
||||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||||
}));
|
}));
|
||||||
notification.connect('destroy', Lang.bind(this,
|
notification.connect('destroy', Lang.bind(this,
|
||||||
function(n) {
|
function(n) {
|
||||||
delete this._currentNotifications[id];
|
delete this._notifications[id];
|
||||||
}));
|
}));
|
||||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
||||||
} else {
|
} else {
|
||||||
@ -261,15 +291,17 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||||
|
|
||||||
let sourceIconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
let sourceIconActor = source.app ? null : this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||||
source.notify(sourceIconActor, notification);
|
source.notify(notification, sourceIconActor);
|
||||||
return id;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
CloseNotification: function(id) {
|
CloseNotification: function(id) {
|
||||||
let notification = this._currentNotifications[id];
|
let ndata = this._notifications[id];
|
||||||
if (notification)
|
if (ndata) {
|
||||||
notification.destroy();
|
if (ndata.notification)
|
||||||
|
ndata.notification.destroy();
|
||||||
|
delete this._notifications[id];
|
||||||
|
}
|
||||||
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
|
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -332,21 +364,25 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
||||||
|
|
||||||
function Source(title) {
|
function Source(title, pid) {
|
||||||
this._init(title);
|
this._init(title, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source.prototype = {
|
Source.prototype = {
|
||||||
__proto__: MessageTray.Source.prototype,
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
_init: function(title) {
|
_init: function(title, pid) {
|
||||||
MessageTray.Source.prototype._init.call(this, title);
|
MessageTray.Source.prototype._init.call(this, title);
|
||||||
|
|
||||||
this.app = null;
|
this.app = Shell.WindowTracker.get_default().get_app_from_pid(pid);
|
||||||
this._openAppRequested = false;
|
if (this.app) {
|
||||||
|
this.title = this.app.get_name();
|
||||||
|
this._setSummaryIcon(this.app.create_icon_texture(this.ICON_SIZE));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function(icon, notification) {
|
notify: function(notification, icon) {
|
||||||
|
if (icon)
|
||||||
this._setSummaryIcon(icon);
|
this._setSummaryIcon(icon);
|
||||||
MessageTray.Source.prototype.notify.call(this, notification);
|
MessageTray.Source.prototype.notify.call(this, notification);
|
||||||
},
|
},
|
||||||
@ -356,22 +392,14 @@ Source.prototype = {
|
|||||||
MessageTray.Source.prototype.clicked.call(this);
|
MessageTray.Source.prototype.clicked.call(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
setApp: function(app) {
|
|
||||||
this.app = app;
|
|
||||||
if (this._openAppRequested)
|
|
||||||
this.openApp();
|
|
||||||
},
|
|
||||||
|
|
||||||
openApp: function() {
|
openApp: function() {
|
||||||
if (this.app == null) {
|
if (this.app == null)
|
||||||
this._openAppRequested = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
let windows = this.app.get_windows();
|
let windows = this.app.get_windows();
|
||||||
if (windows.length > 0) {
|
if (windows.length > 0) {
|
||||||
let mostRecentWindow = windows[0];
|
let mostRecentWindow = windows[0];
|
||||||
Main.activateWindow(mostRecentWindow);
|
Main.activateWindow(mostRecentWindow);
|
||||||
}
|
}
|
||||||
this._openAppRequested = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user