Adding keyboard service.

This allows to show the soft keyboard on Android.

Also updated platform_viewport_android to dispatch key events.

R=abarth@chromium.org
BUG=449002

Review URL: https://codereview.chromium.org/856063002
diff --git a/mojo/services/keyboard/public/interfaces/BUILD.gn b/mojo/services/keyboard/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..4685f86
--- /dev/null
+++ b/mojo/services/keyboard/public/interfaces/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../mojo_sdk_root.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "keyboard.mojom",
+  ]
+}
diff --git a/mojo/services/keyboard/public/interfaces/keyboard.mojom b/mojo/services/keyboard/public/interfaces/keyboard.mojom
new file mode 100644
index 0000000..d11253e
--- /dev/null
+++ b/mojo/services/keyboard/public/interfaces/keyboard.mojom
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo;
+
+interface Keyboard {
+  Show();
+  Hide();
+};
diff --git a/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java b/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
index e8eeb4c..712de95 100644
--- a/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
+++ b/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
@@ -6,10 +6,12 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.View;
 
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
@@ -32,6 +34,9 @@
     public PlatformViewportAndroid(Context context, long nativeViewport) {
         super(context);
 
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+
         mNativeMojoViewport = nativeViewport;
         assert mNativeMojoViewport != 0;
 
@@ -68,11 +73,49 @@
     }
 
     @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        if (visibility == View.VISIBLE) {
+            requestFocusFromTouch();
+            requestFocus();
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent event) {
         return nativeTouchEvent(mNativeMojoViewport, event.getPointerId(0), event.getAction(),
                 event.getX(), event.getY(), event.getEventTime());
     }
 
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (privateDispatchKeyEvent(event)) {
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        if (privateDispatchKeyEvent(event)) {
+            return true;
+        }
+        return super.dispatchKeyEventPreIme(event);
+    }
+
+    @Override
+    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+        if (privateDispatchKeyEvent(event)) {
+            return true;
+        }
+        return super.dispatchKeyShortcutEvent(event);
+    }
+
+    private boolean privateDispatchKeyEvent(KeyEvent event) {
+        return nativeKeyEvent(mNativeMojoViewport, event.getAction() == KeyEvent.ACTION_DOWN,
+                event.getKeyCode(), event.getUnicodeChar());
+    }
+
     private static native void nativeDestroy(long nativePlatformViewportAndroid);
 
     private static native void nativeSurfaceCreated(
@@ -90,4 +133,7 @@
             int action,
             float x, float y,
             long timeMs);
+
+    private static native boolean nativeKeyEvent(
+            long nativePlatformViewportAndroid, boolean pressed, int keyCode, int unicodeCharacter);
 }
diff --git a/services/native_viewport/platform_viewport_android.cc b/services/native_viewport/platform_viewport_android.cc
index 8090625..f3db992 100644
--- a/services/native_viewport/platform_viewport_android.cc
+++ b/services/native_viewport/platform_viewport_android.cc
@@ -11,6 +11,7 @@
 #include "jni/PlatformViewportAndroid_jni.h"
 #include "mojo/converters/geometry/geometry_type_converters.h"
 #include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_code_conversion_android.h"
 #include "ui/gfx/point.h"
 
 namespace native_viewport {
@@ -115,6 +116,24 @@
   return true;
 }
 
+bool PlatformViewportAndroid::KeyEvent(JNIEnv* env,
+                                       jobject obj,
+                                       bool pressed,
+                                       jint key_code,
+                                       jint unicode_character) {
+  ui::KeyEvent event(pressed ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
+                     ui::KeyboardCodeFromAndroidKeyCode(key_code), 0);
+  event.set_platform_keycode(key_code);
+  delegate_->OnEvent(&event);
+  if (pressed && unicode_character) {
+    ui::KeyEvent char_event(unicode_character,
+                            ui::KeyboardCodeFromAndroidKeyCode(key_code), 0);
+    char_event.set_platform_keycode(key_code);
+    delegate_->OnEvent(&char_event);
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformViewportAndroid, PlatformViewport implementation:
 
diff --git a/services/native_viewport/platform_viewport_android.h b/services/native_viewport/platform_viewport_android.h
index 5fdaf1f..88b6908 100644
--- a/services/native_viewport/platform_viewport_android.h
+++ b/services/native_viewport/platform_viewport_android.h
@@ -40,6 +40,11 @@
                       jfloat density);
   bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action,
                   jfloat x, jfloat y, jlong time_ms);
+  bool KeyEvent(JNIEnv* env,
+                jobject obj,
+                bool pressed,
+                jint key_code,
+                jint unicode_character);
 
  private:
   // Overridden from PlatformViewport:
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 2ce1174..d494fc1 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -203,6 +203,8 @@
       "android/android_handler_loader.h",
       "android/background_application_loader.cc",
       "android/background_application_loader.h",
+      "android/keyboard_impl.cc",
+      "android/keyboard_impl.h",
       "android/native_viewport_application_loader.cc",
       "android/native_viewport_application_loader.h",
       "android/ui_application_loader_android.cc",
@@ -213,6 +215,7 @@
       ":jni_headers",
       ":run_android_application_function",
       "//mojo/application:content_handler",
+      "//mojo/services/keyboard/public/interfaces",
       "//services/gles2",
       "//services/native_viewport:lib",
     ]
@@ -232,6 +235,7 @@
     sources = [
       "android/apk/src/org/chromium/mojo/shell/AndroidHandler.java",
       "android/apk/src/org/chromium/mojo/shell/Bootstrap.java",
+      "android/apk/src/org/chromium/mojo/shell/Keyboard.java",
       "android/apk/src/org/chromium/mojo/shell/MojoMain.java",
       "android/tests/src/org/chromium/mojo/shell/ShellTestBase.java",
     ]
@@ -275,6 +279,7 @@
     java_files = [
       "android/apk/src/org/chromium/mojo/shell/AndroidHandler.java",
       "android/apk/src/org/chromium/mojo/shell/FileHelper.java",
+      "android/apk/src/org/chromium/mojo/shell/Keyboard.java",
       "android/apk/src/org/chromium/mojo/shell/MojoMain.java",
       "android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java",
       "android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java",
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/Keyboard.java b/shell/android/apk/src/org/chromium/mojo/shell/Keyboard.java
new file mode 100644
index 0000000..e13ddea
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo/shell/Keyboard.java
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.shell;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Interaction with the keyboard.
+ */
+@JNINamespace("mojo::shell")
+public class Keyboard {
+    @CalledByNative
+    private static void showSoftKeyboard(Activity activity) {
+        View v = activity.getCurrentFocus();
+        InputMethodManager imm =
+                (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
+    }
+
+    @CalledByNative
+    private static void hideSoftKeyboard(Activity activity) {
+        View v = activity.getCurrentFocus();
+        InputMethodManager imm =
+                (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), 0);
+    }
+}
diff --git a/shell/android/keyboard_impl.cc b/shell/android/keyboard_impl.cc
new file mode 100644
index 0000000..4282348
--- /dev/null
+++ b/shell/android/keyboard_impl.cc
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/android/keyboard_impl.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+#include "jni/Keyboard_jni.h"
+
+namespace mojo {
+namespace shell {
+
+KeyboardImpl::KeyboardImpl(InterfaceRequest<Keyboard> request)
+    : binding_(this, request.Pass()) {
+}
+
+KeyboardImpl::~KeyboardImpl() {
+}
+
+void KeyboardImpl::Show() {
+  Java_Keyboard_showSoftKeyboard(base::android::AttachCurrentThread(),
+                                 base::android::GetApplicationContext());
+}
+
+void KeyboardImpl::Hide() {
+  Java_Keyboard_hideSoftKeyboard(base::android::AttachCurrentThread(),
+                                 base::android::GetApplicationContext());
+}
+
+bool RegisterKeyboardJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/android/keyboard_impl.h b/shell/android/keyboard_impl.h
new file mode 100644
index 0000000..e40b7dd
--- /dev/null
+++ b/shell/android/keyboard_impl.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ANDROID_KEYBOARD_IMPL_H_
+#define SHELL_ANDROID_KEYBOARD_IMPL_H_
+
+#include <jni.h>
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/services/keyboard/public/interfaces/keyboard.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+class KeyboardImpl : public Keyboard {
+ public:
+  KeyboardImpl(InterfaceRequest<Keyboard> request);
+  ~KeyboardImpl();
+
+  // Keyboard implementation
+  void Show() override;
+  void Hide() override;
+
+ private:
+  StrongBinding<Keyboard> binding_;
+};
+
+bool RegisterKeyboardJni(JNIEnv* env);
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_ANDROID_KEYBOARD_IMPL_H_
diff --git a/shell/android/library_loader.cc b/shell/android/library_loader.cc
index c112db6..42e02df 100644
--- a/shell/android/library_loader.cc
+++ b/shell/android/library_loader.cc
@@ -9,12 +9,14 @@
 #include "base/logging.h"
 #include "services/native_viewport/platform_viewport_android.h"
 #include "shell/android/android_handler.h"
+#include "shell/android/keyboard_impl.h"
 #include "shell/android/mojo_main.h"
 
 namespace {
 
 base::android::RegistrationMethod kMojoRegisteredMethods[] = {
     {"AndroidHandler", mojo::RegisterAndroidHandlerJni},
+    {"Keyboard", mojo::shell::RegisterKeyboardJni},
     {"MojoMain", mojo::shell::RegisterMojoMain},
     {"PlatformViewportAndroid",
      native_viewport::PlatformViewportAndroid::Register},
diff --git a/shell/android/mojo_main.cc b/shell/android/mojo_main.cc
index 502a340..9c8178b 100644
--- a/shell/android/mojo_main.cc
+++ b/shell/android/mojo_main.cc
@@ -83,6 +83,10 @@
           make_scoped_ptr(new AndroidHandlerLoader()), "android_handler",
           base::MessageLoop::TYPE_DEFAULT)),
       GURL("mojo:android_handler"));
+
+  // By default, the keyboard is handled by the native_viewport_service.
+  context->mojo_url_resolver()->AddCustomMapping(
+      GURL("mojo:keyboard"), GURL("mojo:native_viewport_service"));
 }
 
 void QuitShellThread() {
diff --git a/shell/android/native_viewport_application_loader.cc b/shell/android/native_viewport_application_loader.cc
index e84fa90..9f96417 100644
--- a/shell/android/native_viewport_application_loader.cc
+++ b/shell/android/native_viewport_application_loader.cc
@@ -6,6 +6,7 @@
 
 #include "mojo/public/cpp/application/application_impl.h"
 #include "services/native_viewport/native_viewport_impl.h"
+#include "shell/android/keyboard_impl.h"
 
 namespace mojo {
 namespace shell {
@@ -33,6 +34,7 @@
     mojo::ApplicationConnection* connection) {
   connection->AddService<NativeViewport>(this);
   connection->AddService<Gpu>(this);
+  connection->AddService<Keyboard>(this);
   return true;
 }
 
@@ -45,7 +47,12 @@
 
 void NativeViewportApplicationLoader::Create(
     ApplicationConnection* connection,
-    InterfaceRequest<Gpu> request) {
+    InterfaceRequest<Keyboard> request) {
+  new KeyboardImpl(request.Pass());
+}
+
+void NativeViewportApplicationLoader::Create(ApplicationConnection* connection,
+                                             InterfaceRequest<Gpu> request) {
   if (!gpu_state_)
     gpu_state_ = new gles2::GpuImpl::State;
   new gles2::GpuImpl(request.Pass(), gpu_state_);
diff --git a/shell/android/native_viewport_application_loader.h b/shell/android/native_viewport_application_loader.h
index f5d53ea..060d3aa 100644
--- a/shell/android/native_viewport_application_loader.h
+++ b/shell/android/native_viewport_application_loader.h
@@ -8,6 +8,7 @@
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/interface_factory.h"
 #include "mojo/services/gpu/public/interfaces/gpu.mojom.h"
+#include "mojo/services/keyboard/public/interfaces/keyboard.mojom.h"
 #include "mojo/services/native_viewport/public/interfaces/native_viewport.mojom.h"
 #include "services/gles2/gpu_impl.h"
 #include "shell/application_manager/application_loader.h"
@@ -20,6 +21,7 @@
 
 class NativeViewportApplicationLoader : public ApplicationLoader,
                                         public ApplicationDelegate,
+                                        public InterfaceFactory<Keyboard>,
                                         public InterfaceFactory<NativeViewport>,
                                         public InterfaceFactory<Gpu> {
  public:
@@ -48,6 +50,10 @@
   void Create(ApplicationConnection* connection,
               InterfaceRequest<Gpu> request) override;
 
+  // InterfaceFactory<Keyboard> implementation.
+  void Create(ApplicationConnection* connection,
+              InterfaceRequest<Keyboard> request) override;
+
   scoped_refptr<gles2::GpuImpl::State> gpu_state_;
   scoped_ptr<ApplicationImpl> app_;
 
diff --git a/sky/engine/core/BUILD.gn b/sky/engine/core/BUILD.gn
index afb5dfe..54a92a6 100644
--- a/sky/engine/core/BUILD.gn
+++ b/sky/engine/core/BUILD.gn
@@ -23,6 +23,7 @@
     "//mojo/public/cpp/system",
     "//mojo/public/cpp/utility",
     "//mojo/public/interfaces/application",
+    "//mojo/services/keyboard/public/interfaces",
     "//mojo/services/navigation/public/interfaces",
     "//mojo/services/view_manager/public/cpp",
     "//skia",
diff --git a/sky/framework/sky-input.sky b/sky/framework/sky-input.sky
index 9ad21fb..9ebbaed 100644
--- a/sky/framework/sky-input.sky
+++ b/sky/framework/sky-input.sky
@@ -3,7 +3,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 -->
+<import src="/sky/framework/shell.sky" as="shell" />
 <import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" />
+<import src="/mojo/services/keyboard/public/interfaces/keyboard.mojom.sky" as="keyboard" />
 
 <sky-element name="sky-input" attributes="value:string">
 <template>
@@ -22,9 +24,13 @@
     overflow: hidden;
   }
   </style>
-  <div id="control" contenteditable on-keydown="handleKeyDown">{{ value }}</div>
+  <div id="control" contenteditable
+       on-focus="handleFocus"
+       on-blur="handleBlur"
+       on-keydown="handleKeyDown">{{ value }}</div>
 </template>
 <script>
+var keyboard = shell.connectToService("mojo:keyboard", keyboard.Keyboard);
 module.exports = class extends SkyElement {
   shadowRootReady() {
     var control = this.shadowRoot.getElementById('control');
@@ -54,6 +60,12 @@
     if (event.keyCode == 0xD)
       event.preventDefault();
   }
+  handleFocus(event) {
+    keyboard.show();
+  }
+  handleBlur(event) {
+    keyboard.hide();
+  }
 }.register();
 </script>
 </sky-element>
diff --git a/sky/viewer/document_view.cc b/sky/viewer/document_view.cc
index cf48567..8125379 100644
--- a/sky/viewer/document_view.cc
+++ b/sky/viewer/document_view.cc
@@ -126,6 +126,8 @@
   // TODO(abarth): We should ask the view whether it is focused instead of
   // assuming that we're focused.
   web_view_->setFocus(true);
+  // Needed on android, as the window does not get the focus otherwise.
+  root_->SetFocus();
   root_->AddObserver(this);
 }