chromium/chrome/browser/resources/ash/settings/device_page/customize_tablet_buttons_subpage.ts

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * 'customize-tablet-buttons-subpage' displays the customized tablet buttons
 * and allow users to configure their tablet buttons for each graphics tablet.
 */
import '../icons.html.js';
import '../settings_shared.css.js';
import './input_device_settings_shared.css.js';

import {getInstance as getAnnouncerInstance} from 'chrome://resources/ash/common/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.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 {castExists} from '../assert_extras.js';
import {RouteObserverMixin} from '../common/route_observer_mixin.js';
import {Route, Router, routes} from '../router.js';

import {getTemplate} from './customize_tablet_buttons_subpage.html.js';
import {getInputDeviceSettingsProvider} from './input_device_mojo_interface_provider.js';
import {ActionChoice, GraphicsTablet, GraphicsTabletButtonConfig, InputDeviceSettingsProviderInterface, MetaKey} from './input_device_settings_types.js';

const SettingsCustomizeTabletButtonsSubpageElementBase =
    RouteObserverMixin(I18nMixin(PolymerElement));

export class SettingsCustomizeTabletButtonsSubpageElement extends
    SettingsCustomizeTabletButtonsSubpageElementBase {
  static get is() {
    return 'settings-customize-tablet-buttons-subpage' as const;
  }

  static get template(): HTMLTemplateElement {
    return getTemplate();
  }

  static get properties(): PolymerElementProperties {
    return {
      selectedTablet: {
        type: Object,
      },

      graphicsTablets: {
        type: Array,
      },

      /**
       * Use metaKey to decide which meta key icon to display.
       */
      metaKey_: Object,
    };
  }

  static get observers(): string[] {
    return [
      'onGraphicsTabletListUpdated(graphicsTablets.*)',
    ];
  }

  selectedTablet: GraphicsTablet;
  graphicsTablets: GraphicsTablet[];
  private buttonActionList_: ActionChoice[];
  private inputDeviceSettingsProvider_: InputDeviceSettingsProviderInterface =
      getInputDeviceSettingsProvider();
  private previousRoute_: Route|null = null;
  private isInitialized_: boolean = false;
  private metaKey_: MetaKey = MetaKey.kSearch;

  override async connectedCallback(): Promise<void> {
    super.connectedCallback();

    this.addEventListener('button-remapping-changed', this.onSettingsChanged);
    this.metaKey_ =
        (await this.inputDeviceSettingsProvider_.getMetaKeyToDisplay())
            ?.metaKey;
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();

    this.removeEventListener(
        'button-remapping-changed', this.onSettingsChanged);
  }

  override async currentRouteChanged(route: Route): Promise<void> {
    // Does not apply to this page.
    if (route !== routes.CUSTOMIZE_TABLET_BUTTONS) {
      if (this.previousRoute_ === routes.CUSTOMIZE_TABLET_BUTTONS) {
        this.inputDeviceSettingsProvider_.stopObserving();
      }
      this.previousRoute_ = route;
      return;
    }
    this.previousRoute_ = route;

    if (!this.hasGraphicsTablets()) {
      return;
    }

    if (!this.selectedTablet ||
        this.selectedTablet.id !== this.getGraphicsTabletIdFromUrl()) {
      await this.initializeTablet();
    }
    this.inputDeviceSettingsProvider_.startObserving(this.selectedTablet.id);
    getAnnouncerInstance().announce(
        this.getcustomizeTabletButtonsNudgeHeader_() + ' ' +
        this.getDescription_());
  }

  /**
   * Get the tablet to display according to the graphicsTabletId in the url
   * query, initializing the page and pref with the tablet data.
   */
  private async initializeTablet(): Promise<void> {
    this.isInitialized_ = false;

    const tabletId = this.getGraphicsTabletIdFromUrl();
    const searchedGraphicsTablet = this.graphicsTablets.find(
        (graphicsTablet: GraphicsTablet) => graphicsTablet.id === tabletId);
    this.selectedTablet = castExists(searchedGraphicsTablet);
    this.buttonActionList_ =
        (await this.inputDeviceSettingsProvider_
             .getActionsForGraphicsTabletButtonCustomization())
            ?.options;
    this.isInitialized_ = true;
  }

  private getGraphicsTabletIdFromUrl(): number {
    return Number(
        Router.getInstance().getQueryParameters().get('graphicsTabletId'));
  }

  private hasGraphicsTablets(): boolean {
    return this.graphicsTablets?.length > 0;
  }

  private isTabletConnected(id: number): boolean {
    return !!this.graphicsTablets.find(tablet => tablet.id === id);
  }

  async onGraphicsTabletListUpdated(): Promise<void> {
    if (Router.getInstance().currentRoute !== routes.CUSTOMIZE_TABLET_BUTTONS) {
      return;
    }

    if (!this.hasGraphicsTablets()) {
      Router.getInstance().navigateTo(routes.DEVICE);
      return;
    }

    if (!this.isTabletConnected(this.getGraphicsTabletIdFromUrl())) {
      Router.getInstance().navigateTo(routes.GRAPHICS_TABLET);
      return;
    }
    await this.initializeTablet();
    this.inputDeviceSettingsProvider_.startObserving(this.selectedTablet.id);
  }

  onSettingsChanged(): void {
    if (!this.isInitialized_) {
      return;
    }

    this.inputDeviceSettingsProvider_.setGraphicsTabletSettings(
        this.selectedTablet!.id, this.selectedTablet!.settings);
  }

  private getDescription_(): string {
    if (!this.selectedTablet?.name) {
      return '';
    }
    return this.i18n(
        'customizeTabletButtonSubpageDescription', this.selectedTablet!.name);
  }

  private getcustomizeTabletButtonsNudgeHeader_(): string {
    if (this.selectedTablet?.graphicsTabletButtonConfig !==
        GraphicsTabletButtonConfig.kNoConfig) {
      return this.i18n('customizeTabletButtonsNudgeHeaderWithMetadata');
    } else {
      return this.i18n('customizeTabletButtonsNudgeHeaderWithoutMetadata');
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [SettingsCustomizeTabletButtonsSubpageElement.is]:
        SettingsCustomizeTabletButtonsSubpageElement;
  }
}

customElements.define(
    SettingsCustomizeTabletButtonsSubpageElement.is,
    SettingsCustomizeTabletButtonsSubpageElement);