summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js
diff options
context:
space:
mode:
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.js459
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;
+ },
+});