0

accelerators: Add HasKeyEvent function in KeyboardCapability

HasKeyEvent will be called by AcceleratorAliasConverter class to filter
out accelerators with unsupported key code.

Bug: b:216049298
Test: keyboard_capability_unittest.cc
Change-Id: I1ea55ec1fa1fb5f55d8d30c960d25ee43295a268
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4346571
Commit-Queue: Wenyu Zhang <zhangwenyu@google.com>
Reviewed-by: David Padlipsky <dpad@google.com>
Cr-Commit-Position: refs/heads/main@{#1120831}
This commit is contained in:
wenyu zhang
2023-03-22 22:28:28 +00:00
committed by Chromium LUCI CQ
parent 5b13c56c35
commit 1e4a86ec30
3 changed files with 220 additions and 0 deletions

@ -34,6 +34,27 @@ constexpr char kKbdTopRowLayoutDrallionTag[] = "4";
constexpr int kDeviceId1 = 5;
constexpr int kDeviceId2 = 10;
ui::InputDeviceType INTERNAL = ui::InputDeviceType::INPUT_DEVICE_INTERNAL;
ui::InputDeviceType EXTERNAL_USB = ui::InputDeviceType::INPUT_DEVICE_USB;
ui::InputDeviceType EXTERNAL_BLUETOOTH =
ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH;
// For INPUT_DEVICE_UNKNOWN type, we treat it as external keyboard.
ui::InputDeviceType EXTERNAL_UNKNOWN =
ui::InputDeviceType::INPUT_DEVICE_UNKNOWN;
struct KeyEventTestData {
// All currently connected keyboards' connection type, e.g.
// INPUT_DEVICE_INTERNAL.
std::vector<ui::InputDeviceType> keyboard_connection_types;
// All currently connected keyboards' layout types.
std::vector<std::string> keyboard_layout_types;
ui::KeyboardCode key_code;
// Expected result of whether this key event exists on each keyboard.
std::vector<bool> expected_has_key_event;
// Expected result of whether this key event exists on all connected.
bool expected_has_key_event_on_any_keyboard;
};
// NOTE: This only creates a simple ui::InputDevice based on a device
// capabilities report; it is not suitable for subclasses of ui::InputDevice.
ui::InputDevice InputDeviceFromCapabilities(
@ -75,6 +96,11 @@ class FakeDeviceManager {
std::move(sysfs_properties));
}
void RemoveAllDevices() {
fake_udev_.Reset();
fake_keyboard_devices_.clear();
}
private:
testing::FakeUdevLoader fake_udev_;
std::vector<ui::InputDevice> fake_keyboard_devices_;
@ -329,6 +355,21 @@ TEST_F(KeyboardCapabilityTest, TestRemoveDevicesFromList) {
ASSERT_EQ(0u, keyboard_capability_->keyboard_info_map().size());
}
TEST_F(KeyboardCapabilityTest, TestIsTopRowKey) {
for (const auto& [key_code, _] : ui::kLayout1TopRowKeyToFKeyMap) {
EXPECT_TRUE(keyboard_capability_->IsTopRowKey(key_code));
}
for (const auto& [key_code, _] : ui::kLayout2TopRowKeyToFKeyMap) {
EXPECT_TRUE(keyboard_capability_->IsTopRowKey(key_code));
}
for (const auto& [key_code, _] : ui::kLayoutWilcoDrallionTopRowKeyToFKeyMap) {
EXPECT_TRUE(keyboard_capability_->IsTopRowKey(key_code));
}
// A key not in any of the above maps is not a top row key.
EXPECT_FALSE(keyboard_capability_->IsTopRowKey(ui::KeyboardCode::VKEY_A));
}
class ModifierKeyTest : public KeyboardCapabilityTest,
public testing::WithParamInterface<
std::tuple<ui::DeviceCapabilities,
@ -384,4 +425,113 @@ TEST_P(ModifierKeyTest, TestGetModifierKeys) {
EXPECT_EQ(expected_modifier_keys, modifier_keys);
}
class KeyEventTest : public KeyboardCapabilityTest,
public testing::WithParamInterface<KeyEventTestData> {};
// Tests that given the keyboard connection type and layout type, check if this
// keyboard has a specific key event.
INSTANTIATE_TEST_SUITE_P(
All,
KeyEventTest,
testing::ValuesIn(std::vector<KeyEventTestData>{
// Testing top row keys.
{{INTERNAL},
{kKbdTopRowLayout1Tag},
ui::VKEY_BROWSER_FORWARD,
{true},
true},
{{EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout1Tag},
ui::VKEY_ZOOM,
{true},
true},
{{EXTERNAL_USB},
{kKbdTopRowLayout1Tag},
ui::VKEY_MEDIA_PLAY_PAUSE,
{false},
false},
{{INTERNAL},
{kKbdTopRowLayout2Tag},
ui::VKEY_BROWSER_FORWARD,
{false},
false},
{{EXTERNAL_UNKNOWN},
{kKbdTopRowLayout2Tag},
ui::VKEY_MEDIA_PLAY_PAUSE,
{true},
true},
{{INTERNAL}, {kKbdTopRowLayoutWilcoTag}, ui::VKEY_ZOOM, {true}, true},
{{EXTERNAL_BLUETOOTH},
{kKbdTopRowLayoutDrallionTag},
ui::VKEY_BRIGHTNESS_UP,
{true},
true},
{{INTERNAL, EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout1Tag, kKbdTopRowLayout2Tag},
ui::VKEY_BROWSER_FORWARD,
{true, false},
true},
{{INTERNAL, EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout2Tag, kKbdTopRowLayout2Tag},
ui::VKEY_BROWSER_FORWARD,
{false, false},
false},
{{INTERNAL, EXTERNAL_USB, EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout1Tag, kKbdTopRowLayout2Tag, kKbdTopRowLayoutWilcoTag},
ui::VKEY_VOLUME_UP,
{true, true, true},
true},
// Testing six pack keys.
{{INTERNAL}, {kKbdTopRowLayout1Tag}, ui::VKEY_INSERT, {false}, false},
{{EXTERNAL_USB}, {kKbdTopRowLayout1Tag}, ui::VKEY_INSERT, {true}, true},
{{INTERNAL, EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout1Tag, kKbdTopRowLayoutWilcoTag},
ui::VKEY_HOME,
{false, true},
true},
// Testing other keys.
{{INTERNAL}, {kKbdTopRowLayout1Tag}, ui::VKEY_LEFT, {true}, true},
{{EXTERNAL_BLUETOOTH},
{kKbdTopRowLayout2Tag},
ui::VKEY_ESCAPE,
{true},
true},
{{EXTERNAL_UNKNOWN},
{kKbdTopRowLayoutWilcoTag},
ui::VKEY_A,
{true},
true},
{{INTERNAL}, {kKbdTopRowLayoutDrallionTag}, ui::VKEY_2, {true}, true},
}));
TEST_P(KeyEventTest, TestHasKeyEvent) {
auto [keyboard_connection_types, keyboard_layout_types, key_code,
expected_has_key_event, expected_has_key_event_on_any_keyboard] =
GetParam();
fake_keyboard_manager_->RemoveAllDevices();
for (size_t i = 0; i < keyboard_layout_types.size(); i++) {
std::string layout = keyboard_layout_types[i];
ui::InputDevice fake_keyboard(
/*id=*/i, /*type=*/keyboard_connection_types[i],
/*name=*/layout);
fake_keyboard.sys_path = base::FilePath("path" + layout);
fake_keyboard_manager_->AddFakeKeyboard(fake_keyboard, layout);
if (expected_has_key_event[i]) {
EXPECT_TRUE(keyboard_capability_->HasKeyEvent(key_code, fake_keyboard));
} else {
EXPECT_FALSE(keyboard_capability_->HasKeyEvent(key_code, fake_keyboard));
}
}
if (expected_has_key_event_on_any_keyboard) {
EXPECT_TRUE(keyboard_capability_->HasKeyEventOnAnyKeyboard(key_code));
} else {
EXPECT_FALSE(keyboard_capability_->HasKeyEventOnAnyKeyboard(key_code));
}
}
} // namespace ash

@ -166,6 +166,26 @@ bool KeyboardCapability::HasLauncherButton(
KeyboardTopRowLayout::kKbdTopRowLayout2;
}
// static
bool KeyboardCapability::IsTopRowKey(const KeyboardCode& key_code) {
// A set that includes all top row keys from different keyboards.
static const base::NoDestructor<base::flat_set<KeyboardCode>>
top_row_action_keys({
KeyboardCode::VKEY_BROWSER_BACK,
KeyboardCode::VKEY_BROWSER_FORWARD,
KeyboardCode::VKEY_BROWSER_REFRESH,
KeyboardCode::VKEY_ZOOM,
KeyboardCode::VKEY_MEDIA_LAUNCH_APP1,
KeyboardCode::VKEY_BRIGHTNESS_DOWN,
KeyboardCode::VKEY_BRIGHTNESS_UP,
KeyboardCode::VKEY_MEDIA_PLAY_PAUSE,
KeyboardCode::VKEY_VOLUME_MUTE,
KeyboardCode::VKEY_VOLUME_DOWN,
KeyboardCode::VKEY_VOLUME_UP,
});
return base::Contains(*top_row_action_keys, key_code);
}
// static
bool KeyboardCapability::HasSixPackKey(const InputDevice& keyboard) {
// If the keyboard is an internal keyboard, return false. Otherwise, return
@ -298,4 +318,44 @@ void KeyboardCapability::TrimKeyboardInfoMap() {
}
}
bool KeyboardCapability::HasKeyEvent(const KeyboardCode& key_code,
const InputDevice& keyboard) const {
// Handle top row keys.
if (IsTopRowKey(key_code)) {
KeyboardTopRowLayout layout =
EventRewriterChromeOS::GetKeyboardTopRowLayout(keyboard);
switch (layout) {
case KeyboardTopRowLayout::kKbdTopRowLayout1:
return kLayout1TopRowKeyToFKeyMap.contains(key_code);
case KeyboardTopRowLayout::kKbdTopRowLayout2:
return kLayout2TopRowKeyToFKeyMap.contains(key_code);
case KeyboardTopRowLayout::kKbdTopRowLayoutWilco:
case KeyboardTopRowLayout::kKbdTopRowLayoutDrallion:
return kLayoutWilcoDrallionTopRowKeyToFKeyMap.contains(key_code);
case KeyboardTopRowLayout::kKbdTopRowLayoutCustom:
// TODO(zhangwenyu): Handle custom vivaldi layout.
return true;
}
}
// Handle six pack keys.
if (IsSixPackKey(key_code)) {
return HasSixPackKey(keyboard);
}
// TODO(zhangwenyu): check other specific keys, e.g. assistant key.
return true;
}
bool KeyboardCapability::HasKeyEventOnAnyKeyboard(
const KeyboardCode& key_code) const {
for (const ui::InputDevice& keyboard :
ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
if (HasKeyEvent(key_code, keyboard)) {
return true;
}
}
return false;
}
} // namespace ui

@ -177,6 +177,9 @@ class KeyboardCapability : public InputDeviceEventObserver {
// Enable or disable top row keys as F-Keys.
void SetTopRowKeysAsFKeysEnabledForTesting(bool enabled) const;
// Check if a key code is one of the top row keys.
static bool IsTopRowKey(const KeyboardCode& key_code);
// Check if a key code is one of the six pack keys.
static bool IsSixPackKey(const KeyboardCode& key_code);
@ -217,6 +220,13 @@ class KeyboardCapability : public InputDeviceEventObserver {
void OnDeviceListsComplete() override;
void OnInputDeviceConfigurationChanged(uint8_t input_device_types) override;
// Check if a specific key event exists on a given keyboard.
bool HasKeyEvent(const KeyboardCode& key_code,
const InputDevice& keyboard) const;
// Check if any of the connected keyboards has a specific key event.
bool HasKeyEventOnAnyKeyboard(const KeyboardCode& key_code) const;
const base::flat_map<int, KeyboardInfo>& keyboard_info_map() {
return keyboard_info_map_;
}