blob: a77d83d936f7124b415bab8cf987e6ee5440cc5f [file] [log] [blame]
// Copyright 2015 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 Polymer element for displaying a bluetooth device in a list.
*/
import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import '../os_icons.js';
import '../../settings_shared.css.js';
import {FocusRowBehavior, FocusRowBehaviorInterface} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js';
import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BluetoothPageBrowserProxy, BluetoothPageBrowserProxyImpl} from './bluetooth_page_browser_proxy.js';
/**
* @constructor
* @extends {PolymerElement}
* @implements {I18nBehaviorInterface}
* @implements {FocusRowBehaviorInterface}
*/
const BluetoothDeviceListItemElementBase =
mixinBehaviors([I18nBehavior, FocusRowBehavior], PolymerElement);
/** @polymer */
class BluetoothDeviceListItemElement extends
BluetoothDeviceListItemElementBase {
static get is() {
return 'bluetooth-device-list-item';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/**
* The bluetooth device.
* @type {!chrome.bluetooth.Device}
*/
device: {
type: Object,
observer: 'onDeviceChanged_',
},
/**
* The aria-label attribute for the top-level bluetooth-device-list-item
* element.
*/
ariaLabel: {
type: String,
notify: true,
computed: 'getAriaLabel_(device)',
},
// TODO(crbug.com/1208155) Add managed policy icon that is shown when this
// flag is true.
/** @private */
shouldShowManagedIcon_: {
type: Boolean,
value: false,
},
};
}
/** @override */
ready() {
super.ready();
/** @private {?BluetoothPageBrowserProxy} */
this.browserProxy_ = BluetoothPageBrowserProxyImpl.getInstance();
this.setAttribute('role', 'button');
}
/** @override */
disconnectedCallback() {
super.disconnectedCallback();
// Fire an event in case the tooltip was previously showing for the managed
// icon in this item and this item is being removed.
this.fireTooltipStateChangeEvent_(/*showTooltip=*/ false);
}
/**
* @param {!Event} event
* @private
*/
ignoreEnterKey_(event) {
if (event.key === 'Enter') {
event.stopPropagation();
}
}
/** @private */
tryConnect_() {
if (!this.isDisconnected_(this.device)) {
return;
}
const event = new CustomEvent('device-event', {
bubbles: true,
composed: true,
detail: {
action: 'connect',
device: this.device,
},
});
this.dispatchEvent(event);
}
/** @private */
onClick_() {
this.tryConnect_();
}
/**
* @param {!KeyboardEvent} e
* @private
*/
onKeyDown_(e) {
if (e.key === 'Enter' || e.key === ' ') {
this.tryConnect_();
e.preventDefault();
}
}
/**
* @param {!Event} event
* @private
*/
onMenuButtonTap_(event) {
const button = /** @type {!HTMLElement} */ (event.target);
const menu = /** @type {!CrActionMenuElement} */ (this.$.dotsMenu);
menu.showAt(button);
event.stopPropagation();
}
/** @private */
onConnectActionTap_() {
const action = this.isDisconnected_(this.device) ? 'connect' : 'disconnect';
const event = new CustomEvent('device-event', {
bubbles: true,
composed: true,
detail: {
action: action,
device: this.device,
},
});
this.dispatchEvent(event);
/** @type {!CrActionMenuElement} */ (this.$.dotsMenu).close();
}
/** @private */
onRemoveTap_() {
const event = new CustomEvent('device-event', {
bubbles: true,
composed: true,
detail: {
action: 'remove',
device: this.device,
},
});
this.dispatchEvent(event);
/** @type {!CrActionMenuElement} */ (this.$.dotsMenu).close();
}
/**
* @param {boolean} connected
* @return {string} The text to display for the connect/disconnect menu item.
* @private
*/
getConnectActionText_(connected) {
return this.i18n(connected ? 'bluetoothDisconnect' : 'bluetoothConnect');
}
/**
* @param {!chrome.bluetooth.Device} device
* @return {string} The text to display for |device| in the device list.
* @private
*/
getDeviceName_(device) {
return device.name || device.address;
}
/**
* @param {!chrome.bluetooth.Device} device
* @return {string} The aria label to use for a |device| that will include the
* device name and device type if known.
* @private
*/
getAriaLabel_(device) {
// We need to turn the device name, connection status and type into a single
// localized string.
// The possible device type enum values can be seen here:
// https://developer.chrome.com/apps/bluetooth#type-Device.
// The localization id is computed dynamically to avoid maintaining a
// mapping from the enum string value to the localization id.
// if device.type is not defined, we fall back to an unknown device string.
const deviceName = this.getDeviceName_(device);
const deviceStatus = this.getConnectionStatusText_(device);
const a11ydeviceNameAndType = (device.type) ?
this.i18n('bluetoothDeviceType_' + device.type, deviceName) :
this.i18n('bluetoothDeviceType_unknown', deviceName);
return this.i18n(
'bluetoothDeviceWithConnectionStatus', a11ydeviceNameAndType,
deviceStatus);
}
/**
* @param {!chrome.bluetooth.Device} device
* @return {string} The text to display the connection status of |device|.
* @private
*/
getConnectionStatusText_(device) {
if (!this.hasConnectionStatusText_(device)) {
return '';
}
if (device.connecting) {
return this.i18n('bluetoothConnecting');
}
if (!device.connected) {
return this.i18n('bluetoothNotConnected');
}
return device.batteryPercentage !== undefined ?
this.i18n('bluetoothConnectedWithBattery', device.batteryPercentage) :
this.i18n('bluetoothConnected');
}
/**
* @param {!chrome.bluetooth.Device} device
* @return {boolean} True if connection status should be shown as the
* secondary text of the |device| in device list.
* @private
*/
hasConnectionStatusText_(device) {
return !!(device.paired || device.connecting);
}
/**
* @param {!chrome.bluetooth.Device} device
* @return {boolean}
* @private
*/
isDisconnected_(device) {
return !device.connected && !device.connecting;
}
/**
* Returns device type icon's ID corresponding to the given device.
* To be consistent with the Bluetooth device list in system menu, this
* mapping needs to be synced with ash::tray::GetBluetoothDeviceIcon().
*
* @param {!chrome.bluetooth.Device} device
* @return {string}
* @private
*/
getDeviceIcon_(device) {
switch (device.type) {
case 'computer':
return 'cr:computer';
case 'phone':
return 'os-settings:smartphone';
case 'audio':
case 'carAudio':
return 'os-settings:headset';
case 'video':
return 'cr:videocam';
case 'joystick':
case 'gamepad':
return 'os-settings:gamepad';
case 'keyboard':
case 'keyboardMouseCombo':
return 'os-settings:keyboard';
case 'tablet':
return 'os-settings:tablet';
case 'mouse':
return 'os-settings:mouse';
default:
return device.connected ? 'os-settings:bluetooth-connected' :
'cr:bluetooth';
}
}
/**
* @param {?chrome.bluetooth.Device} newValue
* @param {?chrome.bluetooth.Device} oldValue
* @private
*/
onDeviceChanged_(newValue, oldValue) {
if (!newValue) {
this.shouldShowManagedIcon_ = false;
return;
}
this.browserProxy_.isDeviceBlockedByPolicy(newValue.address)
.then((isBlocked) => {
this.shouldShowManagedIcon_ = isBlocked;
if (!this.shouldShowManagedIcon_) {
// Fire an event in case the tooltip was previously showing for this
// icon and this icon now is hidden.
this.fireTooltipStateChangeEvent_(/*showTooltip=*/ false);
}
});
}
/** @private */
onShowTooltip_() {
this.fireTooltipStateChangeEvent_(/*showTooltip=*/ true);
}
/**
* @param {boolean} showTooltip
*/
fireTooltipStateChangeEvent_(showTooltip) {
const event = new CustomEvent('blocked-tooltip-state-change', {
bubbles: true,
composed: true,
detail: {
address: this.device.address,
show: showTooltip,
element: showTooltip ? this.shadowRoot.querySelector('#managedIcon') :
undefined,
},
});
this.dispatchEvent(event);
}
}
customElements.define(
BluetoothDeviceListItemElement.is, BluetoothDeviceListItemElement);