chromium/chrome/browser/resources/ash/settings/os_a11y_page/audio_and_captions_page.ts

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

/**
 * @fileoverview
 * 'settings-audio-and-captions-page' is the accessibility settings subpage for
 * audio and captions accessibility settings.
 */

import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/ash/common/cr_elements/cr_link_row/cr_link_row.js';
import 'chrome://resources/ash/common/cr_elements/cr_toggle/cr_toggle.js';
import 'chrome://resources/ash/common/cr_elements/icons.html.js';
import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
import '../controls/settings_slider.js';
import '../controls/settings_toggle_button.js';
import '../settings_shared.css.js';
import './captions_subpage.js';
import 'chrome://resources/ash/common/cr_elements/localized_link/localized_link.js';

import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
import {CrToggleElement} from 'chrome://resources/ash/common/cr_elements/cr_toggle/cr_toggle.js';
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {DeepLinkingMixin} from '../common/deep_linking_mixin.js';
import {RouteOriginMixin} from '../common/route_origin_mixin.js';
import {Setting} from '../mojom-webui/setting.mojom-webui.js';
import type {LanguageHelper, LanguagesModel} from '../os_languages_page/languages_types.js';
import {Route, routes} from '../router.js';

import {getTemplate} from './audio_and_captions_page.html.js';
import {AudioAndCaptionsPageBrowserProxy, AudioAndCaptionsPageBrowserProxyImpl} from './audio_and_captions_page_browser_proxy.js';

export enum NotificationColor {
  RED = 0xff0000,
  YELLOW = 0xffff00,
  GREEN = 0x00ff01,
  CYAN = 0x00ffff,
  BLUE = 0x0000fe,
  MAGENTA = 0xff00fe,
  PINK = 0xff017e,
}

export interface SettingsAudioAndCaptionsPageElement {
  $: {
    startupSoundEnabled: CrToggleElement,
  };
}

const SettingsAudioAndCaptionsPageElementBase =
    DeepLinkingMixin(RouteOriginMixin(
        PrefsMixin(WebUiListenerMixin(I18nMixin(PolymerElement)))));

export class SettingsAudioAndCaptionsPageElement extends
    SettingsAudioAndCaptionsPageElementBase {
  static get is() {
    return 'settings-audio-and-captions-page';
  }

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

  static get properties() {
    return {
      /**
       * Read-only reference to the languages model provided by the
       * 'settings-languages' instance.
       */
      languages: {
        type: Object,
      },

      languageHelper: Object,

      /**
       * Whether the user is in kiosk mode.
       */
      isKioskModeActive_: {
        type: Boolean,
        value() {
          return loadTimeData.getBoolean('isKioskModeActive');
        },
      },

      isAccessibilityFlashNotificationFeatureEnabled: {
        readOnly: true,
        type: Boolean,
        value() {
          return loadTimeData.getBoolean(
              'isAccessibilityFlashNotificationFeatureEnabled');
        },
      },

      notificationColorOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {
              value: NotificationColor.RED,
              name: loadTimeData.getString('cursorColorRed'),
            },
            {
              value: NotificationColor.YELLOW,
              name: loadTimeData.getString('cursorColorYellow'),
            },
            {
              value: NotificationColor.GREEN,
              name: loadTimeData.getString('cursorColorGreen'),
            },
            {
              value: NotificationColor.CYAN,
              name: loadTimeData.getString('cursorColorCyan'),
            },
            {
              value: NotificationColor.BLUE,
              name: loadTimeData.getString('cursorColorBlue'),
            },
            {
              value: NotificationColor.MAGENTA,
              name: loadTimeData.getString('cursorColorMagenta'),
            },
            {
              value: NotificationColor.PINK,
              name: loadTimeData.getString('cursorColorPink'),
            },
          ];
        },
      },

      /**
       * Used by DeepLinkingMixin to focus this page's deep links.
       */
      supportedSettingIds: {
        type: Object,
        value: () => new Set<Setting>([
          Setting.kFlashNotifications,
          Setting.kLiveCaption,
          Setting.kMonoAudio,
          Setting.kStartupSound,
        ]),
      },
    };
  }

  languages: LanguagesModel;
  languageHelper: LanguageHelper;
  private audioAndCaptionsBrowserProxy_: AudioAndCaptionsPageBrowserProxy;
  private isKioskModeActive_: boolean;

  constructor() {
    super();

    /** RouteOriginMixin override */
    this.route = routes.A11Y_AUDIO_AND_CAPTIONS;

    this.audioAndCaptionsBrowserProxy_ =
        AudioAndCaptionsPageBrowserProxyImpl.getInstance();
  }

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

    this.addWebUiListener(
        'initial-data-ready',
        (startupSoundEnabled: boolean) =>
            this.updateStartupSoundEnabled_(startupSoundEnabled));
    this.addWebUiListener(
        'startup-sound-setting-retrieved',
        (startupSoundEnabled: boolean) =>
            this.updateStartupSoundEnabled_(startupSoundEnabled));
    this.audioAndCaptionsBrowserProxy_.audioAndCaptionsPageReady();
  }

  /**
   * Overridden from DeepLinkingMixin.
   */
  override beforeDeepLinkAttempt(settingId: Setting): boolean {
    if (settingId === Setting.kLiveCaption) {
      afterNextRender(this, () => {
        const captionsSubpage =
            this.shadowRoot!.querySelector('settings-captions');
        const toggle = captionsSubpage?.getLiveCaptionToggle();
        if (toggle) {
          this.showDeepLinkElement(toggle);
          return;
        }
        console.warn(`Element with deep link id ${settingId} not focusable.`);
      });

      // Stop deep link attempt since we completed it manually.
      return false;
    }

    // Continue with deep linking attempt.
    return true;
  }

  /**
   * Note: Overrides RouteOriginMixin implementation
   */
  override currentRouteChanged(newRoute: Route, prevRoute?: Route): void {
    super.currentRouteChanged(newRoute, prevRoute);

    // Does not apply to this page.
    if (newRoute !== this.route) {
      return;
    }

    this.audioAndCaptionsBrowserProxy_.getStartupSoundEnabled();
    this.attemptDeepLink();
  }

  private toggleStartupSoundEnabled_(e: CustomEvent<boolean>): void {
    this.audioAndCaptionsBrowserProxy_.setStartupSoundEnabled(e.detail);
  }

  /**
   * Handles updating the visibility of the shelf navigation buttons setting
   * and updating whether startupSoundEnabled is checked.
   */
  private updateStartupSoundEnabled_(startupSoundEnabled: boolean): void {
    this.$.startupSoundEnabled.checked = startupSoundEnabled;
  }

  private previewFlashNotification_(): void {
    this.audioAndCaptionsBrowserProxy_.previewFlashNotification();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-audio-and-captions-page': SettingsAudioAndCaptionsPageElement;
  }
}

customElements.define(
    SettingsAudioAndCaptionsPageElement.is,
    SettingsAudioAndCaptionsPageElement);