| // 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. |
| |
| /** |
| * @fileoverview 'settings-live-caption' is a component for showing Live Caption |
| * settings. It appears on the accessibility subpage |
| * (chrome://settings/accessibility) on Mac and some versions of Windows and on |
| * the captions subpage (chrome://settings/captions) on Linux and other versions |
| * of Windows. |
| */ |
| |
| import '//resources/cr_elements/cr_shared_style.css.js'; |
| import '//resources/cr_elements/cr_collapse/cr_collapse.js'; |
| import '../controls/settings_toggle_button.js'; |
| import '../settings_shared.css.js'; |
| |
| import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js'; |
| import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| import type {CaptionsBrowserProxy, LiveCaptionLanguage, LiveCaptionLanguageList} from '/shared/settings/a11y_page/captions_browser_proxy.js'; |
| import {CaptionsBrowserProxyImpl} from '/shared/settings/a11y_page/captions_browser_proxy.js'; |
| import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js'; |
| |
| import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js'; |
| import {loadTimeData} from '../i18n_setup.js'; |
| |
| import {getTemplate} from './live_caption_section.html.js'; |
| |
| // clang-format off |
| // <if expr="not is_chromeos"> |
| import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js'; |
| import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js'; |
| |
| import type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js'; |
| import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js'; |
| |
| import './live_translate_section.js'; |
| import '../languages_page/add_languages_dialog.js'; |
| |
| import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; |
| import {ListPropertyUpdateMixin} from 'chrome://resources/cr_elements/list_property_update_mixin.js'; |
| import {assert} from 'chrome://resources/js/assert.js'; |
| import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js'; |
| import type {DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| // </if> |
| // clang-format on |
| |
| |
| // <if expr="is_chromeos"> |
| const SettingsLiveCaptionElementBase = |
| WebUiListenerMixin(PrefsMixin(PolymerElement)); |
| // </if> |
| // <if expr="not is_chromeos"> |
| const SettingsLiveCaptionElementBase = WebUiListenerMixin( |
| ListPropertyUpdateMixin(PrefsMixin(I18nMixin(PolymerElement)))); |
| |
| export interface SettingsLiveCaptionElement { |
| $: { |
| menu: CrLazyRenderElement<CrActionMenuElement>, |
| }; |
| } |
| // </if> |
| |
| export class SettingsLiveCaptionElement extends SettingsLiveCaptionElementBase { |
| static get is() { |
| return 'settings-live-caption'; |
| } |
| |
| static get template() { |
| return getTemplate(); |
| } |
| |
| static get properties() { |
| return { |
| /** |
| * The subtitle to display under the Live Caption heading. Generally, this |
| * is a generic subtitle describing the feature. While the SODA model is |
| * being downloading, this displays the download progress. |
| */ |
| enableLiveCaptionSubtitle_: { |
| type: String, |
| value: loadTimeData.getString('captionsEnableLiveCaptionSubtitle'), |
| }, |
| |
| enableLiveCaptionMultiLanguage_: { |
| type: Boolean, |
| value: function() { |
| return loadTimeData.getBoolean('enableLiveCaptionMultiLanguage'); |
| }, |
| }, |
| |
| // <if expr="not is_chromeos"> |
| enableLiveTranslate_: { |
| type: Boolean, |
| value: function() { |
| return loadTimeData.getBoolean('enableLiveTranslate'); |
| }, |
| }, |
| |
| installedLanguagePacks_: { |
| type: Array, |
| value: () => [], |
| }, |
| |
| availableLanguagePacks_: { |
| type: Array, |
| value: () => [], |
| }, |
| |
| /** |
| * The language to display the details for. |
| */ |
| detailLanguage_: Object, |
| |
| showAddLanguagesDialog_: Boolean, |
| // </if> |
| }; |
| } |
| |
| // <if expr="not is_chromeos"> |
| declare private enableLiveTranslate_: boolean; |
| declare private installedLanguagePacks_: LiveCaptionLanguageList; |
| declare private availableLanguagePacks_: LiveCaptionLanguageList; |
| declare private detailLanguage_?: LiveCaptionLanguage; |
| declare private showAddLanguagesDialog_: boolean; |
| // </if> |
| private browserProxy_: CaptionsBrowserProxy = |
| CaptionsBrowserProxyImpl.getInstance(); |
| declare private enableLiveCaptionSubtitle_: string; |
| declare private enableLiveCaptionMultiLanguage_: boolean; |
| |
| override ready() { |
| super.ready(); |
| // <if expr="not is_chromeos"> |
| this.browserProxy_.getInstalledLanguagePacks().then( |
| (installedLanguagePacks: LiveCaptionLanguageList) => { |
| this.installedLanguagePacks_ = installedLanguagePacks; |
| }); |
| |
| this.browserProxy_.getAvailableLanguagePacks().then( |
| (availableLanguagePacks: LiveCaptionLanguageList) => { |
| this.availableLanguagePacks_ = availableLanguagePacks; |
| }); |
| // </if> |
| |
| // <if expr="is_chromeos"> |
| this.addWebUiListener( |
| 'soda-download-progress-changed', |
| (sodaDownloadProgress: string) => |
| this.onSodaDownloadProgressChanged_(sodaDownloadProgress)); |
| // </if> |
| // <if expr="not is_chromeos"> |
| this.addWebUiListener( |
| 'soda-download-progress-changed', |
| (sodaDownloadProgress: string, languageCode: string) => |
| this.onSodaDownloadProgressChangedForLanguage_( |
| sodaDownloadProgress, languageCode)); |
| // </if> |
| |
| this.browserProxy_.liveCaptionSectionReady(); |
| } |
| |
| /** |
| * @return the Live Caption toggle element. |
| */ |
| getLiveCaptionToggle(): SettingsToggleButtonElement { |
| return this.shadowRoot!.querySelector<SettingsToggleButtonElement>( |
| '#liveCaptionToggleButton')!; |
| } |
| |
| protected computeMoreButtonAriaLabel_(name: string, code: string): string { |
| let label = this.i18n('moreActionsFor', name); |
| if (this.isDefaultLanguage_(code)) { |
| label += ` ${this.i18n('defaultLanguageLabel')}`; |
| } |
| return label; |
| } |
| |
| private onLiveCaptionEnabledChanged_(event: Event) { |
| const liveCaptionEnabled = |
| (event.target as SettingsToggleButtonElement).checked; |
| chrome.metricsPrivate.recordBoolean( |
| 'Accessibility.LiveCaption.EnableFromSettings', liveCaptionEnabled); |
| |
| // <if expr="not is_chromeos"> |
| if (this.installedLanguagePacks_.length === 0) { |
| this.installLanguagePacks_( |
| [this.getPref('accessibility.captions.live_caption_language').value]); |
| } |
| // </if> |
| } |
| |
| private onLiveCaptionMaskOffensiveWordsChanged_(event: Event) { |
| const liveCaptionMaskOffensiveWords = |
| (event.target as SettingsToggleButtonElement).checked; |
| chrome.metricsPrivate.recordBoolean( |
| 'Accessibility.LiveCaption.MaskOffensiveWords', |
| liveCaptionMaskOffensiveWords); |
| } |
| |
| // <if expr="not is_chromeos"> |
| private onAddLanguagesClick_(e: Event) { |
| e.preventDefault(); |
| this.showAddLanguagesDialog_ = true; |
| } |
| |
| private onAddLanguagesDialogClose_() { |
| this.showAddLanguagesDialog_ = false; |
| const toFocus = this.shadowRoot!.querySelector<HTMLElement>('#addLanguage'); |
| assert(toFocus); |
| focusWithoutInk(toFocus); |
| } |
| |
| private onDotsClick_(e: DomRepeatEvent<LiveCaptionLanguage>) { |
| this.detailLanguage_ = Object.assign({}, e.model.item); |
| this.$.menu.get().showAt(e.target as HTMLElement); |
| } |
| |
| private isDefaultLanguage_(languageCode: string): boolean { |
| if (this.prefs === undefined) { |
| return false; |
| } |
| |
| return languageCode === |
| this.prefs.accessibility.captions.live_caption_language.value; |
| } |
| |
| private onMakeDefaultClick_() { |
| this.$.menu.get().close(); |
| this.setPrefValue( |
| 'accessibility.captions.live_caption_language', |
| this.detailLanguage_!.code); |
| } |
| |
| private onRemoveLanguageClick_() { |
| if (!this.detailLanguage_) { |
| return; |
| } |
| |
| this.$.menu.get().close(); |
| this.installedLanguagePacks_ = this.installedLanguagePacks_.filter( |
| languagePack => languagePack.code !== this.detailLanguage_!.code); |
| this.browserProxy_.removeLanguagePack(this.detailLanguage_.code); |
| |
| if (this.installedLanguagePacks_.length === 0) { |
| this.setPrefValue('accessibility.captions.live_caption_enabled', false); |
| return; |
| } |
| |
| if (!this.installedLanguagePacks_.some( |
| languagePack => languagePack.code === |
| this.getPref('accessibility.captions.live_caption_language') |
| .value)) { |
| this.setPrefValue( |
| 'accessibility.captions.live_caption_language', |
| this.installedLanguagePacks_[0].code); |
| } |
| } |
| |
| private onLanguagesAdded_(e: CustomEvent<string[]>) { |
| this.installLanguagePacks_(e.detail); |
| } |
| |
| private installLanguagePacks_(languageCodes: string[]) { |
| const newLanguagePacks: LiveCaptionLanguageList = []; |
| languageCodes.forEach(languageCode => { |
| const languagePackToAdd = this.availableLanguagePacks_.find( |
| languagePack => languagePack.code === languageCode); |
| if (languagePackToAdd) { |
| newLanguagePacks.push(languagePackToAdd); |
| } |
| }); |
| |
| this.updateList( |
| `installedLanguagePacks_`, item => item.code, |
| this.installedLanguagePacks_.concat(newLanguagePacks)); |
| this.browserProxy_.installLanguagePacks(languageCodes); |
| } |
| |
| private filterAvailableLanguagePacks_( |
| availableLanguagePacks: LiveCaptionLanguageList, |
| installedLanguagePacks: LiveCaptionLanguageList): |
| chrome.languageSettingsPrivate.Language[] { |
| const filteredLanguagePacks = |
| availableLanguagePacks.filter(availableLanguagePack => { |
| return !installedLanguagePacks.some( |
| installedLanguagePack => |
| installedLanguagePack.code === availableLanguagePack.code); |
| }); |
| |
| return filteredLanguagePacks.map( |
| languagePack => ({ |
| code: languagePack.code, |
| displayName: languagePack.displayName, |
| |
| // The native display name for language packs is not shown. |
| nativeDisplayName: languagePack.nativeDisplayName, |
| })); |
| } |
| // </if> |
| |
| // <if expr="is_chromeos"> |
| /** |
| * Displays SODA download progress in the UI. |
| * @param sodaDownloadProgress The message sent from the webui to be displayed |
| * as download progress for Live Caption. |
| */ |
| private onSodaDownloadProgressChanged_(sodaDownloadProgress: string) { |
| this.enableLiveCaptionSubtitle_ = sodaDownloadProgress; |
| } |
| // </if> |
| |
| // <if expr="not is_chromeos"> |
| /** |
| * Displays SODA download progress in the UI. When the language UI is visible, |
| * which occurs when the kLiveCaptionMultiLanguage feature is enabled and when |
| * the kLiveCaptionEnabled pref is true, download progress should appear next |
| * to the selected language. Otherwise, the download progress appears as a |
| * subtitle below the Live Caption toggle. |
| * @param sodaDownloadProgress The message sent from the webui to be displayed |
| * as download progress for Live Caption. |
| * @param languageCode The language code indicating which language pack the |
| * message applies to. |
| */ |
| private onSodaDownloadProgressChangedForLanguage_( |
| sodaDownloadProgress: string, languageCode: string) { |
| if (!this.enableLiveCaptionMultiLanguage_) { |
| this.enableLiveCaptionSubtitle_ = sodaDownloadProgress; |
| return; |
| } |
| |
| for (let i = 0; i < this.installedLanguagePacks_.length; i++) { |
| const language = this.installedLanguagePacks_[i]; |
| if (language.code === languageCode) { |
| language.downloadProgress = sodaDownloadProgress; |
| this.notifyPath('installedLanguagePacks_.' + i + '.downloadProgress'); |
| break; |
| } |
| } |
| } |
| // </if> |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'settings-live-caption': SettingsLiveCaptionElement; |
| } |
| } |
| |
| customElements.define( |
| SettingsLiveCaptionElement.is, SettingsLiveCaptionElement); |