Home | History | Annotate | Download | only in keyboard
      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