chromium/ash/system/input_device_settings/pref_handlers/graphics_tablet_pref_handler_impl.cc

// 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.

#include "ash/system/input_device_settings/pref_handlers/graphics_tablet_pref_handler_impl.h"

#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/system/input_device_settings/input_device_settings_metadata.h"
#include "ash/system/input_device_settings/input_device_settings_pref_names.h"
#include "ash/system/input_device_settings/input_device_settings_utils.h"
#include "base/json/values_util.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/known_user.h"

namespace ash {

namespace {

// Graphics tablet metadata was previously added incorrectly for a set of
// graphics tablets. This trims out the added buttons that do not exist that
// were previously added via metadata.
// TODO(dpad): Remove after 07/2025 (M139) as this trimming will no longer be
// needed.
void TrimButtonRemappingListForGraphicsTabletPen(
    mojom::GraphicsTablet* graphics_tablet) {
  CHECK(graphics_tablet);
  if (graphics_tablet->graphics_tablet_button_config ==
      mojom::GraphicsTabletButtonConfig::kNoConfig) {
    return;
  }

  auto metadata_pen_button_list = GetPenButtonRemappingListForConfig(
      graphics_tablet->graphics_tablet_button_config);
  if (metadata_pen_button_list.size() ==
      graphics_tablet->settings->pen_button_remappings.size()) {
    return;
  }

  // For each pen button, if it matches a saved button, copy over the remapping
  // action. The `metadata_pen_button_list` will be the final source of truth
  // after this trimming happens.
  for (auto& metadata_button_remapping : metadata_pen_button_list) {
    for (const auto& saved_button_remapping :
         graphics_tablet->settings->pen_button_remappings) {
      if (metadata_button_remapping->button == saved_button_remapping->button) {
        metadata_button_remapping->remapping_action =
            saved_button_remapping->remapping_action
                ? saved_button_remapping->remapping_action->Clone()
                : nullptr;
      }
    }
  }

  graphics_tablet->settings->pen_button_remappings =
      std::move(metadata_pen_button_list);
}

}  // namespace

GraphicsTabletPrefHandlerImpl::GraphicsTabletPrefHandlerImpl() = default;
GraphicsTabletPrefHandlerImpl::~GraphicsTabletPrefHandlerImpl() = default;

void GraphicsTabletPrefHandlerImpl::InitializeGraphicsTabletSettings(
    PrefService* pref_service,
    mojom::GraphicsTablet* graphics_tablet) {
  const auto& tablet_button_remappings_dict = pref_service->GetDict(
      prefs::kGraphicsTabletTabletButtonRemappingsDictPref);
  const auto& pen_button_remappings_dict =
      pref_service->GetDict(prefs::kGraphicsTabletPenButtonRemappingsDictPref);
  const auto* tablet_button_remappings_list =
      tablet_button_remappings_dict.FindList(graphics_tablet->device_key);
  const auto* pen_button_remappings_list =
      pen_button_remappings_dict.FindList(graphics_tablet->device_key);
  mojom::GraphicsTabletSettingsPtr settings =
      mojom::GraphicsTabletSettings::New();

  // Retrieve the settings if both tablet and pen button remappings lists
  // exist.
  if (tablet_button_remappings_list && pen_button_remappings_list) {
    settings->tablet_button_remappings = ConvertListToButtonRemappingArray(
        *tablet_button_remappings_list,
        graphics_tablet->customization_restriction);
    settings->pen_button_remappings = ConvertListToButtonRemappingArray(
        *pen_button_remappings_list,
        graphics_tablet->customization_restriction);
  } else {
    settings->tablet_button_remappings = GetTabletButtonRemappingListForConfig(
        graphics_tablet->graphics_tablet_button_config);
    settings->pen_button_remappings = GetPenButtonRemappingListForConfig(
        graphics_tablet->graphics_tablet_button_config);
  }

  graphics_tablet->settings = std::move(settings);
  DCHECK(graphics_tablet->settings);

  TrimButtonRemappingListForGraphicsTabletPen(graphics_tablet);
  UpdateGraphicsTabletSettings(pref_service, *graphics_tablet);
}

void GraphicsTabletPrefHandlerImpl::UpdateGraphicsTabletSettings(
    PrefService* pref_service,
    const mojom::GraphicsTablet& graphics_tablet) {
  DCHECK(graphics_tablet.settings);
  const mojom::GraphicsTabletSettings& settings = *graphics_tablet.settings;
  const base::Time time_stamp = base::Time::Now();
  const auto time_stamp_path =
      base::StrCat({prefs::kLastUpdatedKey, ".", graphics_tablet.device_key});
  base::Value::List tablet_button_remappings =
      ConvertButtonRemappingArrayToList(
          settings.tablet_button_remappings,
          graphics_tablet.customization_restriction);
  base::Value::List pen_button_remappings = ConvertButtonRemappingArrayToList(
      settings.pen_button_remappings,
      graphics_tablet.customization_restriction);

  // Update tablet button remappings dict.
  base::Value::Dict tablet_button_remappings_dict =
      pref_service
          ->GetDict(prefs::kGraphicsTabletTabletButtonRemappingsDictPref)
          .Clone();
  tablet_button_remappings_dict.Set(graphics_tablet.device_key,
                                    std::move(tablet_button_remappings));
  tablet_button_remappings_dict.SetByDottedPath(time_stamp_path,
                                                base::TimeToValue(time_stamp));
  pref_service->SetDict(
      std::string(prefs::kGraphicsTabletTabletButtonRemappingsDictPref),
      std::move(tablet_button_remappings_dict));

  // Update pen button remappings dict.
  base::Value::Dict pen_button_remappings_dict =
      pref_service->GetDict(prefs::kGraphicsTabletPenButtonRemappingsDictPref)
          .Clone();
  pen_button_remappings_dict.Set(graphics_tablet.device_key,
                                 std::move(pen_button_remappings));
  pen_button_remappings_dict.SetByDottedPath(time_stamp_path,
                                             base::TimeToValue(time_stamp));
  pref_service->SetDict(
      std::string(prefs::kGraphicsTabletPenButtonRemappingsDictPref),
      std::move(pen_button_remappings_dict));
}

void GraphicsTabletPrefHandlerImpl::InitializeLoginScreenGraphicsTabletSettings(
    PrefService* local_state,
    const AccountId& account_id,
    mojom::GraphicsTablet* graphics_tablet) {
  // Verify if the flag is enabled.
  if (!features::IsPeripheralCustomizationEnabled()) {
    return;
  }
  CHECK(local_state);

  mojom::GraphicsTabletSettingsPtr settings =
      mojom::GraphicsTabletSettings::New();
  const auto* tablet_button_remappings_list = GetLoginScreenButtonRemappingList(
      local_state, account_id,
      prefs::kGraphicsTabletLoginScreenTabletButtonRemappingListPref);
  const auto* pen_button_remappings_list = GetLoginScreenButtonRemappingList(
      local_state, account_id,
      prefs::kGraphicsTabletLoginScreenPenButtonRemappingListPref);
  if (tablet_button_remappings_list && pen_button_remappings_list) {
    settings->tablet_button_remappings = ConvertListToButtonRemappingArray(
        *tablet_button_remappings_list,
        graphics_tablet->customization_restriction);
    settings->pen_button_remappings = ConvertListToButtonRemappingArray(
        *pen_button_remappings_list,
        graphics_tablet->customization_restriction);
  }
  graphics_tablet->settings = std::move(settings);
}

void GraphicsTabletPrefHandlerImpl::UpdateLoginScreenGraphicsTabletSettings(
    PrefService* local_state,
    const AccountId& account_id,
    const mojom::GraphicsTablet& graphics_tablet) {
  CHECK(local_state);

  user_manager::KnownUser(local_state)
      .SetPath(
          account_id,
          prefs::kGraphicsTabletLoginScreenTabletButtonRemappingListPref,
          std::make_optional<base::Value>(ConvertButtonRemappingArrayToList(
              graphics_tablet.settings->tablet_button_remappings,
              graphics_tablet.customization_restriction,
              /*redact_button_names=*/true)));
  user_manager::KnownUser(local_state)
      .SetPath(
          account_id,
          prefs::kGraphicsTabletLoginScreenPenButtonRemappingListPref,
          std::make_optional<base::Value>(ConvertButtonRemappingArrayToList(
              graphics_tablet.settings->pen_button_remappings,
              graphics_tablet.customization_restriction,
              /*redact_button_names=*/true)));
}

}  // namespace ash