NotificationDaemon: add support for transient notifications
Transient notifications are removed after being shown. If the summary is being shown while they appear, they are represented in it by a new source icon. We always create a new source for new transient notifications to ensure that they don't replace the latest persistent notification associated with the source. Because we generally don't want any new or resident notifications to be replaced by others, associating multiple notifications with a source is the next thing we will implement. https://bugzilla.gnome.org/show_bug.cgi?id=633412
This commit is contained in:
parent
2a19d5f143
commit
22f4aabadf
@ -227,6 +227,8 @@ Notification.prototype = {
|
|||||||
this.source = source;
|
this.source = source;
|
||||||
this.urgent = false;
|
this.urgent = false;
|
||||||
this.resident = false;
|
this.resident = false;
|
||||||
|
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
|
||||||
|
this.isTransient = false;
|
||||||
this.expanded = false;
|
this.expanded = false;
|
||||||
this._useActionIcons = false;
|
this._useActionIcons = false;
|
||||||
this._customContent = false;
|
this._customContent = false;
|
||||||
@ -497,6 +499,10 @@ Notification.prototype = {
|
|||||||
this.resident = resident;
|
this.resident = resident;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTransient: function(isTransient) {
|
||||||
|
this.isTransient = isTransient;
|
||||||
|
},
|
||||||
|
|
||||||
setUseActionIcons: function(useIcons) {
|
setUseActionIcons: function(useIcons) {
|
||||||
this._useActionIcons = useIcons;
|
this._useActionIcons = useIcons;
|
||||||
},
|
},
|
||||||
@ -808,6 +814,11 @@ Source.prototype = {
|
|||||||
this.title = title;
|
this.title = title;
|
||||||
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
||||||
height: this.ICON_SIZE });
|
height: this.ICON_SIZE });
|
||||||
|
this.isTransient = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
setTransient: function(isTransient) {
|
||||||
|
this.isTransient = isTransient;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called to create a new icon actor (of size this.ICON_SIZE).
|
// Called to create a new icon actor (of size this.ICON_SIZE).
|
||||||
@ -1068,6 +1079,14 @@ MessageTray.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._summaryItems.push(summaryItem);
|
this._summaryItems.push(summaryItem);
|
||||||
|
|
||||||
|
// We keep this._newSummaryItems to track any new sources that were added to the
|
||||||
|
// summary and show the summary with them to the user for a short period of time
|
||||||
|
// after notifications are done showing. However, we don't want that to happen for
|
||||||
|
// transient sources, which are removed after the notification is shown, but are
|
||||||
|
// not removed fast enough because of the callbacks to avoid the summary popping up.
|
||||||
|
// So we just don't add transient sources to this._newSummaryItems .
|
||||||
|
if (!source.isTransient)
|
||||||
this._newSummaryItems.push(summaryItem);
|
this._newSummaryItems.push(summaryItem);
|
||||||
|
|
||||||
source.connect('notify', Lang.bind(this, this._onNotify));
|
source.connect('notify', Lang.bind(this, this._onNotify));
|
||||||
@ -1601,7 +1620,10 @@ MessageTray.prototype = {
|
|||||||
this._notification.collapseCompleted();
|
this._notification.collapseCompleted();
|
||||||
this._notification.disconnect(this._notificationClickedId);
|
this._notification.disconnect(this._notificationClickedId);
|
||||||
this._notificationClickedId = 0;
|
this._notificationClickedId = 0;
|
||||||
|
let notification = this._notification;
|
||||||
this._notification = null;
|
this._notification = null;
|
||||||
|
if (notification.isTransient)
|
||||||
|
notification.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
_expandNotification: function(autoExpanding) {
|
_expandNotification: function(autoExpanding) {
|
||||||
@ -1750,6 +1772,8 @@ MessageTray.prototype = {
|
|||||||
this._summaryNotificationClickedId = 0;
|
this._summaryNotificationClickedId = 0;
|
||||||
let summaryNotification = this._summaryNotification;
|
let summaryNotification = this._summaryNotification;
|
||||||
this._summaryNotification = null;
|
this._summaryNotification = null;
|
||||||
|
if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
|
||||||
|
summaryNotification.destroy();
|
||||||
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
||||||
this._onNotify(summaryNotification.source, summaryNotification);
|
this._onNotify(summaryNotification.source, summaryNotification);
|
||||||
this._reNotifyWithSummaryNotificationAfterHide = false;
|
this._reNotifyWithSummaryNotificationAfterHide = false;
|
||||||
|
@ -175,14 +175,43 @@ NotificationDaemon.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_newSource: function(title, pid) {
|
// Returns the source associated with ndata.notification if it is set.
|
||||||
let source = new Source(title, pid);
|
// Otherwise, returns the source associated with the pid if one is
|
||||||
this._sources[pid] = source;
|
// stored in this._sources and the notification is not transient.
|
||||||
|
// Otherwise, creates a new source as long as pid is provided.
|
||||||
|
//
|
||||||
|
// Either a pid or ndata.notification is needed to retrieve or
|
||||||
|
// create a source.
|
||||||
|
_getSource: function(title, pid, ndata) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
|
||||||
|
|
||||||
|
// We don't want to override a persistent notification
|
||||||
|
// with a transient one from the same sender, so we
|
||||||
|
// always create a new source object for new transient notifications
|
||||||
|
// and never add it to this._sources .
|
||||||
|
if (!isForTransientNotification && this._sources[pid])
|
||||||
|
return this._sources[pid];
|
||||||
|
|
||||||
|
let source = new Source(title, pid);
|
||||||
|
source.setTransient(isForTransientNotification);
|
||||||
|
|
||||||
|
if (!isForTransientNotification) {
|
||||||
|
this._sources[pid] = source;
|
||||||
source.connect('destroy', Lang.bind(this,
|
source.connect('destroy', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
delete this._sources[pid];
|
delete this._sources[pid];
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
Main.messageTray.add(source);
|
Main.messageTray.add(source);
|
||||||
return source;
|
return source;
|
||||||
@ -234,7 +263,8 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
let sender = DBus.getCurrentMessageContext().sender;
|
let sender = DBus.getCurrentMessageContext().sender;
|
||||||
let pid = this._senderToPid[sender];
|
let pid = this._senderToPid[sender];
|
||||||
let source = pid ? this._sources[pid] : null;
|
|
||||||
|
let source = this._getSource(appName, pid, ndata);
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
this._notifyForSource(source, ndata);
|
this._notifyForSource(source, ndata);
|
||||||
@ -255,16 +285,23 @@ NotificationDaemon.prototype = {
|
|||||||
if (!ndata)
|
if (!ndata)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._senderToPid[sender] = pid;
|
source = this._getSource(appName, pid, ndata);
|
||||||
source = this._sources[pid];
|
|
||||||
|
|
||||||
if (!source)
|
// We only store sender-pid entries for persistent sources.
|
||||||
source = this._newSource(appName, pid);
|
// Removing the entries once the source is destroyed
|
||||||
|
// would result in the entries associated with transient
|
||||||
|
// sources removed once the notification is shown anyway.
|
||||||
|
// However, keeping these pairs would mean that we would
|
||||||
|
// possibly remove an entry associated with a persistent
|
||||||
|
// source when a transient source for the same sender is
|
||||||
|
// distroyed.
|
||||||
|
if (!source.isTransient) {
|
||||||
|
this._senderToPid[sender] = pid;
|
||||||
source.connect('destroy', Lang.bind(this,
|
source.connect('destroy', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
delete this._senderToPid[sender];
|
delete this._senderToPid[sender];
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
this._notifyForSource(source, ndata);
|
this._notifyForSource(source, ndata);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -309,6 +346,9 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||||
notification.setResident(hints.resident == true);
|
notification.setResident(hints.resident == true);
|
||||||
|
// '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
|
||||||
|
notification.setTransient(hints['transient'] == true);
|
||||||
|
|
||||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||||
source.notify(notification, sourceIconActor);
|
source.notify(notification, sourceIconActor);
|
||||||
@ -378,9 +418,7 @@ NotificationDaemon.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon) {
|
_onTrayIconAdded: function(o, icon) {
|
||||||
let source = this._sources[icon.pid];
|
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
|
||||||
if (!source)
|
|
||||||
source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
|
|
||||||
source.setTrayIcon(icon);
|
source.setTrayIcon(icon);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user