2011-09-28 09:16:26 -04:00
|
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2019-01-31 09:07:06 -05:00
|
|
|
|
/* exported NotificationDaemon */
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2019-02-08 22:21:36 -05:00
|
|
|
|
const { GdkPixbuf, Gio, GLib, Shell, St } = imports.gi;
|
2010-01-13 15:05:20 -05:00
|
|
|
|
const Mainloop = imports.mainloop;
|
|
|
|
|
|
2011-01-11 16:34:09 -05:00
|
|
|
|
const Config = imports.misc.config;
|
2010-01-13 15:05:20 -05:00
|
|
|
|
const Main = imports.ui.main;
|
|
|
|
|
const MessageTray = imports.ui.messageTray;
|
|
|
|
|
const Params = imports.misc.params;
|
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
2011-08-16 08:26:49 -04:00
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
|
// Should really be defined in Gio.js
|
|
|
|
|
const BusIface = loadInterfaceXML('org.freedesktop.DBus');
|
2011-08-16 08:26:49 -04:00
|
|
|
|
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
|
|
|
|
|
function Bus() {
|
|
|
|
|
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
|
|
|
|
|
}
|
2010-02-15 12:27:28 -05:00
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
|
const FdoNotificationsIface = loadInterfaceXML('org.freedesktop.Notifications');
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-07-18 13:47:27 -04:00
|
|
|
|
var NotificationClosedReason = {
|
2010-01-13 15:05:20 -05:00
|
|
|
|
EXPIRED: 1,
|
|
|
|
|
DISMISSED: 2,
|
|
|
|
|
APP_CLOSED: 3,
|
|
|
|
|
UNDEFINED: 4
|
|
|
|
|
};
|
|
|
|
|
|
2017-07-18 13:47:27 -04:00
|
|
|
|
var Urgency = {
|
2010-01-13 15:05:20 -05:00
|
|
|
|
LOW: 0,
|
|
|
|
|
NORMAL: 1,
|
|
|
|
|
CRITICAL: 2
|
|
|
|
|
};
|
|
|
|
|
|
2010-02-16 17:32:19 -05:00
|
|
|
|
const rewriteRules = {
|
|
|
|
|
'XChat': [
|
|
|
|
|
{ pattern: /^XChat: Private message from: (\S*) \(.*\)$/,
|
2010-11-29 11:12:21 -05:00
|
|
|
|
replacement: '<$1>' },
|
2010-02-16 17:32:19 -05:00
|
|
|
|
{ pattern: /^XChat: New public message from: (\S*) \((.*)\)$/,
|
2010-11-29 11:12:21 -05:00
|
|
|
|
replacement: '$2 <$1>' },
|
2010-02-16 17:32:19 -05:00
|
|
|
|
{ pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
|
2010-11-29 11:12:21 -05:00
|
|
|
|
replacement: '$2 <$1>' }
|
2010-02-16 17:32:19 -05:00
|
|
|
|
]
|
|
|
|
|
};
|
2010-06-23 15:20:39 -04:00
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var FdoNotificationDaemon = class FdoNotificationDaemon {
|
|
|
|
|
constructor() {
|
2013-10-13 10:44:28 -04:00
|
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
|
2011-08-16 08:26:49 -04:00
|
|
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2011-12-20 00:51:35 -05:00
|
|
|
|
this._sources = [];
|
2010-09-02 10:45:53 -04:00
|
|
|
|
this._senderToPid = {};
|
|
|
|
|
this._notifications = {};
|
|
|
|
|
this._busProxy = new Bus();
|
2010-02-21 00:25:23 -05:00
|
|
|
|
|
2013-10-13 23:42:59 -04:00
|
|
|
|
this._nextNotificationId = 1;
|
|
|
|
|
|
2014-06-13 09:06:45 -04:00
|
|
|
|
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
2017-12-01 19:27:35 -05:00
|
|
|
|
this._onFocusAppChanged.bind(this));
|
2014-06-13 09:06:45 -04:00
|
|
|
|
Main.overview.connect('hidden',
|
2017-12-01 19:27:35 -05:00
|
|
|
|
this._onFocusAppChanged.bind(this));
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_imageForNotificationData(hints) {
|
2012-12-30 11:45:43 -05:00
|
|
|
|
if (hints['image-data']) {
|
|
|
|
|
let [width, height, rowStride, hasAlpha,
|
2019-01-31 09:08:00 -05:00
|
|
|
|
bitsPerSample, nChannels_, data] = hints['image-data'];
|
2012-12-30 11:45:43 -05:00
|
|
|
|
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
|
|
|
|
bitsPerSample, width, height, rowStride);
|
2018-11-23 11:57:59 -05:00
|
|
|
|
} else if (hints['image-path']) {
|
2019-01-16 22:55:04 -05:00
|
|
|
|
return this._iconForNotificationData(hints['image-path']);
|
2012-12-30 11:45:43 -05:00
|
|
|
|
}
|
2018-11-23 11:57:59 -05:00
|
|
|
|
return null;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2012-12-30 11:45:43 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_fallbackIconForNotificationData(hints) {
|
2012-12-30 11:31:11 -05:00
|
|
|
|
let stockIcon;
|
|
|
|
|
switch (hints.urgency) {
|
2019-02-01 07:21:00 -05:00
|
|
|
|
case Urgency.LOW:
|
|
|
|
|
case Urgency.NORMAL:
|
|
|
|
|
stockIcon = 'dialog-information';
|
|
|
|
|
break;
|
|
|
|
|
case Urgency.CRITICAL:
|
|
|
|
|
stockIcon = 'dialog-error';
|
|
|
|
|
break;
|
2012-12-30 11:31:11 -05:00
|
|
|
|
}
|
|
|
|
|
return new Gio.ThemedIcon({ name: stockIcon });
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2012-12-30 11:31:11 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_iconForNotificationData(icon) {
|
2010-08-05 13:09:27 -04:00
|
|
|
|
if (icon) {
|
|
|
|
|
if (icon.substr(0, 7) == 'file://')
|
2012-07-19 09:05:17 -04:00
|
|
|
|
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
2012-12-30 11:25:28 -05:00
|
|
|
|
else if (icon[0] == '/')
|
2012-07-19 09:05:17 -04:00
|
|
|
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
2012-12-30 11:25:28 -05:00
|
|
|
|
else
|
2012-07-19 09:05:17 -04:00
|
|
|
|
return new Gio.ThemedIcon({ name: icon });
|
2010-08-05 13:09:27 -04:00
|
|
|
|
}
|
2012-12-30 11:31:11 -05:00
|
|
|
|
return null;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-08-05 13:09:27 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_lookupSource(title, pid) {
|
2011-12-20 00:51:35 -05:00
|
|
|
|
for (let i = 0; i < this._sources.length; i++) {
|
|
|
|
|
let source = this._sources[i];
|
2014-06-13 09:06:45 -04:00
|
|
|
|
if (source.pid == pid && source.initialTitle == title)
|
2011-12-20 00:51:35 -05:00
|
|
|
|
return source;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2011-12-20 00:51:35 -05:00
|
|
|
|
|
2010-12-15 16:30:50 -05:00
|
|
|
|
// Returns the source associated with ndata.notification if it is set.
|
2013-10-11 13:13:36 -04:00
|
|
|
|
// If the existing or requested source is associated with a tray icon
|
|
|
|
|
// and passed in pid matches a pid of an existing source, the title
|
|
|
|
|
// match is ignored to enable representing a tray icon and notifications
|
|
|
|
|
// from the same application with a single source.
|
2011-12-20 00:51:35 -05:00
|
|
|
|
//
|
|
|
|
|
// If no existing source is found, a new source is created as long as
|
|
|
|
|
// pid is provided.
|
2010-12-15 16:30:50 -05:00
|
|
|
|
//
|
|
|
|
|
// Either a pid or ndata.notification is needed to retrieve or
|
|
|
|
|
// create a source.
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_getSource(title, pid, ndata, sender) {
|
2010-12-15 16:30:50 -05:00
|
|
|
|
if (!pid && !(ndata && ndata.notification))
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
// We use notification's source for the notifications we still have
|
|
|
|
|
// around that are getting replaced because we don't keep sources
|
|
|
|
|
// for transient notifications in this._sources, but we still want
|
|
|
|
|
// the notification associated with them to get replaced correctly.
|
|
|
|
|
if (ndata && ndata.notification)
|
|
|
|
|
return ndata.notification.source;
|
|
|
|
|
|
2014-06-13 09:06:45 -04:00
|
|
|
|
let source = this._lookupSource(title, pid);
|
2013-10-11 13:13:36 -04:00
|
|
|
|
if (source) {
|
|
|
|
|
source.setTitle(title);
|
|
|
|
|
return source;
|
2011-06-08 02:27:57 -04:00
|
|
|
|
}
|
2010-12-15 16:30:50 -05:00
|
|
|
|
|
2017-06-13 00:49:07 -04:00
|
|
|
|
let appId = ndata ? ndata.hints['desktop-entry'] || null : null;
|
|
|
|
|
source = new FdoNotificationDaemonSource(title, pid, sender, appId);
|
2013-10-11 13:13:36 -04:00
|
|
|
|
|
|
|
|
|
this._sources.push(source);
|
2017-10-30 20:38:18 -04:00
|
|
|
|
source.connect('destroy', () => {
|
2013-10-11 13:13:36 -04:00
|
|
|
|
let index = this._sources.indexOf(source);
|
|
|
|
|
if (index >= 0)
|
|
|
|
|
this._sources.splice(index, 1);
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2010-08-09 12:33:34 -04:00
|
|
|
|
|
|
|
|
|
Main.messageTray.add(source);
|
|
|
|
|
return source;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-08-09 12:33:34 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
NotifyAsync(params, invocation) {
|
2011-08-16 08:26:49 -04:00
|
|
|
|
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
|
2010-09-02 10:45:53 -04:00
|
|
|
|
let id;
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2012-05-31 05:28:41 -04:00
|
|
|
|
for (let hint in hints) {
|
|
|
|
|
// unpack the variants
|
|
|
|
|
hints[hint] = hints[hint].deep_unpack();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
|
|
|
|
|
2011-07-11 07:09:45 -04:00
|
|
|
|
// Filter out chat, presence, calls and invitation notifications from
|
|
|
|
|
// Empathy, since we handle that information from telepathyClient.js
|
2015-03-03 03:30:01 -05:00
|
|
|
|
//
|
|
|
|
|
// Note that empathy uses im.received for one to one chats and
|
|
|
|
|
// x-empathy.im.mentioned for multi-user, so we're good here
|
|
|
|
|
if (appName == 'Empathy' && hints['category'] == 'im.received') {
|
2010-09-02 10:45:53 -04:00
|
|
|
|
// Ignore replacesId since we already sent back a
|
|
|
|
|
// NotificationClosed for that id.
|
2013-10-13 23:42:59 -04:00
|
|
|
|
id = this._nextNotificationId++;
|
2019-01-31 08:43:52 -05:00
|
|
|
|
let idleId = Mainloop.idle_add(() => {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
|
});
|
2019-01-31 08:43:52 -05:00
|
|
|
|
GLib.Source.set_name_by_id(idleId, '[gnome-shell] this._emitNotificationClosed');
|
2011-08-16 08:26:49 -04:00
|
|
|
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
2010-02-02 10:21:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
2010-02-16 17:32:19 -05:00
|
|
|
|
let rewrites = rewriteRules[appName];
|
|
|
|
|
if (rewrites) {
|
|
|
|
|
for (let i = 0; i < rewrites.length; i++) {
|
|
|
|
|
let rule = rewrites[i];
|
|
|
|
|
if (summary.search(rule.pattern) != -1)
|
|
|
|
|
summary = summary.replace(rule.pattern, rule.replacement);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-29 13:11:24 -04:00
|
|
|
|
// Be compatible with the various hints for image data and image path
|
|
|
|
|
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
|
|
|
|
|
|
|
|
|
if (!hints['image-path'] && hints['image_path'])
|
|
|
|
|
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
|
|
|
|
|
|
2013-10-11 13:35:51 -04:00
|
|
|
|
if (!hints['image-data']) {
|
2011-01-19 10:30:01 -05:00
|
|
|
|
if (hints['image_data'])
|
|
|
|
|
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
2011-08-29 13:11:24 -04:00
|
|
|
|
else if (hints['icon_data'] && !hints['image-path'])
|
|
|
|
|
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
|
|
|
|
|
hints['image-data'] = hints['icon_data'];
|
2013-10-11 13:35:51 -04:00
|
|
|
|
}
|
2011-01-19 10:30:01 -05:00
|
|
|
|
|
2010-09-02 10:45:53 -04:00
|
|
|
|
let ndata = { appName: appName,
|
|
|
|
|
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;
|
2013-10-13 23:42:59 -04:00
|
|
|
|
ndata.id = id = this._nextNotificationId++;
|
2010-09-02 10:45:53 -04:00
|
|
|
|
}
|
|
|
|
|
this._notifications[id] = ndata;
|
|
|
|
|
|
2011-08-16 08:26:49 -04:00
|
|
|
|
let sender = invocation.get_sender();
|
2010-09-02 10:45:53 -04:00
|
|
|
|
let pid = this._senderToPid[sender];
|
2010-12-15 16:30:50 -05:00
|
|
|
|
|
2011-12-20 00:51:35 -05:00
|
|
|
|
let source = this._getSource(appName, pid, ndata, sender, null);
|
2010-09-02 10:45:53 -04:00
|
|
|
|
|
|
|
|
|
if (source) {
|
|
|
|
|
this._notifyForSource(source, ndata);
|
2011-08-16 08:26:49 -04:00
|
|
|
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
2010-02-23 10:50:35 -05:00
|
|
|
|
}
|
|
|
|
|
|
2010-09-02 10:45:53 -04:00
|
|
|
|
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.
|
2019-01-28 20:18:52 -05:00
|
|
|
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
2010-09-02 10:45:53 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 20:38:18 -04:00
|
|
|
|
this._busProxy.GetConnectionUnixProcessIDRemote(sender, (result, excp) => {
|
2011-08-16 08:26:49 -04:00
|
|
|
|
// The app may have updated or removed the notification
|
|
|
|
|
ndata = this._notifications[id];
|
|
|
|
|
if (!ndata)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (excp) {
|
|
|
|
|
logError(excp, 'Call to GetConnectionUnixProcessID failed');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let [pid] = result;
|
2011-12-20 00:51:35 -05:00
|
|
|
|
source = this._getSource(appName, pid, ndata, sender, null);
|
2011-08-16 08:26:49 -04:00
|
|
|
|
|
2013-10-11 13:13:36 -04:00
|
|
|
|
this._senderToPid[sender] = pid;
|
2017-10-30 20:38:18 -04:00
|
|
|
|
source.connect('destroy', () => {
|
2013-10-11 13:13:36 -04:00
|
|
|
|
delete this._senderToPid[sender];
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2011-08-16 08:26:49 -04:00
|
|
|
|
this._notifyForSource(source, ndata);
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2010-09-02 10:45:53 -04:00
|
|
|
|
|
2011-08-16 08:26:49 -04:00
|
|
|
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-09-02 10:45:53 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_notifyForSource(source, ndata) {
|
2019-01-31 09:08:00 -05:00
|
|
|
|
let [id_, icon, summary, body, actions, hints, notification] =
|
2010-09-02 10:45:53 -04:00
|
|
|
|
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
|
|
|
|
ndata.actions, ndata.hints, ndata.notification];
|
|
|
|
|
|
2010-02-22 17:19:32 -05:00
|
|
|
|
if (notification == null) {
|
2012-12-30 11:37:43 -05:00
|
|
|
|
notification = new MessageTray.Notification(source);
|
2010-09-02 10:45:53 -04:00
|
|
|
|
ndata.notification = notification;
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notification.connect('destroy', (n, reason) => {
|
|
|
|
|
delete this._notifications[ndata.id];
|
|
|
|
|
let notificationClosedReason;
|
|
|
|
|
switch (reason) {
|
2019-02-01 07:21:00 -05:00
|
|
|
|
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
|
|
|
|
notificationClosedReason = NotificationClosedReason.EXPIRED;
|
|
|
|
|
break;
|
|
|
|
|
case MessageTray.NotificationDestroyedReason.DISMISSED:
|
|
|
|
|
notificationClosedReason = NotificationClosedReason.DISMISSED;
|
|
|
|
|
break;
|
|
|
|
|
case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
|
|
|
|
|
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
|
|
|
|
break;
|
2017-10-30 20:38:18 -04:00
|
|
|
|
}
|
|
|
|
|
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
|
|
|
|
});
|
2010-02-22 17:19:32 -05:00
|
|
|
|
}
|
2011-08-29 13:11:24 -04:00
|
|
|
|
|
2018-11-09 03:08:25 -05:00
|
|
|
|
let gicon = this._iconForNotificationData(icon);
|
2012-12-30 11:31:11 -05:00
|
|
|
|
let gimage = this._imageForNotificationData(hints);
|
|
|
|
|
|
|
|
|
|
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
|
|
|
|
// and don't show a large image. There are currently many applications that use
|
|
|
|
|
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
|
|
|
|
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
|
|
|
|
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
|
|
|
|
// So the logic here does the right thing for this case. If both an icon and either
|
2015-02-11 12:25:29 -05:00
|
|
|
|
// one of 'image-data' or 'image-path' are specified, the icon and takes precedence.
|
|
|
|
|
if (!gicon && gimage)
|
2012-12-30 11:31:11 -05:00
|
|
|
|
gicon = gimage;
|
|
|
|
|
else if (!gicon)
|
|
|
|
|
gicon = this._fallbackIconForNotificationData(hints);
|
|
|
|
|
|
2012-12-30 11:37:43 -05:00
|
|
|
|
notification.update(summary, body, { gicon: gicon,
|
|
|
|
|
bannerMarkup: true,
|
2012-11-05 12:10:24 -05:00
|
|
|
|
clear: true,
|
|
|
|
|
soundFile: hints['sound-file'],
|
|
|
|
|
soundName: hints['sound-name'] });
|
2012-12-30 11:37:43 -05:00
|
|
|
|
|
2013-10-13 23:20:50 -04:00
|
|
|
|
let hasDefaultAction = false;
|
|
|
|
|
|
2010-02-01 15:41:22 -05:00
|
|
|
|
if (actions.length) {
|
2011-08-02 09:42:45 -04:00
|
|
|
|
for (let i = 0; i < actions.length - 1; i += 2) {
|
2019-01-28 20:27:05 -05:00
|
|
|
|
let [actionId, label] = [actions[i], actions[i + 1]];
|
2015-02-16 10:43:30 -05:00
|
|
|
|
if (actionId == 'default')
|
2013-10-13 23:20:50 -04:00
|
|
|
|
hasDefaultAction = true;
|
2015-02-16 10:43:30 -05:00
|
|
|
|
else
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notification.addAction(label, () => {
|
2013-10-13 23:06:09 -04:00
|
|
|
|
this._emitActionInvoked(ndata.id, actionId);
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2011-08-02 09:42:45 -04:00
|
|
|
|
}
|
2010-02-01 15:41:22 -05:00
|
|
|
|
}
|
2013-10-13 23:20:50 -04:00
|
|
|
|
|
|
|
|
|
if (hasDefaultAction) {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notification.connect('activated', () => {
|
2013-10-13 23:20:50 -04:00
|
|
|
|
this._emitActionInvoked(ndata.id, 'default');
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2013-10-13 23:20:50 -04:00
|
|
|
|
} else {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notification.connect('activated', () => {
|
2013-10-13 23:20:50 -04:00
|
|
|
|
source.open();
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2013-10-13 23:20:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 04:34:57 -05:00
|
|
|
|
switch (hints.urgency) {
|
2019-02-01 07:21:00 -05:00
|
|
|
|
case Urgency.LOW:
|
|
|
|
|
notification.setUrgency(MessageTray.Urgency.LOW);
|
|
|
|
|
break;
|
|
|
|
|
case Urgency.NORMAL:
|
|
|
|
|
notification.setUrgency(MessageTray.Urgency.NORMAL);
|
|
|
|
|
break;
|
|
|
|
|
case Urgency.CRITICAL:
|
|
|
|
|
notification.setUrgency(MessageTray.Urgency.CRITICAL);
|
|
|
|
|
break;
|
2011-01-04 04:34:57 -05:00
|
|
|
|
}
|
2017-06-13 00:49:07 -04:00
|
|
|
|
notification.setResident(!!hints.resident);
|
2010-12-15 16:30:50 -05:00
|
|
|
|
// 'transient' is a reserved keyword in JS, so we have to retrieve the value
|
|
|
|
|
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
2017-06-13 00:49:07 -04:00
|
|
|
|
notification.setTransient(!!hints['transient']);
|
2010-04-28 15:34:27 -04:00
|
|
|
|
|
2018-11-01 16:56:25 -04:00
|
|
|
|
let privacyScope = (hints['x-gnome-privacy-scope'] || 'user');
|
|
|
|
|
notification.setPrivacyScope(privacyScope == 'system' ? MessageTray.PrivacyScope.SYSTEM
|
|
|
|
|
: MessageTray.PrivacyScope.USER);
|
|
|
|
|
|
2012-12-30 11:29:32 -05:00
|
|
|
|
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
2012-07-19 09:05:17 -04:00
|
|
|
|
source.processNotification(notification, sourceGIcon);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
CloseNotification(id) {
|
2010-09-02 10:45:53 -04:00
|
|
|
|
let ndata = this._notifications[id];
|
|
|
|
|
if (ndata) {
|
|
|
|
|
if (ndata.notification)
|
2011-01-27 18:26:53 -05:00
|
|
|
|
ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
2010-09-02 10:45:53 -04:00
|
|
|
|
delete this._notifications[id];
|
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
GetCapabilities() {
|
2010-01-13 15:05:20 -05:00
|
|
|
|
return [
|
2010-02-01 15:41:22 -05:00
|
|
|
|
'actions',
|
2015-02-16 10:43:30 -05:00
|
|
|
|
// 'action-icons',
|
2010-01-13 15:05:20 -05:00
|
|
|
|
'body',
|
|
|
|
|
// 'body-hyperlinks',
|
|
|
|
|
// 'body-images',
|
|
|
|
|
'body-markup',
|
|
|
|
|
// 'icon-multi',
|
2010-06-11 17:19:18 -04:00
|
|
|
|
'icon-static',
|
2010-10-08 22:34:04 -04:00
|
|
|
|
'persistence',
|
2012-11-05 12:10:24 -05:00
|
|
|
|
'sound',
|
2010-01-13 15:05:20 -05:00
|
|
|
|
];
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
GetServerInformation() {
|
2010-01-13 15:05:20 -05:00
|
|
|
|
return [
|
2011-01-11 16:34:09 -05:00
|
|
|
|
Config.PACKAGE_NAME,
|
2010-01-13 15:05:20 -05:00
|
|
|
|
'GNOME',
|
2011-01-11 16:34:09 -05:00
|
|
|
|
Config.PACKAGE_VERSION,
|
|
|
|
|
'1.2'
|
2010-01-13 15:05:20 -05:00
|
|
|
|
];
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_onFocusAppChanged() {
|
2010-02-24 15:46:00 -05:00
|
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
2010-08-09 13:18:15 -04:00
|
|
|
|
if (!tracker.focus_app)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-12-20 00:51:35 -05:00
|
|
|
|
for (let i = 0; i < this._sources.length; i++) {
|
|
|
|
|
let source = this._sources[i];
|
2010-08-09 13:18:15 -04:00
|
|
|
|
if (source.app == tracker.focus_app) {
|
2011-03-21 17:43:34 -04:00
|
|
|
|
source.destroyNonResidentNotifications();
|
2010-08-09 13:18:15 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-02-21 00:25:23 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_emitNotificationClosed(id, reason) {
|
2011-08-16 08:26:49 -04:00
|
|
|
|
this._dbusImpl.emit_signal('NotificationClosed',
|
|
|
|
|
GLib.Variant.new('(uu)', [id, reason]));
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-02-01 15:41:22 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_emitActionInvoked(id, action) {
|
2011-08-16 08:26:49 -04:00
|
|
|
|
this._dbusImpl.emit_signal('ActionInvoked',
|
|
|
|
|
GLib.Variant.new('(us)', [id, action]));
|
2010-01-13 15:05:20 -05:00
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
};
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var FdoNotificationDaemonSource =
|
|
|
|
|
class FdoNotificationDaemonSource extends MessageTray.Source {
|
|
|
|
|
constructor(title, pid, sender, appId) {
|
|
|
|
|
super(title);
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2012-11-03 20:53:43 -04:00
|
|
|
|
this.pid = pid;
|
|
|
|
|
this.app = this._getApp(appId);
|
|
|
|
|
|
2011-12-20 00:51:35 -05:00
|
|
|
|
this.initialTitle = title;
|
|
|
|
|
|
2012-11-03 20:53:43 -04:00
|
|
|
|
if (this.app)
|
|
|
|
|
this.title = this.app.get_name();
|
|
|
|
|
else
|
|
|
|
|
this.useNotificationIcon = true;
|
|
|
|
|
|
2011-07-06 17:01:07 -04:00
|
|
|
|
if (sender)
|
2011-08-16 08:26:49 -04:00
|
|
|
|
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
|
|
|
|
Gio.BusNameWatcherFlags.NONE,
|
|
|
|
|
null,
|
2017-12-01 19:27:35 -05:00
|
|
|
|
this._onNameVanished.bind(this));
|
2011-08-16 08:26:49 -04:00
|
|
|
|
else
|
|
|
|
|
this._nameWatcherId = 0;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_createPolicy() {
|
2013-11-04 11:45:22 -05:00
|
|
|
|
if (this.app && this.app.get_app_info()) {
|
2019-01-28 20:27:05 -05:00
|
|
|
|
let id = this.app.get_id().replace(/\.desktop$/, '');
|
2013-10-13 16:01:56 -04:00
|
|
|
|
return new MessageTray.NotificationApplicationPolicy(id);
|
2012-10-16 11:08:54 -04:00
|
|
|
|
} else {
|
2013-10-13 16:01:56 -04:00
|
|
|
|
return new MessageTray.NotificationGenericPolicy();
|
2012-10-16 11:08:54 -04:00
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2012-10-16 11:08:54 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_onNameVanished() {
|
2011-07-06 17:01:07 -04:00
|
|
|
|
// Destroy the notification source when its sender is removed from DBus.
|
2011-07-27 17:08:08 -04:00
|
|
|
|
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
|
|
|
|
|
// of which аre removed from DBus immediately.
|
2011-07-06 17:01:07 -04:00
|
|
|
|
// Sender being removed from DBus would normally result in a tray icon being removed,
|
|
|
|
|
// so allow the code path that handles the tray icon being removed to handle that case.
|
2014-06-13 09:06:45 -04:00
|
|
|
|
if (this.app)
|
2011-07-06 17:01:07 -04:00
|
|
|
|
this.destroy();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2011-07-06 17:01:07 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
processNotification(notification, gicon) {
|
2012-07-19 09:05:17 -04:00
|
|
|
|
if (gicon)
|
|
|
|
|
this._gicon = gicon;
|
2014-06-13 09:06:45 -04:00
|
|
|
|
this.iconUpdated();
|
2011-03-21 17:26:04 -04:00
|
|
|
|
|
|
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
|
|
|
|
if (notification.resident && this.app && tracker.focus_app == this.app)
|
|
|
|
|
this.pushNotification(notification);
|
|
|
|
|
else
|
|
|
|
|
this.notify(notification);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-02-15 12:27:28 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_getApp(appId) {
|
2012-04-08 21:39:59 -04:00
|
|
|
|
let app;
|
|
|
|
|
|
|
|
|
|
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
|
|
|
|
if (app != null)
|
|
|
|
|
return app;
|
|
|
|
|
|
2012-11-03 20:53:43 -04:00
|
|
|
|
if (appId) {
|
2019-01-29 19:18:24 -05:00
|
|
|
|
app = Shell.AppSystem.get_default().lookup_app(`${appId}.desktop`);
|
2012-04-08 21:39:59 -04:00
|
|
|
|
if (app != null)
|
|
|
|
|
return app;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2012-04-08 21:39:59 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
setTitle(title) {
|
2012-08-06 10:34:20 -04:00
|
|
|
|
// Do nothing if .app is set, we don't want to override the
|
|
|
|
|
// app name with whatever is provided through libnotify (usually
|
|
|
|
|
// garbage)
|
|
|
|
|
if (this.app)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
super.setTitle(title);
|
|
|
|
|
}
|
2012-08-06 10:34:20 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
open() {
|
2010-02-15 12:27:28 -05:00
|
|
|
|
this.openApp();
|
2013-10-07 16:11:18 -04:00
|
|
|
|
this.destroyNonResidentNotifications();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2010-09-30 16:56:50 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
openApp() {
|
2010-09-02 10:45:53 -04:00
|
|
|
|
if (this.app == null)
|
2010-02-15 12:27:28 -05:00
|
|
|
|
return;
|
2010-09-02 10:45:53 -04:00
|
|
|
|
|
2013-10-10 16:28:42 -04:00
|
|
|
|
this.app.activate();
|
|
|
|
|
Main.overview.hide();
|
2015-02-13 23:59:01 -05:00
|
|
|
|
Main.panel.closeCalendar();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2011-03-07 18:10:10 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
destroy() {
|
2011-08-16 08:26:49 -04:00
|
|
|
|
if (this._nameWatcherId) {
|
|
|
|
|
Gio.DBus.session.unwatch_name(this._nameWatcherId);
|
|
|
|
|
this._nameWatcherId = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
super.destroy();
|
|
|
|
|
}
|
2011-10-08 18:00:32 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
createIcon(size) {
|
2014-06-13 09:06:45 -04:00
|
|
|
|
if (this.app) {
|
2012-07-19 09:05:17 -04:00
|
|
|
|
return this.app.create_icon_texture(size);
|
|
|
|
|
} else if (this._gicon) {
|
|
|
|
|
return new St.Icon({ gicon: this._gicon,
|
|
|
|
|
icon_size: size });
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2010-01-13 15:05:20 -05:00
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
};
|
2013-10-13 10:44:28 -04:00
|
|
|
|
|
2014-07-01 15:47:31 -04:00
|
|
|
|
const PRIORITY_URGENCY_MAP = {
|
|
|
|
|
low: MessageTray.Urgency.LOW,
|
|
|
|
|
normal: MessageTray.Urgency.NORMAL,
|
|
|
|
|
high: MessageTray.Urgency.HIGH,
|
|
|
|
|
urgent: MessageTray.Urgency.CRITICAL
|
|
|
|
|
};
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var GtkNotificationDaemonNotification =
|
|
|
|
|
class GtkNotificationDaemonNotification extends MessageTray.Notification {
|
|
|
|
|
constructor(source, notification) {
|
|
|
|
|
super(source);
|
2013-10-13 18:52:37 -04:00
|
|
|
|
this._serialized = GLib.Variant.new('a{sv}', notification);
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
|
|
|
|
let { "title": title,
|
|
|
|
|
"body": body,
|
|
|
|
|
"icon": gicon,
|
|
|
|
|
"urgent": urgent,
|
2014-07-01 15:47:31 -04:00
|
|
|
|
"priority": priority,
|
2013-10-13 13:04:02 -04:00
|
|
|
|
"buttons": buttons,
|
|
|
|
|
"default-action": defaultAction,
|
2017-02-26 18:15:29 -05:00
|
|
|
|
"default-action-target": defaultActionTarget,
|
|
|
|
|
"timestamp": time } = notification;
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2014-07-01 15:47:31 -04:00
|
|
|
|
if (priority) {
|
|
|
|
|
let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
|
|
|
|
|
this.setUrgency(urgency != undefined ? urgency : MessageTray.Urgency.NORMAL);
|
|
|
|
|
} else if (urgent) {
|
|
|
|
|
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
|
|
|
|
|
: MessageTray.Urgency.NORMAL);
|
|
|
|
|
} else {
|
|
|
|
|
this.setUrgency(MessageTray.Urgency.NORMAL);
|
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
|
|
|
|
if (buttons) {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
buttons.deep_unpack().forEach(button => {
|
2017-12-01 19:27:35 -05:00
|
|
|
|
this.addAction(button.label.unpack(), () => {
|
|
|
|
|
this._onButtonClicked(button);
|
|
|
|
|
});
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2013-10-13 13:04:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
|
2013-10-21 16:21:22 -04:00
|
|
|
|
this._defaultActionTarget = defaultActionTarget;
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2013-10-27 06:32:27 -04:00
|
|
|
|
this.update(title.unpack(), body ? body.unpack() : null,
|
2017-02-26 18:15:29 -05:00
|
|
|
|
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null,
|
2019-01-28 20:27:05 -05:00
|
|
|
|
datetime: time ? GLib.DateTime.new_from_unix_local(time.unpack()) : null });
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_activateAction(namespacedActionId, target) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
if (namespacedActionId) {
|
|
|
|
|
if (namespacedActionId.startsWith('app.')) {
|
|
|
|
|
let actionId = namespacedActionId.slice('app.'.length);
|
|
|
|
|
this.source.activateAction(actionId, target);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.source.open();
|
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_onButtonClicked(button) {
|
2013-10-25 22:40:22 -04:00
|
|
|
|
let { 'action': action, 'target': actionTarget } = button;
|
2013-10-21 16:21:22 -04:00
|
|
|
|
this._activateAction(action.unpack(), actionTarget);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
activate() {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
this._activateAction(this._defaultAction, this._defaultActionTarget);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
super.activate();
|
|
|
|
|
}
|
2013-10-13 18:52:37 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
serialize() {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
return this._serialized;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
|
|
|
|
};
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
|
const FdoApplicationIface = loadInterfaceXML('org.freedesktop.Application');
|
2013-10-13 13:04:02 -04:00
|
|
|
|
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
|
|
|
|
|
|
|
|
|
|
function objectPathFromAppId(appId) {
|
2016-11-18 09:52:46 -05:00
|
|
|
|
return '/' + appId.replace(/\./g, '/').replace(/-/g, '_');
|
2013-10-13 13:04:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getPlatformData() {
|
2019-01-29 19:18:24 -05:00
|
|
|
|
let startupId = GLib.Variant.new('s', `_TIME${global.get_current_time()}`);
|
2013-10-13 13:04:02 -04:00
|
|
|
|
return { "desktop-startup-id": startupId };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function InvalidAppError() {}
|
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var GtkNotificationDaemonAppSource =
|
|
|
|
|
class GtkNotificationDaemonAppSource extends MessageTray.Source {
|
|
|
|
|
constructor(appId) {
|
|
|
|
|
let objectPath = objectPathFromAppId(appId);
|
|
|
|
|
if (!GLib.Variant.is_object_path(objectPath))
|
2016-11-18 09:52:46 -05:00
|
|
|
|
throw new InvalidAppError();
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2019-01-29 19:18:24 -05:00
|
|
|
|
let app = Shell.AppSystem.get_default().lookup_app(`${appId}.desktop`);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
if (!app)
|
2013-10-13 13:04:02 -04:00
|
|
|
|
throw new InvalidAppError();
|
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
super(app.get_name());
|
|
|
|
|
|
|
|
|
|
this._appId = appId;
|
|
|
|
|
this._app = app;
|
|
|
|
|
this._objectPath = objectPath;
|
|
|
|
|
|
2013-10-13 13:04:02 -04:00
|
|
|
|
this._notifications = {};
|
2016-11-26 13:32:22 -05:00
|
|
|
|
this._notificationPending = false;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
createIcon(size) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
return this._app.create_icon_texture(size);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_createPolicy() {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
return new MessageTray.NotificationApplicationPolicy(this._appId);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_createApp(callback) {
|
2016-11-26 08:59:48 -05:00
|
|
|
|
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath, callback);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2018-10-10 20:28:43 -04:00
|
|
|
|
_createNotification(params) {
|
|
|
|
|
return new GtkNotificationDaemonNotification(this, params);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2018-10-10 20:28:43 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
activateAction(actionId, target) {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
this._createApp((app, error) => {
|
2016-11-26 08:59:48 -05:00
|
|
|
|
if (error == null)
|
|
|
|
|
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
|
|
|
|
else
|
|
|
|
|
logError(error, 'Failed to activate application proxy');
|
|
|
|
|
});
|
2015-02-14 00:01:20 -05:00
|
|
|
|
Main.overview.hide();
|
|
|
|
|
Main.panel.closeCalendar();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
open() {
|
2017-10-30 20:38:18 -04:00
|
|
|
|
this._createApp((app, error) => {
|
2016-11-26 08:59:48 -05:00
|
|
|
|
if (error == null)
|
|
|
|
|
app.ActivateRemote(getPlatformData());
|
|
|
|
|
else
|
|
|
|
|
logError(error, 'Failed to open application proxy');
|
|
|
|
|
});
|
2015-02-14 00:01:20 -05:00
|
|
|
|
Main.overview.hide();
|
|
|
|
|
Main.panel.closeCalendar();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
addNotification(notificationId, notificationParams, showBanner) {
|
2016-11-26 13:32:22 -05:00
|
|
|
|
this._notificationPending = true;
|
|
|
|
|
|
2013-10-13 13:04:02 -04:00
|
|
|
|
if (this._notifications[notificationId])
|
2018-10-10 21:26:31 -04:00
|
|
|
|
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.REPLACED);
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2018-10-10 20:28:43 -04:00
|
|
|
|
let notification = this._createNotification(notificationParams);
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notification.connect('destroy', () => {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
delete this._notifications[notificationId];
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2013-10-13 13:04:02 -04:00
|
|
|
|
this._notifications[notificationId] = notification;
|
|
|
|
|
|
2013-10-13 18:52:37 -04:00
|
|
|
|
if (showBanner)
|
|
|
|
|
this.notify(notification);
|
|
|
|
|
else
|
|
|
|
|
this.pushNotification(notification);
|
2016-11-26 13:32:22 -05:00
|
|
|
|
|
|
|
|
|
this._notificationPending = false;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2016-11-26 13:32:22 -05:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
destroy(reason) {
|
2016-11-26 13:32:22 -05:00
|
|
|
|
if (this._notificationPending)
|
|
|
|
|
return;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
super.destroy(reason);
|
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
removeNotification(notificationId) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
if (this._notifications[notificationId])
|
|
|
|
|
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 18:52:37 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
serialize() {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
let notifications = [];
|
|
|
|
|
for (let notificationId in this._notifications) {
|
|
|
|
|
let notification = this._notifications[notificationId];
|
|
|
|
|
notifications.push([notificationId, notification.serialize()]);
|
|
|
|
|
}
|
2013-10-21 16:02:03 -04:00
|
|
|
|
return [this._appId, notifications];
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
|
|
|
|
};
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
|
const GtkNotificationsIface = loadInterfaceXML('org.gtk.Notifications');
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var GtkNotificationDaemon = class GtkNotificationDaemon {
|
|
|
|
|
constructor() {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
this._sources = {};
|
|
|
|
|
|
2013-10-13 18:52:37 -04:00
|
|
|
|
this._loadNotifications();
|
|
|
|
|
|
2013-10-13 13:04:02 -04:00
|
|
|
|
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);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_ensureAppSource(appId) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
if (this._sources[appId])
|
|
|
|
|
return this._sources[appId];
|
|
|
|
|
|
|
|
|
|
let source = new GtkNotificationDaemonAppSource(appId);
|
|
|
|
|
|
2017-10-30 20:38:18 -04:00
|
|
|
|
source.connect('destroy', () => {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
delete this._sources[appId];
|
2013-10-28 06:26:17 -04:00
|
|
|
|
this._saveNotifications();
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2017-12-01 19:27:35 -05:00
|
|
|
|
source.connect('count-updated', this._saveNotifications.bind(this));
|
2013-10-13 13:04:02 -04:00
|
|
|
|
Main.messageTray.add(source);
|
|
|
|
|
this._sources[appId] = source;
|
|
|
|
|
return source;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_loadNotifications() {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
this._isLoading = true;
|
|
|
|
|
|
2013-10-21 16:02:03 -04:00
|
|
|
|
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
|
2013-10-13 18:52:37 -04:00
|
|
|
|
if (value) {
|
|
|
|
|
let sources = value.deep_unpack();
|
2017-10-30 20:38:18 -04:00
|
|
|
|
sources.forEach(([appId, notifications]) => {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
if (notifications.length == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
let source;
|
|
|
|
|
try {
|
|
|
|
|
source = this._ensureAppSource(appId);
|
2019-01-28 20:26:39 -05:00
|
|
|
|
} catch (e) {
|
2018-07-14 21:17:42 -04:00
|
|
|
|
if (e instanceof InvalidAppError)
|
|
|
|
|
return;
|
|
|
|
|
throw e;
|
2013-10-13 18:52:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 20:38:18 -04:00
|
|
|
|
notifications.forEach(([notificationId, notification]) => {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
source.addNotification(notificationId, notification.deep_unpack(), false);
|
|
|
|
|
});
|
2017-10-30 20:38:18 -04:00
|
|
|
|
});
|
2013-10-13 18:52:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._isLoading = false;
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 18:52:37 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
_saveNotifications() {
|
2013-10-13 18:52:37 -04:00
|
|
|
|
if (this._isLoading)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
let sources = [];
|
|
|
|
|
for (let appId in this._sources) {
|
|
|
|
|
let source = this._sources[appId];
|
|
|
|
|
sources.push(source.serialize());
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 16:02:03 -04:00
|
|
|
|
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 18:52:37 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
AddNotificationAsync(params, invocation) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
let [appId, notificationId, notification] = params;
|
|
|
|
|
|
|
|
|
|
let source;
|
|
|
|
|
try {
|
|
|
|
|
source = this._ensureAppSource(appId);
|
2019-01-28 20:26:39 -05:00
|
|
|
|
} catch (e) {
|
2018-07-14 21:17:42 -04:00
|
|
|
|
if (e instanceof InvalidAppError) {
|
|
|
|
|
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
throw e;
|
2013-10-13 13:04:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 18:15:29 -05:00
|
|
|
|
let timestamp = GLib.DateTime.new_now_local().to_unix();
|
|
|
|
|
notification['timestamp'] = new GLib.Variant('x', timestamp);
|
|
|
|
|
|
2013-10-13 18:52:37 -04:00
|
|
|
|
source.addNotification(notificationId, notification, true);
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
|
|
|
|
invocation.return_value(null);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
2013-10-13 13:04:02 -04:00
|
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
|
RemoveNotificationAsync(params, invocation) {
|
2013-10-13 13:04:02 -04:00
|
|
|
|
let [appId, notificationId] = params;
|
|
|
|
|
let source = this._sources[appId];
|
|
|
|
|
if (source)
|
|
|
|
|
source.removeNotification(notificationId);
|
|
|
|
|
|
|
|
|
|
invocation.return_value(null);
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
|
|
|
|
};
|
2013-10-13 10:44:28 -04:00
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
|
var NotificationDaemon = class NotificationDaemon {
|
|
|
|
|
constructor() {
|
2013-10-13 10:44:28 -04:00
|
|
|
|
this._fdoNotificationDaemon = new FdoNotificationDaemon();
|
2013-10-13 13:04:02 -04:00
|
|
|
|
this._gtkNotificationDaemon = new GtkNotificationDaemon();
|
2017-10-30 21:19:44 -04:00
|
|
|
|
}
|
|
|
|
|
};
|