MessageTray: introduce configurable per-source notification policy

Allow message tray sources to provide a NotificationPolicy object,
that will configure how and if the source is displayed. For notification
daemon sources, this object is hooked to GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
This commit is contained in:
Giovanni Campagna 2012-10-16 17:08:54 +02:00
parent 4dc5bac72f
commit 098bd4509b
4 changed files with 203 additions and 13 deletions

View File

@ -293,7 +293,6 @@ const AutorunResidentSource = new Lang.Class({
_init: function(manager) { _init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable'); this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = []; this._mounts = [];
@ -301,6 +300,10 @@ const AutorunResidentSource = new Lang.Class({
this._notification = new AutorunResidentNotification(this._manager, this); this._notification = new AutorunResidentNotification(this._manager, this);
}, },
_createPolicy: function() {
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
},
buildRightClickMenu: function() { buildRightClickMenu: function() {
return null; return null;
}, },

View File

@ -248,6 +248,32 @@ function strHasSuffix(string, suffix) {
return string.substr(-suffix.length) == suffix; return string.substr(-suffix.length) == suffix;
} }
// NotificationPolicy:
// An object that holds all bits of configurable policy related to a notification
// source, such as whether to play sound or honour the critical bit.
//
// A notification without a policy object will inherit the default one.
const NotificationPolicy = new Lang.Class({
Name: 'NotificationPolicy',
_init: function(params) {
params = Params.parse(params, { enable: true,
enableSound: true,
showBanners: true,
forceExpanded: false,
showInLockScreen: true,
detailsInLockScreen: false
});
Lang.copyProperties(params, this);
},
// Do nothing for the default policy. These methods are only useful for the
// GSettings policy.
store: function() { },
destroy: function() { }
});
Signals.addSignalMethods(NotificationPolicy.prototype);
// Notification: // Notification:
// @source: the notification's Source // @source: the notification's Source
// @title: the title // @title: the title
@ -1089,10 +1115,11 @@ const Source = new Lang.Class({
this.isTransient = false; this.isTransient = false;
this.isChat = false; this.isChat = false;
this.isMuted = false; this.isMuted = false;
this.showInLockScreen = true;
this.keepTrayOnSummaryClick = false; this.keepTrayOnSummaryClick = false;
this.notifications = []; this.notifications = [];
this.policy = this._createPolicy();
}, },
get count() { get count() {
@ -1111,6 +1138,10 @@ const Source = new Lang.Class({
this.emit('count-updated'); this.emit('count-updated');
}, },
_createPolicy: function() {
return new NotificationPolicy();
},
buildRightClickMenu: function() { buildRightClickMenu: function() {
let item; let item;
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu', let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
@ -1200,11 +1231,13 @@ const Source = new Lang.Class({
notify: function(notification) { notify: function(notification) {
notification.acknowledged = false; notification.acknowledged = false;
this.pushNotification(notification); this.pushNotification(notification);
if (!this.isMuted)
this.emit('notify', notification); if (!this.isMuted && this.policy.showBanners)
this.emit('notify', notification);
}, },
destroy: function(reason) { destroy: function(reason) {
this.policy.destroy();
this.emit('destroy', reason); this.emit('destroy', reason);
}, },
@ -1300,6 +1333,14 @@ const SummaryItem = new Lang.Class({
global.focus_manager.add_group(this.rightClickMenu); global.focus_manager.add_group(this.rightClickMenu);
}, },
destroy: function() {
// remove the actor from the summary item so it doesn't get destroyed
// with us
this._sourceBox.remove_actor(this._sourceIcon);
this.actor.destroy();
},
_onKeyPress: function(actor, event) { _onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) { if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1); actor.emit('clicked', 1);
@ -1682,7 +1723,12 @@ const MessageTray = new Lang.Class({
return; return;
} }
this._addSource(source); // Register that we got a notification for this source
source.policy.store();
source.policy.connect('enable-changed', Lang.bind(this, this._onSourceEnableChanged, source));
source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
this._onSourceEnableChanged(source.policy, source);
}, },
_addSource: function(source) { _addSource: function(source) {
@ -1773,6 +1819,18 @@ const MessageTray = new Lang.Class({
}); });
}, },
_onSourceEnableChanged: function(policy, source) {
let wasEnabled = this.contains(source);
let shouldBeEnabled = policy.enable;
if (wasEnabled != shouldBeEnabled) {
if (shouldBeEnabled)
this._addSource(source);
else
this._removeSource(source);
}
},
_onSourceDestroy: function(source) { _onSourceDestroy: function(source) {
this._removeSource(source); this._removeSource(source);
}, },
@ -2303,8 +2361,10 @@ const MessageTray = new Lang.Class({
_updateShowingNotification: function() { _updateShowingNotification: function() {
this._notification.acknowledged = true; this._notification.acknowledged = true;
// We auto-expand notifications with CRITICAL urgency. // We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
if (this._notification.urgency == Urgency.CRITICAL) // is on in the control center.
if (this._notification.urgency == Urgency.CRITICAL ||
this._notification.source.policy.forceExpanded)
this._expandNotification(true); this._expandNotification(true);
// We tween all notifications to full opacity. This ensures that both new notifications and // We tween all notifications to full opacity. This ensures that both new notifications and

View File

@ -103,6 +103,126 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard' 'ibus-ui-gtk': 'keyboard'
}; };
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function() {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
},
store: function() { },
destroy: function() {
this._masterSettings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
get enable() {
return true;
},
get enableSound() {
return true;
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners');
},
get forceExpanded() {
return false;
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return false;
}
});
const NotificationApplicationPolicy = new Lang.Class({
Name: 'NotificationApplicationPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function(id) {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = id;
this._canonicalId = this._canonicalizeId(id)
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
this._settings.connect('changed', Lang.bind(this, this._changed));
},
store: function() {
this._settings.set_string('application-id', this.id + '.desktop');
let apps = this._masterSettings.get_strv('application-children');
if (apps.indexOf(this._canonicalId) < 0) {
apps.push(this._canonicalId);
this._masterSettings.set_strv('application-children', apps);
}
},
destroy: function() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
_canonicalizeId: function(id) {
// Keys are restricted to lowercase alphanumeric characters and dash,
// and two dashes cannot be in succession
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
},
get enable() {
return this._settings.get_boolean('enable');
},
get enableSound() {
return this._settings.get_boolean('enable-sound-alerts');
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners') &&
this._settings.get_boolean('show-banners');
},
get forceExpanded() {
return this._settings.get_boolean('force-expanded');
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen') &&
this._settings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
});
const NotificationDaemon = new Lang.Class({ const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon', Name: 'NotificationDaemon',
@ -546,6 +666,15 @@ const Source = new Lang.Class({
} }
}, },
_createPolicy: function() {
if (this.app) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id);
} else {
return new NotificationGenericPolicy();
}
},
_onNameVanished: function() { _onNameVanished: function() {
// Destroy the notification source when its sender is removed from DBus. // Destroy the notification source when its sender is removed from DBus.
// Only do so if this.app is set to avoid removing "notify-send" sources, senders // Only do so if this.app is set to avoid removing "notify-send" sources, senders

View File

@ -851,12 +851,10 @@ const ScreenShield = new Lang.Class({
this._lockScreenContents.add_actor(this._lockScreenContentsBox); this._lockScreenContents.add_actor(this._lockScreenContentsBox);
if (this._settings.get_boolean('show-notifications')) { this._notificationsBox = new NotificationsBox();
this._notificationsBox = new NotificationsBox(); this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true, y_fill: true,
y_fill: true, expand: true });
expand: true });
}
this._hasLockScreen = true; this._hasLockScreen = true;
}, },