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"
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"
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
,
32 dispatcher
->AsWindowTreeHostDelegate()->OnHostKeyEvent(&event
);
35 base::LazyInstance
<base::Time
> g_keyboard_load_time_start
=
36 LAZY_INSTANCE_INITIALIZER
;
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
) {
57 ui::InputMethod
* input_method
= root_window
->GetProperty(
58 aura::client::kRootWindowInputMethodKey
);
62 ui::TextInputClient
* tic
= input_method
->GetTextInputClient();
63 if (!tic
|| tic
->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
66 tic
->InsertText(text
);
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
,
77 aura::WindowEventDispatcher
* dispatcher
) {
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
)
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
->AsWindowTreeHostDelegate()->OnHostKeyEvent(&press_event
);
96 ui::KeyEvent
release_event(ui::ET_KEY_RELEASED
, codex
, modifier_flags
, 0);
97 dispatcher
->AsWindowTreeHostDelegate()->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
->AsWindowTreeHostDelegate()->OnHostKeyEvent(&press_event
);
104 ui::KeyEvent
release_event(ui::ET_KEY_RELEASED
, codey
, modifier_flags
, 0);
105 dispatcher
->AsWindowTreeHostDelegate()->OnHostKeyEvent(&release_event
);
110 bool SendKeyEvent(const std::string type
,
113 std::string key_name
,
115 aura::WindowEventDispatcher
* dispatcher
) {
116 ui::EventType event_type
= ui::ET_UNKNOWN
;
117 if (type
== kKeyDown
)
118 event_type
= ui::ET_KEY_PRESSED
;
119 else if (type
== kKeyUp
)
120 event_type
= ui::ET_KEY_RELEASED
;
121 if (event_type
== ui::ET_UNKNOWN
)
124 ui::KeyboardCode code
= static_cast<ui::KeyboardCode
>(key_code
);
126 if (code
== ui::VKEY_UNKNOWN
) {
127 // Handling of special printable characters (e.g. accented characters) for
128 // which there is no key code.
129 if (event_type
== ui::ET_KEY_RELEASED
) {
130 ui::InputMethod
* input_method
= dispatcher
->window()->GetProperty(
131 aura::client::kRootWindowInputMethodKey
);
135 ui::TextInputClient
* tic
= input_method
->GetTextInputClient();
137 SendProcessKeyEvent(ui::ET_KEY_PRESSED
, dispatcher
);
138 tic
->InsertChar(static_cast<uint16
>(key_value
), ui::EF_NONE
);
139 SendProcessKeyEvent(ui::ET_KEY_RELEASED
, dispatcher
);
142 if (event_type
== ui::ET_KEY_RELEASED
) {
143 // The number of key press events seen since the last backspace.
144 static int keys_seen
= 0;
145 if (code
== ui::VKEY_BACK
) {
146 // Log the rough lengths of characters typed between backspaces. This
147 // metric will be used to determine the error rate for the keyboard.
148 UMA_HISTOGRAM_CUSTOM_COUNTS(
149 "VirtualKeyboard.KeystrokesBetweenBackspaces",
150 keys_seen
, 1, 1000, 50);
157 ui::KeyEvent
event(event_type
, code
, key_name
, modifiers
, false);
158 dispatcher
->AsWindowTreeHostDelegate()->OnHostKeyEvent(&event
);
163 const void MarkKeyboardLoadStarted() {
164 if (!g_keyboard_load_time_start
.Get().ToInternalValue())
165 g_keyboard_load_time_start
.Get() = base::Time::Now();
168 const void MarkKeyboardLoadFinished() {
169 // Possible to get a load finished without a start if navigating directly to
170 // chrome://keyboard.
171 if (!g_keyboard_load_time_start
.Get().ToInternalValue())
174 // It should not be possible to finish loading the keyboard without starting
176 DCHECK(g_keyboard_load_time_start
.Get().ToInternalValue());
178 static bool logged
= false;
180 // Log the delta only once.
182 "VirtualKeyboard.FirstLoadTime",
183 base::Time::Now() - g_keyboard_load_time_start
.Get());
188 const GritResourceMap
* GetKeyboardExtensionResources(size_t* size
) {
189 // This looks a lot like the contents of a resource map; however it is
190 // necessary to have a custom path for the extension path, so the resource
191 // map cannot be used directly.
192 static const GritResourceMap kKeyboardResources
[] = {
193 {"keyboard/api_adapter.js", IDR_KEYBOARD_API_ADAPTER_JS
},
194 {"keyboard/constants.js", IDR_KEYBOARD_CONSTANTS_JS
},
195 {"keyboard/elements/kb-altkey.html", IDR_KEYBOARD_ELEMENTS_ALTKEY
},
196 {"keyboard/elements/kb-altkey-container.html",
197 IDR_KEYBOARD_ELEMENTS_ALTKEY_CONTAINER
},
198 {"keyboard/elements/kb-altkey-data.html",
199 IDR_KEYBOARD_ELEMENTS_ALTKEY_DATA
},
200 {"keyboard/elements/kb-altkey-set.html", IDR_KEYBOARD_ELEMENTS_ALTKEY_SET
},
201 {"keyboard/elements/kb-key.html", IDR_KEYBOARD_ELEMENTS_KEY
},
202 {"keyboard/elements/kb-key-base.html", IDR_KEYBOARD_ELEMENTS_KEY_BASE
},
203 {"keyboard/elements/kb-key-codes.html", IDR_KEYBOARD_ELEMENTS_KEY_CODES
},
204 {"keyboard/elements/kb-key-import.html",
205 IDR_KEYBOARD_ELEMENTS_KEY_IMPORT
},
206 {"keyboard/elements/kb-key-sequence.html",
207 IDR_KEYBOARD_ELEMENTS_KEY_SEQUENCE
},
208 {"keyboard/elements/kb-keyboard.html", IDR_KEYBOARD_ELEMENTS_KEYBOARD
},
209 {"keyboard/elements/kb-keyset.html", IDR_KEYBOARD_ELEMENTS_KEYSET
},
210 {"keyboard/elements/kb-modifier-key.html",
211 IDR_KEYBOARD_ELEMENTS_MODIFIER_KEY
},
212 {"keyboard/elements/kb-options-menu.html",
213 IDR_KEYBOARD_ELEMENTS_OPTIONS_MENU
},
214 {"keyboard/elements/kb-row.html", IDR_KEYBOARD_ELEMENTS_ROW
},
215 {"keyboard/elements/kb-shift-key.html", IDR_KEYBOARD_ELEMENTS_SHIFT_KEY
},
216 {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW
},
217 {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK
},
218 {"keyboard/images/brightness-down.svg",
219 IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN
},
220 {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP
},
221 {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW
},
222 {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN
},
223 {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD
},
224 {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN
},
225 {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD
},
226 {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT
},
227 {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE
},
228 {"keyboard/images/microphone-green.svg",
229 IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN
},
230 {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE
},
231 {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD
},
232 {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT
},
233 {"keyboard/images/search.svg", IDR_KEYBOARD_IMAGES_SEARCH
},
234 {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN
},
235 {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP
},
236 {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN
},
237 {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP
},
238 {"keyboard/index.html", IDR_KEYBOARD_INDEX
},
239 {"keyboard/layouts/dvorak.html", IDR_KEYBOARD_LAYOUTS_DVORAK
},
240 {"keyboard/layouts/latin-accents.js", IDR_KEYBOARD_LAYOUTS_LATIN_ACCENTS
},
241 {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC
},
242 {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY
},
243 {"keyboard/layouts/symbol-altkeys.js",
244 IDR_KEYBOARD_LAYOUTS_SYMBOL_ALTKEYS
},
245 {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY
},
246 {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW
},
247 {"keyboard/main.js", IDR_KEYBOARD_MAIN_JS
},
248 {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST
},
249 {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS
},
250 {"keyboard/polymer_loader.js", IDR_KEYBOARD_POLYMER_LOADER
},
251 {"keyboard/voice_input.js", IDR_KEYBOARD_VOICE_INPUT_JS
},
253 static const size_t kKeyboardResourcesSize
= arraysize(kKeyboardResources
);
254 *size
= kKeyboardResourcesSize
;
255 return kKeyboardResources
;
258 void LogKeyboardControlEvent(KeyboardControlEvent event
) {
259 UMA_HISTOGRAM_ENUMERATION(
260 "VirtualKeyboard.KeyboardControlEvent",
262 keyboard::KEYBOARD_CONTROL_MAX
);
265 } // namespace keyboard