Keyboard support for a submit button and setting the IME's text content and selection

* Add a KeyboardClient.Submit callback invoked when the submit key is pressed in the IME
* KeyboardService.SetText and SetSelection can be used to update the IME's state to match the contents of an edit widget
* Also copy InputConnectionAdaptor.sendKeyEvent to keep the Mojo version of InputConnectionAdaptor in sync with Sky's version

R=abarth@chromium.org, jamesr@chromium.org

Review URL: https://codereview.chromium.org/1382943003 .
diff --git a/examples/keyboard_client/keyboard_client.cc b/examples/keyboard_client/keyboard_client.cc
index 4c2fa8d..de0690d 100644
--- a/examples/keyboard_client/keyboard_client.cc
+++ b/examples/keyboard_client/keyboard_client.cc
@@ -246,6 +246,8 @@
 
   void SetSelection(int32_t start, int32_t end) override { DrawText(); }
 
+  void Submit(keyboard::SubmitAction action) override {}
+
   // mojo::ViewObserver implementation.
   void OnViewDestroyed(mojo::View* view) override {
     if (view == text_view_) {
diff --git a/mojo/services/keyboard/interfaces/keyboard.mojom b/mojo/services/keyboard/interfaces/keyboard.mojom
index f47ecc4..9794220 100644
--- a/mojo/services/keyboard/interfaces/keyboard.mojom
+++ b/mojo/services/keyboard/interfaces/keyboard.mojom
@@ -18,6 +18,10 @@
   string new_text;
 };
 
+enum SubmitAction {
+  DONE,
+};
+
 interface KeyboardClient {
   CommitCompletion(CompletionData completion);
   CommitCorrection(CorrectionData correction);
@@ -26,6 +30,7 @@
   SetComposingRegion(int32 start, int32 end);
   SetComposingText(string text, int32 newCursorPosition);
   SetSelection(int32 start, int32 end);
+  Submit(SubmitAction action);
 };
 
 // Loosely modeled on Android InputType:
@@ -41,4 +46,6 @@
   Show(KeyboardClient client, KeyboardType type);
   ShowByRequest();
   Hide();
+  SetText(string text);
+  SetSelection(int32 start, int32 end);
 };
diff --git a/services/keyboard/src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java b/services/keyboard/src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java
index 42bdd57..2efbce6 100644
--- a/services/keyboard/src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java
+++ b/services/keyboard/src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.keyboard;
 
+import android.text.Editable;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
@@ -13,19 +15,29 @@
 import org.chromium.mojom.keyboard.CompletionData;
 import org.chromium.mojom.keyboard.CorrectionData;
 import org.chromium.mojom.keyboard.KeyboardClient;
+import org.chromium.mojom.keyboard.SubmitAction;
 
 /**
  * An adaptor between InputConnection and KeyboardClient.
  */
 public class InputConnectionAdaptor extends BaseInputConnection {
     private KeyboardClient mClient;
+    private Editable mEditable;
 
-    public InputConnectionAdaptor(View view, KeyboardClient client, EditorInfo outAttrs) {
+    public InputConnectionAdaptor(View view, KeyboardClient client, Editable editable,
+                                  EditorInfo outAttrs) {
         super(view, true);
         assert client != null;
         mClient = client;
+        mEditable = editable;
         outAttrs.initialSelStart = -1;
         outAttrs.initialSelEnd = -1;
+        outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
+    }
+
+    @Override
+    public Editable getEditable() {
+        return mEditable;
     }
 
     @Override
@@ -71,4 +83,20 @@
         mClient.setSelection(start, end);
         return super.setSelection(start, end);
     }
+
+    // Number keys come through as key events instead of commitText!?
+    @Override
+    public boolean sendKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            // 1 appears to always be the value for newCursorPosition?
+            mClient.commitText(String.valueOf(event.getNumber()), 1);
+        }
+        return super.sendKeyEvent(event);
+    }
+
+    @Override
+    public boolean performEditorAction(int actionCode) {
+        mClient.submit(SubmitAction.DONE);
+        return true;
+    }
 }
diff --git a/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java b/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java
index 42dbc7a..4e0cbfa 100644
--- a/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java
+++ b/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java
@@ -75,4 +75,17 @@
         imm.hideSoftInputFromWindow(sViewState.getView().getApplicationWindowToken(), 0);
         close();
     }
+
+    @Override
+    public void setText(String text) {
+        sViewState.setText(text);
+        InputMethodManager imm =
+                (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.restartInput(sViewState.getView());
+    }
+
+    @Override
+    public void setSelection(int start, int end) {
+        sViewState.setSelection(start, end);
+    }
 }
diff --git a/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceState.java b/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceState.java
index 7196b24..4b025f1 100644
--- a/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceState.java
+++ b/services/keyboard/src/org/chromium/mojo/keyboard/KeyboardServiceState.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.keyboard;
 
+import android.text.Editable;
 import android.text.InputType;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -18,23 +21,26 @@
     private View mView;
     private KeyboardClient mActiveClient;
     private int mRequestedInputType;
+    private Editable mActiveEditable;
 
     public KeyboardServiceState(View view) {
         mView = view;
         mActiveClient = null;
         mRequestedInputType = InputType.TYPE_CLASS_TEXT;
+        mActiveEditable = null;
     }
 
     public InputConnection createInputConnection(EditorInfo outAttrs) {
         if (mActiveClient == null) return null;
         outAttrs.inputType = mRequestedInputType;
-        return new InputConnectionAdaptor(mView, mActiveClient, outAttrs);
+        return new InputConnectionAdaptor(mView, mActiveClient, mActiveEditable, outAttrs);
     }
 
     public void setClient(KeyboardClient client, int inputType) {
         if (mActiveClient != null) mActiveClient.close();
         mActiveClient = client;
         mRequestedInputType = inputType;
+        mActiveEditable = new SpannableStringBuilder();
     }
 
     public View getView() {
@@ -45,5 +51,14 @@
         if (mActiveClient == null) return;
         mActiveClient.close();
         mActiveClient = null;
+        mActiveEditable = null;
+    }
+
+    public void setText(String text) {
+        mActiveEditable.replace(0, mActiveEditable.length(), text);
+    }
+
+    public void setSelection(int start, int end) {
+        Selection.setSelection(mActiveEditable, start, end);
     }
 }
diff --git a/services/keyboard_native/keyboard_service_impl.cc b/services/keyboard_native/keyboard_service_impl.cc
index 45a2fee..27321c0 100644
--- a/services/keyboard_native/keyboard_service_impl.cc
+++ b/services/keyboard_native/keyboard_service_impl.cc
@@ -25,6 +25,12 @@
 void KeyboardServiceImpl::Hide() {
 }
 
+void KeyboardServiceImpl::SetText(const mojo::String& text) {
+}
+
+void KeyboardServiceImpl::SetSelection(int32_t start, int32_t end) {
+}
+
 void KeyboardServiceImpl::OnKey(const char* key) {
   client_->CommitText(key, 1);
 }
diff --git a/services/keyboard_native/keyboard_service_impl.h b/services/keyboard_native/keyboard_service_impl.h
index 0d9213f..17b68fd 100644
--- a/services/keyboard_native/keyboard_service_impl.h
+++ b/services/keyboard_native/keyboard_service_impl.h
@@ -22,6 +22,8 @@
   void Show(KeyboardClientPtr client, KeyboardType type) override;
   void ShowByRequest() override;
   void Hide() override;
+  void SetText(const mojo::String& text) override;
+  void SetSelection(int32_t start, int32_t end) override;
 
   void OnKey(const char* key);
   void OnDelete();