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

// Copyright 2016 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-stylus' is the settings subpage with stylus-specific settings.
 */

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/cr_shared_vars.css.js';
import 'chrome://resources/js/action_link.js';
import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
import '../controls/settings_toggle_button.js';
import '../settings_shared.css.js';

import {CrPolicyIndicatorType} from 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {assertExists} from '../assert_extras.js';
import {DeepLinkingMixin} from '../common/deep_linking_mixin.js';
import {RouteObserverMixin} from '../common/route_observer_mixin.js';
import {PrefsState} from '../common/types.js';
import {recordSettingChange} from '../metrics_recorder.js';
import {Setting} from '../mojom-webui/setting.mojom-webui.js';
import {Route, routes} from '../router.js';

import {DevicePageBrowserProxy, DevicePageBrowserProxyImpl, NoteAppInfo, NoteAppLockScreenSupport} from './device_page_browser_proxy.js';
import {getTemplate} from './stylus.html.js';

export interface SettingsStylusElement {
  $: {
    selectApp: HTMLSelectElement,
  };
}

const FIND_MORE_APPS_URL = 'https://play.google.com/store/apps/' +
    'collection/promotion_30023cb_stylus_apps';

const SettingsStylusElementBase =
    DeepLinkingMixin(RouteObserverMixin(PolymerElement));

export class SettingsStylusElement extends SettingsStylusElementBase {
  static get is() {
    return 'settings-stylus';
  }

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

  static get properties() {
    return {
      /** Preferences state. */
      prefs: {
        type: Object,
        notify: true,
      },

      /**
       * Policy indicator type for user policy - used for policy indicator UI
       * shown when an app that is not allowed to run on lock screen by policy
       * is selected.
       */
      userPolicyIndicator_: {
        type: String,
        value: CrPolicyIndicatorType.USER_POLICY,
      },

      /**
       * Note taking apps the user can pick between.
       */
      appChoices_: {
        type: Array,
        value() {
          return [];
        },
      },

      /**
       * True if the device has an internal stylus.
       */
      hasInternalStylus_: {
        type: Boolean,
        value() {
          return loadTimeData.getBoolean('hasInternalStylus');
        },
        readOnly: true,
      },

      /**
       * Currently selected note taking app.
       */
      selectedApp_: {
        type: Object,
        value: null,
      },

      /**
       * True if the ARC container has not finished starting yet.
       */
      waitingForAndroid_: {
        type: Boolean,
        value: false,
      },

      /**
       * Used by DeepLinkingMixin to focus this page's deep links.
       */
      supportedSettingIds: {
        type: Object,
        value: () => new Set<Setting>([
          Setting.kStylusToolsInShelf,
          Setting.kStylusNoteTakingApp,
          Setting.kStylusNoteTakingFromLockScreen,
          Setting.kStylusLatestNoteOnLockScreen,
        ]),
      },

    };
  }

  prefs: PrefsState;
  private appChoices_: NoteAppInfo[];
  private browserProxy_: DevicePageBrowserProxy;
  private selectedApp_: NoteAppInfo|null;
  private waitingForAndroid_: boolean;

  constructor() {
    super();

    this.browserProxy_ = DevicePageBrowserProxyImpl.getInstance();
  }

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

    this.browserProxy_.setNoteTakingAppsUpdatedCallback(
        this.onNoteAppsUpdated_.bind(this));
    this.browserProxy_.requestNoteTakingApps();
  }

  override currentRouteChanged(route: Route): void {
    // Does not apply to this page.
    if (route !== routes.STYLUS) {
      return;
    }

    this.attemptDeepLink();
  }

  /**
   * @return Whether note taking from the lock screen is supported
   *     by the selected note-taking app.
   */
  private supportsLockScreen_(): boolean {
    return !!this.selectedApp_ &&
        this.selectedApp_.lockScreenSupport !==
        NoteAppLockScreenSupport.NOT_SUPPORTED;
  }

  /**
   * @return Whether the selected app is disallowed to handle note
   *     actions from lock screen as a result of a user policy.
   */
  private disallowedOnLockScreenByPolicy_(): boolean {
    return !!this.selectedApp_ &&
        this.selectedApp_.lockScreenSupport ===
        NoteAppLockScreenSupport.NOT_ALLOWED_BY_POLICY;
  }

  /**
   * @return Whether the selected app is enabled as a note action
   *     handler on the lock screen.
   */
  private lockScreenSupportEnabled_(): boolean {
    return !!this.selectedApp_ &&
        this.selectedApp_.lockScreenSupport ===
        NoteAppLockScreenSupport.ENABLED;
  }

  /**
   * Finds note app info with the provided app id.
   */
  private findApp_(id: string): NoteAppInfo|null {
    return this.appChoices_.find((app) => app.value === id) || null;
  }

  /**
   * Toggles whether the selected app is enabled as a note action handler on
   * the lock screen.
   */
  private toggleLockScreenSupport_(): void {
    assertExists(this.selectedApp_);
    if (this.selectedApp_.lockScreenSupport !==
            NoteAppLockScreenSupport.ENABLED &&
        this.selectedApp_.lockScreenSupport !==
            NoteAppLockScreenSupport.SUPPORTED) {
      return;
    }

    const isSupported = this.selectedApp_.lockScreenSupport ===
        NoteAppLockScreenSupport.SUPPORTED;
    this.browserProxy_.setPreferredNoteTakingAppEnabledOnLockScreen(
        isSupported);
    recordSettingChange(
        Setting.kStylusNoteTakingFromLockScreen, {boolValue: isSupported});
  }

  private onSelectedAppChanged_(): void {
    const app = this.findApp_(this.$.selectApp.value);
    this.selectedApp_ = app;

    if (app && !app.preferred) {
      this.browserProxy_.setPreferredNoteTakingApp(app.value);
      recordSettingChange(Setting.kStylusNoteTakingApp);
    }
  }

  private onNoteAppsUpdated_(apps: NoteAppInfo[], waitingForAndroid: boolean):
      void {
    this.waitingForAndroid_ = waitingForAndroid;
    this.appChoices_ = apps;

    // Wait until app selection UI is updated before setting the selected app.
    microTask.run(this.onSelectedAppChanged_.bind(this));
  }

  private showNoApps_(apps: NoteAppInfo[], waitingForAndroid: boolean):
      boolean {
    return apps.length === 0 && !waitingForAndroid;
  }

  private showApps_(apps: NoteAppInfo[], waitingForAndroid: boolean): boolean {
    return apps.length > 0 && !waitingForAndroid;
  }

  private onFindAppsClick_(): void {
    this.browserProxy_.showPlayStore(FIND_MORE_APPS_URL);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-stylus': SettingsStylusElement;
  }
}

customElements.define(SettingsStylusElement.is, SettingsStylusElement);