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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/omnibox/view/AutocompleteEditText.java

Issue 2885973002: Refactor autocomplete-specific logic into a separate class (Closed)
Patch Set: fix junit Created 3 years, 7 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
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.omnibox.view;
Ted C 2017/05/17 18:29:33 I wouldn't introduce a new package yet. The omnib
Changwan Ryu 2017/05/17 20:18:57 Done.
6
7 import android.content.Context;
8 import android.graphics.Rect;
9 import android.os.StrictMode;
10 import android.text.Editable;
11 import android.text.Selection;
12 import android.text.Spanned;
13 import android.text.TextUtils;
14 import android.util.AttributeSet;
15 import android.view.accessibility.AccessibilityEvent;
16 import android.view.accessibility.AccessibilityManager;
17 import android.view.accessibility.AccessibilityNodeInfo;
18 import android.view.inputmethod.BaseInputConnection;
19 import android.view.inputmethod.EditorInfo;
20 import android.view.inputmethod.InputConnection;
21 import android.view.inputmethod.InputConnectionWrapper;
22 import android.widget.EditText;
23
24 import org.chromium.base.Log;
25 import org.chromium.base.VisibleForTesting;
26 import org.chromium.chrome.browser.widget.VerticallyFixedEditText;
27
28 /**
29 * An {@link EditText} that shows autocomplete text at the end.
30 */
31 public class AutocompleteEditText extends VerticallyFixedEditText {
32 private static final String TAG = "cr_AutocompleteEdit";
33
34 private static final boolean DEBUG = false;
Ted C 2017/05/17 18:29:33 should we make this protected and have UrlBar key
Changwan Ryu 2017/05/17 20:18:57 At least for me, I prefer to keep DEBUG value sepa
35
36 private final AutocompleteSpan mAutocompleteSpan;
37 private final AccessibilityManager mAccessibilityManager;
38
39 /**
40 * Whether default TextView scrolling should be disabled because autocomplet e has been added.
41 * This allows the user entered text to be shown instead of the end of the a utocomplete.
42 */
43 private boolean mDisableTextScrollingFromAutocomplete;
44
45 private boolean mInBatchEditMode;
46 private int mBeforeBatchEditAutocompleteIndex = -1;
47 private String mBeforeBatchEditFullText;
48 private boolean mSelectionChangedInBatchMode;
49 private boolean mTextDeletedInBatchMode;
50
51 private boolean mIsPastedText;
52
53 // Set to true when the text is modified programmatically. Initially set to true until the old
54 // state has been loaded.
55 private boolean mIgnoreTextChangeFromAutocomplete = true;
56 private boolean mLastEditWasDelete;
57
58 public AutocompleteEditText(Context context, AttributeSet attrs) {
59 super(context, attrs);
60 mAutocompleteSpan = new AutocompleteSpan();
61 mAccessibilityManager =
62 (AccessibilityManager) context.getSystemService(Context.ACCESSIB ILITY_SERVICE);
63 }
64
65 /**
66 * Sets whether text changes should trigger autocomplete.
67 *
68 * @param ignoreAutocomplete Whether text changes should be ignored and no a uto complete
69 * triggered.
70 */
71 public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete) {
72 if (DEBUG) Log.i(TAG, "setIgnoreTextChangesForAutocomplete: " + ignoreAu tocomplete);
73 mIgnoreTextChangeFromAutocomplete = ignoreAutocomplete;
74 }
75
76 /** @return Text that includes autocomplete. */
77 public String getTextWithAutocomplete() {
78 return getEditableText() != null ? getEditableText().toString() : "";
79 }
80
81 /**
82 * @return Whether the current cursor position is at the end of the user typ ed text (i.e.
83 * at the beginning of the inline autocomplete text if present other wise the very
84 * end of the current text).
85 */
86 private boolean isCursorAtEndOfTypedText() {
87 final int selectionStart = getSelectionStart();
88 final int selectionEnd = getSelectionEnd();
89
90 int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan);
91 int expectedSelectionEnd = getText().length();
92 if (expectedSelectionStart < 0) {
93 expectedSelectionStart = expectedSelectionEnd;
94 }
95
96 return selectionStart == expectedSelectionStart && selectionEnd == expec tedSelectionEnd;
97 }
98
99 /**
100 * @return Whether the URL is currently in batch edit mode triggered by an I ME. No external
101 * text changes should be triggered while this is true.
102 */
103 // isInBatchEditMode is a package protected method on TextView, so we intent ionally chose
104 // a different name.
105 private boolean isHandlingBatchInput() {
106 return mInBatchEditMode;
107 }
108
109 /**
110 * @return The user text without the autocomplete text.
111 */
112 public String getTextWithoutAutocomplete() {
113 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan);
114 if (autoCompleteIndex < 0) {
115 return getTextWithAutocomplete();
116 } else {
117 return getTextWithAutocomplete().substring(0, autoCompleteIndex);
118 }
119 }
120
121 /** @return Whether any autocomplete information is specified on the current text. */
122 @VisibleForTesting
123 public boolean hasAutocomplete() {
124 return getText().getSpanStart(mAutocompleteSpan) >= 0
125 || mAutocompleteSpan.mAutocompleteText != null
126 || mAutocompleteSpan.mUserText != null;
127 }
128
129 /**
130 * Whether we want to be showing inline autocomplete results. We don't want to show them as the
131 * user deletes input. Also if there is a composition (e.g. while using the Japanese IME),
132 * we must not autocomplete or we'll destroy the composition.
133 * @return Whether we want to be showing inline autocomplete results.
134 */
135 public boolean shouldAutocomplete() {
136 if (mLastEditWasDelete) return false;
137 Editable text = getText();
138
139 return isCursorAtEndOfTypedText() && !isPastedText() && !isHandlingBatch Input()
140 && BaseInputConnection.getComposingSpanEnd(text)
141 == BaseInputConnection.getComposingSpanStart(text);
142 }
143
144 @Override
145 public void onBeginBatchEdit() {
146 if (DEBUG) Log.i(TAG, "onBeginBatchEdit");
147 mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAutocomplete Span);
148 mBeforeBatchEditFullText = getText().toString();
149
150 super.onBeginBatchEdit();
151 mInBatchEditMode = true;
152 mTextDeletedInBatchMode = false;
153 }
154
155 @Override
156 public void onEndBatchEdit() {
157 if (DEBUG) Log.i(TAG, "onEndBatchEdit");
158 super.onEndBatchEdit();
159 mInBatchEditMode = false;
160 if (mSelectionChangedInBatchMode) {
161 validateSelection(getSelectionStart(), getSelectionEnd());
162 mSelectionChangedInBatchMode = false;
163 }
164
165 String newText = getText().toString();
166 if (!TextUtils.equals(mBeforeBatchEditFullText, newText)
167 || getText().getSpanStart(mAutocompleteSpan) != mBeforeBatchEdit AutocompleteIndex) {
168 // If the text being typed is a single character that matches the ne xt character in the
169 // previously visible autocomplete text, we reapply the autocomplete text to prevent
170 // a visual flickering when the autocomplete text is cleared and the n quickly reapplied
171 // when the next round of suggestions is received.
172 if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1
173 && mBeforeBatchEditFullText != null
174 && mBeforeBatchEditFullText.startsWith(newText) && !mTextDel etedInBatchMode
175 && newText.length() - mBeforeBatchEditAutocompleteIndex == 1 ) {
176 setAutocompleteText(newText, mBeforeBatchEditFullText.substring( newText.length()));
177 }
178 notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode, true);
179 }
180
181 mTextDeletedInBatchMode = false;
182 mBeforeBatchEditAutocompleteIndex = -1;
183 mBeforeBatchEditFullText = null;
184 }
185
186 @Override
187 protected void onSelectionChanged(int selStart, int selEnd) {
188 if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd);
189 if (!mInBatchEditMode) {
190 int beforeTextLength = getText().length();
191 if (validateSelection(selStart, selEnd)) {
192 boolean textDeleted = getText().length() < beforeTextLength;
193 notifyAutocompleteTextStateChanged(textDeleted, false);
194 }
195 } else {
196 mSelectionChangedInBatchMode = true;
197 }
198 super.onSelectionChanged(selStart, selEnd);
199 }
200
201 /**
202 * Validates the selection and clears the autocomplete span if needed. The autocomplete text
203 * will be deleted if the selection occurs entirely before the autocomplete region.
204 *
205 * @param selStart The start of the selection.
206 * @param selEnd The end of the selection.
207 * @return Whether the autocomplete span was removed as a result of this val idation.
208 */
209 private boolean validateSelection(int selStart, int selEnd) {
210 int spanStart = getText().getSpanStart(mAutocompleteSpan);
211 int spanEnd = getText().getSpanEnd(mAutocompleteSpan);
212
213 if (DEBUG) {
214 Log.i(TAG, "validateSelection -- selStart: %d, selEnd: %d, spanStart : %d, spanEnd: %d",
215 selStart, selEnd, spanStart, spanEnd);
216 }
217
218 if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) {
219 CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompl eteText;
220
221 // On selection changes, the autocomplete text has been accepted by the user or needs
222 // to be deleted below.
223 mAutocompleteSpan.clearSpan();
224
225 // The autocomplete text will be deleted any time the selection occu rs entirely before
226 // the start of the autocomplete text. This is required because cer tain keyboards will
227 // insert characters temporarily when starting a key entry gesture ( whether it be
228 // swyping a word or long pressing to get a special character). Whe n this temporary
229 // character appears, Chrome may decide to append some autocomplete, but the keyboard
230 // will then remove this temporary character only while leaving the autocomplete text
231 // alone. See crbug/273763 for more details.
232 if (selEnd <= spanStart
233 && TextUtils.equals(previousAutocompleteText,
234 getText().subSequence(spanStart, getText().length ()))) {
235 getText().delete(spanStart, getText().length());
236 }
237 return true;
238 }
239 return false;
240 }
241
242 @Override
243 protected void onFocusChanged(boolean focused, int direction, Rect previousl yFocusedRect) {
244 if (!focused) mAutocompleteSpan.clearSpan();
245 super.onFocusChanged(focused, direction, previouslyFocusedRect);
246 }
247
248 @Override
249 public boolean bringPointIntoView(int offset) {
250 if (mDisableTextScrollingFromAutocomplete) return false;
251 return super.bringPointIntoView(offset);
252 }
253
254 @Override
255 public boolean onPreDraw() {
256 boolean retVal = super.onPreDraw();
257 if (mDisableTextScrollingFromAutocomplete) {
258 // super.onPreDraw will put the selection at the end of the text sel ection, but
259 // in the case of autocomplete we want the last typed character to b e shown, which
260 // is the start of selection.
261 mDisableTextScrollingFromAutocomplete = false;
262 bringPointIntoView(getSelectionStart());
263 retVal = true;
264 }
265 return retVal;
266 }
267
268 /** Call this when text is pasted. */
269 public void onPaste() {
270 mIsPastedText = true;
271 }
272
273 /**
274 * Autocompletes the text on the url bar and selects the text that was not e ntered by the
275 * user. Using append() instead of setText() to preserve the soft-keyboard l ayout.
276 * @param userText user The text entered by the user.
277 * @param inlineAutocompleteText The suggested autocompletion for the user's text.
278 */
279 public void setAutocompleteText(CharSequence userText, CharSequence inlineAu tocompleteText) {
280 if (DEBUG) {
281 Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteT ext: %s", userText,
282 inlineAutocompleteText);
283 }
284 boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText);
285
286 if (!emptyAutocomplete) mDisableTextScrollingFromAutocomplete = true;
287
288 int autocompleteIndex = userText.length();
289
290 String previousText = getTextWithAutocomplete();
291 CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText );
292
293 setIgnoreTextChangesForAutocomplete(true);
294
295 if (!TextUtils.equals(previousText, newText)) {
296 // The previous text may also have included autocomplete text, so we only
297 // append the new autocomplete text that has changed.
298 if (TextUtils.indexOf(newText, previousText) == 0) {
299 append(newText.subSequence(previousText.length(), newText.length ()));
300 } else {
301 setTextFromAutocomplete(newText.toString());
302 }
303 }
304
305 if (getSelectionStart() != autocompleteIndex || getSelectionEnd() != get Text().length()) {
306 setSelection(autocompleteIndex, getText().length());
307
308 if (inlineAutocompleteText.length() != 0) {
309 // Sending a TYPE_VIEW_TEXT_SELECTION_CHANGED accessibility even t causes the
310 // previous TYPE_VIEW_TEXT_CHANGED event to be swallowed. As a r esult the user
311 // hears the autocomplete text but *not* the text they typed. In stead we send a
312 // TYPE_ANNOUNCEMENT event, which doesn't swallow the text-chang ed event.
313 announceForAccessibility(inlineAutocompleteText);
314 }
315 }
316
317 if (emptyAutocomplete) {
318 mAutocompleteSpan.clearSpan();
319 } else {
320 mAutocompleteSpan.setSpan(userText, inlineAutocompleteText);
321 }
322
323 setIgnoreTextChangesForAutocomplete(false);
324 }
325
326 /**
327 * Returns the length of the autocomplete text currently displayed, zero if none is
328 * currently displayed.
329 */
330 public int getAutocompleteLength() {
331 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan);
332 if (autoCompleteIndex < 0) return 0;
333 return getText().length() - autoCompleteIndex;
334 }
335
336 @Override
337 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
338 if (DEBUG) {
339 Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d, lengthAfter: %d",
340 text, start, lengthBefore, lengthAfter);
341 }
342
343 super.onTextChanged(text, start, lengthBefore, lengthAfter);
344 boolean textDeleted = lengthAfter == 0;
345 if (!mInBatchEditMode) {
346 notifyAutocompleteTextStateChanged(textDeleted, true);
347 } else {
348 mTextDeletedInBatchMode = textDeleted;
349 }
350 mIsPastedText = false;
351 }
352
353 @Override
354 public void setText(CharSequence text, BufferType type) {
355 if (DEBUG) Log.i(TAG, "setText -- text: %s", text);
356
357 mDisableTextScrollingFromAutocomplete = false;
358
359 // Avoid setting the same text to the URL bar as it will mess up the scr oll/cursor
360 // position.
361 // Setting the text is also quite expensive, so only do it when the text has changed
362 // (since we apply spans when the URL is not focused, we only optimize t his when the
363 // URL is being edited).
364 if (!TextUtils.equals(getEditableText(), text)) {
365 // Certain OEM implementations of setText trigger disk reads. crbug. com/633298
366 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads( );
367 try {
368 super.setText(text, type);
369 } finally {
370 StrictMode.setThreadPolicy(oldPolicy);
371 }
372 }
373
374 // Verify the autocomplete is still valid after the text change.
375 // Note: mAutocompleteSpan may be still null here if setText() is called in View
376 // constructor.
377 if (mAutocompleteSpan != null && mAutocompleteSpan.mUserText != null
378 && mAutocompleteSpan.mAutocompleteText != null) {
379 if (getText().getSpanStart(mAutocompleteSpan) < 0) {
380 mAutocompleteSpan.clearSpan();
381 } else {
382 clearAutocompleteSpanIfInvalid();
383 }
384 }
385 }
386
387 private void clearAutocompleteSpanIfInvalid() {
388 Editable editableText = getEditableText();
389 CharSequence previousUserText = mAutocompleteSpan.mUserText;
390 CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteT ext;
391 if (editableText.length()
392 != (previousUserText.length() + previousAutocompleteText.length( ))) {
393 mAutocompleteSpan.clearSpan();
394 } else if (TextUtils.indexOf(getText(), previousUserText) != 0
395 || TextUtils.indexOf(getText(), previousAutocompleteText, previo usUserText.length())
396 != 0) {
397 mAutocompleteSpan.clearSpan();
398 }
399 }
400
401 @Override
402 public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
403 if (mIgnoreTextChangeFromAutocomplete) {
404 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECT ION_CHANGED
405 || event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT _CHANGED) {
406 return;
407 }
408 }
409 super.sendAccessibilityEventUnchecked(event);
410 }
411
412 @Override
413 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
414 // Certain OEM implementations of onInitializeAccessibilityNodeInfo trig ger disk reads
415 // to access the clipboard. crbug.com/640993
416 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
417 try {
418 super.onInitializeAccessibilityNodeInfo(info);
419 } finally {
420 StrictMode.setThreadPolicy(oldPolicy);
421 }
422 }
423
424 @VisibleForTesting
425 public InputConnectionWrapper getInputConnection() {
426 return mInputConnection;
427 }
428
429 private InputConnectionWrapper mInputConnection = new InputConnectionWrapper (null, true) {
430 private final char[] mTempSelectionChar = new char[1];
431
432 @Override
433 public boolean commitText(CharSequence text, int newCursorPosition) {
434 if (DEBUG) Log.i(TAG, "commitText: [%s]", text);
435 Editable currentText = getText();
436 if (currentText == null) return super.commitText(text, newCursorPosi tion);
437
438 int selectionStart = Selection.getSelectionStart(currentText);
439 int selectionEnd = Selection.getSelectionEnd(currentText);
440 int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan);
441 // If the text being committed is a single character that matches th e next character
442 // in the selection (assumed to be the autocomplete text), we only m ove the text
443 // selection instead clearing the autocomplete text causing flickeri ng as the
444 // autocomplete text will appear once the next suggestions are recei ved.
445 //
446 // To be confident that the selection is an autocomplete, we ensure the selection
447 // is at least one character and the end of the selection is the end of the
448 // currently entered text.
449 if (newCursorPosition == 1 && selectionStart > 0 && selectionStart ! = selectionEnd
450 && selectionEnd >= currentText.length() && autocompleteIndex == selectionStart
451 && text.length() == 1) {
452 currentText.getChars(selectionStart, selectionStart + 1, mTempSe lectionChar, 0);
453 if (mTempSelectionChar[0] == text.charAt(0)) {
454 // Since the text isn't changing, TalkBack won't read out th e typed characters.
455 // To work around this, explicitly send an accessibility eve nt. crbug.com/416595
456 if (mAccessibilityManager != null && mAccessibilityManager.i sEnabled()) {
457 AccessibilityEvent event = AccessibilityEvent.obtain(
458 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
459 event.setFromIndex(selectionStart);
460 event.setRemovedCount(0);
461 event.setAddedCount(1);
462 event.setBeforeText(currentText.toString().substring(0, selectionStart));
463 sendAccessibilityEventUnchecked(event);
464 }
465
466 setAutocompleteText(currentText.subSequence(0, selectionStar t + 1),
467 currentText.subSequence(selectionStart + 1, selectio nEnd));
468 if (!mInBatchEditMode) {
469 notifyAutocompleteTextStateChanged(false, false);
470 }
471 return true;
472 }
473 }
474
475 boolean retVal = super.commitText(text, newCursorPosition);
476
477 // Ensure the autocomplete span is removed if it is no longer valid after committing the
478 // text.
479 if (getText().getSpanStart(mAutocompleteSpan) >= 0) clearAutocomplet eSpanIfInvalid();
480
481 return retVal;
482 }
483
484 @Override
485 public boolean setComposingText(CharSequence text, int newCursorPosition ) {
486 if (DEBUG) Log.i(TAG, "setComposingText: [%s]", text);
487 Editable currentText = getText();
488 int autoCompleteSpanStart = currentText.getSpanStart(mAutocompleteSp an);
489 if (autoCompleteSpanStart >= 0) {
490 int composingEnd = BaseInputConnection.getComposingSpanEnd(curre ntText);
491
492 // On certain device/keyboard combinations, the composing region s are specified
493 // with a noticeable delay after the initial character is typed, and in certain
494 // circumstances it does not check that the current state of the text matches the
495 // expectations of it's composing region.
496 // For example, you can be typing:
497 // chrome://f
498 // Chrome will autocomplete to:
499 // chrome://f[lags]
500 // And after the autocomplete has been set, the keyboard will se t the composing
501 // region to the last character and it assumes it is 'f' as it w as the last
502 // character the keyboard sent. If we commit this composition, the text will
503 // look like:
504 // chrome://flag[f]
505 // And if we use the autocomplete clearing logic below, it will look like:
506 // chrome://f[f]
507 // To work around this, we see if the composition matches all th e characters prior
508 // to the autocomplete and just readjust the composing region to be that subset.
509 //
510 // See crbug.com/366732
511 if (composingEnd == currentText.length() && autoCompleteSpanStar t >= text.length()
512 && TextUtils.equals(
513 currentText.subSequence(autoCompleteSpanStart - text.length(),
514 autoCompleteSpanStart),
515 text)) {
516 setComposingRegion(
517 autoCompleteSpanStart - text.length(), autoCompleteS panStart);
518 }
519
520 // Once composing text is being modified, the autocomplete text has been accepted
521 // or has to be deleted.
522 mAutocompleteSpan.clearSpan();
523 Selection.setSelection(currentText, autoCompleteSpanStart);
524 currentText.delete(autoCompleteSpanStart, currentText.length());
525 }
526 return super.setComposingText(text, newCursorPosition);
527 }
528 };
529
530 @Override
531 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
532 if (DEBUG) Log.i(TAG, "onCreateInputConnection");
533 return createInputConnection(super.onCreateInputConnection(outAttrs));
534 }
535
536 @VisibleForTesting
537 public InputConnection createInputConnection(InputConnection target) {
538 mInputConnection.setTarget(target);
539 return mInputConnection;
540 }
541
542 /**
543 * @return Whether the current UrlBar input has been pasted from the clipboa rd.
544 */
545 public boolean isPastedText() {
546 return mIsPastedText;
Ted C 2017/05/17 18:29:33 Does this need to be in this class? Looks like it
Changwan Ryu 2017/05/17 20:18:57 Done. Note that I overrode shouldAutocomplete() to
547 }
548
549 private void notifyAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {
550 if (DEBUG) {
551 Log.i(TAG, "notifyAutocompleteTextStateChanged: DEL[%b] DIS[%b] IGN[ %b]", textDeleted,
552 updateDisplay, mIgnoreTextChangeFromAutocomplete);
553 }
554 if (mIgnoreTextChangeFromAutocomplete) return;
555 if (!hasFocus()) return;
556 mLastEditWasDelete = textDeleted;
557 onAutocompleteTextStateChanged(textDeleted, updateDisplay);
558 }
559
560 /**
561 * This is called when autocomplete replaces the whole text.
562 *
563 * @param text The text.
564 */
565 public void setTextFromAutocomplete(String text) {
Ted C 2017/05/17 18:29:33 seems like this should be protected. We really do
Changwan Ryu 2017/05/17 20:18:57 Done, thanks for the naming suggestion!
566 setText(text);
567 }
568
569 /**
570 * This is called when autocomplete text state changes.
571 * @param textDeleted True if text is just deleted.
572 * @param updateDisplay True if string is changed.
573 */
574 public void onAutocompleteTextStateChanged(boolean textDeleted, boolean upda teDisplay) {}
575
576 /**
577 * Simple span used for tracking the current autocomplete state.
578 */
579 private class AutocompleteSpan {
580 private CharSequence mUserText;
581 private CharSequence mAutocompleteText;
582
583 /**
584 * Adds the span to the current text.
585 * @param userText The user entered text.
586 * @param autocompleteText The autocomplete text being appended.
587 */
588 public void setSpan(CharSequence userText, CharSequence autocompleteText ) {
589 Editable text = getText();
590 text.removeSpan(this);
591 mAutocompleteText = autocompleteText;
592 mUserText = userText;
593 text.setSpan(this, userText.length(), text.length(), Spanned.SPAN_EX CLUSIVE_EXCLUSIVE);
594 }
595
596 /** Removes this span from the current text and clears the internal stat e. */
597 public void clearSpan() {
598 getText().removeSpan(this);
599 mAutocompleteText = null;
600 mUserText = null;
601 }
602 }
603 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698