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

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

Issue 2627243002: bluetooth: Add control for reading/writing of characteristics to internals page. (Closed)
Patch Set: Rename last_value, cleanup Created 3 years, 11 months 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
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * Javascript for ValueControl, served from chrome://bluetooth-internals/.
7 */
8
9 cr.define('value_control', function() {
10 /** @const */ var Snackbar = snackbar.Snackbar;
11 /** @const */ var SnackbarType = snackbar.SnackbarType;
12
13 /** @const {!Array<string>} */
14 var VALUE_DATA_TYPES = ['Hexadecimal', 'UTF-8', 'Decimal'];
15
16 /**
17 * A set of inputs that allow a user to request reads and writes of values.
18 * This control allows the value to be displayed in multiple forms
19 * as defined by the |VALUE_DATA_TYPES| array. Values must be written
20 * in these formats. Read and write capability is controlled by a
21 * 'permissions' bitfield provided by the characteristic.
22 * @constructor
23 */
24 var ValueControl = cr.ui.define('div');
25
26 ValueControl.prototype = {
27 __proto__: HTMLDivElement.prototype,
28
29 /**
30 * Decorates the element as a ValueControl. Creates the layout for the value
31 * control by creating a text input, select element, and two buttons for
32 * read/write requests. Event handlers are attached and references to these
33 * elements are stored for later use.
34 * @override
35 */
36 decorate: function() {
37 this.classList.add('value-control');
38
39 /** @private {!Array<number>} */
40 this.value_ = [];
41 /** @private {?string} */
42 this.deviceAddress_ = null;
43 /** @private {?string} */
44 this.serviceId_ = null;
45 /** @private {!interfaces.BluetoothDevice.CharacteristicInfo} */
46 this.characteristicInfo_ = null;
47
48 this.unavailableMessage_ = document.createElement('h3');
49 this.unavailableMessage_.textContent = 'Value cannot be read or written.';
50
51 this.valueInput_ = document.createElement('input');
52 this.valueInput_.addEventListener('change', function() {
53 this.value_ = this.convertValueToArray_();
54 }.bind(this));
55
56 this.typeSelect_ = document.createElement('select');
57
58 VALUE_DATA_TYPES.forEach(function(type) {
59 var option = document.createElement('option');
60 option.value = type;
61 option.text = type;
62 this.typeSelect_.add(option);
63 }, this);
64
65 this.typeSelect_.addEventListener('change', this.redraw.bind(this));
66
67 var inputDiv = document.createElement('div');
68 inputDiv.appendChild(this.valueInput_);
69 inputDiv.appendChild(this.typeSelect_);
70
71 this.readBtn_ = document.createElement('button');
72 this.readBtn_.textContent = 'Read';
73 this.readBtn_.addEventListener('click', this.readValue_.bind(this));
74
75 this.writeBtn_ = document.createElement('button');
76 this.writeBtn_.textContent = 'Write';
77 this.writeBtn_.addEventListener('click', this.writeValue_.bind(this));
78
79 var buttonsDiv = document.createElement('div');
80 buttonsDiv.appendChild(this.readBtn_);
81 buttonsDiv.appendChild(this.writeBtn_);
82
83 this.appendChild(this.unavailableMessage_);
84 this.appendChild(inputDiv);
85 this.appendChild(buttonsDiv);
86 },
87
88 /**
89 * Sets the settings used by the value control and redraws the control to
90 * match the read/write settings provided in
91 * |characteristicInfo.permissions|.
92 * @param {string} deviceAddress
93 * @param {string} serviceId
94 * @param {!interfaces.BluetoothDevice.CharacteristicInfo}
95 * characteristicInfo
96 */
97 load: function(deviceAddress, serviceId, characteristicInfo) {
98 this.deviceAddress_ = deviceAddress;
99 this.serviceId_ = serviceId;
100 this.characteristicInfo_ = characteristicInfo;
101
102 this.redraw();
103 },
104
105 /**
106 * Redraws the value control with updated layout depending on the
107 * availability of reads and writes and the current cached value.
108 */
109 redraw: function() {
110 // TODO(crbug.com/682856): Remove once permissions for characteristics on
ortuno 2017/01/20 03:45:22 We should always use properties. Neither BlueZ nor
mbrunson 2017/01/21 01:49:00 Done.
111 // Linux are implemented.
112 if (navigator.userAgent.indexOf('Linux') >= 0) {
113 this.readBtn_.hidden = (this.characteristicInfo_.properties &
114 interfaces.BluetoothDevice.Property.READ) === 0;
115 this.writeBtn_.hidden = (this.characteristicInfo_.properties &
116 interfaces.BluetoothDevice.Property.WRITE) === 0;
117 } else {
118 this.readBtn_.hidden = (this.characteristicInfo_.permissions &
119 interfaces.BluetoothDevice.Permission.READ) === 0;
120 this.writeBtn_.hidden = (this.characteristicInfo_.permissions &
121 interfaces.BluetoothDevice.Permission.WRITE) === 0;
122 }
123
124 var isAvailable = !this.readBtn_.hidden || !this.writeBtn_.hidden;
125 this.unavailableMessage_.hidden = isAvailable;
126 this.valueInput_.hidden = !isAvailable;
127 this.typeSelect_.hidden = !isAvailable;
128
129 if (!isAvailable) return;
130
131 var type = this.typeSelect_.selectedOptions[0].value;
132
133 switch (type) {
134 case 'Hexadecimal':
135 this.valueInput_.value = this.value_.reduce(
136 function(result, value, index) {
137 var answer = result + ('0' + value.toString(16)).substr(-2);
138 if (index === 0)
139 return '0x' + answer;
140
141 return answer;
142 }, '');
143 break;
144
145 case 'UTF-8':
146 this.valueInput_.value = this.value_.reduce(function(result, value) {
147 return result + String.fromCharCode(value);
148 }, '');
149 break;
150
151 case 'Decimal':
152 this.valueInput_.value = this.value_.reduce(
153 function(result, value, index) {
154 if (index === this.value_.length - 1)
155 return result + value.toString();
156 else
157 return result + value.toString() + '-';
158 }.bind(this), '');
159 break;
160 }
161 },
162
163 /**
164 * Sets the value of the control.
165 * @param {!Array<number>} value
166 */
167 setValue: function(value) {
168 this.value_ = value;
169 this.redraw();
170 },
171
172 /**
173 * Converts the current value of the text input into an array of numbers
174 * using the currently selected format in the type select element. The
175 * numbers are created such that they will not exceed 8-bits in length to be
176 * compatible with the expected data type of values set in a characteristic.
177 * @return {!Array<number>}
178 * @private
179 */
180 convertValueToArray_: function() {
181 var value = this.valueInput_.value;
182 if (!value) return;
183
184 var type = this.typeSelect_.selectedOptions[0].value;
185 switch (type) {
186 case 'Hexadecimal':
187 value = value.replace('0x', '');
188 return Array.from(value).reduce(function(result, value, index) {
189 if (index % 2 == 0)
190 result.push(parseInt(value, 16) << 4);
191 else
192 result[Math.floor(index/2)] += parseInt(value, 16);
193 return result;
194 }, []);
195
196 case 'UTF-8':
197 return Array.from(value).map(function(char) {
198 return char.charCodeAt(0);
199 });
200
201 case 'Decimal':
202 return value.split('-').map(parseInt);
203 }
204 },
205
206 /**
207 * Called when the read button is pressed. Connects to the device and
208 * retrieves the current value of the characteristic in the |service_id|
209 * with id |characteristic_id|
210 * @private
211 */
212 readValue_: function() {
213 this.readBtn_.disabled = true;
214
215 device_broker.connectToDevice(this.deviceAddress_).then(function(device) {
216 return device.readValueForCharacteristic(
217 this.serviceId_, this.characteristicInfo_.id);
218 }.bind(this)).then(function(response) {
219 this.readBtn_.disabled = false;
220 var GattResult = interfaces.BluetoothDevice.GattResult;
221
222 if (response.result === GattResult.SUCCESS) {
223 this.setValue(response.value);
224 Snackbar.show(
225 this.deviceAddress_ + ': Read succeeded', SnackbarType.SUCCESS);
226 return;
227 }
228
229 // TODO(crbug.com/663394): Replace with more descriptive error
230 // messages.
231 var errorString = Object.keys(GattResult).find(function(key) {
232 return GattResult[key] === response.result;
233 });
234
235 Snackbar.show(
236 this.deviceAddress_ + ': ' + errorString, SnackbarType.ERROR,
237 'Retry', this.readValue_.bind(this));
238 }.bind(this));
239 },
240
241 /**
242 * Called when the write button is pressed. Connects to the device and
243 * retrieves the current value of the characteristic in the |service_id|
244 * with id |characteristic_id|
245 * @private
246 */
247 writeValue_: function() {
248 this.writeBtn_.disabled = true;
249
250 device_broker.connectToDevice(this.deviceAddress_).then(function(device) {
251 return device.writeValueForCharacteristic(
252 this.value_, this.serviceId_, this.characteristicInfo_.id);
253 }.bind(this)).then(function(response) {
254 this.writeBtn_.disabled = false;
255 var GattResult = interfaces.BluetoothDevice.GattResult;
256
257 if (response.result === GattResult.SUCCESS) {
258 Snackbar.show(
259 this.deviceAddress_ + ': Write succeeded', SnackbarType.SUCCESS);
260 return;
261 }
262
263 // TODO(crbug.com/663394): Replace with more descriptive error
264 // messages.
265 var errorString = Object.keys(GattResult).find(function(key) {
266 return GattResult[key] === response.result;
267 });
268
269 Snackbar.show(
270 this.deviceAddress_ + ': ' + errorString, SnackbarType.ERROR,
271 'Retry', this.writeValue_.bind(this));
272 }.bind(this));
273 },
274 }
275
276 return {
277 ValueControl: ValueControl,
278 };
279 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698