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

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

Issue 759033002: Handle accent keys from physical keyboards. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: changed where characters get accented so hidden editor contents are also correct (and test works pr… Created 6 years 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.os.SystemClock; 7 import android.os.SystemClock;
8 import android.text.Editable; 8 import android.text.Editable;
9 import android.text.InputType; 9 import android.text.InputType;
10 import android.text.Selection; 10 import android.text.Selection;
11 import android.text.TextUtils; 11 import android.text.TextUtils;
12 import android.util.Log; 12 import android.util.Log;
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 20
20 import org.chromium.base.VisibleForTesting; 21 import org.chromium.base.VisibleForTesting;
21 22
22 /** 23 /**
23 * InputConnection is created by ContentView.onCreateInputConnection. 24 * InputConnection is created by ContentView.onCreateInputConnection.
24 * It then adapts android's IME to chrome's RenderWidgetHostView using the 25 * It then adapts android's IME to chrome's RenderWidgetHostView using the
25 * native ImeAdapterAndroid via the class ImeAdapter. 26 * native ImeAdapterAndroid via the class ImeAdapter.
26 */ 27 */
27 public class AdapterInputConnection extends BaseInputConnection { 28 public class AdapterInputConnection extends BaseInputConnection {
28 private static final String TAG = "AdapterInputConnection"; 29 private static final String TAG = "AdapterInputConnection";
29 private static final boolean DEBUG = false; 30 private static final boolean DEBUG = false;
30 /** 31 /**
31 * Selection value should be -1 if not known. See EditorInfo.java for detail s. 32 * Selection value should be -1 if not known. See EditorInfo.java for detail s.
32 */ 33 */
33 public static final int INVALID_SELECTION = -1; 34 public static final int INVALID_SELECTION = -1;
34 public static final int INVALID_COMPOSITION = -1; 35 public static final int INVALID_COMPOSITION = -1;
35 36
36 private final View mInternalView; 37 private final View mInternalView;
37 private final ImeAdapter mImeAdapter; 38 private final ImeAdapter mImeAdapter;
38 private final Editable mEditable; 39 private final Editable mEditable;
39 40
40 private boolean mSingleLine; 41 private boolean mSingleLine;
41 private int mNumNestedBatchEdits = 0; 42 private int mNumNestedBatchEdits = 0;
43 private int mPendingAccent;
42 44
43 private int mLastUpdateSelectionStart = INVALID_SELECTION; 45 private int mLastUpdateSelectionStart = INVALID_SELECTION;
44 private int mLastUpdateSelectionEnd = INVALID_SELECTION; 46 private int mLastUpdateSelectionEnd = INVALID_SELECTION;
45 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; 47 private int mLastUpdateCompositionStart = INVALID_COMPOSITION;
46 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; 48 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION;
47 49
48 @VisibleForTesting 50 @VisibleForTesting
49 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable, 51 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable,
50 EditorInfo outAttrs) { 52 EditorInfo outAttrs) {
51 super(view, true); 53 super(view, true);
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 + compositionStart + " " + compositionEnd + "]"); 210 + compositionStart + " " + compositionEnd + "]");
209 } 211 }
210 // updateSelection should be called every time the selection or composit ion changes 212 // updateSelection should be called every time the selection or composit ion changes
211 // if it happens not within a batch edit, or at the end of each top leve l batch edit. 213 // if it happens not within a batch edit, or at the end of each top leve l batch edit.
212 getInputMethodManagerWrapper().updateSelection(mInternalView, 214 getInputMethodManagerWrapper().updateSelection(mInternalView,
213 selectionStart, selectionEnd, compositionStart, compositionEnd); 215 selectionStart, selectionEnd, compositionStart, compositionEnd);
214 mLastUpdateSelectionStart = selectionStart; 216 mLastUpdateSelectionStart = selectionStart;
215 mLastUpdateSelectionEnd = selectionEnd; 217 mLastUpdateSelectionEnd = selectionEnd;
216 mLastUpdateCompositionStart = compositionStart; 218 mLastUpdateCompositionStart = compositionStart;
217 mLastUpdateCompositionEnd = compositionEnd; 219 mLastUpdateCompositionEnd = compositionEnd;
220 // Change in selection or cursor position invalidates any pending accent .
221 mPendingAccent = 0;
Ted C 2014/12/03 02:30:14 do we need to do this as well on restartInput (or
bcwhite 2014/12/03 15:49:09 Seems the smart thing to do. Done.
218 } 222 }
219 223
220 /** 224 /**
221 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) 225 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int)
222 */ 226 */
223 @Override 227 @Override
224 public boolean setComposingText(CharSequence text, int newCursorPosition) { 228 public boolean setComposingText(CharSequence text, int newCursorPosition) {
225 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos ition + "]"); 229 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos ition + "]");
226 if (maybePerformEmptyCompositionWorkaround(text)) return true; 230 if (maybePerformEmptyCompositionWorkaround(text)) return true;
227 super.setComposingText(text, newCursorPosition); 231 super.setComposingText(text, newCursorPosition);
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 } 365 }
362 366
363 /** 367 /**
364 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) 368 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent)
365 */ 369 */
366 @Override 370 @Override
367 public boolean sendKeyEvent(KeyEvent event) { 371 public boolean sendKeyEvent(KeyEvent event) {
368 if (DEBUG) { 372 if (DEBUG) {
369 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]"); 373 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]");
370 } 374 }
375
376 if (KeyEvent.isModifierKey(event.getKeyCode())) {
377 return mImeAdapter.translateAndSendNativeEvents(event, 0);
378 }
379
380 // Physical keyboards also have their events come through here though no t
381 // by BaseInputConnection. In order to support "accent" key sequences
382 // such as "~n" or "^o" we have to record that one has been pressed
383 // and, if an accentable letter follows, delete the accent glyph and
384 // insert the composed character.
385
386 int unicodeChar = event.getUnicodeChar();
387 int pendingAccent = mPendingAccent;
388 int nextAccent = pendingAccent;
Ted C 2014/12/03 02:30:14 In what cases is this used? Just wondering what c
bcwhite 2014/12/03 15:49:09 It's used to keep all the accent-processing code a
389
390 if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
391 pendingAccent = 0;
392 nextAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK;
393 } else if (pendingAccent != 0) {
394 if (event.getAction() == KeyEvent.ACTION_DOWN) {
395 int combined = KeyEvent.getDeadChar(pendingAccent, unicodeChar);
396 if (combined != 0) {
397 // Previous accent combines with new character to create
398 // a new accented character. First delete the displayed
399 // accent so it appears overwritten by the composition.
400 // Note that deleting the displayed accent will cause an
401 // update and clearing of mPendingAccent which is why we've
402 // copied it to a local variable above.
403 super.deleteSurroundingText(1, 0);
404 mImeAdapter.deleteSurroundingText(1, 0);
405 } else {
406 // Previous accent doesn't combine with this character
407 // so assume both are completely independent.
408 pendingAccent = 0;
409 nextAccent = 0;
410 }
411 }
412
413 if (event.getAction() == KeyEvent.ACTION_UP) {
414 // Forget accent after release of key being accented.
415 nextAccent = 0;
416 }
417 }
418
371 // If this is a key-up, and backspace/del or if the key has a character representation, 419 // If this is a key-up, and backspace/del or if the key has a character representation,
372 // need to update the underlying Editable (i.e. the local representation of the text 420 // need to update the underlying Editable (i.e. the local representation of the text
373 // being edited). 421 // being edited).
374 if (event.getAction() == KeyEvent.ACTION_UP) { 422 if (event.getAction() == KeyEvent.ACTION_UP) {
375 if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { 423 if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
376 deleteSurroundingText(1, 0); 424 deleteSurroundingText(1, 0);
377 return true; 425 return true;
378 } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) { 426 } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
379 deleteSurroundingText(0, 1); 427 deleteSurroundingText(0, 1);
380 return true; 428 return true;
381 } else { 429 } else if (unicodeChar != 0) {
382 int unicodeChar = event.getUnicodeChar(); 430 int combinedChar = unicodeChar;
383 if (unicodeChar != 0) { 431 if (pendingAccent != 0) {
384 int selectionStart = Selection.getSelectionStart(mEditable); 432 combinedChar = KeyEvent.getDeadChar(pendingAccent, unicodeCh ar);
385 int selectionEnd = Selection.getSelectionEnd(mEditable); 433 if (combinedChar == 0) {
386 if (selectionStart > selectionEnd) { 434 combinedChar = unicodeChar;
387 int temp = selectionStart;
388 selectionStart = selectionEnd;
389 selectionEnd = temp;
390 } 435 }
391 mEditable.replace(selectionStart, selectionEnd,
392 Character.toString((char) unicodeChar));
393 } 436 }
437 int selectionStart = Selection.getSelectionStart(mEditable);
438 int selectionEnd = Selection.getSelectionEnd(mEditable);
439 if (selectionStart > selectionEnd) {
440 int temp = selectionStart;
441 selectionStart = selectionEnd;
442 selectionEnd = temp;
443 }
444 mEditable.replace(selectionStart, selectionEnd,
445 Character.toString((char) combinedChar));
394 } 446 }
395 } else if (event.getAction() == KeyEvent.ACTION_DOWN) { 447 } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
396 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi xed. 448 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi xed.
397 if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { 449 if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
398 beginBatchEdit(); 450 beginBatchEdit();
399 finishComposingText(); 451 finishComposingText();
400 mImeAdapter.translateAndSendNativeEvents(event); 452 mImeAdapter.translateAndSendNativeEvents(event, 0);
401 endBatchEdit(); 453 endBatchEdit();
402 return true; 454 return true;
403 } else if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { 455 } else if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
404 return true; 456 return true;
405 } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) { 457 } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
406 return true; 458 return true;
407 } 459 }
408 } 460 }
409 mImeAdapter.translateAndSendNativeEvents(event); 461 mImeAdapter.translateAndSendNativeEvents(event, pendingAccent);
462 mPendingAccent = nextAccent;
410 return true; 463 return true;
411 } 464 }
412 465
413 /** 466 /**
414 * @see BaseInputConnection#finishComposingText() 467 * @see BaseInputConnection#finishComposingText()
415 */ 468 */
416 @Override 469 @Override
417 public boolean finishComposingText() { 470 public boolean finishComposingText() {
418 if (DEBUG) Log.w(TAG, "finishComposingText"); 471 if (DEBUG) Log.w(TAG, "finishComposingText");
419 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) { 472 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) {
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
534 @VisibleForTesting 587 @VisibleForTesting
535 ImeState getImeStateForTesting() { 588 ImeState getImeStateForTesting() {
536 String text = mEditable.toString(); 589 String text = mEditable.toString();
537 int selectionStart = Selection.getSelectionStart(mEditable); 590 int selectionStart = Selection.getSelectionStart(mEditable);
538 int selectionEnd = Selection.getSelectionEnd(mEditable); 591 int selectionEnd = Selection.getSelectionEnd(mEditable);
539 int compositionStart = getComposingSpanStart(mEditable); 592 int compositionStart = getComposingSpanStart(mEditable);
540 int compositionEnd = getComposingSpanEnd(mEditable); 593 int compositionEnd = getComposingSpanEnd(mEditable);
541 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd); 594 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd);
542 } 595 }
543 } 596 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698