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