diff options
Diffstat (limited to 'extension.js')
-rw-r--r-- | extension.js | 246 |
1 files changed, 104 insertions, 142 deletions
diff --git a/extension.js b/extension.js index 403d956..b08d77b 100644 --- a/extension.js +++ b/extension.js @@ -15,219 +15,181 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. const Main = imports.ui.main; -const GnomeBluetooth = imports.gi.GnomeBluetooth; -const PopupMenu = imports.ui.popupMenu; -const Util = imports.misc.util; const GLib = imports.gi.GLib; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); -const Convenience = Me.imports.convenience; - -class BluetoothDevice { - constructor(model, device) { - this._name = model.get_value(device, GnomeBluetooth.Column.NAME); - this._isConnected = model.get_value(device, GnomeBluetooth.Column.CONNECTED); - this._isPaired = model.get_value(device, GnomeBluetooth.Column.PAIRED); - this._mac = model.get_value(device, GnomeBluetooth.Column.ADDRESS); - } - - get name() { - return this._name; - } - - get isConnected() { - return this._isConnected; - } - - get isPaired() { - return this._isPaired; - } - - get mac() { - return this._mac; - } - - get item() { - if (!this._item) - this._buildMenuItem(); - - return this._item; - } - - _buildMenuItem() { - this._item = new PopupMenu.PopupSwitchMenuItem(this.name, this.isConnected); - this._item.isDeviceSwitcher = true; - this._item.connect('toggled', (item, state) => { - if (state) - this._connect(); - else - this._disconnect(); - }); - } - - _disconnect() { - this._call_bluetoothctl(`disconnect ${this.mac}`) - } - - _connect() { - this._call_bluetoothctl(`connect ${this.mac}`) - } +const UiExtension = Me.imports.ui; +const Bluetooth = Me.imports.bluetooth; +const Utils = Me.imports.utils; +const Settings = Me.imports.settings.Settings; - _call_bluetoothctl(command) { - let btctl_command = `echo -e "${command}\\n" | bluetoothctl`; - Util.spawn(['/usr/bin/env', 'bash', '-c', btctl_command]); - } -} class BluetoothQuickConnect { constructor(bluetooth, settings) { + this._logger = new Utils.Logger(settings); + this._logger.info('Initializing extension'); this._menu = bluetooth._item.menu; this._proxy = bluetooth._proxy; - this._settings = settings; + this._controller = new Bluetooth.BluetoothController(); + this._settings = settings - this._signals = []; + this._items = {}; } enable() { - this._loadBluetoothModel(); + this._logger.info('Enabling extension'); + this._controller.enable(); + this._refresh(); + this._connectControllerSignals(); + this._connectIdleMonitor(); + this._connectMenuSignals(); + } + + _connectMenuSignals() { this._connectSignal(this._menu, 'open-state-changed', (menu, isOpen) => { - if (isOpen && this._autoPowerOnEnabled()) + this._logger.info(`Menu toggled: ${isOpen}`); + if (isOpen) + this._disconnectIdleMonitor() + else + this._connectIdleMonitor(); + + if (isOpen && this._settings.isAutoPowerOnEnabled() && this._proxy.BluetoothAirplaneMode) { + this._logger.info('Disabling airplane mode'); this._proxy.BluetoothAirplaneMode = false; + } }); - - this._connectSignal(this._model, 'row-changed', () => this._sync()); - this._connectSignal(this._model, 'row-deleted', () => this._sync()); - this._connectSignal(this._model, 'row-inserted', () => this._sync()); - - this._idleMonitor(); - if (!this._proxy.BluetoothAirplaneMode) { - this._sync(); - } } disable() { + this._logger.info('Disabling extension'); this._destroy(); } test() { try { + this._logger.info('Testing bluetoothctl'); GLib.spawn_command_line_sync("bluetoothctl --version"); - } catch(error) { - Main.notifyError(`Bluetooth quick connect: error trying to execute "bluetoothctl"`); + this._logger.info('Test succeeded'); + } catch (error) { + Main.notifyError(_('Bluetooth quick connect'), _(`Error trying to execute "bluetoothctl"`)); + this._logger.info('Test failed'); } } - _idleMonitor() { - this._idleMonitorId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._autoPowerOffCheckingInterval() * 1000, () => { - if (this._autoPowerOffEnabled() && this._getConnectedDevices().length === 0) - this._proxy.BluetoothAirplaneMode = true; + _connectControllerSignals() { + this._logger.info('Connecting bluetooth controller signals'); - return true; + this._connectSignal(this._controller, 'device-inserted', (ctrl, device) => { + this._logger.info(`Device inserted event: ${device.name}`); + this._addMenuItem(device); + }); + this._connectSignal(this._controller, 'device-changed', (ctrl, device) => { + this._logger.info(`Device changed event: ${device.name}`); + if (device.isDefault) + this._refresh(); + else + this._syncMenuItem(device); + }); + this._connectSignal(this._controller, 'device-deleted', () => { + this._logger.info(`Device deleted event`); + this._refresh(); }); - } - _connectSignal(subject, signal_name, method) { - let signal_id = subject.connect(signal_name, method); - this._signals.push({ - subject: subject, - signal_id: signal_id + this._connectSignal(Main.sessionMode, 'updated', () => { + this._refresh() }); } - _loadBluetoothModel() { - this._client = new GnomeBluetooth.Client(); - this._model = this._client.get_model(); + _syncMenuItem(device) { + this._logger.info(`Synchronizing device menu item: ${device.name}`); + let item = this._items[device.mac] || this._addMenuItem(device); + item.sync(device); } - _getDefaultAdapter() { - let [ret, iter] = this._model.get_iter_first(); - while (ret) { - let isDefault = this._model.get_value(iter, GnomeBluetooth.Column.DEFAULT); - let isPowered = this._model.get_value(iter, GnomeBluetooth.Column.POWERED); - if (isDefault && isPowered) - return iter; - ret = this._model.iter_next(iter); - } - return null; + _addMenuItem(device) { + this._logger.info(`Adding device menu item: ${device.name}`); + let menuItem = new UiExtension.PopupBluetoothDeviceMenuItem( + device, + { + showRefreshButton: this._settings.isShowRefreshButtonEnabled(), + closeMenuOnAction: !this._settings.isKeepMenuOnToggleEnabled() + } + ); + this._items[device.mac] = menuItem; + this._menu.addMenuItem(menuItem, 1); + + return menuItem; } - _getDevices() { - let adapter = this._getDefaultAdapter(); - if (!adapter) - return []; + _connectIdleMonitor() { + if (this._idleMonitorId) return; - let devices = []; + this._logger.info('Connecting idle monitor'); - let [ret, iter] = this._model.iter_children(adapter); - while (ret) { - devices.push(new BluetoothDevice(this._model, iter)); - ret = this._model.iter_next(iter); - } + this._idleMonitorId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._settings.autoPowerOffCheckingInterval() * 1000, () => { + if (this._settings.isAutoPowerOffEnabled() && this._controller.getConnectedDevices().length === 0) + this._proxy.BluetoothAirplaneMode = true; - return devices; + return true; + }); } - _getPairedDevices() { - return this._getDevices().filter((device) => { - return device.isPaired || device.isConnected; - }); + _disconnectIdleMonitor() { + if (!this._idleMonitorId) return; + + this._logger.info('Disconnecting idle monitor'); + + GLib.Source.remove(this._idleMonitorId); + this._idleMonitorId = null; } - _getConnectedDevices() { - return this._getDevices().filter((device) => { - return device.isConnected; + _connectSignal(subject, signal_name, method) { + let signal_id = subject.connect(signal_name, method); + this._signals.push({ + subject: subject, + signal_id: signal_id }); } - _sync() { + _refresh() { this._removeDevicesFromMenu(); this._addDevicesToMenu(); + + this._logger.info('Refreshing devices list'); } _addDevicesToMenu() { - this._getPairedDevices().forEach((device) => { - this._menu.addMenuItem(device.item, 1); + this._controller.getDevices().forEach((device) => { + this._addMenuItem(device); }); } _removeDevicesFromMenu() { - this._menu._getMenuItems().forEach((item) => { - if (item.isDeviceSwitcher) { - item.destroy(); - } + Object.values(this._items).forEach((item) => { + item.destroy(); }); + + this._items = {}; } _destroy() { - this._signals.forEach((signal) => { - signal.subject.disconnect(signal.signal_id); - }); - this._signals = []; + this._disconnectSignals(); this._removeDevicesFromMenu(); - - if (this._idleMonitorId) - GLib.Source.remove(this._idleMonitorId); - } - - _autoPowerOnEnabled() { - return this._settings.get_boolean('bluetooth-auto-power-on'); + this._disconnectIdleMonitor(); + if (this._controller) + this._controller.destroy(); } +} - _autoPowerOffEnabled() { - return this._settings.get_boolean('bluetooth-auto-power-off'); - } +Utils.addSignalsHelperMethods(BluetoothQuickConnect.prototype); - _autoPowerOffCheckingInterval() { - return this._settings.get_int('bluetooth-auto-power-off-interval'); - } -} let bluetoothQuickConnect = null; function init() { let bluetooth = Main.panel.statusArea.aggregateMenu._bluetooth; - let settings = Convenience.getSettings(); + let settings = new Settings(); bluetoothQuickConnect = new BluetoothQuickConnect(bluetooth, settings); } |