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

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

Issue 1162863007: Translate physical keyboard accents to IME compositions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix bugs, add test Created 5 years, 6 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
« no previous file with comments | « no previous file | content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import org.chromium.blink_public.web.WebInputEventType; 22 import org.chromium.blink_public.web.WebInputEventType;
22 import org.chromium.blink_public.web.WebTextInputFlags; 23 import org.chromium.blink_public.web.WebTextInputFlags;
(...skipping 12 matching lines...) Expand all
35 */ 36 */
36 public static final int INVALID_SELECTION = -1; 37 public static final int INVALID_SELECTION = -1;
37 public static final int INVALID_COMPOSITION = -1; 38 public static final int INVALID_COMPOSITION = -1;
38 39
39 private final View mInternalView; 40 private final View mInternalView;
40 private final ImeAdapter mImeAdapter; 41 private final ImeAdapter mImeAdapter;
41 private final Editable mEditable; 42 private final Editable mEditable;
42 43
43 private boolean mSingleLine; 44 private boolean mSingleLine;
44 private int mNumNestedBatchEdits = 0; 45 private int mNumNestedBatchEdits = 0;
46 private int mPendingAccent;
45 47
46 private int mLastUpdateSelectionStart = INVALID_SELECTION; 48 private int mLastUpdateSelectionStart = INVALID_SELECTION;
47 private int mLastUpdateSelectionEnd = INVALID_SELECTION; 49 private int mLastUpdateSelectionEnd = INVALID_SELECTION;
48 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; 50 private int mLastUpdateCompositionStart = INVALID_COMPOSITION;
49 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; 51 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION;
50 52
51 @VisibleForTesting 53 @VisibleForTesting
52 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable, 54 AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable,
53 EditorInfo outAttrs) { 55 EditorInfo outAttrs) {
54 super(view, true); 56 super(view, true);
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 mLastUpdateCompositionEnd = compositionEnd; 237 mLastUpdateCompositionEnd = compositionEnd;
236 } 238 }
237 239
238 /** 240 /**
239 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) 241 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int)
240 */ 242 */
241 @Override 243 @Override
242 public boolean setComposingText(CharSequence text, int newCursorPosition) { 244 public boolean setComposingText(CharSequence text, int newCursorPosition) {
243 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos ition + "]"); 245 if (DEBUG) Log.w(TAG, "setComposingText [" + text + "] [" + newCursorPos ition + "]");
244 if (maybePerformEmptyCompositionWorkaround(text)) return true; 246 if (maybePerformEmptyCompositionWorkaround(text)) return true;
247 mPendingAccent = 0;
245 super.setComposingText(text, newCursorPosition); 248 super.setComposingText(text, newCursorPosition);
246 updateSelectionIfRequired(); 249 updateSelectionIfRequired();
247 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos ition, false); 250 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos ition, false);
248 } 251 }
249 252
250 /** 253 /**
251 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) 254 * @see BaseInputConnection#commitText(java.lang.CharSequence, int)
252 */ 255 */
253 @Override 256 @Override
254 public boolean commitText(CharSequence text, int newCursorPosition) { 257 public boolean commitText(CharSequence text, int newCursorPosition) {
255 if (DEBUG) Log.w(TAG, "commitText [" + text + "] [" + newCursorPosition + "]"); 258 if (DEBUG) Log.w(TAG, "commitText [" + text + "] [" + newCursorPosition + "]");
256 if (maybePerformEmptyCompositionWorkaround(text)) return true; 259 if (maybePerformEmptyCompositionWorkaround(text)) return true;
260 mPendingAccent = 0;
257 super.commitText(text, newCursorPosition); 261 super.commitText(text, newCursorPosition);
258 updateSelectionIfRequired(); 262 updateSelectionIfRequired();
259 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos ition, 263 return mImeAdapter.checkCompositionQueueAndCallNative(text, newCursorPos ition,
260 text.length() > 0); 264 text.length() > 0);
261 } 265 }
262 266
263 /** 267 /**
264 * @see BaseInputConnection#performEditorAction(int) 268 * @see BaseInputConnection#performEditorAction(int)
265 */ 269 */
266 @Override 270 @Override
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 @Override 348 @Override
345 public boolean deleteSurroundingText(int beforeLength, int afterLength) { 349 public boolean deleteSurroundingText(int beforeLength, int afterLength) {
346 return deleteSurroundingTextImpl(beforeLength, afterLength, false); 350 return deleteSurroundingTextImpl(beforeLength, afterLength, false);
347 } 351 }
348 352
349 private boolean deleteSurroundingTextImpl( 353 private boolean deleteSurroundingTextImpl(
350 int beforeLength, int afterLength, boolean fromPhysicalKey) { 354 int beforeLength, int afterLength, boolean fromPhysicalKey) {
351 if (DEBUG) { 355 if (DEBUG) {
352 Log.w(TAG, "deleteSurroundingText [" + beforeLength + " " + afterLen gth + "]"); 356 Log.w(TAG, "deleteSurroundingText [" + beforeLength + " " + afterLen gth + "]");
353 } 357 }
358
359 if (mPendingAccent != 0) {
360 mPendingAccent = 0;
361 finishComposingText();
362 }
363
354 int originalBeforeLength = beforeLength; 364 int originalBeforeLength = beforeLength;
355 int originalAfterLength = afterLength; 365 int originalAfterLength = afterLength;
356 int availableBefore = Selection.getSelectionStart(mEditable); 366 int availableBefore = Selection.getSelectionStart(mEditable);
357 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi table); 367 int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEdi table);
358 beforeLength = Math.min(beforeLength, availableBefore); 368 beforeLength = Math.min(beforeLength, availableBefore);
359 afterLength = Math.min(afterLength, availableAfter); 369 afterLength = Math.min(afterLength, availableAfter);
360 super.deleteSurroundingText(beforeLength, afterLength); 370 super.deleteSurroundingText(beforeLength, afterLength);
361 updateSelectionIfRequired(); 371 updateSelectionIfRequired();
362 372
363 // If this was called due to a physical key, no need to generate a key e vent here as 373 // If this was called due to a physical key, no need to generate a key e vent here as
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 @Override 406 @Override
397 public boolean sendKeyEvent(KeyEvent event) { 407 public boolean sendKeyEvent(KeyEvent event) {
398 if (DEBUG) { 408 if (DEBUG) {
399 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]"); 409 Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getK eyCode() + "]");
400 } 410 }
401 411
402 int action = event.getAction(); 412 int action = event.getAction();
403 int keycode = event.getKeyCode(); 413 int keycode = event.getKeyCode();
404 int unicodeChar = event.getUnicodeChar(); 414 int unicodeChar = event.getUnicodeChar();
405 415
416 // If we're not focused in a textbox or if this isn't a KeyDown event, n o need to update
417 // composition state; just pass the key event through and return.
418 if (mImeAdapter.getTextInputType() == TextInputType.NONE
419 || action != KeyEvent.ACTION_DOWN) {
aurimas (slooooooooow) 2015/06/03 00:44:20 Would this result in us sending KeyUp events witho
aelias_OOO_until_Jul13 2015/06/03 01:15:01 Yes, it does and I spent some time looking into if
bcwhite 2015/06/03 15:28:58 I've been trying this patch with a physical keyboa
aelias_OOO_until_Jul13 2015/06/03 21:10:29 Good catch, thanks. It looks like this is a side
420 mImeAdapter.translateAndSendNativeEvents(event);
421 return true;
422 }
423
406 // If this is backspace/del or if the key has a character representation , 424 // If this is backspace/del or if the key has a character representation ,
407 // need to update the underlying Editable (i.e. the local representation of the text 425 // need to update the underlying Editable (i.e. the local representation of the text
408 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi x in delete 426 // being edited). Some IMEs like Jellybean stock IME and Samsung IME mi x in delete
409 // KeyPress events instead of calling deleteSurroundingText. 427 // KeyPress events instead of calling deleteSurroundingText.
410 if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_DEL) { 428 if (keycode == KeyEvent.KEYCODE_DEL) {
411 deleteSurroundingTextImpl(1, 0, true); 429 deleteSurroundingTextImpl(1, 0, true);
412 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE _FORWARD_DEL) { 430 } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) {
aurimas (slooooooooow) 2015/06/03 00:44:20 What would happen if we have DEL, FORWARD_DEL or E
aelias_OOO_until_Jul13 2015/06/03 01:15:01 I did try out those scenarios earlier today and ha
413 deleteSurroundingTextImpl(0, 1, true); 431 deleteSurroundingTextImpl(0, 1, true);
414 } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE _ENTER) { 432 } else if (keycode == KeyEvent.KEYCODE_ENTER) {
415 // Finish text composition when pressing enter, as that may submit a form field. 433 // Finish text composition when pressing enter, as that may submit a form field.
416 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi xed. 434 // TODO(aurimas): remove this workaround when crbug.com/278584 is fi xed.
417 beginBatchEdit(); 435 beginBatchEdit();
418 finishComposingText(); 436 finishComposingText();
419 mImeAdapter.translateAndSendNativeEvents(event); 437 mImeAdapter.translateAndSendNativeEvents(event);
420 endBatchEdit(); 438 endBatchEdit();
421 return true; 439 return true;
422 } else if (action == KeyEvent.ACTION_UP && unicodeChar != 0) { 440 } else if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
441 // Store a pending accent character and make it the current composit ion.
442 int pendingAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_M ASK;
443 StringBuilder builder = new StringBuilder();
444 builder.appendCodePoint(pendingAccent);
445 setComposingText(builder.toString(), 1);
446 mPendingAccent = pendingAccent;
447 return true;
448 }
449
450 if (unicodeChar != 0) {
451 if (mPendingAccent != 0) {
452 int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar) ;
453 if (combined != 0) {
454 StringBuilder builder = new StringBuilder();
455 builder.appendCodePoint(combined);
456 commitText(builder.toString(), 1);
457 return true;
458 }
459 // Noncombinable character; commit the accent character and fall through to sending
460 // the key event for the character afterwards.
461 finishComposingText();
462 }
463
464 // Update the mEditable state to reflect what Blink will do in respo nse to the KeyDown
465 // for a unicode-mapped key event.
423 int selectionStart = Selection.getSelectionStart(mEditable); 466 int selectionStart = Selection.getSelectionStart(mEditable);
424 int selectionEnd = Selection.getSelectionEnd(mEditable); 467 int selectionEnd = Selection.getSelectionEnd(mEditable);
425 if (selectionStart > selectionEnd) { 468 if (selectionStart > selectionEnd) {
426 int temp = selectionStart; 469 int temp = selectionStart;
427 selectionStart = selectionEnd; 470 selectionStart = selectionEnd;
428 selectionEnd = temp; 471 selectionEnd = temp;
429 } 472 }
430 mEditable.replace(selectionStart, selectionEnd, 473 mEditable.replace(selectionStart, selectionEnd,
431 Character.toString((char) unicodeChar)); 474 Character.toString((char) unicodeChar));
432 } 475 }
476
433 mImeAdapter.translateAndSendNativeEvents(event); 477 mImeAdapter.translateAndSendNativeEvents(event);
434 return true; 478 return true;
435 } 479 }
436 480
437 /** 481 /**
438 * @see BaseInputConnection#finishComposingText() 482 * @see BaseInputConnection#finishComposingText()
439 */ 483 */
440 @Override 484 @Override
441 public boolean finishComposingText() { 485 public boolean finishComposingText() {
442 if (DEBUG) Log.w(TAG, "finishComposingText"); 486 if (DEBUG) Log.w(TAG, "finishComposingText");
443 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) { 487 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) {
444 return true; 488 return true;
445 } 489 }
446 490
491 mPendingAccent = 0;
447 super.finishComposingText(); 492 super.finishComposingText();
448 updateSelectionIfRequired(); 493 updateSelectionIfRequired();
449 mImeAdapter.finishComposingText(); 494 mImeAdapter.finishComposingText();
450 495
451 return true; 496 return true;
452 } 497 }
453 498
454 /** 499 /**
455 * @see BaseInputConnection#setSelection(int, int) 500 * @see BaseInputConnection#setSelection(int, int)
456 */ 501 */
457 @Override 502 @Override
458 public boolean setSelection(int start, int end) { 503 public boolean setSelection(int start, int end) {
459 if (DEBUG) Log.w(TAG, "setSelection [" + start + " " + end + "]"); 504 if (DEBUG) Log.w(TAG, "setSelection [" + start + " " + end + "]");
460 int textLength = mEditable.length(); 505 int textLength = mEditable.length();
461 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true; 506 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true;
462 super.setSelection(start, end); 507 super.setSelection(start, end);
463 updateSelectionIfRequired(); 508 updateSelectionIfRequired();
464 return mImeAdapter.setEditableSelectionOffsets(start, end); 509 return mImeAdapter.setEditableSelectionOffsets(start, end);
465 } 510 }
466 511
467 /** 512 /**
468 * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that the text 513 * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that the text
469 * state is no longer what the IME has and that it needs to be updated. 514 * state is no longer what the IME has and that it needs to be updated.
470 */ 515 */
471 void restartInput() { 516 void restartInput() {
472 if (DEBUG) Log.w(TAG, "restartInput"); 517 if (DEBUG) Log.w(TAG, "restartInput");
473 getInputMethodManagerWrapper().restartInput(mInternalView); 518 getInputMethodManagerWrapper().restartInput(mInternalView);
474 mNumNestedBatchEdits = 0; 519 mNumNestedBatchEdits = 0;
520 mPendingAccent = 0;
475 } 521 }
476 522
477 /** 523 /**
478 * @see BaseInputConnection#setComposingRegion(int, int) 524 * @see BaseInputConnection#setComposingRegion(int, int)
479 */ 525 */
480 @Override 526 @Override
481 public boolean setComposingRegion(int start, int end) { 527 public boolean setComposingRegion(int start, int end) {
482 if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]"); 528 if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]");
483 int textLength = mEditable.length(); 529 int textLength = mEditable.length();
484 int a = Math.min(start, end); 530 int a = Math.min(start, end);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 @VisibleForTesting 604 @VisibleForTesting
559 ImeState getImeStateForTesting() { 605 ImeState getImeStateForTesting() {
560 String text = mEditable.toString(); 606 String text = mEditable.toString();
561 int selectionStart = Selection.getSelectionStart(mEditable); 607 int selectionStart = Selection.getSelectionStart(mEditable);
562 int selectionEnd = Selection.getSelectionEnd(mEditable); 608 int selectionEnd = Selection.getSelectionEnd(mEditable);
563 int compositionStart = getComposingSpanStart(mEditable); 609 int compositionStart = getComposingSpanStart(mEditable);
564 int compositionEnd = getComposingSpanEnd(mEditable); 610 int compositionEnd = getComposingSpanEnd(mEditable);
565 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd); 611 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd);
566 } 612 }
567 } 613 }
OLDNEW
« no previous file with comments | « no previous file | content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698