109e2968e2
There's two ways bluetooth can be powered off/on for us: One way is to go via airplane mode (which uses rfkill), and the second way is to tell BlueZ to turn off the device. Now rfkill always has the final say on whether bluetooth is off, BlueZ OTOH has the final say on whether bluetooth is on. This means when we want to know whether bluetooth is turned on, we only have to ask BlueZ, so simply read this._client.default_adapter_powered for that. For turning bluetooth on or off we use rfkill, but when turning it on, make sure it's turned on in Bluez, too. FTR, this is exactly the same way the Bluetooth panel in Settings handles this. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2188>
168 lines
6.0 KiB
JavaScript
168 lines
6.0 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported Indicator */
|
|
|
|
const { Gio, GLib, GnomeBluetooth, GObject } = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
|
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
|
|
|
|
const RfkillManagerInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Rfkill');
|
|
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
|
|
|
|
const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup';
|
|
|
|
var Indicator = GObject.registerClass(
|
|
class Indicator extends PanelMenu.SystemIndicator {
|
|
_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._sync.bind(this));
|
|
|
|
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
|
(proxy, error) => {
|
|
if (error) {
|
|
log(error.message);
|
|
return;
|
|
}
|
|
|
|
this._sync();
|
|
});
|
|
this._proxy.connect('g-properties-changed', this._queueSync.bind(this));
|
|
|
|
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
|
this._item.icon.icon_name = 'bluetooth-active-symbolic';
|
|
|
|
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();
|
|
|
|
const deviceStore = this._client.get_devices();
|
|
for (let i = 0; i < deviceStore.get_n_items(); i++)
|
|
this._connectDeviceNotify(deviceStore.get_item(i));
|
|
|
|
this._client.connect('device-removed', (c, path) => {
|
|
this._deviceNotifyConnected.delete(path);
|
|
this._queueSync.bind(this);
|
|
});
|
|
this._client.connect('device-added', (c, device) => {
|
|
this._connectDeviceNotify(device);
|
|
this._sync();
|
|
});
|
|
Main.sessionMode.connect('updated', this._sync.bind(this));
|
|
this._sync();
|
|
}
|
|
|
|
_setHadSetupDevices(value) {
|
|
if (this._hadSetupDevices === value)
|
|
return;
|
|
|
|
this._hadSetupDevices = value;
|
|
global.settings.set_boolean(
|
|
HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
|
|
}
|
|
|
|
_connectDeviceNotify(device) {
|
|
const path = device.get_object_path();
|
|
|
|
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));
|
|
|
|
this._deviceNotifyConnected.add(path);
|
|
}
|
|
|
|
_getDeviceInfos() {
|
|
const deviceStore = this._client.get_devices();
|
|
let deviceInfos = [];
|
|
|
|
for (let i = 0; i < deviceStore.get_n_items(); i++) {
|
|
const device = deviceStore.get_item(i);
|
|
|
|
if (device.paired || device.trusted) {
|
|
deviceInfos.push({
|
|
connected: device.connected,
|
|
name: device.alias,
|
|
});
|
|
}
|
|
}
|
|
|
|
return deviceInfos;
|
|
}
|
|
|
|
_queueSync() {
|
|
if (this._syncId)
|
|
return;
|
|
this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
|
this._syncId = 0;
|
|
this._sync();
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
}
|
|
|
|
_sync() {
|
|
let devices = this._getDeviceInfos();
|
|
const connectedDevices = devices.filter(dev => dev.connected);
|
|
const nConnectedDevices = connectedDevices.length;
|
|
|
|
if (this._client.default_adapter && this._adapter)
|
|
this._setHadSetupDevices(devices.length > 0);
|
|
this._adapter = this._client.default_adapter ?? null;
|
|
|
|
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
|
|
|
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;
|
|
|
|
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 = _('Bluetooth On');
|
|
else
|
|
this._item.label.text = _('Bluetooth Off');
|
|
|
|
this._toggleItem.label.text = adapterPowered ? _('Turn Off') : _('Turn On');
|
|
}
|
|
});
|