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/window_tree_host.h"
     18 #include "ui/base/ime/input_method.h"
     19 #include "ui/base/ime/text_input_client.h"
     20 #include "ui/events/event_processor.h"
     21 #include "ui/keyboard/keyboard_switches.h"
     22 #include "url/gurl.h"
     23 
     24 namespace {
     25 
     26 const char kKeyDown[] ="keydown";
     27 const char kKeyUp[] = "keyup";
     28 
     29 void SendProcessKeyEvent(ui::EventType type,
     30                          aura::WindowTreeHost* host) {
     31   ui::KeyEvent event(type, ui::VKEY_PROCESSKEY, ui::EF_NONE, false);
     32   event.SetTranslated(true);
     33   ui::EventDispatchDetails details =
     34       host->event_processor()->OnEventFromSource(&event);
     35   CHECK(!details.dispatcher_destroyed);
     36 }
     37 
     38 base::LazyInstance<base::Time> g_keyboard_load_time_start =
     39     LAZY_INSTANCE_INITIALIZER;
     40 
     41 bool g_accessibility_keyboard_enabled = false;
     42 
     43 base::LazyInstance<GURL> g_override_content_url = LAZY_INSTANCE_INITIALIZER;
     44 
     45 bool g_touch_keyboard_enabled = false;
     46 
     47 keyboard::KeyboardOverscrolOverride g_keyboard_overscroll_override =
     48     keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE;
     49 
     50 keyboard::KeyboardShowOverride g_keyboard_show_override =
     51     keyboard::KEYBOARD_SHOW_OVERRIDE_NONE;
     52 
     53 }  // namespace
     54 
     55 namespace keyboard {
     56 
     57 gfx::Rect DefaultKeyboardBoundsFromWindowBounds(
     58     const gfx::Rect& window_bounds) {
     59   // Initialize default keyboard height to 0. The keyboard window height should
     60   // only be set by window.resizeTo in virtual keyboard web contents. Otherwise,
     61   // the default height may conflict with the new height and causing some
     62   // strange animation issues. For keyboard usability experiments, a full screen
     63   // virtual keyboard window is always preferred.
     64   int keyboard_height =
     65       keyboard::IsKeyboardUsabilityExperimentEnabled() ?
     66           window_bounds.height() : 0;
     67 
     68   return KeyboardBoundsFromWindowBounds(window_bounds, keyboard_height);
     69 }
     70 
     71 gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds,
     72                                          int keyboard_height) {
     73   return gfx::Rect(
     74       window_bounds.x(),
     75       window_bounds.bottom() - keyboard_height,
     76       window_bounds.width(),
     77       keyboard_height);
     78 }
     79 
     80 void SetAccessibilityKeyboardEnabled(bool enabled) {
     81   g_accessibility_keyboard_enabled = enabled;
     82 }
     83 
     84 bool GetAccessibilityKeyboardEnabled() {
     85   return g_accessibility_keyboard_enabled;
     86 }
     87 
     88 void SetTouchKeyboardEnabled(bool enabled) {
     89   g_touch_keyboard_enabled = enabled;
     90 }
     91 
     92 bool GetTouchKeyboardEnabled() {
     93   return g_touch_keyboard_enabled;
     94 }
     95 
     96 std::string GetKeyboardLayout() {
     97   // TODO(bshe): layout string is currently hard coded. We should use more
     98   // standard keyboard layouts.
     99   return GetAccessibilityKeyboardEnabled() ? "system-qwerty" : "qwerty";
    100 }
    101 
    102 bool IsKeyboardEnabled() {
    103   // Accessibility setting prioritized over policy setting.
    104   if (g_accessibility_keyboard_enabled)
    105     return true;
    106   // Policy strictly disables showing a virtual keyboard.
    107   if (g_keyboard_show_override == keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED)
    108     return false;
    109   // Check if any of the flags are enabled.
    110   return CommandLine::ForCurrentProcess()->HasSwitch(
    111              switches::kEnableVirtualKeyboard) ||
    112          IsKeyboardUsabilityExperimentEnabled() ||
    113          g_touch_keyboard_enabled ||
    114          (g_keyboard_show_override == keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED);
    115 }
    116 
    117 bool IsKeyboardUsabilityExperimentEnabled() {
    118   return CommandLine::ForCurrentProcess()->HasSwitch(
    119       switches::kKeyboardUsabilityExperiment);
    120 }
    121 
    122 bool IsKeyboardOverscrollEnabled() {
    123   if (!IsKeyboardEnabled())
    124     return false;
    125 
    126   // Users of the accessibility on-screen keyboard are likely to be using mouse
    127   // input, which may interfere with overscrolling.
    128   if (g_accessibility_keyboard_enabled)
    129     return false;
    130 
    131   // If overscroll enabled override is set, use it instead. Currently
    132   // login / out-of-box disable keyboard overscroll. http://crbug.com/363635
    133   if (g_keyboard_overscroll_override != KEYBOARD_OVERSCROLL_OVERRIDE_NONE) {
    134     return g_keyboard_overscroll_override ==
    135         KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED;
    136   }
    137 
    138   if (CommandLine::ForCurrentProcess()->HasSwitch(
    139       switches::kDisableVirtualKeyboardOverscroll)) {
    140     return false;
    141   }
    142   return true;
    143 }
    144 
    145 void SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override) {
    146   g_keyboard_overscroll_override = override;
    147 }
    148 
    149 void SetKeyboardShowOverride(KeyboardShowOverride override) {
    150   g_keyboard_show_override = override;
    151 }
    152 
    153 bool IsInputViewEnabled() {
    154   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableInputView))
    155     return true;
    156   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableInputView))
    157     return false;
    158   // Default value if no command line flags specified.
    159   return true;
    160 }
    161 
    162 bool IsExperimentalInputViewEnabled() {
    163   if (CommandLine::ForCurrentProcess()->HasSwitch(
    164       switches::kEnableExperimentalInputViewFeatures)) {
    165     return true;
    166   }
    167   return false;
    168 }
    169 
    170 bool InsertText(const base::string16& text, aura::Window* root_window) {
    171   if (!root_window)
    172     return false;
    173 
    174   ui::InputMethod* input_method = root_window->GetProperty(
    175       aura::client::kRootWindowInputMethodKey);
    176   if (!input_method)
    177     return false;
    178 
    179   ui::TextInputClient* tic = input_method->GetTextInputClient();
    180   if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
    181     return false;
    182 
    183   tic->InsertText(text);
    184 
    185   return true;
    186 }
    187 
    188 // TODO(varunjain): It would be cleaner to have something in the
    189 // ui::TextInputClient interface, say MoveCaretInDirection(). The code in
    190 // here would get the ui::InputMethod from the root_window, and the
    191 // ui::TextInputClient from that (see above in InsertText()).
    192 bool MoveCursor(int swipe_direction,
    193                 int modifier_flags,
    194                 aura::WindowTreeHost* host) {
    195   if (!host)
    196     return false;
    197   ui::KeyboardCode codex = ui::VKEY_UNKNOWN;
    198   ui::KeyboardCode codey = ui::VKEY_UNKNOWN;
    199   if (swipe_direction & kCursorMoveRight)
    200     codex = ui::VKEY_RIGHT;
    201   else if (swipe_direction & kCursorMoveLeft)
    202     codex = ui::VKEY_LEFT;
    203 
    204   if (swipe_direction & kCursorMoveUp)
    205     codey = ui::VKEY_UP;
    206   else if (swipe_direction & kCursorMoveDown)
    207     codey = ui::VKEY_DOWN;
    208 
    209   // First deal with the x movement.
    210   if (codex != ui::VKEY_UNKNOWN) {
    211     ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, modifier_flags, 0);
    212     ui::EventDispatchDetails details =
    213         host->event_processor()->OnEventFromSource(&press_event);
    214     CHECK(!details.dispatcher_destroyed);
    215     ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, modifier_flags, 0);
    216     details = host->event_processor()->OnEventFromSource(&release_event);
    217     CHECK(!details.dispatcher_destroyed);
    218   }
    219 
    220   // Then deal with the y movement.
    221   if (codey != ui::VKEY_UNKNOWN) {
    222     ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, modifier_flags, 0);
    223     ui::EventDispatchDetails details =
    224         host->event_processor()->OnEventFromSource(&press_event);
    225     CHECK(!details.dispatcher_destroyed);
    226     ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, modifier_flags, 0);
    227     details = host->event_processor()->OnEventFromSource(&release_event);
    228     CHECK(!details.dispatcher_destroyed);
    229   }
    230   return true;
    231 }
    232 
    233 bool SendKeyEvent(const std::string type,
    234                   int key_value,
    235                   int key_code,
    236                   std::string key_name,
    237                   int modifiers,
    238                   aura::WindowTreeHost* host) {
    239   ui::EventType event_type = ui::ET_UNKNOWN;
    240   if (type == kKeyDown)
    241     event_type = ui::ET_KEY_PRESSED;
    242   else if (type == kKeyUp)
    243     event_type = ui::ET_KEY_RELEASED;
    244   if (event_type == ui::ET_UNKNOWN)
    245     return false;
    246 
    247   ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code);
    248 
    249   if (code == ui::VKEY_UNKNOWN) {
    250     // Handling of special printable characters (e.g. accented characters) for
    251     // which there is no key code.
    252     if (event_type == ui::ET_KEY_RELEASED) {
    253       ui::InputMethod* input_method = host->window()->GetProperty(
    254           aura::client::kRootWindowInputMethodKey);
    255       if (!input_method)
    256         return false;
    257 
    258       ui::TextInputClient* tic = input_method->GetTextInputClient();
    259 
    260       SendProcessKeyEvent(ui::ET_KEY_PRESSED, host);
    261       tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE);
    262       SendProcessKeyEvent(ui::ET_KEY_RELEASED, host);
    263     }
    264   } else {
    265     if (event_type == ui::ET_KEY_RELEASED) {
    266       // The number of key press events seen since the last backspace.
    267       static int keys_seen = 0;
    268       if (code == ui::VKEY_BACK) {
    269         // Log the rough lengths of characters typed between backspaces. This
    270         // metric will be used to determine the error rate for the keyboard.
    271         UMA_HISTOGRAM_CUSTOM_COUNTS(
    272             "VirtualKeyboard.KeystrokesBetweenBackspaces",
    273             keys_seen, 1, 1000, 50);
    274         keys_seen = 0;
    275       } else {
    276         ++keys_seen;
    277       }
    278     }
    279 
    280     ui::KeyEvent event(event_type, code, key_name, modifiers, false);
    281     ui::EventDispatchDetails details =
    282         host->event_processor()->OnEventFromSource(&event);
    283     CHECK(!details.dispatcher_destroyed);
    284   }
    285   return true;
    286 }
    287 
    288 const void MarkKeyboardLoadStarted() {
    289   if (!g_keyboard_load_time_start.Get().ToInternalValue())
    290     g_keyboard_load_time_start.Get() = base::Time::Now();
    291 }
    292 
    293 const void MarkKeyboardLoadFinished() {
    294   // Possible to get a load finished without a start if navigating directly to
    295   // chrome://keyboard.
    296   if (!g_keyboard_load_time_start.Get().ToInternalValue())
    297     return;
    298 
    299   // It should not be possible to finish loading the keyboard without starting
    300   // to load it first.
    301   DCHECK(g_keyboard_load_time_start.Get().ToInternalValue());
    302 
    303   static bool logged = false;
    304   if (!logged) {
    305     // Log the delta only once.
    306     UMA_HISTOGRAM_TIMES(
    307         "VirtualKeyboard.FirstLoadTime",
    308         base::Time::Now() - g_keyboard_load_time_start.Get());
    309     logged = true;
    310   }
    311 }
    312 
    313 const GritResourceMap* GetKeyboardExtensionResources(size_t* size) {
    314   // This looks a lot like the contents of a resource map; however it is
    315   // necessary to have a custom path for the extension path, so the resource
    316   // map cannot be used directly.
    317   static const GritResourceMap kKeyboardResources[] = {
    318     {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW},
    319     {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK},
    320     {"keyboard/images/backspace.png", IDR_KEYBOARD_IMAGES_BACKSPACE},
    321     {"keyboard/images/brightness-down.svg",
    322         IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN},
    323     {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP},
    324     {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW},
    325     {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN},
    326     {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD},
    327     {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN},
    328     {"keyboard/images/hide-keyboard.png", IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD},
    329     {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD},
    330     {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT},
    331     {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE},
    332     {"keyboard/images/microphone-green.svg",
    333         IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN},
    334     {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE},
    335     {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD},
    336     {"keyboard/images/return.png", IDR_KEYBOARD_IMAGES_RETURN},
    337     {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT},
    338     {"keyboard/images/search.png", IDR_KEYBOARD_IMAGES_SEARCH},
    339     {"keyboard/images/shift.png", IDR_KEYBOARD_IMAGES_SHIFT},
    340     {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN},
    341     {"keyboard/images/tab.png", IDR_KEYBOARD_IMAGES_TAB},
    342     {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP},
    343     {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN},
    344     {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP},
    345     {"keyboard/index.html", IDR_KEYBOARD_INDEX},
    346     {"keyboard/keyboard.js", IDR_KEYBOARD_JS},
    347     {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC},
    348     {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY},
    349     {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY},
    350     {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW},
    351     {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST},
    352     {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS},
    353     {"keyboard/polymer_loader.js", IDR_KEYBOARD_POLYMER_LOADER},
    354     {"keyboard/roboto_bold.ttf", IDR_KEYBOARD_ROBOTO_BOLD_TTF},
    355     {"keyboard/sounds/keypress-delete.wav",
    356         IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE},
    357     {"keyboard/sounds/keypress-return.wav",
    358         IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN},
    359     {"keyboard/sounds/keypress-spacebar.wav",
    360         IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR},
    361     {"keyboard/sounds/keypress-standard.wav",
    362         IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD},
    363   };
    364   static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources);
    365   *size = kKeyboardResourcesSize;
    366   return kKeyboardResources;
    367 }
    368 
    369 void SetOverrideContentUrl(const GURL& url) {
    370   g_override_content_url.Get() = url;
    371 }
    372 
    373 const GURL& GetOverrideContentUrl() {
    374   return g_override_content_url.Get();
    375 }
    376 
    377 void LogKeyboardControlEvent(KeyboardControlEvent event) {
    378   UMA_HISTOGRAM_ENUMERATION(
    379       "VirtualKeyboard.KeyboardControlEvent",
    380       event,
    381       keyboard::KEYBOARD_CONTROL_MAX);
    382 }
    383 
    384 }  // namespace keyboard
    385