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. 4 5 #include "ui/keyboard/keyboard_util.h" 6 7 #include <string> 8 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" 21 22 namespace { 23 24 const char kKeyDown[] ="keydown"; 25 const char kKeyUp[] = "keyup"; 26 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); 33 } 34 35 base::LazyInstance<base::Time> g_keyboard_load_time_start = 36 LAZY_INSTANCE_INITIALIZER; 37 38 } // namespace 39 40 namespace keyboard { 41 42 bool IsKeyboardEnabled() { 43 return CommandLine::ForCurrentProcess()->HasSwitch( 44 switches::kEnableVirtualKeyboard) || 45 IsKeyboardUsabilityExperimentEnabled(); 46 } 47 48 bool IsKeyboardUsabilityExperimentEnabled() { 49 return CommandLine::ForCurrentProcess()->HasSwitch( 50 switches::kKeyboardUsabilityExperiment); 51 } 52 53 bool InsertText(const base::string16& text, aura::Window* root_window) { 54 if (!root_window) 55 return false; 56 57 ui::InputMethod* input_method = root_window->GetProperty( 58 aura::client::kRootWindowInputMethodKey); 59 if (!input_method) 60 return false; 61 62 ui::TextInputClient* tic = input_method->GetTextInputClient(); 63 if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 64 return false; 65 66 tic->InsertText(text); 67 68 return true; 69 } 70 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; 86 87 if (swipe_direction & kCursorMoveUp) 88 codey = ui::VKEY_UP; 89 else if (swipe_direction & kCursorMoveDown) 90 codey = ui::VKEY_DOWN; 91 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); 98 } 99 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); 106 } 107 return true; 108 } 109 110 bool SendKeyEvent(const std::string type, 111 int key_value, 112 int key_code, 113 std::string key_name, 114 int modifiers, 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) 122 return false; 123 124 ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code); 125 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); 132 if (!input_method) 133 return false; 134 135 ui::TextInputClient* tic = input_method->GetTextInputClient(); 136 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); 140 } 141 } else { 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); 151 keys_seen = 0; 152 } else { 153 ++keys_seen; 154 } 155 } 156 157 ui::KeyEvent event(event_type, code, key_name, modifiers, false); 158 dispatcher->AsRootWindowHostDelegate()->OnHostKeyEvent(&event); 159 } 160 return true; 161 } 162 163 const void MarkKeyboardLoadStarted() { 164 if (!g_keyboard_load_time_start.Get().ToInternalValue()) 165 g_keyboard_load_time_start.Get() = base::Time::Now(); 166 } 167 168 const void MarkKeyboardLoadFinished() { 169 // It should not be possible to finish loading the keyboard without starting 170 // to load it first. 171 DCHECK(g_keyboard_load_time_start.Get().ToInternalValue()); 172 173 static bool logged = false; 174 if (!logged) { 175 // Log the delta only once. 176 UMA_HISTOGRAM_TIMES( 177 "VirtualKeyboard.FirstLoadTime", 178 base::Time::Now() - g_keyboard_load_time_start.Get()); 179 logged = true; 180 } 181 } 182 183 const GritResourceMap* GetKeyboardExtensionResources(size_t* size) { 184 // This looks a lot like the contents of a resource map; however it is 185 // necessary to have a custom path for the extension path, so the resource 186 // map cannot be used directly. 187 static const GritResourceMap kKeyboardResources[] = { 188 {"keyboard/api_adapter.js", IDR_KEYBOARD_API_ADAPTER_JS}, 189 {"keyboard/constants.js", IDR_KEYBOARD_CONSTANTS_JS}, 190 {"keyboard/elements/kb-altkey.html", IDR_KEYBOARD_ELEMENTS_ALTKEY}, 191 {"keyboard/elements/kb-altkey-container.html", 192 IDR_KEYBOARD_ELEMENTS_ALTKEY_CONTAINER}, 193 {"keyboard/elements/kb-altkey-data.html", 194 IDR_KEYBOARD_ELEMENTS_ALTKEY_DATA}, 195 {"keyboard/elements/kb-altkey-set.html", IDR_KEYBOARD_ELEMENTS_ALTKEY_SET}, 196 {"keyboard/elements/kb-key.html", IDR_KEYBOARD_ELEMENTS_KEY}, 197 {"keyboard/elements/kb-key-base.html", IDR_KEYBOARD_ELEMENTS_KEY_BASE}, 198 {"keyboard/elements/kb-key-codes.html", IDR_KEYBOARD_ELEMENTS_KEY_CODES}, 199 {"keyboard/elements/kb-key-import.html", 200 IDR_KEYBOARD_ELEMENTS_KEY_IMPORT}, 201 {"keyboard/elements/kb-key-sequence.html", 202 IDR_KEYBOARD_ELEMENTS_KEY_SEQUENCE}, 203 {"keyboard/elements/kb-keyboard.html", IDR_KEYBOARD_ELEMENTS_KEYBOARD}, 204 {"keyboard/elements/kb-keyset.html", IDR_KEYBOARD_ELEMENTS_KEYSET}, 205 {"keyboard/elements/kb-modifier-key.html", 206 IDR_KEYBOARD_ELEMENTS_MODIFIER_KEY}, 207 {"keyboard/elements/kb-options-menu.html", 208 IDR_KEYBOARD_ELEMENTS_OPTIONS_MENU}, 209 {"keyboard/elements/kb-row.html", IDR_KEYBOARD_ELEMENTS_ROW}, 210 {"keyboard/elements/kb-shift-key.html", IDR_KEYBOARD_ELEMENTS_SHIFT_KEY}, 211 {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW}, 212 {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK}, 213 {"keyboard/images/brightness-down.svg", 214 IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN}, 215 {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP}, 216 {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW}, 217 {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN}, 218 {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD}, 219 {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN}, 220 {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD}, 221 {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT}, 222 {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE}, 223 {"keyboard/images/microphone-green.svg", 224 IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN}, 225 {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE}, 226 {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD}, 227 {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT}, 228 {"keyboard/images/search.svg", IDR_KEYBOARD_IMAGES_SEARCH}, 229 {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN}, 230 {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP}, 231 {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN}, 232 {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP}, 233 {"keyboard/index.html", IDR_KEYBOARD_INDEX}, 234 {"keyboard/layouts/dvorak.html", IDR_KEYBOARD_LAYOUTS_DVORAK}, 235 {"keyboard/layouts/latin-accents.js", IDR_KEYBOARD_LAYOUTS_LATIN_ACCENTS}, 236 {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC}, 237 {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY}, 238 {"keyboard/layouts/symbol-altkeys.js", 239 IDR_KEYBOARD_LAYOUTS_SYMBOL_ALTKEYS}, 240 {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY}, 241 {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW}, 242 {"keyboard/main.js", IDR_KEYBOARD_MAIN_JS}, 243 {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST}, 244 {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS}, 245 {"keyboard/polymer_loader.js", IDR_KEYBOARD_POLYMER_LOADER}, 246 {"keyboard/voice_input.js", IDR_KEYBOARD_VOICE_INPUT_JS}, 247 }; 248 static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources); 249 *size = kKeyboardResourcesSize; 250 return kKeyboardResources; 251 } 252 253 void LogKeyboardControlEvent(KeyboardControlEvent event) { 254 UMA_HISTOGRAM_ENUMERATION( 255 "VirtualKeyboard.KeyboardControlEvent", 256 event, 257 keyboard::KEYBOARD_CONTROL_MAX); 258 } 259 260 } // namespace keyboard 261