OLD | NEW |
---|---|
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.text.Editable; | 7 import android.text.Editable; |
8 import android.text.InputType; | |
9 import android.text.Selection; | 8 import android.text.Selection; |
10 import android.util.StringBuilderPrinter; | |
11 import android.view.KeyCharacterMap; | 9 import android.view.KeyCharacterMap; |
12 import android.view.KeyEvent; | 10 import android.view.KeyEvent; |
13 import android.view.View; | 11 import android.view.View; |
14 import android.view.inputmethod.BaseInputConnection; | 12 import android.view.inputmethod.BaseInputConnection; |
15 import android.view.inputmethod.EditorInfo; | 13 import android.view.inputmethod.EditorInfo; |
16 import android.view.inputmethod.ExtractedText; | 14 import android.view.inputmethod.ExtractedText; |
17 import android.view.inputmethod.ExtractedTextRequest; | 15 import android.view.inputmethod.ExtractedTextRequest; |
18 | 16 |
19 import org.chromium.base.Log; | 17 import org.chromium.base.Log; |
18 import org.chromium.base.ThreadUtils; | |
20 import org.chromium.base.VisibleForTesting; | 19 import org.chromium.base.VisibleForTesting; |
21 import org.chromium.blink_public.web.WebTextInputFlags; | |
22 import org.chromium.ui.base.ime.TextInputType; | |
23 | |
24 import java.util.Locale; | |
25 | 20 |
26 /** | 21 /** |
27 * InputConnection is created by ContentView.onCreateInputConnection. | 22 * InputConnection is created by ContentView.onCreateInputConnection. |
28 * It then adapts android's IME to chrome's RenderWidgetHostView using the | 23 * It then adapts android's IME to chrome's RenderWidgetHostView using the |
29 * native ImeAdapterAndroid via the class ImeAdapter. | 24 * native ImeAdapterAndroid via the class ImeAdapter. |
30 */ | 25 */ |
31 public class AdapterInputConnection extends BaseInputConnection { | 26 public class AdapterInputConnection |
aelias_OOO_until_Jul13
2016/01/21 07:43:04
Could you rename this class and file to ReplicaInp
Changwan Ryu
2016/01/22 10:22:16
Done.
| |
27 extends BaseInputConnection implements ChromiumBaseInputConnection { | |
32 private static final String TAG = "cr_Ime"; | 28 private static final String TAG = "cr_Ime"; |
33 /** | 29 /** |
34 * Selection value should be -1 if not known. See EditorInfo.java for detail s. | 30 * Selection value should be -1 if not known. See EditorInfo.java for detail s. |
35 */ | 31 */ |
36 public static final int INVALID_SELECTION = -1; | 32 public static final int INVALID_SELECTION = -1; |
33 | |
37 public static final int INVALID_COMPOSITION = -1; | 34 public static final int INVALID_COMPOSITION = -1; |
38 | 35 |
39 private final ImeAdapter mImeAdapter; | 36 private final ImeAdapter mImeAdapter; |
40 | 37 |
38 // This holds the state of editable text (e.g. contents of <input>, contente ditable) of | |
39 // a focused element. | |
40 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new | |
41 // state must be reflected to this to keep consistency. | |
42 private final Editable mEditable; | |
43 | |
41 private boolean mSingleLine; | 44 private boolean mSingleLine; |
42 private int mNumNestedBatchEdits = 0; | 45 private int mNumNestedBatchEdits = 0; |
43 private int mPendingAccent; | 46 private int mPendingAccent; |
47 private final ThreadManager mThreadManager; | |
44 | 48 |
45 private int mLastUpdateSelectionStart = INVALID_SELECTION; | 49 private int mLastUpdateSelectionStart = INVALID_SELECTION; |
46 private int mLastUpdateSelectionEnd = INVALID_SELECTION; | 50 private int mLastUpdateSelectionEnd = INVALID_SELECTION; |
47 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; | 51 private int mLastUpdateCompositionStart = INVALID_COMPOSITION; |
48 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; | 52 private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; |
49 | 53 |
54 /** | |
55 * Default factory for AdapterInputConnection classes. | |
56 */ | |
57 static class Factory implements ChromiumBaseInputConnection.Factory { | |
58 // Note: we share Editable among input connections so that data remains the same on | |
59 // switching inputs. However, the downside is that initial value cannot be correct, and | |
60 // wrong value will be used when jumping from one input to another. | |
61 protected final Editable mEditable; | |
62 private final ThreadManager mThreadManager; | |
63 | |
64 Factory() { | |
65 mThreadManager = new ThreadManager(); | |
66 mEditable = Editable.Factory.getInstance().newEditable(""); | |
67 Selection.setSelection(mEditable, 0); | |
68 } | |
69 | |
70 @Override | |
71 public AdapterInputConnection get(View view, ImeAdapter imeAdapter, int inputType, | |
72 int inputFlags, EditorInfo outAttrs) { | |
73 return new AdapterInputConnection( | |
74 view, imeAdapter, mThreadManager, mEditable, inputType, inpu tFlags, outAttrs); | |
75 } | |
76 } | |
77 | |
78 static class ThreadManager implements ChromiumBaseInputConnection.ThreadMana ger { | |
79 @Override | |
80 public boolean runningOnThisThread() { | |
81 return ThreadUtils.runningOnUiThread(); | |
82 } | |
83 | |
84 @Override | |
85 public void post(Runnable runnable) { | |
86 ThreadUtils.postOnUiThread(runnable); | |
87 } | |
88 } | |
89 | |
50 @VisibleForTesting | 90 @VisibleForTesting |
51 AdapterInputConnection(View view, ImeAdapter imeAdapter, int initialSelStart , int initialSelEnd, | 91 AdapterInputConnection(View view, ImeAdapter imeAdapter, ThreadManager threa dManager, |
52 EditorInfo outAttrs) { | 92 Editable editable, int inputType, int inputFlags, EditorInfo outAttr s) { |
53 super(view, true); | 93 super(view, true); |
54 mImeAdapter = imeAdapter; | 94 mImeAdapter = imeAdapter; |
55 mImeAdapter.setInputConnection(this); | 95 mEditable = editable; |
96 mThreadManager = threadManager; | |
56 | 97 |
57 mSingleLine = true; | 98 int initialSelStart = Selection.getSelectionStart(editable); |
58 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | 99 int initialSelEnd = Selection.getSelectionEnd(editable); |
59 | EditorInfo.IME_FLAG_NO_EXTRACT_UI; | 100 ImeUtils.computeEditorInfo(inputType, inputFlags, initialSelStart, initi alSelEnd, outAttrs); |
60 outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT | |
61 | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; | |
62 | 101 |
63 int inputType = imeAdapter.getTextInputType(); | 102 mLastUpdateSelectionStart = initialSelStart; |
64 int inputFlags = imeAdapter.getTextInputFlags(); | 103 mLastUpdateSelectionEnd = initialSelEnd; |
65 if ((inputFlags & WebTextInputFlags.AutocompleteOff) != 0) { | |
66 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |
67 } | |
68 | 104 |
69 if (inputType == TextInputType.TEXT) { | 105 Log.d(TAG, "Constructor called with outAttrs: %s", ImeUtils.dumpEditorIn fo(outAttrs)); |
70 // Normal text field | |
71 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
72 if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) { | |
73 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; | |
74 } | |
75 } else if (inputType == TextInputType.TEXT_AREA | |
76 || inputType == TextInputType.CONTENT_EDITABLE) { | |
77 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; | |
78 if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) { | |
79 outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; | |
80 } | |
81 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE; | |
82 mSingleLine = false; | |
83 } else if (inputType == TextInputType.PASSWORD) { | |
84 // Password | |
85 outAttrs.inputType = InputType.TYPE_CLASS_TEXT | |
86 | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD; | |
87 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
88 } else if (inputType == TextInputType.SEARCH) { | |
89 // Search | |
90 outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH; | |
91 } else if (inputType == TextInputType.URL) { | |
92 // Url | |
93 outAttrs.inputType = InputType.TYPE_CLASS_TEXT | |
94 | InputType.TYPE_TEXT_VARIATION_URI; | |
95 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
96 } else if (inputType == TextInputType.EMAIL) { | |
97 // Email | |
98 outAttrs.inputType = InputType.TYPE_CLASS_TEXT | |
99 | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; | |
100 outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; | |
101 } else if (inputType == TextInputType.TELEPHONE) { | |
102 // Telephone | |
103 // Number and telephone do not have both a Tab key and an | |
104 // action in default OSK, so set the action to NEXT | |
105 outAttrs.inputType = InputType.TYPE_CLASS_PHONE; | |
106 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; | |
107 } else if (inputType == TextInputType.NUMBER) { | |
108 // Number | |
109 outAttrs.inputType = InputType.TYPE_CLASS_NUMBER | |
110 | InputType.TYPE_NUMBER_VARIATION_NORMAL | |
111 | InputType.TYPE_NUMBER_FLAG_DECIMAL; | |
112 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; | |
113 } | |
114 | |
115 // Handling of autocapitalize. Blink will send the flag taking into acco unt the element's | |
116 // type. This is not using AutocapitalizeNone because Android does not a utocapitalize by | |
117 // default and there is no way to express no capitalization. | |
118 // Autocapitalize is meant as a hint to the virtual keyboard. | |
119 if ((inputFlags & WebTextInputFlags.AutocapitalizeCharacters) != 0) { | |
120 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; | |
121 } else if ((inputFlags & WebTextInputFlags.AutocapitalizeWords) != 0) { | |
122 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; | |
123 } else if ((inputFlags & WebTextInputFlags.AutocapitalizeSentences) != 0 ) { | |
124 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; | |
125 } | |
126 // Content editable doesn't use autocapitalize so we need to set it manu ally. | |
127 if (inputType == TextInputType.CONTENT_EDITABLE) { | |
128 outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; | |
129 } | |
130 | |
131 outAttrs.initialSelStart = initialSelStart; | |
132 outAttrs.initialSelEnd = initialSelEnd; | |
133 mLastUpdateSelectionStart = outAttrs.initialSelStart; | |
134 mLastUpdateSelectionEnd = outAttrs.initialSelEnd; | |
135 Log.d(TAG, "Constructor called with outAttrs: %s", dumpEditorInfo(outAtt rs)); | |
136 } | |
137 | |
138 private static String dumpEditorInfo(EditorInfo editorInfo) { | |
139 StringBuilder builder = new StringBuilder(); | |
140 StringBuilderPrinter printer = new StringBuilderPrinter(builder); | |
141 editorInfo.dump(printer, ""); | |
142 return builder.toString(); | |
143 } | |
144 | |
145 private static String dumpEditable(Editable editable) { | |
146 return String.format(Locale.US, "Editable {[%s] SEL[%d %d] COM[%d %d]}", | |
147 editable.toString(), | |
148 Selection.getSelectionStart(editable), | |
149 Selection.getSelectionEnd(editable), | |
150 getComposingSpanStart(editable), | |
151 getComposingSpanEnd(editable)); | |
152 } | 106 } |
153 | 107 |
154 /** | 108 /** |
155 * Updates the AdapterInputConnection's internal representation of the text being edited and | 109 * Updates the AdapterInputConnection's internal representation of the text being edited and |
156 * its selection and composition properties. The resulting Editable is acces sible through the | 110 * its selection and composition properties. The resulting Editable is acces sible through the |
157 * getEditable() method. If the text has not changed, this also calls update Selection on the | 111 * getEditable() method. If the text has not changed, this also calls update Selection on the |
158 * InputMethodManager. | 112 * InputMethodManager. |
159 * | 113 * |
160 * @param text The String contents of the field being edited. | 114 * @param text The String contents of the field being edited. |
161 * @param selectionStart The character offset of the selection start, or the caret position if | 115 * @param selectionStart The character offset of the selection start, or the caret position if |
162 * there is no selection. | 116 * there is no selection. |
163 * @param selectionEnd The character offset of the selection end, or the car et position if there | 117 * @param selectionEnd The character offset of the selection end, or the car et position if there |
164 * is no selection. | 118 * is no selection. |
165 * @param compositionStart The character offset of the composition start, or -1 if there is no | 119 * @param compositionStart The character offset of the composition start, or -1 if there is no |
166 * composition. | 120 * composition. |
167 * @param compositionEnd The character offset of the composition end, or -1 if there is no | 121 * @param compositionEnd The character offset of the composition end, or -1 if there is no |
168 * selection. | 122 * selection. |
169 * @param isNonImeChange True when the update was caused by non-IME (e.g. Ja vascript). | 123 * @param isNonImeChange True when the update was caused by non-IME (e.g. Ja vascript). |
170 */ | 124 */ |
171 @VisibleForTesting | 125 @Override |
172 public void updateState(String text, int selectionStart, int selectionEnd, i nt compositionStart, | 126 public void updateStateOnUiThread(String text, int selectionStart, int selec tionEnd, |
173 int compositionEnd, boolean isNonImeChange) { | 127 int compositionStart, int compositionEnd, boolean singleLine, boolea n isNonImeChange) { |
174 Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b]", text, selectionStart , | 128 Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b] [%b]", text, selection Start, |
175 selectionEnd, compositionStart, compositionEnd, isNonImeChange); | 129 selectionEnd, compositionStart, compositionEnd, singleLine, isNo nImeChange); |
130 mSingleLine = singleLine; | |
131 | |
176 // If this update is from the IME, no further state modification is nece ssary because the | 132 // If this update is from the IME, no further state modification is nece ssary because the |
177 // state should have been updated already by the IM framework directly. | 133 // state should have been updated already by the IM framework directly. |
178 if (!isNonImeChange) return; | 134 if (!isNonImeChange) return; |
179 | 135 |
180 // Non-breaking spaces can cause the IME to get confused. Replace with n ormal spaces. | 136 // Non-breaking spaces can cause the IME to get confused. Replace with n ormal spaces. |
181 text = text.replace('\u00A0', ' '); | 137 text = text.replace('\u00A0', ' '); |
182 | 138 |
183 selectionStart = Math.min(selectionStart, text.length()); | 139 selectionStart = Math.min(selectionStart, text.length()); |
184 selectionEnd = Math.min(selectionEnd, text.length()); | 140 selectionEnd = Math.min(selectionEnd, text.length()); |
185 compositionStart = Math.min(compositionStart, text.length()); | 141 compositionStart = Math.min(compositionStart, text.length()); |
186 compositionEnd = Math.min(compositionEnd, text.length()); | 142 compositionEnd = Math.min(compositionEnd, text.length()); |
187 | 143 |
188 Editable editable = getEditableInternal(); | 144 String prevText = mEditable.toString(); |
189 | |
190 String prevText = editable.toString(); | |
191 boolean textUnchanged = prevText.equals(text); | 145 boolean textUnchanged = prevText.equals(text); |
192 | 146 |
193 if (!textUnchanged) { | 147 if (!textUnchanged) { |
194 editable.replace(0, editable.length(), text); | 148 mEditable.replace(0, mEditable.length(), text); |
195 } | 149 } |
196 | 150 |
197 Selection.setSelection(editable, selectionStart, selectionEnd); | 151 Selection.setSelection(mEditable, selectionStart, selectionEnd); |
198 | 152 |
199 if (compositionStart == compositionEnd) { | 153 if (compositionStart == compositionEnd) { |
200 removeComposingSpans(editable); | 154 removeComposingSpans(mEditable); |
201 } else { | 155 } else { |
202 super.setComposingRegion(compositionStart, compositionEnd); | 156 super.setComposingRegion(compositionStart, compositionEnd); |
203 } | 157 } |
204 updateSelectionIfRequired(); | 158 updateSelectionIfRequired(); |
205 } | 159 } |
206 | 160 |
207 /** | 161 /** |
208 * @see BaseInputConnection#getEditable() | 162 * @see BaseInputConnection#getEditable() |
209 */ | 163 */ |
210 @Override | 164 @Override |
211 public Editable getEditable() { | 165 public Editable getEditable() { |
212 Editable editable = getEditableInternal(); | 166 Log.d(TAG, "getEditable: %s", ImeUtils.dumpEditable(mEditable)); |
213 Log.d(TAG, "getEditable: %s", dumpEditable(editable)); | 167 return mEditable; |
214 return editable; | |
215 } | |
216 | |
217 private Editable getEditableInternal() { | |
218 return mImeAdapter.getEditable(); | |
219 } | 168 } |
220 | 169 |
221 /** | 170 /** |
222 * Sends selection update to the InputMethodManager unless we are currently in a batch edit or | 171 * Sends selection update to the InputMethodManager unless we are currently in a batch edit or |
223 * if the exact same selection and composition update was sent already. | 172 * if the exact same selection and composition update was sent already. |
224 */ | 173 */ |
225 private void updateSelectionIfRequired() { | 174 private void updateSelectionIfRequired() { |
226 if (mNumNestedBatchEdits != 0) return; | 175 if (mNumNestedBatchEdits != 0) return; |
227 Editable editable = getEditableInternal(); | 176 int selectionStart = Selection.getSelectionStart(mEditable); |
228 int selectionStart = Selection.getSelectionStart(editable); | 177 int selectionEnd = Selection.getSelectionEnd(mEditable); |
229 int selectionEnd = Selection.getSelectionEnd(editable); | 178 int compositionStart = getComposingSpanStart(mEditable); |
230 int compositionStart = getComposingSpanStart(editable); | 179 int compositionEnd = getComposingSpanEnd(mEditable); |
231 int compositionEnd = getComposingSpanEnd(editable); | |
232 // Avoid sending update if we sent an exact update already previously. | 180 // Avoid sending update if we sent an exact update already previously. |
233 if (mLastUpdateSelectionStart == selectionStart | 181 if (mLastUpdateSelectionStart == selectionStart |
234 && mLastUpdateSelectionEnd == selectionEnd | 182 && mLastUpdateSelectionEnd == selectionEnd |
235 && mLastUpdateCompositionStart == compositionStart | 183 && mLastUpdateCompositionStart == compositionStart |
236 && mLastUpdateCompositionEnd == compositionEnd) { | 184 && mLastUpdateCompositionEnd == compositionEnd) { |
237 return; | 185 return; |
238 } | 186 } |
239 Log.d(TAG, "updateSelectionIfRequired [%d %d] [%d %d]", selectionStart, selectionEnd, | 187 Log.d(TAG, "updateSelectionIfRequired [%d %d] [%d %d]", selectionStart, selectionEnd, |
240 compositionStart, compositionEnd); | 188 compositionStart, compositionEnd); |
241 // updateSelection should be called every time the selection or composit ion changes | 189 // updateSelection should be called every time the selection or composit ion changes |
242 // if it happens not within a batch edit, or at the end of each top leve l batch edit. | 190 // if it happens not within a batch edit, or at the end of each top leve l batch edit. |
243 mImeAdapter.updateSelection(selectionStart, selectionEnd, compositionSta rt, compositionEnd); | 191 mImeAdapter.updateSelection(selectionStart, selectionEnd, compositionSta rt, compositionEnd); |
244 mLastUpdateSelectionStart = selectionStart; | 192 mLastUpdateSelectionStart = selectionStart; |
245 mLastUpdateSelectionEnd = selectionEnd; | 193 mLastUpdateSelectionEnd = selectionEnd; |
246 mLastUpdateCompositionStart = compositionStart; | 194 mLastUpdateCompositionStart = compositionStart; |
247 mLastUpdateCompositionEnd = compositionEnd; | 195 mLastUpdateCompositionEnd = compositionEnd; |
248 } | 196 } |
249 | 197 |
250 /** | 198 /** |
251 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) | 199 * @see BaseInputConnection#setComposingText(java.lang.CharSequence, int) |
252 */ | 200 */ |
253 @Override | 201 @Override |
254 public boolean setComposingText(CharSequence text, int newCursorPosition) { | 202 public boolean setComposingText(CharSequence text, int newCursorPosition) { |
255 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); | 203 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); |
256 mPendingAccent = 0; | 204 mPendingAccent = 0; |
257 | 205 |
258 Editable editable = getEditableInternal(); | 206 int selectionStartAfterReplacement = BaseInputConnection.getComposingSpa nStart(mEditable); |
259 int selectionStartAfterReplacement = BaseInputConnection.getComposingSpa nStart(editable); | |
260 if (selectionStartAfterReplacement == INVALID_COMPOSITION) { | 207 if (selectionStartAfterReplacement == INVALID_COMPOSITION) { |
261 selectionStartAfterReplacement = Selection.getSelectionStart(editabl e); | 208 selectionStartAfterReplacement = Selection.getSelectionStart(mEditab le); |
262 } | 209 } |
263 int selectionEndAfterReplacement = selectionStartAfterReplacement + text .length(); | 210 int selectionEndAfterReplacement = selectionStartAfterReplacement + text .length(); |
264 | 211 |
265 super.setComposingText(text, newCursorPosition); | 212 super.setComposingText(text, newCursorPosition); |
266 | 213 |
267 // Due to an error in BaseInputConnection (b/21476564), the new cursor p osition | 214 // Due to an error in BaseInputConnection (b/21476564), the new cursor p osition |
268 // may not be correct when newCursorPosition != 1. We fix it here: | 215 // may not be correct when newCursorPosition != 1. We fix it here: |
269 if (newCursorPosition > 1) { | 216 if (newCursorPosition > 1) { |
270 int newPos = selectionEndAfterReplacement - 1 + newCursorPosition; | 217 int newPos = selectionEndAfterReplacement - 1 + newCursorPosition; |
271 int len = editable.length(); | 218 int len = mEditable.length(); |
272 if (newPos > len) newPos = len; | 219 if (newPos > len) newPos = len; |
273 Selection.setSelection(editable, newPos); | 220 Selection.setSelection(mEditable, newPos); |
274 } else if (newCursorPosition <= 0) { | 221 } else if (newCursorPosition <= 0) { |
275 int newPos = selectionStartAfterReplacement + newCursorPosition; | 222 int newPos = selectionStartAfterReplacement + newCursorPosition; |
276 if (newPos < 0) newPos = 0; | 223 if (newPos < 0) newPos = 0; |
277 Selection.setSelection(editable, newPos); | 224 Selection.setSelection(mEditable, newPos); |
278 } | 225 } |
279 | 226 |
280 updateSelectionIfRequired(); | 227 updateSelectionIfRequired(); |
281 return mImeAdapter.setComposingText(text, newCursorPosition); | 228 return mImeAdapter.setComposingText(text, newCursorPosition); |
282 } | 229 } |
283 | 230 |
284 /** | 231 /** |
285 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) | 232 * @see BaseInputConnection#commitText(java.lang.CharSequence, int) |
286 */ | 233 */ |
287 @Override | 234 @Override |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
320 return mImeAdapter.performContextMenuAction(id); | 267 return mImeAdapter.performContextMenuAction(id); |
321 } | 268 } |
322 | 269 |
323 /** | 270 /** |
324 * @see BaseInputConnection#getExtractedText(android.view.inputmethod.Extrac tedTextRequest, | 271 * @see BaseInputConnection#getExtractedText(android.view.inputmethod.Extrac tedTextRequest, |
325 * int) | 272 * int) |
326 */ | 273 */ |
327 @Override | 274 @Override |
328 public ExtractedText getExtractedText(ExtractedTextRequest request, int flag s) { | 275 public ExtractedText getExtractedText(ExtractedTextRequest request, int flag s) { |
329 Log.d(TAG, "getExtractedText"); | 276 Log.d(TAG, "getExtractedText"); |
330 Editable editable = getEditableInternal(); | |
331 ExtractedText et = new ExtractedText(); | 277 ExtractedText et = new ExtractedText(); |
332 et.text = editable.toString(); | 278 et.text = mEditable.toString(); |
333 et.partialEndOffset = editable.length(); | 279 et.partialEndOffset = mEditable.length(); |
334 et.selectionStart = Selection.getSelectionStart(editable); | 280 et.selectionStart = Selection.getSelectionStart(mEditable); |
335 et.selectionEnd = Selection.getSelectionEnd(editable); | 281 et.selectionEnd = Selection.getSelectionEnd(mEditable); |
336 et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0; | 282 et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0; |
337 return et; | 283 return et; |
338 } | 284 } |
339 | 285 |
340 /** | 286 /** |
341 * @see BaseInputConnection#beginBatchEdit() | 287 * @see BaseInputConnection#beginBatchEdit() |
342 */ | 288 */ |
343 @Override | 289 @Override |
344 public boolean beginBatchEdit() { | 290 public boolean beginBatchEdit() { |
345 Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0)); | 291 Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0)); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 } | 326 } |
381 | 327 |
382 private boolean deleteSurroundingTextImpl( | 328 private boolean deleteSurroundingTextImpl( |
383 int beforeLength, int afterLength, boolean fromPhysicalKey) { | 329 int beforeLength, int afterLength, boolean fromPhysicalKey) { |
384 Log.d(TAG, "deleteSurroundingText [%d %d %b]", beforeLength, afterLength , fromPhysicalKey); | 330 Log.d(TAG, "deleteSurroundingText [%d %d %b]", beforeLength, afterLength , fromPhysicalKey); |
385 | 331 |
386 if (mPendingAccent != 0) { | 332 if (mPendingAccent != 0) { |
387 finishComposingText(); | 333 finishComposingText(); |
388 } | 334 } |
389 | 335 |
390 Editable editable = getEditableInternal(); | 336 int selectionStart = Selection.getSelectionStart(mEditable); |
391 int selectionStart = Selection.getSelectionStart(editable); | 337 int selectionEnd = Selection.getSelectionEnd(mEditable); |
392 int selectionEnd = Selection.getSelectionEnd(editable); | |
393 int availableBefore = selectionStart; | 338 int availableBefore = selectionStart; |
394 int availableAfter = editable.length() - selectionEnd; | 339 int availableAfter = mEditable.length() - selectionEnd; |
395 beforeLength = Math.min(beforeLength, availableBefore); | 340 beforeLength = Math.min(beforeLength, availableBefore); |
396 afterLength = Math.min(afterLength, availableAfter); | 341 afterLength = Math.min(afterLength, availableAfter); |
397 | 342 |
398 // Adjust these values even before calling super.deleteSurroundingText() to be consistent | 343 // Adjust these values even before calling super.deleteSurroundingText() to be consistent |
399 // with the super class. | 344 // with the super class. |
400 if (isIndexBetweenUtf16SurrogatePair(editable, selectionStart - beforeLe ngth)) { | 345 if (isIndexBetweenUtf16SurrogatePair(mEditable, selectionStart - beforeL ength)) { |
401 beforeLength += 1; | 346 beforeLength += 1; |
402 } | 347 } |
403 if (isIndexBetweenUtf16SurrogatePair(editable, selectionEnd + afterLengt h)) { | 348 if (isIndexBetweenUtf16SurrogatePair(mEditable, selectionEnd + afterLeng th)) { |
404 afterLength += 1; | 349 afterLength += 1; |
405 } | 350 } |
406 | 351 |
407 super.deleteSurroundingText(beforeLength, afterLength); | 352 super.deleteSurroundingText(beforeLength, afterLength); |
408 updateSelectionIfRequired(); | 353 updateSelectionIfRequired(); |
409 | 354 |
410 // If this was called due to a physical key, no need to generate a key e vent here as | 355 // If this was called due to a physical key, no need to generate a key e vent here as |
411 // the caller will take care of forwarding the original. | 356 // the caller will take care of forwarding the original. |
412 if (fromPhysicalKey) { | 357 if (fromPhysicalKey) { |
413 return true; | 358 return true; |
414 } | 359 } |
415 | 360 |
416 return mImeAdapter.deleteSurroundingText(beforeLength, afterLength); | 361 return mImeAdapter.deleteSurroundingText(beforeLength, afterLength); |
417 } | 362 } |
418 | 363 |
419 /** | 364 /** |
365 * @see ChromiumBaseInputConnection#sendKeyEventOnUiThread(KeyEvent) | |
366 */ | |
367 @Override | |
368 public boolean sendKeyEventOnUiThread(KeyEvent event) { | |
369 return sendKeyEvent(event); | |
370 } | |
371 | |
372 /** | |
420 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) | 373 * @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) |
421 */ | 374 */ |
422 @Override | 375 @Override |
423 public boolean sendKeyEvent(KeyEvent event) { | 376 public boolean sendKeyEvent(KeyEvent event) { |
424 Log.d(TAG, "sendKeyEvent [%d] [%d] [%d]", event.getAction(), event.getKe yCode(), | 377 Log.d(TAG, "sendKeyEvent [%d] [%d] [%d]", event.getAction(), event.getKe yCode(), |
425 event.getUnicodeChar()); | 378 event.getUnicodeChar()); |
426 | 379 |
427 int action = event.getAction(); | 380 int action = event.getAction(); |
428 int keycode = event.getKeyCode(); | 381 int keycode = event.getKeyCode(); |
429 int unicodeChar = event.getUnicodeChar(); | 382 int unicodeChar = event.getUnicodeChar(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
473 return true; | 426 return true; |
474 } | 427 } |
475 | 428 |
476 /** | 429 /** |
477 * Update the Editable to reflect what Blink will do in response to the KeyD own for a | 430 * Update the Editable to reflect what Blink will do in response to the KeyD own for a |
478 * unicode-mapped key event. | 431 * unicode-mapped key event. |
479 * @param unicodeChar The Unicode character to update selection with. | 432 * @param unicodeChar The Unicode character to update selection with. |
480 */ | 433 */ |
481 private void replaceSelectionWithUnicodeChar(int unicodeChar) { | 434 private void replaceSelectionWithUnicodeChar(int unicodeChar) { |
482 if (unicodeChar == 0) return; | 435 if (unicodeChar == 0) return; |
483 Editable editable = getEditableInternal(); | 436 int selectionStart = Selection.getSelectionStart(mEditable); |
484 int selectionStart = Selection.getSelectionStart(editable); | 437 int selectionEnd = Selection.getSelectionEnd(mEditable); |
485 int selectionEnd = Selection.getSelectionEnd(editable); | |
486 if (selectionStart > selectionEnd) { | 438 if (selectionStart > selectionEnd) { |
487 int temp = selectionStart; | 439 int temp = selectionStart; |
488 selectionStart = selectionEnd; | 440 selectionStart = selectionEnd; |
489 selectionEnd = temp; | 441 selectionEnd = temp; |
490 } | 442 } |
491 editable.replace(selectionStart, selectionEnd, Character.toString((char) unicodeChar)); | 443 mEditable.replace(selectionStart, selectionEnd, Character.toString((char ) unicodeChar)); |
492 updateSelectionIfRequired(); | 444 updateSelectionIfRequired(); |
493 } | 445 } |
494 | 446 |
495 /** | 447 /** |
496 * @see BaseInputConnection#finishComposingText() | 448 * @see BaseInputConnection#finishComposingText() |
497 */ | 449 */ |
498 @Override | 450 @Override |
499 public boolean finishComposingText() { | 451 public boolean finishComposingText() { |
500 Log.d(TAG, "finishComposingText"); | 452 Log.d(TAG, "finishComposingText"); |
501 mPendingAccent = 0; | 453 mPendingAccent = 0; |
502 | 454 |
503 if (getComposingSpanStart(getEditableInternal()) | 455 if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) { |
504 == getComposingSpanEnd(getEditableInternal())) { | |
505 return true; | 456 return true; |
506 } | 457 } |
507 | 458 |
508 super.finishComposingText(); | 459 super.finishComposingText(); |
509 updateSelectionIfRequired(); | 460 updateSelectionIfRequired(); |
510 mImeAdapter.finishComposingText(); | 461 mImeAdapter.finishComposingText(); |
511 | 462 |
512 return true; | 463 return true; |
513 } | 464 } |
514 | 465 |
515 /** | 466 /** |
516 * @see BaseInputConnection#setSelection(int, int) | 467 * @see BaseInputConnection#setSelection(int, int) |
517 */ | 468 */ |
518 @Override | 469 @Override |
519 public boolean setSelection(int start, int end) { | 470 public boolean setSelection(int start, int end) { |
520 Log.d(TAG, "setSelection [%d %d]", start, end); | 471 Log.d(TAG, "setSelection [%d %d]", start, end); |
521 int textLength = getEditableInternal().length(); | 472 int textLength = mEditable.length(); |
522 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true; | 473 if (start < 0 || end < 0 || start > textLength || end > textLength) retu rn true; |
523 super.setSelection(start, end); | 474 super.setSelection(start, end); |
524 updateSelectionIfRequired(); | 475 updateSelectionIfRequired(); |
525 return mImeAdapter.setEditableSelectionOffsets(start, end); | 476 return mImeAdapter.setEditableSelectionOffsets(start, end); |
526 } | 477 } |
527 | 478 |
528 /** | 479 @Override |
529 * Call this when restartInput() is called. | 480 public void onRestartInputOnUiThread() { |
530 */ | 481 Log.d(TAG, "onRestartInputOnUiThread"); |
531 void onRestartInput() { | |
532 Log.d(TAG, "onRestartInput"); | |
533 mNumNestedBatchEdits = 0; | 482 mNumNestedBatchEdits = 0; |
534 mPendingAccent = 0; | 483 mPendingAccent = 0; |
535 } | 484 } |
536 | 485 |
537 /** | 486 /** |
538 * @see BaseInputConnection#setComposingRegion(int, int) | 487 * @see BaseInputConnection#setComposingRegion(int, int) |
539 */ | 488 */ |
540 @Override | 489 @Override |
541 public boolean setComposingRegion(int start, int end) { | 490 public boolean setComposingRegion(int start, int end) { |
542 Log.d(TAG, "setComposingRegion [%d %d]", start, end); | 491 Log.d(TAG, "setComposingRegion [%d %d]", start, end); |
543 Editable editable = getEditableInternal(); | 492 int textLength = mEditable.length(); |
544 int textLength = editable.length(); | |
545 int a = Math.min(start, end); | 493 int a = Math.min(start, end); |
546 int b = Math.max(start, end); | 494 int b = Math.max(start, end); |
547 if (a < 0) a = 0; | 495 if (a < 0) a = 0; |
548 if (b < 0) b = 0; | 496 if (b < 0) b = 0; |
549 if (a > textLength) a = textLength; | 497 if (a > textLength) a = textLength; |
550 if (b > textLength) b = textLength; | 498 if (b > textLength) b = textLength; |
551 | 499 |
552 if (a == b) { | 500 if (a == b) { |
553 removeComposingSpans(editable); | 501 removeComposingSpans(mEditable); |
554 } else { | 502 } else { |
555 super.setComposingRegion(a, b); | 503 super.setComposingRegion(a, b); |
556 } | 504 } |
557 updateSelectionIfRequired(); | 505 updateSelectionIfRequired(); |
506 return mImeAdapter.setComposingRegion(a, b); | |
507 } | |
558 | 508 |
559 CharSequence regionText = null; | 509 @Override |
560 if (b > a) { | 510 public void moveCursorToSelectionEndOnUiThread() { |
561 regionText = editable.subSequence(a, b); | 511 Log.d(TAG, "movecursorToEnd"); |
562 } | 512 int selectionEnd = Selection.getSelectionEnd(mEditable); |
563 return mImeAdapter.setComposingRegion(regionText, a, b); | 513 setSelection(selectionEnd, selectionEnd); |
514 } | |
515 | |
516 @Override | |
517 public ThreadManager getThreadManager() { | |
518 return mThreadManager; | |
564 } | 519 } |
565 | 520 |
566 @VisibleForTesting | 521 @VisibleForTesting |
567 static class ImeState { | 522 static class ImeState { |
568 public final String text; | 523 public final String text; |
569 public final int selectionStart; | 524 public final int selectionStart; |
570 public final int selectionEnd; | 525 public final int selectionEnd; |
571 public final int compositionStart; | 526 public final int compositionStart; |
572 public final int compositionEnd; | 527 public final int compositionEnd; |
573 | 528 |
574 public ImeState(String text, int selectionStart, int selectionEnd, | 529 public ImeState(String text, int selectionStart, int selectionEnd, |
575 int compositionStart, int compositionEnd) { | 530 int compositionStart, int compositionEnd) { |
576 this.text = text; | 531 this.text = text; |
577 this.selectionStart = selectionStart; | 532 this.selectionStart = selectionStart; |
578 this.selectionEnd = selectionEnd; | 533 this.selectionEnd = selectionEnd; |
579 this.compositionStart = compositionStart; | 534 this.compositionStart = compositionStart; |
580 this.compositionEnd = compositionEnd; | 535 this.compositionEnd = compositionEnd; |
581 } | 536 } |
582 } | 537 } |
583 | 538 |
584 @VisibleForTesting | 539 @VisibleForTesting |
585 ImeState getImeStateForTesting() { | 540 ImeState getImeStateForTesting() { |
586 Editable editable = getEditableInternal(); | 541 String text = mEditable.toString(); |
587 String text = editable.toString(); | 542 int selectionStart = Selection.getSelectionStart(mEditable); |
588 int selectionStart = Selection.getSelectionStart(editable); | 543 int selectionEnd = Selection.getSelectionEnd(mEditable); |
589 int selectionEnd = Selection.getSelectionEnd(editable); | 544 int compositionStart = getComposingSpanStart(mEditable); |
590 int compositionStart = getComposingSpanStart(editable); | 545 int compositionEnd = getComposingSpanEnd(mEditable); |
591 int compositionEnd = getComposingSpanEnd(editable); | |
592 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd); | 546 return new ImeState(text, selectionStart, selectionEnd, compositionStart , compositionEnd); |
593 } | 547 } |
594 } | 548 } |
OLD | NEW |