From fcd08fae9400799dfdfe847e2601690ca2aac77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 28 Jul 2022 15:00:45 +0200 Subject: [PATCH] status/bluetooth: Split out BtClient object The new class abstracts away the nitty-gritty of bluetooth- and airplane-mode handling, and exposes just what the UI needs. This will become more important with quick settings, where there's a stronger separation between top bar icon and quick toggle. Part-of: --- js/ui/status/bluetooth.js | 167 ++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 70 deletions(-) diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js index 93dde6a76..fe5956c00 100644 --- a/js/ui/status/bluetooth.js +++ b/js/ui/status/bluetooth.js @@ -17,28 +17,42 @@ const rfkillManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(RfkillManagerInterfa const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup'; -var Indicator = GObject.registerClass( -class Indicator extends PanelMenu.SystemIndicator { +const BtClient = GObject.registerClass({ + Properties: { + 'available': GObject.ParamSpec.boolean('available', '', '', + GObject.ParamFlags.READABLE, + false), + 'active': GObject.ParamSpec.boolean('active', '', '', + GObject.ParamFlags.READABLE, + false), + }, + Signals: { + 'devices-changed': {}, + }, +}, class BtClient extends GObject.Object { _init() { super._init(); - this._indicator = this._addIndicator(); - this._indicator.icon_name = 'bluetooth-active-symbolic'; this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP); this._client = new GnomeBluetooth.Client(); + this._client.connect('notify::default-adapter-powered', () => { + this.notify('active'); + this.notify('available'); + }); this._client.connect('notify::default-adapter', () => { const newAdapter = this._client.default_adapter ?? null; if (newAdapter && this._adapter) - this._setHadSetupDevices(this._getDeviceInfos().length > 0); + this._setHadSetupDevices([...this._getDevices()].length > 0); this._adapter = newAdapter; - this._deviceNotifyConnected.clear(); - this._sync(); + this.emit('devices-changed'); + + this.notify('active'); + this.notify('available'); }); - this._client.connect('notify::default-adapter-powered', this._sync.bind(this)); this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session, @@ -49,28 +63,11 @@ class Indicator extends PanelMenu.SystemIndicator { }); this._proxy.connect('g-properties-changed', (p, properties) => { if ('BluetoothHardwareAirplaneMode' in properties.unpack()) - this._sync(); + this.notify('available'); }); this._proxy.init_async(GLib.PRIORITY_DEFAULT, null) .catch(e => console.error(e.message)); - this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); - - this._toggleItem = new PopupMenu.PopupMenuItem(''); - this._toggleItem.connect('activate', () => { - if (!this._client.default_adapter_powered) { - this._proxy.BluetoothAirplaneMode = false; - this._client.default_adapter_powered = true; - } else { - this._proxy.BluetoothAirplaneMode = true; - } - }); - this._item.menu.addMenuItem(this._toggleItem); - - this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); - this.menu.addMenuItem(this._item); - - this._syncId = 0; this._adapter = null; this._deviceNotifyConnected = new Set(); @@ -81,14 +78,51 @@ class Indicator extends PanelMenu.SystemIndicator { this._client.connect('device-removed', (c, path) => { this._deviceNotifyConnected.delete(path); - this._queueSync.bind(this); + this.emit('devices-changed'); }); this._client.connect('device-added', (c, device) => { this._connectDeviceNotify(device); - this._sync(); + this.emit('devices-changed'); + }); + } + + get available() { + // If there were set up devices, assume there is an adapter + // that can be powered on as long as we're not hard blocked + return this._hadSetupDevices + ? !this._proxy.BluetoothHardwareAirplaneMode + : this.active; + } + + get active() { + return this._client.default_adapter_powered; + } + + toggleActive() { + this._proxy.BluetoothAirplaneMode = this.active; + if (!this._client.default_adapter_powered) + this._client.default_adapter_powered = true; + } + + *getDevices() { + const deviceStore = this._client.get_devices(); + + for (let i = 0; i < deviceStore.get_n_items(); i++) { + const device = deviceStore.get_item(i); + + if (device.paired || device.trusted) + yield device; + } + } + + _queueDevicesChanged() { + if (this._devicesChangedId) + return; + this._devicesChangedId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { + delete this._devicesChangedId; + this.emit('devices-changed'); + return GLib.SOURCE_REMOVE; }); - Main.sessionMode.connect('updated', this._sync.bind(this)); - this._sync(); } _setHadSetupDevices(value) { @@ -106,44 +140,46 @@ class Indicator extends PanelMenu.SystemIndicator { if (this._deviceNotifyConnected.has(path)) return; - device.connect('notify::alias', this._queueSync.bind(this)); - device.connect('notify::paired', this._queueSync.bind(this)); - device.connect('notify::trusted', this._queueSync.bind(this)); - device.connect('notify::connected', this._queueSync.bind(this)); + device.connect('notify::alias', () => this._queueDevicesChanged()); + device.connect('notify::paired', () => this._queueDevicesChanged()); + device.connect('notify::trusted', () => this._queueDevicesChanged()); + device.connect('notify::connected', () => this._queueDevicesChanged()); this._deviceNotifyConnected.add(path); } +}); - _getDeviceInfos() { - const deviceStore = this._client.get_devices(); - let deviceInfos = []; +var Indicator = GObject.registerClass( +class Indicator extends PanelMenu.SystemIndicator { + _init() { + super._init(); - for (let i = 0; i < deviceStore.get_n_items(); i++) { - const device = deviceStore.get_item(i); + this._indicator = this._addIndicator(); + this._indicator.icon_name = 'bluetooth-active-symbolic'; - if (device.paired || device.trusted) { - deviceInfos.push({ - connected: device.connected, - name: device.alias, - }); - } - } + this._client = new BtClient(); + this._client.connectObject( + 'notify::active', () => this._sync(), + 'devices-changed', () => this._sync(), this); - return deviceInfos; - } + this._item = new PopupMenu.PopupSubMenuMenuItem(_('Bluetooth'), true); + this._client.bind_property('available', + this._item, 'visible', + GObject.BindingFlags.SYNC_CREATE); - _queueSync() { - if (this._syncId) - return; - this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { - this._syncId = 0; - this._sync(); - return GLib.SOURCE_REMOVE; - }); + this._toggleItem = new PopupMenu.PopupMenuItem(''); + this._toggleItem.connect('activate', () => this._client.toggleActive()); + this._item.menu.addMenuItem(this._toggleItem); + + this._item.menu.addSettingsAction(_('Bluetooth Settings'), 'gnome-bluetooth-panel.desktop'); + this.menu.addMenuItem(this._item); + + Main.sessionMode.connect('updated', this._sync.bind(this)); + this._sync(); } _sync() { - const devices = this._getDeviceInfos(); + const devices = [...this._client.getDevices()]; const connectedDevices = devices.filter(dev => dev.connected); const nConnectedDevices = connectedDevices.length; @@ -152,28 +188,19 @@ class Indicator extends PanelMenu.SystemIndicator { this.menu.setSensitive(sensitive); this._indicator.visible = nConnectedDevices > 0; - const adapterPowered = this._client.default_adapter_powered; - - // Remember if there were setup devices and show the menu - // if we've seen setup devices and we're not hard blocked - if (this._hadSetupDevices) - this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode; - else - this._item.visible = adapterPowered; - - this._item.icon.icon_name = adapterPowered + this._item.icon.icon_name = this._client.active ? 'bluetooth-active-symbolic' : 'bluetooth-disabled-symbolic'; if (nConnectedDevices > 1) /* Translators: this is the number of connected bluetooth devices */ this._item.label.text = ngettext('%d Connected', '%d Connected', nConnectedDevices).format(nConnectedDevices); else if (nConnectedDevices === 1) - this._item.label.text = connectedDevices[0].name; - else if (adapterPowered) + this._item.label.text = connectedDevices[0].alias; + else if (this._client.active) this._item.label.text = _('Bluetooth On'); else this._item.label.text = _('Bluetooth Off'); - this._toggleItem.label.text = adapterPowered ? _('Turn Off') : _('Turn On'); + this._toggleItem.label.text = this._client.active ? _('Turn Off') : _('Turn On'); } });