aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2021-12-03 05:52:21 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-12-03 05:52:21 +0000
commit61f2fa99a341ebf40fe16235c6808a86aad2c4be (patch)
treeaacf8343554449640916f35759e9d60d2887fbfa
parentbd03a4367b365bbcf5af48e73cc02af738dc146b (diff)
parent39fc29e102b5850b47aaedfe1d1d32aa444ac535 (diff)
downloadcuttlefish-61f2fa99a341ebf40fe16235c6808a86aad2c4be.tar.gz
Merge changes I064d41eb,Iba74d3e4,I29de99d8
* changes: Implement add, del, and list functionality in BT Wizard Connect Bluetooth Wizard modal dialogs together Update CSS stylesheet for Bluetooth Wizard
-rw-r--r--host/frontend/webrtc/client/js/app.js85
-rw-r--r--host/frontend/webrtc/client/js/controls.js100
-rw-r--r--host/frontend/webrtc/client/style.css32
3 files changed, 194 insertions, 23 deletions
diff --git a/host/frontend/webrtc/client/js/app.js b/host/frontend/webrtc/client/js/app.js
index 0e74a450f..2b9a178e9 100644
--- a/host/frontend/webrtc/client/js/app.js
+++ b/host/frontend/webrtc/client/js/app.js
@@ -100,6 +100,8 @@ class DeviceControlApp {
#displayDescriptions = [];
#buttons = {};
#recording = {};
+ #phys = {};
+ #deviceCount = 0;
constructor(deviceConnection) {
this.#deviceConnection = deviceConnection;
@@ -150,12 +152,49 @@ class DeviceControlApp {
this.#buttons['volumeup'] = createControlPanelButton(
'volumeup', 'Volume Up', 'volume_up',
evt => this.#onControlPanelButton(evt));
+
createModalButton(
'device-details-button', 'device-details-modal',
'device-details-close');
createModalButton(
- 'bluetooth-console-button', 'bluetooth-console-modal',
- 'bluetooth-console-close');
+ 'bluetooth-modal-button', 'bluetooth-prompt',
+ 'bluetooth-prompt-close');
+ createModalButton(
+ 'bluetooth-prompt-wizard', 'bluetooth-wizard',
+ 'bluetooth-wizard-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-wizard-device', 'bluetooth-wizard-confirm',
+ 'bluetooth-wizard-confirm-close', 'bluetooth-wizard');
+ createModalButton(
+ 'bluetooth-wizard-another', 'bluetooth-wizard',
+ 'bluetooth-wizard-close', 'bluetooth-wizard-confirm');
+ createModalButton(
+ 'bluetooth-prompt-list', 'bluetooth-list',
+ 'bluetooth-list-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-prompt-console', 'bluetooth-console',
+ 'bluetooth-console-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-wizard-cancel', 'bluetooth-prompt',
+ 'bluetooth-wizard-close', 'bluetooth-wizard');
+
+ positionModal('device-details-button', 'bluetooth-modal');
+ positionModal('device-details-button', 'bluetooth-prompt');
+ positionModal('device-details-button', 'bluetooth-wizard');
+ positionModal('device-details-button', 'bluetooth-wizard-confirm');
+ positionModal('device-details-button', 'bluetooth-list');
+ positionModal('device-details-button', 'bluetooth-console');
+
+ createButtonListener('bluetooth-prompt-list', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
+ createButtonListener('bluetooth-wizard-device', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "add", evt));
+ createButtonListener('bluetooth-list-trash', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "del", evt));
+ createButtonListener('bluetooth-prompt-wizard', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
+ createButtonListener('bluetooth-wizard-another', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
if (this.#deviceConnection.description.custom_control_panel_buttons.length >
0) {
@@ -239,10 +278,50 @@ class DeviceControlApp {
createRootcanalMessage(command, args));
});
this.#deviceConnection.onBluetoothMessage(msg => {
- bluetoothConsole.addLine(decodeRootcanalMessage(msg));
+ let decoded = decodeRootcanalMessage(msg);
+ let deviceCount = btUpdateDeviceList(decoded);
+ if (deviceCount > 0) {
+ this.#deviceCount = deviceCount;
+ createButtonListener('bluetooth-list-trash', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "del", evt));
+ }
+ btUpdateAdded(decoded);
+ let phyList = btParsePhys(decoded);
+ if (phyList) {
+ this.#phys = phyList;
+ }
+ bluetoothConsole.addLine(decoded);
});
}
+ #onRootCanalCommand(deviceConnection, cmd, evt) {
+ if (cmd == "list") {
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("list", []));
+ }
+ if (cmd == "del") {
+ let id = evt.srcElement.getAttribute("data-device-id");
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("del", [id]));
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("list", []));
+ }
+ if (cmd == "add") {
+ let name = document.getElementById('bluetooth-wizard-name').value;
+ let type = document.getElementById('bluetooth-wizard-type').value;
+ if (type == "remote_loopback") {
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add", [type]));
+ } else {
+ let mac = document.getElementById('bluetooth-wizard-mac').value;
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add", [type, mac]));
+ }
+ let phyId = this.#phys["LOW_ENERGY"].toString();
+ if (type == "remote_loopback") {
+ phyId = this.#phys["BR_EDR"].toString();
+ }
+ let devId = this.#deviceCount.toString();
+ this.#deviceCount++;
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add_device_to_phy", [devId, phyId]));
+ }
+ }
+
#showWebrtcError() {
document.getElementById('status-message').className = 'error';
document.getElementById('status-message').textContent =
diff --git a/host/frontend/webrtc/client/js/controls.js b/host/frontend/webrtc/client/js/controls.js
index 21ca75fda..268c7d830 100644
--- a/host/frontend/webrtc/client/js/controls.js
+++ b/host/frontend/webrtc/client/js/controls.js
@@ -79,32 +79,55 @@ function validateMacAddress(val) {
return (regex.test(val));
}
-$('[validate-mac]').bind('input', function() {
- var button = document.getElementById("bluetooth-wizard-device");
- if (validateMacAddress($(this).val())) {
+function validateMacWrapper() {
+ let type = document.getElementById('bluetooth-wizard-type').value;
+ let button = document.getElementById("bluetooth-wizard-device");
+ let macField = document.getElementById('bluetooth-wizard-mac');
+ if (this.id == 'bluetooth-wizard-type') {
+ if (type == "remote_loopback") {
button.disabled = false;
- this.setCustomValidity('');
- } else {
- button.disabled = true;
- this.setCustomValidity('MAC address invalid');
+ macField.setCustomValidity('');
+ macField.disabled = true;
+ macField.required = false;
+ macField.placeholder = 'N/A';
+ macField.value = '';
+ return;
}
-});
+ }
+ macField.disabled = false;
+ macField.required = true;
+ macField.placeholder = 'Device MAC';
+ if (validateMacAddress($(macField).val())) {
+ button.disabled = false;
+ macField.setCustomValidity('');
+ } else {
+ button.disabled = true;
+ macField.setCustomValidity('MAC address invalid');
+ }
+}
+
+$('[validate-mac]').bind('input', validateMacWrapper);
+$('[validate-mac]').bind('select', validateMacWrapper);
function parseDevice(device) {
- let id = device.substring(0, device.indexOf(":"));
- device = device.substring(device.indexOf(":")+1);
- let name = device.substring(0, device.indexOf("@"));
- let mac = device.substring(device.indexOf("@")+1);
+ let id, name, mac;
+ var regex = /([0-9]+):([^@ ]*)(@(([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})))?/;
+ if (regex.test(device)) {
+ let regexMatches = device.match(regex);
+ id = regexMatches[1];
+ name = regexMatches[2];
+ mac = regexMatches[4];
+ }
+ if (mac === undefined) {
+ mac = "";
+ }
return [id, name, mac];
}
function btUpdateAdded(devices) {
let deviceArr = devices.split('\r\n');
- if (deviceArr[0].indexOf("Devices:") >= 0) {
- return false;
- }
- if (deviceArr[0].indexOf(":") >= 0 && deviceArr[0].indexOf("@") >= 0) {
- let [id, name, mac] = parseDevice(deviceArr[0]);
+ let [id, name, mac] = parseDevice(deviceArr[0]);
+ if (name) {
let div = document.getElementById('bluetooth-wizard-confirm').getElementsByClassName('bluetooth-text')[1];
div.innerHTML = "";
div.innerHTML += "<p>Name: <b>" + id + "</b></p>";
@@ -115,6 +138,32 @@ function btUpdateAdded(devices) {
return false;
}
+function parsePhy(phy) {
+ let id = phy.substring(0, phy.indexOf(":"));
+ phy = phy.substring(phy.indexOf(":") + 1);
+ let name = phy.substring(0, phy.indexOf(":"));
+ let devices = phy.substring(phy.indexOf(":") + 1);
+ return [id, name, devices];
+}
+
+function btParsePhys(phys) {
+ if (phys.indexOf("Phys:") < 0) {
+ return null;
+ }
+ let phyDict = {};
+ phys = phys.split('Phys:')[1];
+ let phyArr = phys.split('\r\n');
+ for (var phy of phyArr.slice(1)) {
+ phy = phy.trim();
+ if (phy.length == 0 || phy.indexOf("deleted") >= 0) {
+ continue;
+ }
+ let [id, name, devices] = parsePhy(phy);
+ phyDict[name] = id;
+ }
+ return phyDict;
+}
+
function btUpdateDeviceList(devices) {
let deviceArr = devices.split('\r\n');
if (deviceArr[0].indexOf("Devices:") >= 0) {
@@ -168,15 +217,23 @@ function createControlPanelButton(
return button;
}
-function createModalButton(button_id, modal_id, close_id) {
+function positionModal(button_id, modal_id) {
const modalButton = document.getElementById(button_id);
const modalDiv = document.getElementById(modal_id);
- const modalHeader = modalDiv.querySelector('.modal-header');
- const modalClose = document.getElementById(close_id);
// Position the modal to the right of the show modal button.
modalDiv.style.top = modalButton.offsetTop;
modalDiv.style.left = modalButton.offsetWidth + 30;
+}
+
+function createModalButton(button_id, modal_id, close_id, hide_id) {
+ const modalButton = document.getElementById(button_id);
+ const modalDiv = document.getElementById(modal_id);
+ const modalHeader = modalDiv.querySelector('.modal-header');
+ const modalClose = document.getElementById(close_id);
+ const modalDivHide = document.getElementById(hide_id);
+
+ positionModal(button_id, modal_id);
function showHideModal(show) {
if (show) {
@@ -186,6 +243,9 @@ function createModalButton(button_id, modal_id, close_id) {
modalButton.classList.remove('modal-button-opened')
modalDiv.style.display = 'none';
}
+ if (modalDivHide != null) {
+ modalDivHide.style.display = 'none';
+ }
}
// Allow the show modal button to toggle the modal,
modalButton.addEventListener(
diff --git a/host/frontend/webrtc/client/style.css b/host/frontend/webrtc/client/style.css
index 184a8e889..93aaa05fa 100644
--- a/host/frontend/webrtc/client/style.css
+++ b/host/frontend/webrtc/client/style.css
@@ -157,6 +157,38 @@ body {
outline: none;
background-color: transparent;
}
+.modal-button, .modal-button-highlight {
+ background: #e8eaed; /* Google grey 200 */
+ border-radius: 10px;
+ box-shadow: 1px 1px #444444;
+ padding: 10px 20px;
+ color: #000000;
+ display: inline-block;
+ font: normal bold 14px/1 "Open Sans", sans-serif;
+ text-align: center;
+}
+#bluetooth-wizard-mac:valid {
+ border: 2px solid black;
+}
+#bluetooth-wizard-mac:invalid {
+ border: 2px solid red;
+}
+#bluetooth-wizard-mac:invalid + span::before {
+ font-weight: bold;
+ content: 'X';
+ color: red;
+}
+#bluetooth-wizard-mac:valid + span::before {
+ font-weight: bold;
+ content: 'OK';
+ color: green;
+}
+.modal-button {
+ background: #e8eaed; /* Google grey 200 */
+}
+.modal-button-highlight {
+ background: #f4cccc;
+}
#device-details-modal span {
white-space: pre;
}