• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SendProcessKeyEvent(ui::EventType type,aura::WindowEventDispatcher * dispatcher)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 
IsKeyboardEnabled()42 bool IsKeyboardEnabled() {
43   return CommandLine::ForCurrentProcess()->HasSwitch(
44       switches::kEnableVirtualKeyboard) ||
45           IsKeyboardUsabilityExperimentEnabled();
46 }
47 
IsKeyboardUsabilityExperimentEnabled()48 bool IsKeyboardUsabilityExperimentEnabled() {
49   return CommandLine::ForCurrentProcess()->HasSwitch(
50       switches::kKeyboardUsabilityExperiment);
51 }
52 
InsertText(const base::string16 & text,aura::Window * root_window)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()).
MoveCursor(int swipe_direction,int modifier_flags,aura::WindowEventDispatcher * dispatcher)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 
SendKeyEvent(const std::string type,int key_value,int key_code,std::string key_name,int modifiers,aura::WindowEventDispatcher * dispatcher)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 
MarkKeyboardLoadStarted()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 
MarkKeyboardLoadFinished()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 
GetKeyboardExtensionResources(size_t * size)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 
LogKeyboardControlEvent(KeyboardControlEvent event)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