// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/ash/common/cr_elements/policy/cr_tooltip_icon.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import './diagnostics_card_frame.js';
import './icons.html.js';
import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js';
import {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ConnectionType, KeyboardInfo} from './input.mojom-webui.js';
import {getTemplate} from './input_card.html.js';
import {InputDataProviderInterface, TouchDeviceInfo} from './input_data_provider.mojom-webui.js';
import {HostDeviceStatus} from './input_list.js';
import {getInputDataProvider} from './mojo_interface_provider.js';
declare global {
interface HTMLElementEventMap {
'test-button-click': CustomEvent<{evdevId: number}>;
}
}
/**
* @fileoverview
* 'input-card' is responsible for displaying a list of input devices with links
* to their testers.
*/
/**
* Enum of device types supported by input-card elements.
*/
export enum InputCardType {
KEYBOARD = 'keyboard',
TOUCHPAD = 'touchpad',
TOUCHSCREEN = 'touchscreen',
}
const InputCardElementBase = I18nMixin(PolymerElement);
export class InputCardElement extends InputCardElementBase {
static get is(): string {
return 'input-card';
}
static get template(): HTMLTemplateElement {
return getTemplate();
}
static get properties(): PolymerElementProperties {
return {
/**
* The type of input device to be displayed. Valid values are 'keyboard',
* 'touchpad', and 'touchscreen'.
*/
deviceType: String,
devices: {
type: Array,
value: () => [],
},
deviceIcon: {
type: String,
computed: 'computeDeviceIcon(deviceType)',
},
hostDeviceStatus: {
type: Object,
},
};
}
deviceType: InputCardType;
devices: KeyboardInfo[]|TouchDeviceInfo[];
hostDeviceStatus: HostDeviceStatus;
private deviceIcon: string;
private inputDataProvider: InputDataProviderInterface =
getInputDataProvider();
private computeDeviceIcon(deviceType: InputCardType): string {
return {
[InputCardType.KEYBOARD]: 'diagnostics:keyboard',
[InputCardType.TOUCHPAD]: 'diagnostics:touchpad',
[InputCardType.TOUCHSCREEN]: 'diagnostics:touchscreen',
}[deviceType];
}
/**
* Fetches the description string for a device based on its connection type
* (e.g. "Bluetooth keyboard", "Internal touchpad").
*/
private getDeviceDescription(device: KeyboardInfo|TouchDeviceInfo): string {
if (device.connectionType === ConnectionType.kUnknown) {
return '';
}
const connectionTypeString = {
[ConnectionType.kInternal]: 'Internal',
[ConnectionType.kUsb]: 'Usb',
[ConnectionType.kBluetooth]: 'Bluetooth',
}[device.connectionType];
const deviceTypeString = {
[InputCardType.KEYBOARD]: 'Keyboard',
[InputCardType.TOUCHPAD]: 'Touchpad',
[InputCardType.TOUCHSCREEN]: 'Touchscreen',
}[this.deviceType];
return loadTimeData.getString(
'inputDescription' + connectionTypeString + deviceTypeString);
}
private isInternalKeyboard(device: KeyboardInfo|TouchDeviceInfo): boolean {
return this.deviceType == InputCardType.KEYBOARD &&
device.connectionType == ConnectionType.kInternal;
}
private isInternalKeyboardTestable(): boolean {
return !this.hostDeviceStatus.isTabletMode &&
this.hostDeviceStatus.isLidOpen;
}
/**
* Grey out the test button if the test device is untestable. e.g. if the
* laptop's lid is closed, the internal touchscreen is untestable.
*/
private getDeviceTestability(device: KeyboardInfo|TouchDeviceInfo): boolean {
// If the device has the key 'testable', we check its testable state.
if ('testable' in device) {
return (device as TouchDeviceInfo).testable;
}
if (this.isInternalKeyboard(device)) {
return this.isInternalKeyboardTestable();
}
return true;
}
private getDeviceTestabilityErrorMessage(device: KeyboardInfo|
TouchDeviceInfo): string {
// If it is not an internal keyboard, return the generic untestable string.
if (!this.isInternalKeyboard(device)) {
return loadTimeData.getString('inputDeviceUntestableNote');
}
// Otherwise, differentiate the string based on the reason it is untestable.
if (this.hostDeviceStatus.isTabletMode) {
return loadTimeData.getString('inputKeyboardUntestableTabletModeNote');
}
if (!this.hostDeviceStatus.isLidOpen) {
return loadTimeData.getString('inputKeyboardUntestableLidClosedNote');
}
// Otherwise, there is no error.
return '';
}
private handleTestButtonClick(e: PointerEvent): void {
const inputDeviceButton = e.target as CrButtonElement;
assert(inputDeviceButton);
const closestDevice: HTMLDivElement|null =
inputDeviceButton.closest('.device');
assert(closestDevice);
const dataEvdevId = closestDevice.getAttribute('data-evdev-id');
assert(typeof dataEvdevId === 'string');
const evdevId = parseInt(dataEvdevId, 10);
this.dispatchEvent(new CustomEvent(
'test-button-click', {composed: true, detail: {evdevId: evdevId}}));
}
}
declare global {
interface HTMLElementTagNameMap {
'input-card': InputCardElement;
}
}
customElements.define(InputCardElement.is, InputCardElement);