diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-12-03 05:52:21 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-12-03 05:52:21 +0000 |
commit | 61f2fa99a341ebf40fe16235c6808a86aad2c4be (patch) | |
tree | aacf8343554449640916f35759e9d60d2887fbfa | |
parent | bd03a4367b365bbcf5af48e73cc02af738dc146b (diff) | |
parent | 39fc29e102b5850b47aaedfe1d1d32aa444ac535 (diff) | |
download | cuttlefish-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.js | 85 | ||||
-rw-r--r-- | host/frontend/webrtc/client/js/controls.js | 100 | ||||
-rw-r--r-- | host/frontend/webrtc/client/style.css | 32 |
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; } |