[gnome-shell] MessageTray: introduce configurable per-source notification policy



commit 098bd4509ba0de91ada3cfdce29439c9754a2d44
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Tue Oct 16 17:08:54 2012 +0200

    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

 js/ui/components/autorunManager.js |    5 +-
 js/ui/messageTray.js               |   72 ++++++++++++++++++--
 js/ui/notificationDaemon.js        |  129 ++++++++++++++++++++++++++++++++++++
 js/ui/screenShield.js              |   10 +--
 4 files changed, 203 insertions(+), 13 deletions(-)
---
diff --git a/js/ui/components/autorunManager.js b/js/ui/components/autorunManager.js
index 7709a3d..6119c3a 100644
--- a/js/ui/components/autorunManager.js
+++ b/js/ui/components/autorunManager.js
@@ -293,7 +293,6 @@ const AutorunResidentSource = new Lang.Class({
 
     _init: function(manager) {
         this.parent(_("Removable Devices"), 'media-removable');
-        this.showInLockScreen = false;
 
         this._mounts = [];
 
@@ -301,6 +300,10 @@ const AutorunResidentSource = new Lang.Class({
         this._notification = new AutorunResidentNotification(this._manager, this);
     },
 
+    _createPolicy: function() {
+        return new MessageTray.NotificationPolicy({ showInLockScreen: false });
+    },
+
     buildRightClickMenu: function() {
         return null;
     },
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index eec5a3b..103248c 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -248,6 +248,32 @@ function strHasSuffix(string, 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:
 // @source: the notification's Source
 // @title: the title
@@ -1089,10 +1115,11 @@ const Source = new Lang.Class({
         this.isTransient = false;
         this.isChat = false;
         this.isMuted = false;
-        this.showInLockScreen = true;
         this.keepTrayOnSummaryClick = false;
 
         this.notifications = [];
+
+        this.policy = this._createPolicy();
     },
 
     get count() {
@@ -1111,6 +1138,10 @@ const Source = new Lang.Class({
         this.emit('count-updated');
     },
 
+    _createPolicy: function() {
+        return new NotificationPolicy();
+    },
+
     buildRightClickMenu: function() {
         let item;
         let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
@@ -1200,11 +1231,13 @@ const Source = new Lang.Class({
     notify: function(notification) {
         notification.acknowledged = false;
         this.pushNotification(notification);
-        if (!this.isMuted)
-             this.emit('notify', notification);
+
+        if (!this.isMuted && this.policy.showBanners)
+            this.emit('notify', notification);
     },
 
     destroy: function(reason) {
+        this.policy.destroy();
         this.emit('destroy', reason);
     },
 
@@ -1300,6 +1333,14 @@ const SummaryItem = new Lang.Class({
             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) {
         if (event.get_key_symbol() == Clutter.KEY_Up) {
             actor.emit('clicked', 1);
@@ -1682,7 +1723,12 @@ const MessageTray = new Lang.Class({
             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) {
@@ -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) {
         this._removeSource(source);
     },
@@ -2303,8 +2361,10 @@ const MessageTray = new Lang.Class({
     _updateShowingNotification: function() {
         this._notification.acknowledged = true;
 
-        // We auto-expand notifications with CRITICAL urgency.
-        if (this._notification.urgency == Urgency.CRITICAL)
+        // We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
+        // is on in the control center.
+        if (this._notification.urgency == Urgency.CRITICAL ||
+            this._notification.source.policy.forceExpanded)
             this._expandNotification(true);
 
         // We tween all notifications to full opacity. This ensures that both new notifications and
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index e353d8d..cbe2831 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -103,6 +103,126 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
     '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({
     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() {
         // 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
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index 699fb96..38da9df 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -851,12 +851,10 @@ const ScreenShield = new Lang.Class({
 
         this._lockScreenContents.add_actor(this._lockScreenContentsBox);
 
-        if (this._settings.get_boolean('show-notifications')) {
-            this._notificationsBox = new NotificationsBox();
-            this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
-                                                                            y_fill: true,
-                                                                            expand: true });
-        }
+        this._notificationsBox = new NotificationsBox();
+        this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
+                                                                        y_fill: true,
+                                                                        expand: true });
 
         this._hasLockScreen = true;
     },



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