summaryrefslogtreecommitdiffstats
path: root/js/ui/status/bluetooth.js
blob: 98ccc3d3146fbd7766162adfbc055f4cd1b01196 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// -*- 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._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', () => {
            this._proxy.BluetoothAirplaneMode = !this._proxy.BluetoothAirplaneMode;
        });
        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._client = new GnomeBluetooth.Client();
        this._model = this._client.get_model();
        this._model.connect('row-deleted', this._queueSync.bind(this));
        this._model.connect('row-changed', this._queueSync.bind(this));
        this._model.connect('row-inserted', this._sync.bind(this));
        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);
    }

    _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;
    }

    _getDeviceInfos(adapter) {
        if (!adapter)
            return [];

        let deviceInfos = [];
        let [ret, iter] = this._model.iter_children(adapter);
        while (ret) {
            const isPaired = this._model.get_value(iter,
                GnomeBluetooth.Column.PAIRED);
            const isTrusted = this._model.get_value(iter,
                GnomeBluetooth.Column.TRUSTED);

            if (isPaired || isTrusted) {
                deviceInfos.push({
                    connected: this._model.get_value(iter,
                        GnomeBluetooth.Column.CONNECTED),
                    name: this._model.get_value(iter,
                        GnomeBluetooth.Column.ALIAS),
                });
            }

            ret = this._model.iter_next(iter);
        }

        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 adapter = this._getDefaultAdapter();
        let devices = this._getDeviceInfos(adapter);
        const connectedDevices = devices.filter(dev => dev.connected);
        const nConnectedDevices = connectedDevices.length;

        if (adapter && this._adapter)
            this._setHadSetupDevices(devices.length > 0);
        this._adapter = adapter;

        let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;

        this.menu.setSensitive(sensitive);
        this._indicator.visible = nConnectedDevices > 0;

        // 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 = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;

        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 (adapter === null)
            this._item.label.text = _('Bluetooth Off');
        else
            this._item.label.text = _('Bluetooth On');

        this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _('Turn On') : _('Turn Off');
    }
});