Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/ImeAdapter.java

Issue 12045030: Merge 177774 (Closed) Base URL: svn://svn.chromium.org/chrome/branches/1364/src/
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser; 5 package org.chromium.content.browser;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.os.Handler; 8 import android.os.Handler;
9 import android.os.ResultReceiver; 9 import android.os.ResultReceiver;
10 import android.text.Editable; 10 import android.text.Editable;
11 import android.text.InputType; 11 import android.text.InputType;
12 import android.text.Selection; 12 import android.text.Selection;
13 import android.view.KeyCharacterMap;
13 import android.view.KeyEvent; 14 import android.view.KeyEvent;
14 import android.view.View; 15 import android.view.View;
15 import android.view.inputmethod.BaseInputConnection; 16 import android.view.inputmethod.BaseInputConnection;
16 import android.view.inputmethod.EditorInfo; 17 import android.view.inputmethod.EditorInfo;
17 import android.view.inputmethod.ExtractedText; 18 import android.view.inputmethod.ExtractedText;
18 import android.view.inputmethod.ExtractedTextRequest; 19 import android.view.inputmethod.ExtractedTextRequest;
19 import android.view.inputmethod.InputMethodManager; 20 import android.view.inputmethod.InputMethodManager;
20 21
21 import org.chromium.base.CalledByNative; 22 import org.chromium.base.CalledByNative;
22 import org.chromium.base.JNINamespace; 23 import org.chromium.base.JNINamespace;
23 24
24 // We have to adapt and plumb android IME service and chrome text input API. 25 /**
25 // ImeAdapter provides an interface in both ways native <-> java: 26 We have to adapt and plumb android IME service and chrome text input API.
26 // 1. InputConnectionAdapter notifies native code of text composition state and 27 ImeAdapter provides an interface in both ways native <-> java:
27 // dispatch key events from java -> WebKit. 28 1. InputConnectionAdapter notifies native code of text composition state and
28 // 2. Native ImeAdapter notifies java side to clear composition text. 29 dispatch key events from java -> WebKit.
29 // 30 2. Native ImeAdapter notifies java side to clear composition text.
30 // The basic flow is: 31
31 // 1. Intercept dispatchKeyEventPreIme() to record the current key event, but do 32 The basic flow is:
32 // nothing else. 33 1. When InputConnectionAdapter gets called with composition or result text:
33 // 2. When InputConnectionAdapter gets called with composition or result text: 34 If we receive a composition text or a result text, then we just need to
34 // a) If a key event has been recorded in dispatchKeyEventPreIme() and we 35 dispatch a synthetic key event with special keycode 229, and then dispatch
35 // receive a result text with single character, then we probably need to 36 the composition or result text.
36 // send the result text as a Char event rather than a ConfirmComposition 37 2. Intercept dispatchKeyEvent() method for key events not handled by IME, we
37 // event. So we need to dispatch the recorded key event followed by a 38 need to dispatch them to webkit and check webkit's reply. Then inject a
38 // synthetic Char event. 39 new key event for further processing if webkit didn't handle it.
39 // b) If we receive a composition text or a result text with more than one 40 */
40 // characters, then no matter if we recorded a key event or not in
41 // dispatchKeyEventPreIme(), we just need to dispatch a synthetic key
42 // event with special keycode 229, and then dispatch the composition or
43 // result text.
44 // 3. Intercept dispatchKeyEvent() method for key events not handled by IME, we
45 // need to dispatch them to webkit and check webkit's reply. Then inject a
46 // new key event for further processing if webkit didn't handle it.
47 @JNINamespace("content") 41 @JNINamespace("content")
48 class ImeAdapter { 42 class ImeAdapter {
49 interface ViewEmbedder { 43 interface ViewEmbedder {
50 /** 44 /**
51 * @param isFinish whether the event is occuring because input is finish ed. 45 * @param isFinish whether the event is occuring because input is finish ed.
52 */ 46 */
53 public void onImeEvent(boolean isFinish); 47 public void onImeEvent(boolean isFinish);
54 public void onSetFieldValue(); 48 public void onSetFieldValue();
55 public void onDismissInput(); 49 public void onDismissInput();
56 public View getAttachedView(); 50 public View getAttachedView();
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 sTextInputTypeTel = textInputTypeTel; 104 sTextInputTypeTel = textInputTypeTel;
111 sTextInputTypeNumber = textInputTypeNumber; 105 sTextInputTypeNumber = textInputTypeNumber;
112 sTextInputTypeWeek = textInputTypeWeek; 106 sTextInputTypeWeek = textInputTypeWeek;
113 sTextInputTypeContentEditable = textInputTypeContentEditable; 107 sTextInputTypeContentEditable = textInputTypeContentEditable;
114 InputDialogContainer.initializeInputTypes(textInputTypeDate, textInputTy peDateTime, 108 InputDialogContainer.initializeInputTypes(textInputTypeDate, textInputTy peDateTime,
115 textInputTypeDateTimeLocal, textInputTypeMonth, textInputTypeTim e); 109 textInputTypeDateTimeLocal, textInputTypeMonth, textInputTypeTim e);
116 } 110 }
117 111
118 private int mNativeImeAdapterAndroid; 112 private int mNativeImeAdapterAndroid;
119 private int mTextInputType; 113 private int mTextInputType;
120 private int mPreImeEventCount;
121 114
122 private Context mContext; 115 private Context mContext;
123 private SelectionHandleController mSelectionHandleController; 116 private SelectionHandleController mSelectionHandleController;
124 private InsertionHandleController mInsertionHandleController; 117 private InsertionHandleController mInsertionHandleController;
125 private AdapterInputConnection mInputConnection; 118 private AdapterInputConnection mInputConnection;
126 private ViewEmbedder mViewEmbedder; 119 private ViewEmbedder mViewEmbedder;
127 private Handler mHandler; 120 private Handler mHandler;
128 private InputDialogContainer mInputDialogContainer; 121 private InputDialogContainer mInputDialogContainer;
129 122
130 private class DelayedDismissInput implements Runnable { 123 private class DelayedDismissInput implements Runnable {
(...skipping 14 matching lines...) Expand all
145 138
146 // Delay introduced to avoid hiding the keyboard if new show requests are re ceived. 139 // Delay introduced to avoid hiding the keyboard if new show requests are re ceived.
147 // The time required by the unfocus-focus events triggered by tab has been m easured in soju: 140 // The time required by the unfocus-focus events triggered by tab has been m easured in soju:
148 // Mean: 18.633 ms, Standard deviation: 7.9837 ms. 141 // Mean: 18.633 ms, Standard deviation: 7.9837 ms.
149 // The value here should be higher enough to cover these cases, but not too high to avoid 142 // The value here should be higher enough to cover these cases, but not too high to avoid
150 // letting the user perceiving important delays. 143 // letting the user perceiving important delays.
151 private static final int INPUT_DISMISS_DELAY = 150; 144 private static final int INPUT_DISMISS_DELAY = 150;
152 145
153 ImeAdapter(Context context, SelectionHandleController selectionHandleControl ler, 146 ImeAdapter(Context context, SelectionHandleController selectionHandleControl ler,
154 InsertionHandleController insertionHandleController, ViewEmbedder em bedder) { 147 InsertionHandleController insertionHandleController, ViewEmbedder em bedder) {
155 mPreImeEventCount = 0;
156 mContext = context; 148 mContext = context;
157 mSelectionHandleController = selectionHandleController; 149 mSelectionHandleController = selectionHandleController;
158 mInsertionHandleController = insertionHandleController; 150 mInsertionHandleController = insertionHandleController;
159 mViewEmbedder = embedder; 151 mViewEmbedder = embedder;
160 mHandler = new Handler(); 152 mHandler = new Handler();
161 mInputDialogContainer = new InputDialogContainer(context, 153 mInputDialogContainer = new InputDialogContainer(context,
162 new InputDialogContainer.InputActionDelegate() { 154 new InputDialogContainer.InputActionDelegate() {
163 155
164 @Override 156 @Override
165 public void replaceDateTime(String text) { 157 public void replaceDateTime(String text) {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 } 287 }
296 288
297 boolean hasTextInputType() { 289 boolean hasTextInputType() {
298 return isTextInputType(mTextInputType); 290 return isTextInputType(mTextInputType);
299 } 291 }
300 292
301 boolean hasDialogInputType() { 293 boolean hasDialogInputType() {
302 return InputDialogContainer.isDialogInputType(mTextInputType); 294 return InputDialogContainer.isDialogInputType(mTextInputType);
303 } 295 }
304 296
305 void dispatchKeyEventPreIme(KeyEvent event) {
306 // We only register that a key was pressed, but we don't actually interc ept
307 // it.
308 ++mPreImeEventCount;
309 }
310
311 boolean dispatchKeyEvent(KeyEvent event) { 297 boolean dispatchKeyEvent(KeyEvent event) {
312 mPreImeEventCount = 0;
313 return translateAndSendNativeEvents(event); 298 return translateAndSendNativeEvents(event);
314 } 299 }
315 300
316 void commitText() { 301 void commitText() {
317 cancelComposition(); 302 cancelComposition();
318 if (mNativeImeAdapterAndroid != 0) { 303 if (mNativeImeAdapterAndroid != 0) {
319 nativeCommitText(mNativeImeAdapterAndroid, ""); 304 nativeCommitText(mNativeImeAdapterAndroid, "");
320 } 305 }
321 } 306 }
322 307
(...skipping 11 matching lines...) Expand all
334 return false; 319 return false;
335 } 320 }
336 321
337 // Committing an empty string finishes the current composition. 322 // Committing an empty string finishes the current composition.
338 boolean isFinish = text.isEmpty(); 323 boolean isFinish = text.isEmpty();
339 if (!isFinish) { 324 if (!isFinish) {
340 mSelectionHandleController.hideAndDisallowAutomaticShowing(); 325 mSelectionHandleController.hideAndDisallowAutomaticShowing();
341 mInsertionHandleController.hideAndDisallowAutomaticShowing(); 326 mInsertionHandleController.hideAndDisallowAutomaticShowing();
342 } 327 }
343 mViewEmbedder.onImeEvent(isFinish); 328 mViewEmbedder.onImeEvent(isFinish);
344 boolean hasSingleChar = mPreImeEventCount == 1 && text.length() == 1; 329 int keyCode = shouldSendKeyEventWithKeyCode(text);
345 int keyCode = hasSingleChar ? text.codePointAt(0) : COMPOSITION_KEY_CODE ;
346 int keyChar = hasSingleChar ? text.codePointAt(0) : 0;
347 long timeStampMs = System.currentTimeMillis(); 330 long timeStampMs = System.currentTimeMillis();
348 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawKeyDo wn, 331
349 timeStampMs, keyCode, keyChar); 332 if (keyCode != COMPOSITION_KEY_CODE) {
350 if (hasSingleChar) { 333 sendKeyEventWithKeyCode(keyCode,
351 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeChar , 334 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE) ;
352 timeStampMs, text.codePointAt(0), text.codePointAt(0));
353 } else { 335 } else {
336 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeRawK eyDown,
337 timeStampMs, keyCode, 0);
354 if (isCommit) { 338 if (isCommit) {
355 nativeCommitText(mNativeImeAdapterAndroid, text); 339 nativeCommitText(mNativeImeAdapterAndroid, text);
356 } else { 340 } else {
357 nativeSetComposingText(mNativeImeAdapterAndroid, text, newCursor Position); 341 nativeSetComposingText(mNativeImeAdapterAndroid, text, newCursor Position);
358 } 342 }
343 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyU p,
344 timeStampMs, keyCode, 0);
359 } 345 }
360 nativeSendSyntheticKeyEvent(mNativeImeAdapterAndroid, sEventTypeKeyUp, 346
361 timeStampMs, keyCode, keyChar);
362 mPreImeEventCount = 0;
363 return true; 347 return true;
364 } 348 }
365 349
350 private int shouldSendKeyEventWithKeyCode(String text) {
351 if (text.length() != 1) return COMPOSITION_KEY_CODE;
352
353 if (text.equals("\n")) return KeyEvent.KEYCODE_ENTER;
354 else if (text.equals("\t")) return KeyEvent.KEYCODE_TAB;
355 else return COMPOSITION_KEY_CODE;
356 }
357
358 private void sendKeyEventWithKeyCode(int keyCode, int flags) {
359 long eventTime = System.currentTimeMillis();
360 translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime,
361 KeyEvent.ACTION_DOWN, keyCode, 0, 0,
362 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
363 flags));
364 translateAndSendNativeEvents(new KeyEvent(System.currentTimeMillis(), ev entTime,
365 KeyEvent.ACTION_UP, keyCode, 0, 0,
366 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
367 flags));
368 }
369
366 private boolean translateAndSendNativeEvents(KeyEvent event) { 370 private boolean translateAndSendNativeEvents(KeyEvent event) {
367 if (mNativeImeAdapterAndroid == 0) { 371 if (mNativeImeAdapterAndroid == 0) {
368 return false; 372 return false;
369 } 373 }
370 int action = event.getAction(); 374 int action = event.getAction();
371 if (action != KeyEvent.ACTION_DOWN && 375 if (action != KeyEvent.ACTION_DOWN &&
372 action != KeyEvent.ACTION_UP) { 376 action != KeyEvent.ACTION_UP) {
373 // action == KeyEvent.ACTION_MULTIPLE 377 // action == KeyEvent.ACTION_MULTIPLE
374 // TODO(bulach): confirm the actual behavior. Apparently: 378 // TODO(bulach): confirm the actual behavior. Apparently:
375 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a 379 // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 // Send TAB key event 606 // Send TAB key event
603 long timeStampMs = System.currentTimeMillis(); 607 long timeStampMs = System.currentTimeMillis();
604 mImeAdapter.sendSyntheticKeyEvent( 608 mImeAdapter.sendSyntheticKeyEvent(
605 sEventTypeRawKeyDown, timeStampMs, KeyEvent.KEYCODE_ TAB, 0); 609 sEventTypeRawKeyDown, timeStampMs, KeyEvent.KEYCODE_ TAB, 0);
606 return true; 610 return true;
607 case EditorInfo.IME_ACTION_GO: 611 case EditorInfo.IME_ACTION_GO:
608 case EditorInfo.IME_ACTION_SEARCH: 612 case EditorInfo.IME_ACTION_SEARCH:
609 mImeAdapter.dismissInput(true); 613 mImeAdapter.dismissInput(true);
610 break; 614 break;
611 } 615 }
612 616 mImeAdapter.sendKeyEventWithKeyCode(KeyEvent.KEYCODE_ENTER,
613 return super.performEditorAction(actionCode); 617 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
618 | KeyEvent.FLAG_EDITOR_ACTION);
619 return true;
614 } 620 }
615 621
616 @Override 622 @Override
617 public boolean performContextMenuAction(int id) { 623 public boolean performContextMenuAction(int id) {
618 switch (id) { 624 switch (id) {
619 case android.R.id.selectAll: 625 case android.R.id.selectAll:
620 return mImeAdapter.selectAll(); 626 return mImeAdapter.selectAll();
621 case android.R.id.cut: 627 case android.R.id.cut:
622 return mImeAdapter.cut(); 628 return mImeAdapter.cut();
623 case android.R.id.copy: 629 case android.R.id.copy:
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
674 if (selectionStart > selectionEnd) { 680 if (selectionStart > selectionEnd) {
675 int temp = selectionStart; 681 int temp = selectionStart;
676 selectionStart = selectionEnd; 682 selectionStart = selectionEnd;
677 selectionEnd = temp; 683 selectionEnd = temp;
678 } 684 }
679 editable.replace(selectionStart, selectionEnd, 685 editable.replace(selectionStart, selectionEnd,
680 Character.toString((char)unicodeChar)); 686 Character.toString((char)unicodeChar));
681 } 687 }
682 } 688 }
683 } 689 }
684 return super.sendKeyEvent(event); 690 mImeAdapter.translateAndSendNativeEvents(event);
691 return true;
685 } 692 }
686 693
687 @Override 694 @Override
688 public boolean finishComposingText() { 695 public boolean finishComposingText() {
689 if (mEditable == null 696 if (mEditable == null
690 || (getComposingSpanStart(mEditable) == getComposingSpanEnd( mEditable))) { 697 || (getComposingSpanStart(mEditable) == getComposingSpanEnd( mEditable))) {
691 return true; 698 return true;
692 } 699 }
693 super.finishComposingText(); 700 super.finishComposingText();
694 return mImeAdapter.checkCompositionQueueAndCallNative("", 0, true); 701 return mImeAdapter.checkCompositionQueueAndCallNative("", 0, true);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
804 811
805 private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid, 812 private native void nativeDeleteSurroundingText(int nativeImeAdapterAndroid,
806 int before, int after); 813 int before, int after);
807 814
808 private native void nativeUnselect(int nativeImeAdapterAndroid); 815 private native void nativeUnselect(int nativeImeAdapterAndroid);
809 private native void nativeSelectAll(int nativeImeAdapterAndroid); 816 private native void nativeSelectAll(int nativeImeAdapterAndroid);
810 private native void nativeCut(int nativeImeAdapterAndroid); 817 private native void nativeCut(int nativeImeAdapterAndroid);
811 private native void nativeCopy(int nativeImeAdapterAndroid); 818 private native void nativeCopy(int nativeImeAdapterAndroid);
812 private native void nativePaste(int nativeImeAdapterAndroid); 819 private native void nativePaste(int nativeImeAdapterAndroid);
813 } 820 }
OLDNEW
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698