Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: chrome/browser/resources/bluetooth_internals/device.js

Issue 2448713002: bluetooth: Add Device connection logic and accompanying user interface. (Closed)
Patch Set: Renaming mojom, ConnectErrorCode changes Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * Javascript for Device-related features, served from 6 * Javascript for Device-related features, served from
7 * chrome://bluetooth-internals/. 7 * chrome://bluetooth-internals/.
8 */ 8 */
9 9
10 /** @typedef {function(device.Device): Promise} */
11 var ConnectionHandler;
12
10 cr.define('device', function() { 13 cr.define('device', function() {
11 14
15
12 var REMOVED_CSS = 'removed'; 16 var REMOVED_CSS = 'removed';
13 17
14 /** 18 /**
15 * A table that lists the devices and responds to changes in the given 19 * A table that lists the devices and responds to changes in the given
16 * DeviceCollection. 20 * DeviceCollection.
17 * @constructor 21 * @constructor
18 * @extends {HTMLTableElement} 22 * @extends {HTMLTableElement}
19 */ 23 */
20 var DeviceTable = cr.ui.define(function() { 24 var DeviceTable = cr.ui.define(function() {
21 this.devices_ = null; 25 this.devices_ = null;
26 this.connectHandler_ = null;
27 this.disconnectHandler_ = null;
22 28
23 return document.importNode($('table-template').content.children[0], 29 return document.importNode($('table-template').content.children[0],
24 true /* deep */); 30 true /* deep */);
25 }); 31 });
26 DeviceTable.prototype = { 32 DeviceTable.prototype = {
27 __proto__: HTMLTableElement.prototype, 33 __proto__: HTMLTableElement.prototype,
28 34
29 /** 35 /**
30 * Decorates an element as a UI element class. Caches references to the 36 * Decorates an element as a UI element class. Caches references to the
31 * table body and headers. 37 * table body and headers.
32 */ 38 */
33 decorate: function() { 39 decorate: function() {
34 this.body_ = this.tBodies[0]; 40 this.body_ = this.tBodies[0];
35 this.headers_ = this.tHead.rows[0].cells; 41 this.headers_ = this.tHead.rows[0].cells;
36 }, 42 },
37 43
38 /** 44 /**
45 * Sets the connection handlers for this device table. Handlers are expected
46 * to return a Promise which resolves or throws an Error. The message
47 * given in the error is displayed in the last column of the table.
48 * @param {ConnectionHandler} connectHandler Handler that is called
49 * when a connection needs to be created.
50 * @param {ConnectionHandler} disconnectHandler Handler that is
51 * called when a connection needs to be closed.
52 */
53 setConnectionHandlers: function(connectHandler, disconnectHandler) {
54 this.connectHandler_ = connectHandler;
55 this.disconnectHandler_ = disconnectHandler;
56 },
57
58 /**
39 * Sets the tables device collection. 59 * Sets the tables device collection.
40 * @param {!DeviceCollection} deviceCollection 60 * @param {!DeviceCollection} deviceCollection
41 */ 61 */
42 setDevices: function(deviceCollection) { 62 setDevices: function(deviceCollection) {
43 if (this.devices_) { 63 if (this.devices_) {
44 this.devices_.removeEventListener('sorted', this.redraw_.bind(this)); 64 this.devices_.removeEventListener('sorted', this.redraw_.bind(this));
45 this.devices_.removeEventListener('change', 65 this.devices_.removeEventListener('change',
46 this.handleChange_.bind(this)); 66 this.handleChange_.bind(this));
47 this.devices_.removeEventListener('splice', 67 this.devices_.removeEventListener('splice',
48 this.handleSplice_.bind(this)); 68 this.handleSplice_.bind(this));
49 } 69 }
50 70
51 this.devices_ = deviceCollection; 71 this.devices_ = deviceCollection;
52 this.devices_.addEventListener('sorted', this.redraw_.bind(this)); 72 this.devices_.addEventListener('sorted', this.redraw_.bind(this));
53 this.devices_.addEventListener('change', this.handleChange_.bind(this)); 73 this.devices_.addEventListener('change', this.handleChange_.bind(this));
54 this.devices_.addEventListener('splice', this.handleSplice_.bind(this)); 74 this.devices_.addEventListener('splice', this.handleSplice_.bind(this));
55 75
56 this.redraw_(); 76 this.redraw_();
57 }, 77 },
58 78
59 /** 79 /**
60 * Updates table row on change event of the device collection. 80 * Updates table row on change event of the device collection.
61 * @param {!Event} event 81 * @param {!Event} event
62 */ 82 */
63 handleChange_: function(event) { 83 handleChange_: function(event) {
64 this.updateRow_(this.devices_.item(event.index), event.index); 84 this.updateRow_(this.devices_.item(event.index), event.index);
65 }, 85 },
66 86
67 /** 87 /**
88 * Generates a function to handle click events on the connect button for the
89 * given |row|.
90 * @param {HTMLTableRowElement} row The table row that was clicked.
91 * @return {function(Event)}
92 */
93 handleConnect_: function(row) {
94 return function(event) {
95 var cellCount = row.cells.length;
96 var connectCell = row.cells[cellCount - 2];
97 var connectButton = connectCell.children[0];
98 var connectErrorCell = row.cells[cellCount - 1];
99
100 connectErrorCell.textContent = '';
101
102 var device = this.devices_.getByAddress(row.id);
103 if (this.connectHandler_ && !device.proxy) {
104 connectButton.textContent = 'Connecting...';
105 connectButton.disabled = true;
106
107 this.connectHandler_(device).catch(function(error) {
108 connectErrorCell.textContent = error.message;
109 connectButton.textContent = 'Connect';
110 connectButton.disabled = false;
111 });
112 } else if (this.disconnectHandler_ && device.proxy) {
113 connectButton.textContent = 'Disconnecting...';
114 connectButton.disabled = true;
115
116 this.disconnectHandler_(device);
117 }
118 }.bind(this);
119 },
120
121 /**
68 * Updates table row on splice event of the device collection. 122 * Updates table row on splice event of the device collection.
69 * @param {!Event} event 123 * @param {!Event} event
70 */ 124 */
71 handleSplice_: function(event) { 125 handleSplice_: function(event) {
72 event.removed.forEach(function() { 126 event.removed.forEach(function() {
73 this.removeRow(event.index); 127 this.removeRow(event.index);
74 }, this); 128 }, this);
75 129
76 event.added.forEach(function(device, index) { 130 event.added.forEach(function(device, index) {
77 this.newRowForDevice_(device, event.index + index); 131 this.newRowForDevice_(device, event.index + index);
78 }, this); 132 }, this);
79 }, 133 },
80 134
81 /** 135 /**
82 * Inserts a new row at |index| and updates it with info from |device|. 136 * Inserts a new row at |index| and updates it with info from |device|.
83 * @param {!Device} device 137 * @param {!Device} device
84 * @param {?number} index 138 * @param {?number} index
85 */ 139 */
86 newRowForDevice_: function(device, index) { 140 newRowForDevice_: function(device, index) {
87 var row = this.body_.insertRow(index); 141 var row = this.body_.insertRow(index);
88 row.id = device.info.address; 142 row.id = device.info.address;
89 143
90 for (var i = 0; i < this.headers_.length; i++) { 144 for (var i = 0; i < this.headers_.length; i++) {
91 row.insertCell(); 145 row.insertCell();
92 } 146 }
93 147
148 // Make two extra cells for the connect button and connect errors.
149 var connectCell = row.insertCell();
150 var connectErrorCell = row.insertCell();
151
152 var connectButton = document.createElement('button');
153 connectCell.appendChild(connectButton);
154
155 connectButton.textContent = 'Connect';
156 connectButton.addEventListener('click',
157 this.handleConnect_(row).bind(this));
158
94 this.updateRow_(device, row.sectionRowIndex); 159 this.updateRow_(device, row.sectionRowIndex);
95 }, 160 },
96 161
97 /** 162 /**
98 * Deletes and recreates the table using the cached |devices_|. 163 * Deletes and recreates the table using the cached |devices_|.
99 */ 164 */
100 redraw_: function() { 165 redraw_: function() {
101 this.removeChild(this.body_); 166 this.removeChild(this.body_);
102 this.appendChild(document.createElement('tbody')); 167 this.appendChild(document.createElement('tbody'));
103 this.body_ = this.tBodies[0]; 168 this.body_ = this.tBodies[0];
(...skipping 11 matching lines...) Expand all
115 */ 180 */
116 updateRow_: function(device, index) { 181 updateRow_: function(device, index) {
117 var row = this.body_.rows[index]; 182 var row = this.body_.rows[index];
118 183
119 if (device.removed) { 184 if (device.removed) {
120 row.classList.add(REMOVED_CSS); 185 row.classList.add(REMOVED_CSS);
121 } else { 186 } else {
122 row.classList.remove(REMOVED_CSS); 187 row.classList.remove(REMOVED_CSS);
123 } 188 }
124 189
190 var cellCount = row.cells.length;
191 var connectCell = row.cells[cellCount - 2];
192 var connectButton = connectCell.children[0];
193 var connectErrorCell = row.cells[cellCount - 1];
194
195 if (device.info.is_gatt_connected) {
196 connectButton.textContent = 'Disconnect';
197 } else if (device.proxy) {
198 connectButton.textContent = 'Connect';
199 connectErrorCell.textContent = 'Lost connection';
200 device.proxy = null;
201 } else {
202 connectButton.textContent = 'Connect';
203 }
204
205 connectButton.disabled = false;
206
125 // Update the properties based on the header field path. 207 // Update the properties based on the header field path.
126 for (var i = 0; i < this.headers_.length; i++) { 208 for (var i = 0; i < this.headers_.length; i++) {
127 var header = this.headers_[i]; 209 var header = this.headers_[i];
128 var propName = header.dataset.field; 210 var propName = header.dataset.field;
129 211
130 var parts = propName.split('.'); 212 var parts = propName.split('.');
131 var obj = device.info; 213 var obj = device.info;
132 while (obj != null && parts.length > 0) { 214 while (obj != null && parts.length > 0) {
133 var part = parts.shift(); 215 var part = parts.shift();
134 obj = obj[part]; 216 obj = obj[part];
135 } 217 }
136 218
137 var cell = row.cells[i]; 219 var cell = row.cells[i];
138 cell.textContent = obj || 'Unknown'; 220 cell.textContent = obj || 'Unknown';
139 cell.dataset.label = header.innerText; 221 cell.dataset.label = header.textContent;
140 } 222 }
141 }, 223 },
142 }; 224 };
143 225
144 /* 226 /**
145 * Collection of devices with helper functions for searching and updating by 227 * Collection of devices with helper functions for searching and updating by
146 * device address. 228 * device address.
147 * @constructor 229 * @constructor
148 * @param {!Array} array the starting collection of devices. 230 * @param {!Array} array the starting collection of devices.
149 * @extends {cr.ui.ArrayDataModel} 231 * @extends {cr.ui.ArrayDataModel}
150 */ 232 */
151 var DeviceCollection = function(array) { 233 var DeviceCollection = function(array) {
152 cr.ui.ArrayDataModel.call(this, array); 234 cr.ui.ArrayDataModel.call(this, array);
153 }; 235 };
154 DeviceCollection.prototype = { 236 DeviceCollection.prototype = {
155 __proto__: cr.ui.ArrayDataModel.prototype, 237 __proto__: cr.ui.ArrayDataModel.prototype,
156 getByAddress: function(address) { 238 getByAddress: function(address) {
157 for (var i = 0; i < this.length; i++) { 239 for (var i = 0; i < this.length; i++) {
158 var device = this.item(i); 240 var device = this.item(i);
159 if (address == device.info.address) 241 if (address == device.info.address)
160 return device; 242 return device;
161 } 243 }
162 return null; 244 return null;
163 }, 245 },
164 update: function(device) { 246 update: function(device) {
165 var oldDevice = this.getByAddress(device.info.address); 247 var oldDevice = this.getByAddress(device.info.address);
166 if (oldDevice) { 248 if (oldDevice) {
167 this.replaceItem(oldDevice, device); 249 this.replaceItem(oldDevice, device);
168 } else { 250 } else {
169 this.push(device); 251 this.push(device);
170 } 252 }
171 } 253 }
172 }; 254 };
173 255
174 /* 256 /**
175 * Data model for a cached device. 257 * Data model for a cached device.
176 * @constructor 258 * @constructor
177 * @param {!bluetoothDevice.DeviceInfo} info 259 * @param {!bluetoothDevice.DeviceInfo} info
178 */ 260 */
179 var Device = function(info) { 261 var Device = function(info) {
180 this.info = info; 262 this.info = info;
263 this.proxy = null;
181 this.removed = false; 264 this.removed = false;
182 }; 265 };
183 266
184 return { 267 return {
185 Device: Device, 268 Device: Device,
186 DeviceCollection, DeviceCollection, 269 DeviceCollection, DeviceCollection,
187 DeviceTable: DeviceTable, 270 DeviceTable: DeviceTable,
188 }; 271 };
189 }); 272 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698