Adds support for the search key in the Virtual Keyboard. We currently do not use...
[chromium-blink-merge.git] / ui / keyboard / keyboard_util.cc
blob42c26a0a354ef914c929d4ae5f79bd2a42e051fb
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/keyboard/keyboard_util.h"
7 #include <string>
9 #include "base/command_line.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string16.h"
14 #include "grit/keyboard_resources.h"
15 #include "grit/keyboard_resources_map.h"
16 #include "ui/aura/client/aura_constants.h"
17 #include "ui/aura/root_window.h"
18 #include "ui/base/ime/input_method.h"
19 #include "ui/base/ime/text_input_client.h"
20 #include "ui/keyboard/keyboard_switches.h"
22 namespace {
24 const char kKeyDown[] ="keydown";
25 const char kKeyUp[] = "keyup";
27 void SendProcessKeyEvent(ui::EventType type,
28 aura::WindowEventDispatcher* dispatcher) {
29 ui::TranslatedKeyEvent event(type == ui::ET_KEY_PRESSED,
30 ui::VKEY_PROCESSKEY,
31 ui::EF_NONE);
32 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&event);
35 base::LazyInstance<base::Time> g_keyboard_load_time_start =
36 LAZY_INSTANCE_INITIALIZER;
38 } // namespace
40 namespace keyboard {
42 bool IsKeyboardEnabled() {
43 return CommandLine::ForCurrentProcess()->HasSwitch(
44 switches::kEnableVirtualKeyboard) ||
45 IsKeyboardUsabilityExperimentEnabled();
48 bool IsKeyboardUsabilityExperimentEnabled() {
49 return CommandLine::ForCurrentProcess()->HasSwitch(
50 switches::kKeyboardUsabilityExperiment);
53 bool InsertText(const base::string16& text, aura::Window* root_window) {
54 if (!root_window)
55 return false;
57 ui::InputMethod* input_method = root_window->GetProperty(
58 aura::client::kRootWindowInputMethodKey);
59 if (!input_method)
60 return false;
62 ui::TextInputClient* tic = input_method->GetTextInputClient();
63 if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
64 return false;
66 tic->InsertText(text);
68 return true;
71 // TODO(varunjain): It would be cleaner to have something in the
72 // ui::TextInputClient interface, say MoveCaretInDirection(). The code in
73 // here would get the ui::InputMethod from the root_window, and the
74 // ui::TextInputClient from that (see above in InsertText()).
75 bool MoveCursor(int swipe_direction,
76 int modifier_flags,
77 aura::WindowEventDispatcher* dispatcher) {
78 if (!dispatcher)
79 return false;
80 ui::KeyboardCode codex = ui::VKEY_UNKNOWN;
81 ui::KeyboardCode codey = ui::VKEY_UNKNOWN;
82 if (swipe_direction & kCursorMoveRight)
83 codex = ui::VKEY_RIGHT;
84 else if (swipe_direction & kCursorMoveLeft)
85 codex = ui::VKEY_LEFT;
87 if (swipe_direction & kCursorMoveUp)
88 codey = ui::VKEY_UP;
89 else if (swipe_direction & kCursorMoveDown)
90 codey = ui::VKEY_DOWN;
92 // First deal with the x movement.
93 if (codex != ui::VKEY_UNKNOWN) {
94 ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, modifier_flags, 0);
95 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&press_event);
96 ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, modifier_flags, 0);
97 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&release_event);
100 // Then deal with the y movement.
101 if (codey != ui::VKEY_UNKNOWN) {
102 ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, modifier_flags, 0);
103 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&press_event);
104 ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, modifier_flags, 0);
105 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&release_event);
107 return true;
110 bool SendKeyEvent(const std::string type,
111 int key_value,
112 int key_code,
113 int modifiers,
114 aura::WindowEventDispatcher* dispatcher) {
115 ui::EventType event_type = ui::ET_UNKNOWN;
116 if (type == kKeyDown)
117 event_type = ui::ET_KEY_PRESSED;
118 else if (type == kKeyUp)
119 event_type = ui::ET_KEY_RELEASED;
120 if (event_type == ui::ET_UNKNOWN)
121 return false;
123 ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code);
125 if (code == ui::VKEY_UNKNOWN) {
126 // Handling of special printable characters (e.g. accented characters) for
127 // which there is no key code.
128 if (event_type == ui::ET_KEY_RELEASED) {
129 ui::InputMethod* input_method = dispatcher->window()->GetProperty(
130 aura::client::kRootWindowInputMethodKey);
131 if (!input_method)
132 return false;
134 ui::TextInputClient* tic = input_method->GetTextInputClient();
136 SendProcessKeyEvent(ui::ET_KEY_PRESSED, dispatcher);
137 tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE);
138 SendProcessKeyEvent(ui::ET_KEY_RELEASED, dispatcher);
140 } else {
141 if (event_type == ui::ET_KEY_RELEASED) {
142 // The number of key press events seen since the last backspace.
143 static int keys_seen = 0;
144 if (code == ui::VKEY_BACK) {
145 // Log the rough lengths of characters typed between backspaces. This
146 // metric will be used to determine the error rate for the keyboard.
147 UMA_HISTOGRAM_CUSTOM_COUNTS(
148 "VirtualKeyboard.KeystrokesBetweenBackspaces",
149 keys_seen, 1, 1000, 50);
150 keys_seen = 0;
151 } else {
152 ++keys_seen;
156 ui::KeyEvent event(event_type, code, modifiers, false);
157 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&event);
159 return true;
162 const void MarkKeyboardLoadStarted() {
163 if (!g_keyboard_load_time_start.Get().ToInternalValue())
164 g_keyboard_load_time_start.Get() = base::Time::Now();
167 const void MarkKeyboardLoadFinished() {
168 // It should not be possible to finish loading the keyboard without starting
169 // to load it first.
170 DCHECK(g_keyboard_load_time_start.Get().ToInternalValue());
172 static bool logged = false;
173 if (!logged) {
174 // Log the delta only once.
175 UMA_HISTOGRAM_TIMES(
176 "VirtualKeyboard.FirstLoadTime",
177 base::Time::Now() - g_keyboard_load_time_start.Get());
178 logged = true;
182 const GritResourceMap* GetKeyboardExtensionResources(size_t* size) {
183 // This looks a lot like the contents of a resource map; however it is
184 // necessary to have a custom path for the extension path, so the resource
185 // map cannot be used directly.
186 static const GritResourceMap kKeyboardResources[] = {
187 {"keyboard/api_adapter.js", IDR_KEYBOARD_API_ADAPTER_JS},
188 {"keyboard/constants.js", IDR_KEYBOARD_CONSTANTS_JS},
189 {"keyboard/elements/kb-altkey.html", IDR_KEYBOARD_ELEMENTS_ALTKEY},
190 {"keyboard/elements/kb-altkey-container.html",
191 IDR_KEYBOARD_ELEMENTS_ALTKEY_CONTAINER},
192 {"keyboard/elements/kb-altkey-data.html",
193 IDR_KEYBOARD_ELEMENTS_ALTKEY_DATA},
194 {"keyboard/elements/kb-altkey-set.html", IDR_KEYBOARD_ELEMENTS_ALTKEY_SET},
195 {"keyboard/elements/kb-key.html", IDR_KEYBOARD_ELEMENTS_KEY},
196 {"keyboard/elements/kb-key-base.html", IDR_KEYBOARD_ELEMENTS_KEY_BASE},
197 {"keyboard/elements/kb-key-codes.html", IDR_KEYBOARD_ELEMENTS_KEY_CODES},
198 {"keyboard/elements/kb-key-import.html",
199 IDR_KEYBOARD_ELEMENTS_KEY_IMPORT},
200 {"keyboard/elements/kb-key-sequence.html",
201 IDR_KEYBOARD_ELEMENTS_KEY_SEQUENCE},
202 {"keyboard/elements/kb-keyboard.html", IDR_KEYBOARD_ELEMENTS_KEYBOARD},
203 {"keyboard/elements/kb-keyset.html", IDR_KEYBOARD_ELEMENTS_KEYSET},
204 {"keyboard/elements/kb-modifier-key.html",
205 IDR_KEYBOARD_ELEMENTS_MODIFIER_KEY},
206 {"keyboard/elements/kb-options-menu.html",
207 IDR_KEYBOARD_ELEMENTS_OPTIONS_MENU},
208 {"keyboard/elements/kb-row.html", IDR_KEYBOARD_ELEMENTS_ROW},
209 {"keyboard/elements/kb-shift-key.html", IDR_KEYBOARD_ELEMENTS_SHIFT_KEY},
210 {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW},
211 {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK},
212 {"keyboard/images/brightness-down.svg",
213 IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN},
214 {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP},
215 {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW},
216 {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN},
217 {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD},
218 {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN},
219 {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD},
220 {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT},
221 {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE},
222 {"keyboard/images/microphone-green.svg",
223 IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN},
224 {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE},
225 {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD},
226 {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT},
227 {"keyboard/images/search.svg", IDR_KEYBOARD_IMAGES_SEARCH},
228 {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN},
229 {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP},
230 {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN},
231 {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP},
232 {"keyboard/index.html", IDR_KEYBOARD_INDEX},
233 {"keyboard/layouts/dvorak.html", IDR_KEYBOARD_LAYOUTS_DVORAK},
234 {"keyboard/layouts/latin-accents.js", IDR_KEYBOARD_LAYOUTS_LATIN_ACCENTS},
235 {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC},
236 {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY},
237 {"keyboard/layouts/symbol-altkeys.js",
238 IDR_KEYBOARD_LAYOUTS_SYMBOL_ALTKEYS},
239 {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY},
240 {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW},
241 {"keyboard/main.js", IDR_KEYBOARD_MAIN_JS},
242 {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST},
243 {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS},
244 {"keyboard/polymer.min.js", IDR_KEYBOARD_POLYMER},
245 {"keyboard/voice_input.js", IDR_KEYBOARD_VOICE_INPUT_JS},
247 static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources);
248 *size = kKeyboardResourcesSize;
249 return kKeyboardResources;
252 void LogKeyboardControlEvent(KeyboardControlEvent event) {
253 UMA_HISTOGRAM_ENUMERATION(
254 "VirtualKeyboard.KeyboardControlEvent",
255 event,
256 keyboard::KEYBOARD_CONTROL_MAX);
259 } // namespace keyboard