diff options
Diffstat (limited to 'chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js')
-rw-r--r-- | chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js new file mode 100644 index 00000000000..b061349e266 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js @@ -0,0 +1,459 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * Dialog used for pairing a provided |pairing-device|. Set |show-error| to + * show the error results from a pairing event instead of the pairing UI. + * NOTE: This module depends on I18nBehavior which depends on loadTimeData. + */ + +var PairingEventType = chrome.bluetoothPrivate.PairingEventType; + +Polymer({ + is: 'bluetooth-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** + * Interface for bluetooth calls. Set in bluetooth-page. + * @type {Bluetooth} + * @private + */ + bluetooth: { + type: Object, + value: chrome.bluetooth, + }, + + /** + * Interface for bluetoothPrivate calls. + * @type {BluetoothPrivate} + */ + bluetoothPrivate: { + type: Object, + value: chrome.bluetoothPrivate, + }, + + /** Dialog title */ + title: String, + + /** + * Current Pairing device. + * @type {!chrome.bluetooth.Device|undefined} + */ + pairingDevice: Object, + + /** + * Current Pairing event. + * @private {?chrome.bluetoothPrivate.PairingEvent} + */ + pairingEvent_: { + type: Object, + value: null, + }, + + /** + * May be set by the host to show a pairing error result, or may be + * set by the dialog if a pairing or connect error occured. + * @private + */ + errorMessage_: String, + + /** + * Pincode or passkey value, used to trigger connect enabled changes. + * @private + */ + pinOrPass_: String, + + /** + * @const {!Array<number>} + * @private + */ + digits_: { + type: Array, + readOnly: true, + value: [0, 1, 2, 3, 4, 5], + }, + }, + + observers: [ + 'dialogUpdated_(errorMessage_, pairingEvent_)', + 'pairingChanged_(pairingDevice, pairingEvent_)', + ], + + /** + * Listener for chrome.bluetoothPrivate.onPairing events. + * @private {?function(!chrome.bluetoothPrivate.PairingEvent)} + */ + bluetoothPrivateOnPairingListener_: null, + + /** + * Listener for chrome.bluetooth.onBluetoothDeviceChanged events. + * @private {?function(!chrome.bluetooth.Device)} + */ + bluetoothDeviceChangedListener_: null, + + open: function() { + this.startPairing(); + this.pinOrPass_ = ''; + this.getDialog_().showModal(); + this.itemWasFocused_ = false; + }, + + close: function() { + this.endPairing(); + var dialog = this.getDialog_(); + if (dialog.open) + dialog.close(); + }, + + /** + * Updates the dialog after a connect attempt. + * @param {!chrome.bluetooth.Device} device The device connected to + * @param {!{message: string}} lastError chrome.runtime.lastError + * @param {chrome.bluetoothPrivate.ConnectResultType} result The connect + * result + * @return {boolean} + */ + handleError: function(device, lastError, result) { + var error; + if (lastError) { + error = lastError.message; + } else { + switch (result) { + case chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS: + case chrome.bluetoothPrivate.ConnectResultType.ALREADY_CONNECTED: + case chrome.bluetoothPrivate.ConnectResultType.AUTH_CANCELED: + case chrome.bluetoothPrivate.ConnectResultType.SUCCESS: + this.errorMessage_ = ''; + return false; + default: + error = result; + } + } + + var name = device.name || device.address; + var id = 'bluetooth_connect_' + error; + if (this.i18nExists(id)) { + this.errorMessage_ = this.i18n(id, name); + } else { + this.errorMessage_ = error; + console.error('Unexpected error connecting to: ' + name + ': ' + error); + } + return true; + }, + + /** @private */ + dialogUpdated_: function() { + if (this.showEnterPincode_()) + this.$$('#pincode').focus(); + else if (this.showEnterPasskey_()) + this.$$('#passkey').focus(); + }, + + /** + * @return {!CrDialogElement} + * @private + */ + getDialog_: function() { + return /** @type {!CrDialogElement} */ (this.$.dialog); + }, + + /** @private */ + onCancelTap_: function() { + this.getDialog_().cancel(); + }, + + /** @private */ + onDialogCanceled_: function() { + if (!this.errorMessage_) + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CANCEL); + this.endPairing(); + }, + + /** Called when the dialog is opened. Starts listening for pairing events. */ + startPairing: function() { + if (!this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivateOnPairingListener_ = + this.onBluetoothPrivateOnPairing_.bind(this); + this.bluetoothPrivate.onPairing.addListener( + this.bluetoothPrivateOnPairingListener_); + } + if (!this.bluetoothDeviceChangedListener_) { + this.bluetoothDeviceChangedListener_ = + this.onBluetoothDeviceChanged_.bind(this); + this.bluetooth.onDeviceChanged.addListener( + this.bluetoothDeviceChangedListener_); + } + }, + + /** Called when the dialog is closed. */ + endPairing: function() { + if (this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivate.onPairing.removeListener( + this.bluetoothPrivateOnPairingListener_); + this.bluetoothPrivateOnPairingListener_ = null; + } + if (this.bluetoothDeviceChangedListener_) { + this.bluetooth.onDeviceChanged.removeListener( + this.bluetoothDeviceChangedListener_); + this.bluetoothDeviceChangedListener_ = null; + } + this.pairingEvent_ = null; + }, + + /** + * Process bluetoothPrivate.onPairing events. + * @param {!chrome.bluetoothPrivate.PairingEvent} event + * @private + */ + onBluetoothPrivateOnPairing_: function(event) { + if (!this.pairingDevice || + event.device.address != this.pairingDevice.address) { + return; + } + if (event.pairing == PairingEventType.KEYS_ENTERED && + event.passkey === undefined && this.pairingEvent_) { + // 'keysEntered' event might not include the updated passkey so preserve + // the current one. + event.passkey = this.pairingEvent_.passkey; + } + this.pairingEvent_ = event; + }, + + /** + * Process bluetooth.onDeviceChanged events. This ensures that the dialog + * updates when the connection state changes. + * @param {!chrome.bluetooth.Device} device + * @private + */ + onBluetoothDeviceChanged_: function(device) { + if (!this.pairingDevice || device.address != this.pairingDevice.address) + return; + this.pairingDevice = device; + }, + + /** @private */ + pairingChanged_: function() { + // Auto-close the dialog when pairing completes. + if (this.pairingDevice.paired && !this.pairingDevice.connecting && + this.pairingDevice.connected) { + this.close(); + return; + } + this.errorMessage_ = ''; + this.pinOrPass_ = ''; + }, + + /** + * @return {string} + * @private + */ + getMessage_: function() { + var message; + if (!this.pairingEvent_) + message = 'bluetoothStartConnecting'; + else + message = this.getEventDesc_(this.pairingEvent_.pairing); + return this.i18n(message, this.pairingDevice.name); + }, + + /** + * @return {boolean} + * @private + */ + showEnterPincode_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE; + }, + + /** + * @return {boolean} + * @private + */ + showEnterPasskey_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + showDisplayPassOrPin_: function() { + if (!this.pairingEvent_) + return false; + var pairing = this.pairingEvent_.pairing; + return ( + pairing == PairingEventType.DISPLAY_PINCODE || + pairing == PairingEventType.DISPLAY_PASSKEY || + pairing == PairingEventType.CONFIRM_PASSKEY || + pairing == PairingEventType.KEYS_ENTERED); + }, + + /** + * @return {boolean} + * @private + */ + showAcceptReject_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + showConnect_: function() { + if (!this.pairingEvent_) + return false; + var pairing = this.pairingEvent_.pairing; + return pairing == PairingEventType.REQUEST_PINCODE || + pairing == PairingEventType.REQUEST_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + enableConnect_: function() { + if (!this.showConnect_()) + return false; + var inputId = + (this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE) ? + '#pincode' : + '#passkey'; + var paperInput = /** @type {!PaperInputElement} */ (this.$$(inputId)); + assert(paperInput); + /** @type {string} */ var value = paperInput.value; + return !!value && paperInput.validate(); + }, + + /** + * @return {boolean} + * @private + */ + showDismiss_: function() { + return this.pairingDevice.paired || + (!!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.COMPLETE); + }, + + /** @private */ + onAcceptTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM); + }, + + /** @private */ + onConnectTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM); + }, + + /** @private */ + onRejectTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.REJECT); + }, + + /** + * @param {!chrome.bluetoothPrivate.PairingResponse} response + * @private + */ + sendResponse_: function(response) { + if (!this.pairingDevice) + return; + var options = + /** @type {!chrome.bluetoothPrivate.SetPairingResponseOptions} */ ( + {device: this.pairingDevice, response: response}); + if (response == chrome.bluetoothPrivate.PairingResponse.CONFIRM) { + var pairing = this.pairingEvent_.pairing; + if (pairing == PairingEventType.REQUEST_PINCODE) + options.pincode = this.$$('#pincode').value; + else if (pairing == PairingEventType.REQUEST_PASSKEY) + options.passkey = parseInt(this.$$('#passkey').value, 10); + } + this.bluetoothPrivate.setPairingResponse(options, () => { + if (chrome.runtime.lastError) { + // TODO(stevenjb): Show error. + console.error( + 'Error setting pairing response: ' + options.device.name + + ': Response: ' + options.response + + ': Error: ' + chrome.runtime.lastError.message); + } + this.close(); + }); + + this.fire('response', options); + }, + + /** + * @param {!PairingEventType} eventType + * @return {string} + * @private + */ + getEventDesc_: function(eventType) { + assert(eventType); + if (eventType == PairingEventType.COMPLETE || + eventType == PairingEventType.KEYS_ENTERED || + eventType == PairingEventType.REQUEST_AUTHORIZATION) { + return 'bluetoothStartConnecting'; + } + return 'bluetooth_' + /** @type {string} */ (eventType); + }, + + /** + * @param {number} index + * @return {string} + * @private + */ + getPinDigit_: function(index) { + if (!this.pairingEvent_) + return ''; + var digit = '0'; + var pairing = this.pairingEvent_.pairing; + if (pairing == PairingEventType.DISPLAY_PINCODE && + this.pairingEvent_.pincode && + index < this.pairingEvent_.pincode.length) { + digit = this.pairingEvent_.pincode[index]; + } else if ( + this.pairingEvent_.passkey && + (pairing == PairingEventType.DISPLAY_PASSKEY || + pairing == PairingEventType.KEYS_ENTERED || + pairing == PairingEventType.CONFIRM_PASSKEY)) { + var passkeyString = String(this.pairingEvent_.passkey); + if (index < passkeyString.length) + digit = passkeyString[index]; + } + return digit; + }, + + /** + * @param {number} index + * @return {string} + * @private + */ + getPinClass_: function(index) { + if (!this.pairingEvent_) + return ''; + if (this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY) + return 'confirm'; + var cssClass = 'display'; + if (this.pairingEvent_.pairing == PairingEventType.DISPLAY_PASSKEY) { + if (index == 0) + cssClass += ' next'; + else + cssClass += ' untyped'; + } else if ( + this.pairingEvent_.pairing == PairingEventType.KEYS_ENTERED && + this.pairingEvent_.enteredKey) { + var enteredKey = this.pairingEvent_.enteredKey; // 1-7 + var lastKey = this.digits_.length; // 6 + if ((index == -1 && enteredKey > lastKey) || (index + 1 == enteredKey)) + cssClass += ' next'; + else if (index > enteredKey) + cssClass += ' untyped'; + } + return cssClass; + }, +}); |