chromium/chrome/browser/resources/ash/settings/device_page/input_device_settings_utils.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.

import {stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
import {AudioOutputCapability, BluetoothDeviceProperties, DeviceConnectionState, DeviceType} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';

import {Button, DeviceSettings, InputDeviceSettingsPolicy, InputDeviceType, KeyEvent, PolicyStatus} from './input_device_settings_types.js';

function objectsAreEqual(
    obj1: {[key: string]: any}, obj2: {[key: string]: any}): boolean {
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) {
    return false;
  }

  for (let i = 0; i < keys1.length; i++) {
    const key = keys1[i];
    const val1 = obj1[key];
    const val2 = obj2[key];
    if (val1 instanceof Object || val2 instanceof Object) {
      if (!(val1 instanceof Object) || !(val2 instanceof Object) ||
          !objectsAreEqual(val1, val2)) {
        return false;
      }
    } else if (val1 !== val2) {
      return false;
    }
  }

  return true;
}

function deviceInList(
    deviceId: number, deviceList: InputDeviceType[]): boolean {
  for (const device of deviceList) {
    if (device.id === deviceId) {
      return true;
    }
  }

  return false;
}

export function settingsAreEqual(
    settings1: DeviceSettings, settings2: DeviceSettings): boolean {
  return objectsAreEqual(settings1, settings2);
}

export function buttonsAreEqual(button1: Button, button2: Button): boolean {
  return objectsAreEqual(button1, button2);
}

export function keyEventsAreEqual(
    keyEvent1: KeyEvent, keyEvent2: KeyEvent): boolean {
  return objectsAreEqual(keyEvent1, keyEvent2);
}

interface PrefPolicyFields {
  controlledBy?: chrome.settingsPrivate.ControlledBy;
  enforcement?: chrome.settingsPrivate.Enforcement;
  recommendedValue?: boolean;
}

export function getPrefPolicyFields(policy: InputDeviceSettingsPolicy|
                                    null): PrefPolicyFields {
  if (policy) {
    const enforcement = policy.policyStatus === PolicyStatus.kManaged ?
        chrome.settingsPrivate.Enforcement.ENFORCED :
        chrome.settingsPrivate.Enforcement.RECOMMENDED;
    return {
      controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
      enforcement,
      recommendedValue: policy.value,
    };
  }

  // These fields must be set back to undefined so the html badge is properly
  // removed from the UI.
  return {
    controlledBy: undefined,
    enforcement: undefined,
    recommendedValue: undefined,
  };
}

export function getDeviceStateChangesToAnnounce(
    newDeviceList: InputDeviceType[],
    prevDeviceList: InputDeviceType[]): {msgId: string, deviceNames: string[]} {
  let msgId: string;
  let devices: InputDeviceType[];
  if (newDeviceList.length > prevDeviceList.length) {
    devices = newDeviceList.filter(
        (device) => !deviceInList(device.id, prevDeviceList));
    msgId = 'deviceConnectedA11yLabel';
  } else {
    msgId = 'deviceDisconnectedA11yLabel';
    devices = prevDeviceList.filter(
        (device) => !deviceInList(device.id, newDeviceList));
  }

  return {msgId, deviceNames: devices.map(device => device.name)};
}

export function createBluetoothDeviceProperties(
    id: string,
    publicName: string,
    batteryPercentage: number,
    ): BluetoothDeviceProperties {
  return {
    id: id,
    address: id,
    publicName: stringToMojoString16(publicName),
    deviceType: DeviceType.kMouse,
    audioCapability: AudioOutputCapability.kNotCapableOfAudioOutput,
    connectionState: DeviceConnectionState.kConnected,
    isBlockedByPolicy: false,
    batteryInfo: {
      defaultProperties: {batteryPercentage},
      leftBudInfo: undefined,
      rightBudInfo: undefined,
      caseInfo: undefined,
    },
    imageInfo: undefined,
  };
}