[gnome-shell] audioDeviceSelection: Add audio device selection dialog



commit 30c7545ff36297f14f0e97ea5db0797af9e6c293
Author: Florian Müllner <fmuellner gnome org>
Date:   Tue Feb 9 23:03:26 2016 +0100

    audioDeviceSelection: Add audio device selection dialog
    
    It is not always possible to determine the type of audio device that
    got plugged in. Add a system modal dialog to query the user in that
    case and export in on the bus to gnome-settings-daemon.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=760284

 data/theme/gnome-shell-high-contrast.css |   28 ++++
 data/theme/gnome-shell-sass              |    2 +-
 data/theme/gnome-shell.css               |   28 ++++
 js/js-resources.gresource.xml            |    1 +
 js/ui/audioDeviceSelection.js            |  216 ++++++++++++++++++++++++++++++
 js/ui/main.js                            |    3 +
 6 files changed, 277 insertions(+), 1 deletions(-)
---
diff --git a/data/theme/gnome-shell-high-contrast.css b/data/theme/gnome-shell-high-contrast.css
index 47c3090..c27717d 100644
--- a/data/theme/gnome-shell-high-contrast.css
+++ b/data/theme/gnome-shell-high-contrast.css
@@ -399,6 +399,34 @@ StScrollBar {
   width: 48px;
   height: 48px; }
 
+/* Audio selection dialog */
+.audio-device-selection-dialog {
+  spacing: 30px; }
+
+.audio-selection-content {
+  spacing: 20px;
+  padding: 24px; }
+
+.audio-selection-title {
+  font-weight: bold;
+  text-align: center; }
+
+.audio-selection-box {
+  spacing: 20px; }
+
+.audio-selection-device {
+  border: 1px solid rgba(238, 238, 236, 0.2);
+  border-radius: 12px; }
+  .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
+    background-color: #215d9c; }
+
+.audio-selection-device-box {
+  padding: 20px;
+  spacing: 20px; }
+
+.audio-selection-device-icon {
+  icon-size: 64px; }
+
 /* Network Agent Dialog */
 .network-dialog-secret-table {
   spacing-rows: 15px;
diff --git a/data/theme/gnome-shell-sass b/data/theme/gnome-shell-sass
index c674996..9fb3918 160000
--- a/data/theme/gnome-shell-sass
+++ b/data/theme/gnome-shell-sass
@@ -1 +1 @@
-Subproject commit c67499686eb40b821784561fbb0596cc470d8017
+Subproject commit 9fb3918831459cd002f3d621494cf5eac70fe46a
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 0c94b19..2e09c1c 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -399,6 +399,34 @@ StScrollBar {
   width: 48px;
   height: 48px; }
 
+/* Audio selection dialog */
+.audio-device-selection-dialog {
+  spacing: 30px; }
+
+.audio-selection-content {
+  spacing: 20px;
+  padding: 24px; }
+
+.audio-selection-title {
+  font-weight: bold;
+  text-align: center; }
+
+.audio-selection-box {
+  spacing: 20px; }
+
+.audio-selection-device {
+  border: 1px solid rgba(238, 238, 236, 0.2);
+  border-radius: 12px; }
+  .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
+    background-color: #215d9c; }
+
+.audio-selection-device-box {
+  padding: 20px;
+  spacing: 20px; }
+
+.audio-selection-device-icon {
+  icon-size: 64px; }
+
 /* Network Agent Dialog */
 .network-dialog-secret-table {
   spacing-rows: 15px;
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index bd8f307..169d952 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -35,6 +35,7 @@
     <file>ui/animation.js</file>
     <file>ui/appDisplay.js</file>
     <file>ui/appFavorites.js</file>
+    <file>ui/audioDeviceSelection.js</file>
     <file>ui/backgroundMenu.js</file>
     <file>ui/background.js</file>
     <file>ui/boxpointer.js</file>
diff --git a/js/ui/audioDeviceSelection.js b/js/ui/audioDeviceSelection.js
new file mode 100644
index 0000000..c97184d
--- /dev/null
+++ b/js/ui/audioDeviceSelection.js
@@ -0,0 +1,216 @@
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+
+const AudioDevice = {
+    HEADPHONES: 1 << 0,
+    HEADSET:    1 << 1,
+    MICROPHONE: 1 << 2
+};
+
+const AudioDeviceSelectionIface = '<node> \
+<interface name="org.gnome.Shell.AudioDeviceSelection"> \
+<method name="Open"> \
+    <arg name="devices" direction="in" type="as" /> \
+</method> \
+<method name="Close"> \
+</method> \
+<signal name="DeviceSelected"> \
+    <arg name="device" type="s" /> \
+</signal> \
+</interface> \
+</node>';
+
+const AudioDeviceSelectionDialog = new Lang.Class({
+    Name: 'AudioDeviceSelectionDialog',
+    Extends: ModalDialog.ModalDialog,
+
+    _init: function(devices) {
+        this.parent({ styleClass: 'audio-device-selection-dialog' });
+
+        this._deviceItems = {};
+
+        this._buildLayout();
+
+        if (devices & AudioDevice.HEADPHONES)
+            this._addDevice(AudioDevice.HEADPHONES);
+        if (devices & AudioDevice.HEADSET)
+            this._addDevice(AudioDevice.HEADSET);
+        if (devices & AudioDevice.MICROPHONE)
+            this._addDevice(AudioDevice.MICROPHONE);
+
+        if (this._selectionBox.get_n_children() < 2)
+            throw new Error('Too few devices for a selection');
+    },
+
+    destroy: function() {
+        this.parent();
+    },
+
+    _buildLayout: function(devices) {
+        let title = new St.Label({ style_class: 'audio-selection-title',
+                                   text: _("Select Audio Device"),
+                                   x_align: Clutter.ActorAlign.CENTER });
+
+        this.contentLayout.style_class = 'audio-selection-content';
+        this.contentLayout.add(title);
+
+        this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' });
+        this.contentLayout.add(this._selectionBox, { expand: true });
+
+        this.addButton({ action: Lang.bind(this, this._openSettings),
+                         label: _("Sound Settings") });
+        this.addButton({ action: Lang.bind(this, this.close),
+                         label: _("Cancel"),
+                         key: Clutter.Escape });
+    },
+
+    _getDeviceLabel: function(device) {
+        switch(device) {
+            case AudioDevice.HEADPHONES:
+                return _("Headphones");
+            case AudioDevice.HEADSET:
+                return _("Headset");
+            case AudioDevice.MICROPHONE:
+                return _("Microphone");
+            default:
+                return null;
+        }
+    },
+
+    _getDeviceIcon: function(device) {
+        switch(device) {
+            case AudioDevice.HEADPHONES:
+                return 'audio-headphones-symbolic';
+            case AudioDevice.HEADSET:
+                return 'audio-headset-symbolic';
+            case AudioDevice.MICROPHONE:
+                return 'audio-input-microphone-symbolic';
+            default:
+                return null;
+        }
+    },
+
+    _addDevice: function(device) {
+        let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
+                                     vertical: true });
+        box.connect('notify::height',
+            function() {
+                Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
+                    function() {
+                        box.width = box.height;
+                    });
+            });
+
+        let icon = new St.Icon({ style_class: 'audio-selection-device-icon',
+                                 icon_name: this._getDeviceIcon(device) });
+        box.add(icon);
+
+        let label = new St.Label({ style_class: 'audio-selection-device-label',
+                                   text: this._getDeviceLabel(device),
+                                   x_align: Clutter.ActorAlign.CENTER });
+        box.add(label);
+
+        let button = new St.Button({ style_class: 'audio-selection-device',
+                                     can_focus: true,
+                                     child: box });
+        this._selectionBox.add(button);
+
+        button.connect('clicked', Lang.bind(this,
+            function() {
+                this.emit('device-selected', device);
+                this.close();
+                Main.overview.hide();
+            }));
+    },
+
+    _openSettings: function() {
+        let desktopFile = 'gnome-sound-panel.desktop'
+        let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
+
+        if (!app) {
+            log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
+            return;
+        }
+
+        this.close();
+        Main.overview.hide();
+        app.activate();
+    }
+});
+
+const AudioDeviceSelectionDBus = new Lang.Class({
+    Name: 'AudioDeviceSelectionDBus',
+
+    _init: function() {
+        this._audioSelectionDialog = null;
+
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
+
+        Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, 
null, null);
+    },
+
+    _onDialogClosed: function() {
+        this._audioSelectionDialog = null;
+    },
+
+    _onDeviceSelected: function(dialog, device) {
+        let connection = this._dbusImpl.get_connection();
+        let info = this._dbusImpl.get_info();
+        let deviceName = Object.keys(AudioDevice).filter(
+            function(dev) {
+                return AudioDevice[dev] == device;
+            })[0].toLowerCase();
+        connection.emit_signal(this._audioSelectionDialog._sender,
+                               this._dbusImpl.get_object_path(),
+                               info ? info.name : null,
+                               'DeviceSelected',
+                               GLib.Variant.new('(s)', [deviceName]));
+    },
+
+    OpenAsync: function(params, invocation) {
+        if (this._audioSelectionDialog) {
+            invocation.return_value(null);
+            return;
+        }
+
+        let [deviceNames] = params;
+        let devices = 0;
+        deviceNames.forEach(function(n) {
+            devices |= AudioDevice[n.toUpperCase()];
+        });
+
+        let dialog;
+        try {
+            dialog = new AudioDeviceSelectionDialog(devices);
+        } catch(e) {
+            invocation.return_value(null);
+            return;
+        }
+        dialog._sender = invocation.get_sender();
+
+        dialog.connect('closed', Lang.bind(this, this._onDialogClosed));
+        dialog.connect('device-selected',
+                       Lang.bind(this, this._onDeviceSelected));
+        dialog.open();
+
+        this._audioSelectionDialog = dialog;
+        invocation.return_value(null);
+    },
+
+    CloseAsync: function(params, invocation) {
+        if (this._audioSelectionDialog &&
+            this._audioSelectionDialog._sender == invocation.get_sender())
+            this._audioSelectionDialog.close();
+
+        invocation.return_value(null);
+    }
+});
diff --git a/js/ui/main.js b/js/ui/main.js
index 3770403..ad95ce0 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -11,6 +11,7 @@ const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 const St = imports.gi.St;
 
+const AudioDeviceSelection = imports.ui.audioDeviceSelection;
 const Components = imports.ui.components;
 const CtrlAltTab = imports.ui.ctrlAltTab;
 const EndSessionDialog = imports.ui.endSessionDialog;
@@ -62,6 +63,7 @@ let ctrlAltTabManager = null;
 let osdWindowManager = null;
 let osdMonitorLabeler = null;
 let sessionMode = null;
+let shellAudioSelectionDBusService = null;
 let shellDBusService = null;
 let shellMountOpDBusService = null;
 let screenSaverDBus = null;
@@ -120,6 +122,7 @@ function start() {
                                        _loadDefaultStylesheet);
     _initializeUI();
 
+    shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
     shellDBusService = new ShellDBus.GnomeShell();
     shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
 


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