chromium/chrome/browser/ui/webui/ash/settings/pages/device/device_keyboard_handler.cc

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

#include "chrome/browser/ui/webui/ash/settings/pages/device/device_keyboard_handler.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "content/public/browser/web_ui.h"
#include "ui/display/screen.h"
#include "ui/events/ash/keyboard_capability.h"
#include "ui/events/ash/keyboard_layout_util.h"
#include "ui/events/devices/keyboard_device.h"

namespace {

struct KeyboardsStateResult {
  bool has_launcher_key = false;  // ChromeOS launcher key.
  bool has_external_apple_keyboard = false;
  bool has_external_chromeos_keyboard = false;
  bool has_external_generic_keyboard = false;
};

KeyboardsStateResult GetKeyboardsState() {
  KeyboardsStateResult result;
  for (const ui::KeyboardDevice& keyboard :
       ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
    switch (ash::Shell::Get()->keyboard_capability()->GetDeviceType(keyboard)) {
      case ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard:
      case ui::KeyboardCapability::DeviceType::kDeviceInternalRevenKeyboard:
        result.has_launcher_key = true;
        break;
      case ui::KeyboardCapability::DeviceType::kDeviceExternalAppleKeyboard:
        result.has_external_apple_keyboard = true;
        break;
      case ui::KeyboardCapability::DeviceType::kDeviceExternalChromeOsKeyboard:
        result.has_external_chromeos_keyboard = true;
        break;
      case ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard:
      case ui::KeyboardCapability::DeviceType::
          kDeviceExternalNullTopRowChromeOsKeyboard:
      case ui::KeyboardCapability::DeviceType::kDeviceExternalUnknown:
        result.has_external_generic_keyboard = true;
        break;
      case ui::KeyboardCapability::DeviceType::kDeviceHotrodRemote:
      case ui::KeyboardCapability::DeviceType::kDeviceVirtualCoreKeyboard:
      case ui::KeyboardCapability::DeviceType::kDeviceUnknown:
        break;
    }
  }

  return result;
}

}  // namespace

namespace ash::settings {

const char KeyboardHandler::kShowKeysChangedName[] = "show-keys-changed";

void KeyboardHandler::TestAPI::Initialize() {
  handler_->HandleInitialize(base::Value::List());
}

KeyboardHandler::KeyboardHandler() = default;
KeyboardHandler::~KeyboardHandler() = default;

void KeyboardHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "initializeKeyboardSettings",
      base::BindRepeating(&KeyboardHandler::HandleInitialize,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "showShortcutCustomizationApp",
      base::BindRepeating(&KeyboardHandler::HandleShowShortcutCustomizationApp,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "initializeKeyboardWatcher",
      base::BindRepeating(&KeyboardHandler::HandleKeyboardChange,
                          base::Unretained(this)));
}

void KeyboardHandler::OnJavascriptAllowed() {
  observation_.Observe(ui::DeviceDataManager::GetInstance());
}

void KeyboardHandler::OnJavascriptDisallowed() {
  observation_.Reset();
}

void KeyboardHandler::OnInputDeviceConfigurationChanged(
    uint8_t input_device_types) {
  if (input_device_types & ui::InputDeviceEventObserver::kKeyboard) {
    AllowJavascript();
    UpdateShowKeys();
    UpdateKeyboards();
  }
}

void KeyboardHandler::HandleInitialize(const base::Value::List& args) {
  AllowJavascript();
  UpdateShowKeys();
  UpdateKeyboards();
}

void KeyboardHandler::HandleShowShortcutCustomizationApp(
    const base::Value::List& args) const {
  ash::LaunchSystemWebAppAsync(ProfileManager::GetActiveUserProfile(),
                               ash::SystemWebAppType::SHORTCUT_CUSTOMIZATION);
}

void KeyboardHandler::HandleKeyboardChange(const base::Value::List& args) {
  AllowJavascript();
  UpdateKeyboards();
}

void KeyboardHandler::UpdateKeyboards() {
  bool physical_keyboard = false;
  // In tablet mode, physical keybards are disabled / ignored.
  if (!display::Screen::GetScreen()->InTabletMode()) {
    physical_keyboard = true;
  }
  if (!physical_keyboard) {
    for (const ui::InputDevice& keyboard :
         ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
      if (keyboard.type != ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
        physical_keyboard = true;
        break;
      }
    }
  }
  FireWebUIListener("has-hardware-keyboard", base::Value(physical_keyboard));
}

void KeyboardHandler::UpdateShowKeys() {
  // kHasChromeOSKeyboard will be unset on Chromebooks that have standalone Caps
  // Lock keys.
  const KeyboardsStateResult keyboards_state = GetKeyboardsState();
  const bool has_caps_lock = keyboards_state.has_external_apple_keyboard ||
                             keyboards_state.has_external_generic_keyboard ||
                             !base::CommandLine::ForCurrentProcess()->HasSwitch(
                                 switches::kHasChromeOSKeyboard);

  base::Value::Dict keyboard_params;
  keyboard_params.Set("showCapsLock", has_caps_lock);
  keyboard_params.Set("showExternalMetaKey",
                      keyboards_state.has_external_generic_keyboard);
  keyboard_params.Set("showAppleCommandKey",
                      keyboards_state.has_external_apple_keyboard);
  // An external (USB/BT) ChromeOS keyboard is treated similarly to an internal
  // ChromeOS keyboard. i.e. they are functionally the same.
  keyboard_params.Set("hasLauncherKey",
                      keyboards_state.has_launcher_key ||
                          keyboards_state.has_external_chromeos_keyboard);

  const bool show_assistant_key_settings = ui::DeviceKeyboardHasAssistantKey();
  keyboard_params.Set("hasAssistantKey", show_assistant_key_settings);

  FireWebUIListener(kShowKeysChangedName, keyboard_params);
}

}  // namespace ash::settings