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

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

Issue 1589953005: Support InputMethodManager#updateCursorAnchorInfo for Android 5.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix Findbugs warning / Simplify the state transition. Created 4 years, 10 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
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 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.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.content.res.Configuration; 7 import android.content.res.Configuration;
8 import android.os.ResultReceiver; 8 import android.os.ResultReceiver;
9 import android.os.SystemClock; 9 import android.os.SystemClock;
10 import android.text.Editable; 10 import android.text.Editable;
11 import android.text.Selection; 11 import android.text.Selection;
12 import android.text.SpannableString; 12 import android.text.SpannableString;
13 import android.text.TextUtils; 13 import android.text.TextUtils;
14 import android.text.style.BackgroundColorSpan; 14 import android.text.style.BackgroundColorSpan;
15 import android.text.style.CharacterStyle; 15 import android.text.style.CharacterStyle;
16 import android.text.style.UnderlineSpan; 16 import android.text.style.UnderlineSpan;
17 import android.view.KeyCharacterMap; 17 import android.view.KeyCharacterMap;
18 import android.view.KeyEvent; 18 import android.view.KeyEvent;
19 import android.view.View; 19 import android.view.View;
20 import android.view.inputmethod.BaseInputConnection; 20 import android.view.inputmethod.BaseInputConnection;
21 import android.view.inputmethod.CursorAnchorInfo;
21 import android.view.inputmethod.EditorInfo; 22 import android.view.inputmethod.EditorInfo;
23 import android.view.inputmethod.InputMethodManager;
22 24
23 import org.chromium.base.Log; 25 import org.chromium.base.Log;
24 import org.chromium.base.VisibleForTesting; 26 import org.chromium.base.VisibleForTesting;
25 import org.chromium.base.annotations.CalledByNative; 27 import org.chromium.base.annotations.CalledByNative;
26 import org.chromium.base.annotations.JNINamespace; 28 import org.chromium.base.annotations.JNINamespace;
27 import org.chromium.blink_public.web.WebInputEventModifier; 29 import org.chromium.blink_public.web.WebInputEventModifier;
28 import org.chromium.blink_public.web.WebInputEventType; 30 import org.chromium.blink_public.web.WebInputEventType;
31 import org.chromium.content.browser.RenderCoordinates;
29 import org.chromium.ui.base.ime.TextInputType; 32 import org.chromium.ui.base.ime.TextInputType;
30 import org.chromium.ui.picker.InputDialogContainer; 33 import org.chromium.ui.picker.InputDialogContainer;
31 34
32 /** 35 /**
33 * Adapts and plumbs android IME service onto the chrome text input API. 36 * Adapts and plumbs android IME service onto the chrome text input API.
34 * ImeAdapter provides an interface in both ways native <-> java: 37 * ImeAdapter provides an interface in both ways native <-> java:
35 * 1. InputConnectionAdapter notifies native code of text composition state and 38 * 1. InputConnectionAdapter notifies native code of text composition state and
36 * dispatch key events from java -> WebKit. 39 * dispatch key events from java -> WebKit.
37 * 2. Native ImeAdapter notifies java side to clear composition text. 40 * 2. Native ImeAdapter notifies java side to clear composition text.
38 * 41 *
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 private InputMethodManagerWrapper mInputMethodManagerWrapper; 97 private InputMethodManagerWrapper mInputMethodManagerWrapper;
95 private AdapterInputConnection mInputConnection; 98 private AdapterInputConnection mInputConnection;
96 private AdapterInputConnectionFactory mInputConnectionFactory; 99 private AdapterInputConnectionFactory mInputConnectionFactory;
97 private final ImeAdapterDelegate mViewEmbedder; 100 private final ImeAdapterDelegate mViewEmbedder;
98 101
99 // This holds the state of editable text (e.g. contents of <input>, contente ditable) of 102 // This holds the state of editable text (e.g. contents of <input>, contente ditable) of
100 // a focused element. 103 // a focused element.
101 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new 104 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
102 // state must be reflected to this to keep consistency. 105 // state must be reflected to this to keep consistency.
103 private final Editable mEditable; 106 private final Editable mEditable;
107 // This holds the information necessary for constructing CursorAnchorInfo, a nd notifies to
108 // InputMethodManager on appropriate timing, depending on how IME requested the information
109 // via InputConnection. The update request is per InputConnection, hence for each time it is
110 // re-created, the monitoring status will be reset.
111 private final CursorAnchorInfoController mCursorAnchorInfoController;
112
113 @VisibleForTesting
114 int mLastSyntheticKeyCode;
104 115
105 private int mTextInputType = TextInputType.NONE; 116 private int mTextInputType = TextInputType.NONE;
106 private int mTextInputFlags; 117 private int mTextInputFlags;
107 118
108 // Keep the current configuration to detect the change when onConfigurationC hanged() is called. 119 // Keep the current configuration to detect the change when onConfigurationC hanged() is called.
109 private Configuration mCurrentConfig; 120 private Configuration mCurrentConfig;
110 121
111 /** 122 /**
112 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to 123 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to
113 * InputMethodManager. 124 * InputMethodManager.
114 * @param embedder The view that is used for callbacks from ImeAdapter. 125 * @param embedder The view that is used for callbacks from ImeAdapter.
115 */ 126 */
116 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { 127 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) {
117 mInputMethodManagerWrapper = wrapper; 128 mInputMethodManagerWrapper = wrapper;
118 mViewEmbedder = embedder; 129 mViewEmbedder = embedder;
119 mInputConnectionFactory = new AdapterInputConnectionFactory(); 130 mInputConnectionFactory = new AdapterInputConnectionFactory();
120 mEditable = Editable.Factory.getInstance().newEditable(""); 131 mEditable = Editable.Factory.getInstance().newEditable("");
121 Selection.setSelection(mEditable, 0); 132 Selection.setSelection(mEditable, 0);
122 // Deep copy newConfig so that we can notice the difference. 133 // Deep copy newConfig so that we can notice the difference.
123 mCurrentConfig = new Configuration( 134 mCurrentConfig = new Configuration(
124 mViewEmbedder.getAttachedView().getResources().getConfiguration( )); 135 mViewEmbedder.getAttachedView().getResources().getConfiguration( ));
136 mCursorAnchorInfoController = CursorAnchorInfoController.create(wrapper) ;
125 } 137 }
126 138
127 /** 139 /**
128 * Default factory for AdapterInputConnection classes. 140 * Default factory for AdapterInputConnection classes.
129 */ 141 */
130 static class AdapterInputConnectionFactory { 142 static class AdapterInputConnectionFactory {
131 AdapterInputConnection get(View view, ImeAdapter imeAdapter, int initial SelStart, 143 AdapterInputConnection get(View view, ImeAdapter imeAdapter, int initial SelStart,
132 int initialSelEnd, EditorInfo outAttrs) { 144 int initialSelEnd, EditorInfo outAttrs) {
133 return new AdapterInputConnection( 145 return new AdapterInputConnection(
134 view, imeAdapter, initialSelStart, initialSelEnd, outAttrs); 146 view, imeAdapter, initialSelStart, initialSelEnd, outAttrs);
(...skipping 20 matching lines...) Expand all
155 if (!isTextInputType(mTextInputType)) { 167 if (!isTextInputType(mTextInputType)) {
156 // Although onCheckIsTextEditor will return false in this case, the EditorInfo 168 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
157 // is still used by the InputMethodService. Need to make sure the IM E doesn't 169 // is still used by the InputMethodService. Need to make sure the IM E doesn't
158 // enter fullscreen mode. 170 // enter fullscreen mode.
159 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; 171 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
160 } 172 }
161 int initialSelStart = Selection.getSelectionStart(mEditable); 173 int initialSelStart = Selection.getSelectionStart(mEditable);
162 int initialSelEnd = outAttrs.initialSelEnd = Selection.getSelectionEnd(m Editable); 174 int initialSelEnd = outAttrs.initialSelEnd = Selection.getSelectionEnd(m Editable);
163 mInputConnection = mInputConnectionFactory.get( 175 mInputConnection = mInputConnectionFactory.get(
164 mViewEmbedder.getAttachedView(), this, initialSelStart, initialS elEnd, outAttrs); 176 mViewEmbedder.getAttachedView(), this, initialSelStart, initialS elEnd, outAttrs);
177 if (mCursorAnchorInfoController != null) {
178 mCursorAnchorInfoController.resetMonitoringState();
aelias_OOO_until_Jul13 2016/02/10 08:23:42 Anything else needs to be reset here? How about m
kinaba 2016/02/19 12:28:29 Done (mHasPendingImmediateRequest will also be res
179 }
165 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection"); 180 if (DEBUG_LOGS) Log.w(TAG, "onCreateInputConnection");
166 return mInputConnection; 181 return mInputConnection;
167 } 182 }
168 183
169 /** 184 /**
170 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call s to 185 * Overrides the InputMethodManagerWrapper that ImeAdapter uses to make call s to
171 * InputMethodManager. 186 * InputMethodManager.
172 * @param immw InputMethodManagerWrapper that should be used to call InputMe thodManager. 187 * @param immw InputMethodManagerWrapper that should be used to call InputMe thodManager.
173 */ 188 */
174 @VisibleForTesting 189 @VisibleForTesting
175 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper im mw) { 190 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper im mw) {
176 mInputMethodManagerWrapper = immw; 191 mInputMethodManagerWrapper = immw;
192 if (mCursorAnchorInfoController != null) {
193 mCursorAnchorInfoController.setInputMethodManagerWrapper(immw);
194 }
177 } 195 }
178 196
179 @VisibleForTesting 197 @VisibleForTesting
180 void setInputConnectionFactory(AdapterInputConnectionFactory factory) { 198 void setInputConnectionFactory(AdapterInputConnectionFactory factory) {
181 mInputConnectionFactory = factory; 199 mInputConnectionFactory = factory;
182 } 200 }
183 201
184 /** 202 /**
185 * Set the current active InputConnection when a new InputConnection is cons tructed. 203 * Set the current active InputConnection when a new InputConnection is cons tructed.
186 * @param inputConnection The input connection that is currently used with I ME. 204 * @param inputConnection The input connection that is currently used with I ME.
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
555 */ 573 */
556 boolean setComposingRegion(CharSequence text, int start, int end) { 574 boolean setComposingRegion(CharSequence text, int start, int end) {
557 if (mNativeImeAdapterAndroid == 0) return false; 575 if (mNativeImeAdapterAndroid == 0) return false;
558 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end); 576 nativeSetComposingRegion(mNativeImeAdapterAndroid, start, end);
559 return true; 577 return true;
560 } 578 }
561 579
562 @CalledByNative 580 @CalledByNative
563 private void focusedNodeChanged(boolean isEditable) { 581 private void focusedNodeChanged(boolean isEditable) {
564 if (DEBUG_LOGS) Log.w(TAG, "focusedNodeChanged: isEditable [%b]", isEdit able); 582 if (DEBUG_LOGS) Log.w(TAG, "focusedNodeChanged: isEditable [%b]", isEdit able);
583
584 // Update controller before the connection is restarted.
585 if (mCursorAnchorInfoController != null) {
586 mCursorAnchorInfoController.focusedNodeChanged(isEditable);
587 }
588
565 if (mTextInputType != TextInputType.NONE && mInputConnection != null && isEditable) { 589 if (mTextInputType != TextInputType.NONE && mInputConnection != null && isEditable) {
566 restartInput(); 590 restartInput();
567 } 591 }
568 } 592 }
569 593
594 public boolean onRequestCursorUpdates(int cursorUpdateMode) {
595 if (mCursorAnchorInfoController == null) return false;
596 return mCursorAnchorInfoController.onRequestCursorUpdates(cursorUpdateMo de,
597 mViewEmbedder.getAttachedView());
598 }
599
600 /**
601 * Update the parameters that are to be passed to the IME through
602 * {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)} .
603 * @param text Text in the focused text field.
604 * @param selectionStart Index where the text selection starts. {@code -1} i f there is no
605 * selection.
606 * @param selectionEnd Index where the text selection ends. {@code -1} if th ere is no
607 * selection.
608 * @param compositionStart Index where the text composition starts. {@code - 1} if there is no
609 * selection.
610 * @param compositionEnd Index where the text composition ends. {@code -1} i f there is no
611 * selection.
612 */
613 public void updateTextAndSelection(String text, int selectionStart, int sele ctionEnd,
614 int compositionStart, int compositionEnd) {
615 if (mCursorAnchorInfoController == null) return;
616 mCursorAnchorInfoController.updateTextAndSelection(text, compositionStar t, compositionEnd,
617 selectionStart, selectionEnd);
618 }
619
620 /**
621 * Notify the location of composing characters to the IME if it explicitly r equested them.
622 * @param renderCoordinates coordinate information to convert CSS (document) coordinates to
623 * View-local Physical (screen) coordinates
624 * @param hasInsertionMarker Whether the insertion marker is visible or not.
625 * @param insertionMarkerHorizontal X coordinates (in view-local DIP pixels) of the insertion
626 * marker if it exists. Will be ignored otherwise.
627 * @param insertionMarkerTop Y coordinates (in view-local DIP pixels) of the top of the
628 * insertion marker if it exists. Will be ignored otherwise.
629 * @param insertionMarkerBottom Y coordinates (in view-local DIP pixels) of the bottom of
630 * the insertion marker if it exists. Will be ignored otherwise.
631 */
632 public void onUpdateFrameInfo(RenderCoordinates renderCoordinates, boolean h asInsertionMarker,
633 boolean isInsertionMarkerVisible, float insertionMarkerHorizontal,
634 float insertionMarkerTop, float insertionMarkerBottom) {
635 if (mCursorAnchorInfoController == null) return;
636 mCursorAnchorInfoController.onUpdateFrameInfo(renderCoordinates, hasInse rtionMarker,
637 isInsertionMarkerVisible, insertionMarkerHorizontal, insertionMa rkerTop,
638 insertionMarkerBottom, mViewEmbedder.getAttachedView());
639 }
640
570 @CalledByNative 641 @CalledByNative
571 private void populateUnderlinesFromSpans(CharSequence text, long underlines) { 642 private void populateUnderlinesFromSpans(CharSequence text, long underlines) {
572 if (DEBUG_LOGS) { 643 if (DEBUG_LOGS) {
573 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines); 644 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines);
574 } 645 }
575 if (!(text instanceof SpannableString)) return; 646 if (!(text instanceof SpannableString)) return;
576 647
577 SpannableString spannableString = ((SpannableString) text); 648 SpannableString spannableString = ((SpannableString) text);
578 CharacterStyle spans[] = 649 CharacterStyle spans[] =
579 spannableString.getSpans(0, text.length(), CharacterStyle.class) ; 650 spannableString.getSpans(0, text.length(), CharacterStyle.class) ;
580 for (CharacterStyle span : spans) { 651 for (CharacterStyle span : spans) {
581 if (span instanceof BackgroundColorSpan) { 652 if (span instanceof BackgroundColorSpan) {
582 nativeAppendBackgroundColorSpan(underlines, spannableString.getS panStart(span), 653 nativeAppendBackgroundColorSpan(underlines, spannableString.getS panStart(span),
583 spannableString.getSpanEnd(span), 654 spannableString.getSpanEnd(span),
584 ((BackgroundColorSpan) span).getBackgroundColor()); 655 ((BackgroundColorSpan) span).getBackgroundColor());
585 } else if (span instanceof UnderlineSpan) { 656 } else if (span instanceof UnderlineSpan) {
586 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), 657 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span),
587 spannableString.getSpanEnd(span)); 658 spannableString.getSpanEnd(span));
588 } 659 }
589 } 660 }
590 } 661 }
591 662
592 @CalledByNative 663 @CalledByNative
593 private void cancelComposition() { 664 private void cancelComposition() {
594 if (DEBUG_LOGS) Log.w(TAG, "cancelComposition"); 665 if (DEBUG_LOGS) Log.w(TAG, "cancelComposition");
595 if (mInputConnection != null) restartInput(); 666 if (mInputConnection != null) restartInput();
596 } 667 }
597 668
598 @CalledByNative 669 @CalledByNative
670 private void setCharacterBounds(float[] characterBounds) {
671 if (mCursorAnchorInfoController == null) return;
672 mCursorAnchorInfoController.setCompositionCharacterBounds(characterBound s);
673 }
674
675 @CalledByNative
599 private void detach() { 676 private void detach() {
600 if (DEBUG_LOGS) Log.w(TAG, "detach"); 677 if (DEBUG_LOGS) Log.w(TAG, "detach");
601 mNativeImeAdapterAndroid = 0; 678 mNativeImeAdapterAndroid = 0;
679 mTextInputType = 0;
680 if (mCursorAnchorInfoController != null) {
681 mCursorAnchorInfoController.focusedNodeChanged(false);
682 }
602 } 683 }
603 684
604 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid, 685 private native boolean nativeSendSyntheticKeyEvent(long nativeImeAdapterAndr oid,
605 int eventType, long timestampMs, int keyCode, int modifiers, int uni codeChar); 686 int eventType, long timestampMs, int keyCode, int modifiers, int uni codeChar);
606 687
607 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event, 688 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event,
608 int action, int modifiers, long timestampMs, int keyCode, int scanCo de, 689 int action, int modifiers, long timestampMs, int keyCode, int scanCo de,
609 boolean isSystemKey, int unicodeChar); 690 boolean isSystemKey, int unicodeChar);
610 691
611 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end); 692 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end);
(...skipping 13 matching lines...) Expand all
625 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid, 706 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid,
626 int start, int end); 707 int start, int end);
627 708
628 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end); 709 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end);
629 710
630 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , 711 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid ,
631 int before, int after); 712 int before, int after);
632 713
633 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); 714 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid);
634 } 715 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698