blob: ad5c8cb8eef1859b30f8f7cb19126ef5cd451536 [file] [log] [blame]
// Copyright 2021 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
* Settings subpage providing high level summary of the state of Bluetooth and
* its connected devices.
*/
import '../../settings_shared.css.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/icons.m.js';
import {getDeviceName} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
import {getBluetoothConfig} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
import {BluetoothSystemProperties, BluetoothSystemState, DeviceConnectionState, PairedBluetoothDeviceProperties} from 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {loadTimeData} from '../../i18n_setup.js';
import {Router} from '../../router.js';
import {routes} from '../os_route.js';
import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
import {RouteOriginBehavior, RouteOriginBehaviorInterface} from '../route_origin_behavior.js';
/**
* Refers to Bluetooth secondary text label, used to distinguish between
* accessibility string and UI text string.
* @enum {number}
*/
const LabelType = {
A11Y: 1,
DISPLAYED_TEXT: 2,
};
/**
* @constructor
* @extends {PolymerElement}
* @implements {I18nBehaviorInterface}
* @implements {RouteObserverBehaviorInterface}
* @implements {RouteOriginBehaviorInterface}
*/
const SettingsBluetoothSummaryElementBase = mixinBehaviors(
[I18nBehavior, RouteObserverBehavior, RouteOriginBehavior], PolymerElement);
/** @polymer */
class SettingsBluetoothSummaryElement extends
SettingsBluetoothSummaryElementBase {
static get is() {
return 'os-settings-bluetooth-summary';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/**
* @type {!BluetoothSystemProperties}
*/
systemProperties: {
type: Object,
observer: 'onSystemPropertiesChanged_',
},
/**
* Reflects the current state of the toggle button. This will be set when
* the |systemProperties| state changes or when the user presses the
* toggle.
* @private
*/
isBluetoothToggleOn_: {
type: Boolean,
observer: 'onBluetoothToggleChanged_',
},
/** @private */
LabelType: {
type: Object,
value: LabelType,
},
/**
* Whether the user is a secondary user.
* @private
*/
isSecondaryUser_: {
type: Boolean,
value() {
return loadTimeData.getBoolean('isSecondaryUser');
},
readOnly: true,
},
/**
* Email address for the primary user.
* @private
*/
primaryUserEmail_: {
type: String,
value() {
return loadTimeData.getString('primaryUserEmail');
},
readOnly: true,
},
};
}
constructor() {
super();
/** RouteOriginBehaviorInterface override */
this.route_ = routes.BASIC;
}
/** @override */
ready() {
super.ready();
this.addFocusConfig(routes.BLUETOOTH_DEVICES, '.subpage-arrow');
}
/** @private */
onSystemPropertiesChanged_() {
if (this.isToggleDisabled_()) {
return;
}
this.isBluetoothToggleOn_ =
this.systemProperties.systemState === BluetoothSystemState.kEnabled ||
this.systemProperties.systemState === BluetoothSystemState.kEnabling;
}
/**
* Observer for isBluetoothToggleOn_ that returns early until the previous
* value was not undefined to avoid wrongly toggling the Bluetooth state.
* @param {boolean} newValue
* @param {boolean} oldValue
* @private
*/
onBluetoothToggleChanged_(newValue, oldValue) {
if (oldValue === undefined) {
return;
}
getBluetoothConfig().setBluetoothEnabledState(this.isBluetoothToggleOn_);
}
/**
* @return {boolean}
* @private
*/
isToggleDisabled_() {
if (!this.systemProperties) {
return false;
}
// TODO(crbug.com/1010321): Add check for modification state when variable
// is available.
return this.systemProperties.systemState ===
BluetoothSystemState.kUnavailable;
}
/**
* @param {LabelType} labelType
* @return {string}
* @private
*/
getSecondaryLabel_(labelType) {
if (!this.isBluetoothToggleOn_) {
return this.i18n('bluetoothSummaryPageOff');
}
const connectedDevices = this.getConnectedDevices_();
if (!connectedDevices.length) {
return this.i18n('bluetoothSummaryPageOn');
}
const isA11yLabel = labelType === LabelType.A11Y;
const firstConnectedDeviceName = getDeviceName(connectedDevices[0]);
if (connectedDevices.length === 1) {
return isA11yLabel ? this.i18n(
'bluetoothSummaryPageConnectedA11yOneDevice',
firstConnectedDeviceName) :
firstConnectedDeviceName;
}
if (connectedDevices.length === 2) {
const secondConnectedDeviceName = getDeviceName(connectedDevices[1]);
return isA11yLabel ?
this.i18n(
'bluetoothSummaryPageConnectedA11yTwoDevices',
firstConnectedDeviceName, secondConnectedDeviceName) :
this.i18n(
'bluetoothSummaryPageTwoDevicesDescription',
firstConnectedDeviceName, secondConnectedDeviceName);
}
return isA11yLabel ?
this.i18n(
'bluetoothSummaryPageConnectedA11yTwoOrMoreDevices',
firstConnectedDeviceName, connectedDevices.length - 1) :
this.i18n(
'bluetoothSummaryPageTwoOrMoreDevicesDescription',
firstConnectedDeviceName, connectedDevices.length - 1);
}
/**
* @return {Array<?PairedBluetoothDeviceProperties>}
* @private
*/
getConnectedDevices_() {
const pairedDevices = this.systemProperties.pairedDevices;
if (!pairedDevices) {
return [];
}
return pairedDevices.filter(
device => device.deviceProperties.connectionState ===
DeviceConnectionState.kConnected);
}
/**
* @return {string}
* @private
*/
getBluetoothStatusIconName_() {
if (!this.isBluetoothToggleOn_) {
return 'os-settings:bluetooth-disabled';
}
if (this.getConnectedDevices_().length) {
return 'os-settings:bluetooth-connected';
}
return 'cr:bluetooth';
}
/**
* @return {boolean}
* @private
*/
shouldShowSubpageArrow_() {
if (this.isToggleDisabled_()) {
return false;
}
return this.isBluetoothToggleOn_;
}
/**
* @param {!Event} e
* @private
*/
onSubpageArrowClick_(e) {
this.navigateToBluetoothDevicesSubpage_();
e.stopPropagation();
}
/** @private */
navigateToBluetoothDevicesSubpage_() {
Router.getInstance().navigateTo(routes.BLUETOOTH_DEVICES);
}
/** @private */
onWrapperClick_() {
if (this.isToggleDisabled_()) {
return;
}
if (this.systemProperties.systemState === BluetoothSystemState.kDisabled ||
this.systemProperties.systemState === BluetoothSystemState.kDisabling) {
this.isBluetoothToggleOn_ = true;
return;
}
this.navigateToBluetoothDevicesSubpage_();
}
/** @private */
onPairNewDeviceBtnClick_() {
this.dispatchEvent(new CustomEvent('start-pairing', {
bubbles: true,
composed: true,
}));
}
/** @private */
annouceBluetoothStateChange_() {
getAnnouncerInstance().announce(
this.isBluetoothToggleOn_ ? this.i18n('bluetoothEnabledA11YLabel') :
this.i18n('bluetoothDisabledA11YLabel'));
}
}
customElements.define(
SettingsBluetoothSummaryElement.is, SettingsBluetoothSummaryElement);