Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 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.chrome.browser.omnibox; | 5 package org.chromium.chrome.browser.omnibox; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.graphics.Rect; | 8 import android.graphics.Rect; |
| 9 import android.os.StrictMode; | 9 import android.os.StrictMode; |
| 10 import android.text.Editable; | 10 import android.text.Editable; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 | 36 |
| 37 private final AutocompleteSpan mAutocompleteSpan; | 37 private final AutocompleteSpan mAutocompleteSpan; |
| 38 private final AccessibilityManager mAccessibilityManager; | 38 private final AccessibilityManager mAccessibilityManager; |
| 39 | 39 |
| 40 /** | 40 /** |
| 41 * Whether default TextView scrolling should be disabled because autocomplet e has been added. | 41 * Whether default TextView scrolling should be disabled because autocomplet e has been added. |
| 42 * This allows the user entered text to be shown instead of the end of the a utocomplete. | 42 * This allows the user entered text to be shown instead of the end of the a utocomplete. |
| 43 */ | 43 */ |
| 44 private boolean mDisableTextScrollingFromAutocomplete; | 44 private boolean mDisableTextScrollingFromAutocomplete; |
| 45 | 45 |
| 46 private boolean mInBatchEditMode; | 46 private int mBatchEditNestCount; |
|
Ted C
2017/05/26 23:43:45
The thing I struggle with is that this moves more
Changwan Ryu
2017/06/05 19:07:39
Hmm.. I didn’t really think much about how to spli
| |
| 47 private int mBeforeBatchEditAutocompleteIndex = -1; | 47 private int mBeforeBatchEditAutocompleteIndex = -1; |
| 48 private String mBeforeBatchEditFullText; | 48 private String mBeforeBatchEditFullText; |
| 49 private boolean mSelectionChangedInBatchMode; | 49 private boolean mSelectionChangedInBatchMode; |
| 50 private boolean mTextDeletedInBatchMode; | 50 private boolean mTextDeletedInBatchMode; |
| 51 | 51 |
| 52 // Set to true when the text is modified programmatically. Initially set to true until the old | 52 // Set to true when the text is modified programmatically. Initially set to true until the old |
| 53 // state has been loaded. | 53 // state has been loaded. |
| 54 private boolean mIgnoreTextChangeFromAutocomplete = true; | 54 private boolean mIgnoreTextChangeFromAutocomplete = true; |
| 55 private boolean mLastEditWasDelete; | 55 private boolean mLastEditWasDelete; |
| 56 | 56 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan); | 91 int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan); |
| 92 int expectedSelectionEnd = getText().length(); | 92 int expectedSelectionEnd = getText().length(); |
| 93 if (expectedSelectionStart < 0) { | 93 if (expectedSelectionStart < 0) { |
| 94 expectedSelectionStart = expectedSelectionEnd; | 94 expectedSelectionStart = expectedSelectionEnd; |
| 95 } | 95 } |
| 96 | 96 |
| 97 return selectionStart == expectedSelectionStart && selectionEnd == expec tedSelectionEnd; | 97 return selectionStart == expectedSelectionStart && selectionEnd == expec tedSelectionEnd; |
| 98 } | 98 } |
| 99 | 99 |
| 100 /** | 100 /** |
| 101 * @return Whether the URL is currently in batch edit mode triggered by an I ME. No external | |
| 102 * text changes should be triggered while this is true. | |
| 103 */ | |
| 104 // isInBatchEditMode is a package protected method on TextView, so we intent ionally chose | |
| 105 // a different name. | |
| 106 private boolean isHandlingBatchInput() { | |
| 107 return mInBatchEditMode; | |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * @return The user text without the autocomplete text. | 101 * @return The user text without the autocomplete text. |
| 112 */ | 102 */ |
| 113 public String getTextWithoutAutocomplete() { | 103 public String getTextWithoutAutocomplete() { |
| 114 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan); | 104 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan); |
| 115 if (autoCompleteIndex < 0) { | 105 if (autoCompleteIndex < 0) { |
| 116 return getTextWithAutocomplete(); | 106 return getTextWithAutocomplete(); |
| 117 } else { | 107 } else { |
| 118 return getTextWithAutocomplete().substring(0, autoCompleteIndex); | 108 return getTextWithAutocomplete().substring(0, autoCompleteIndex); |
| 119 } | 109 } |
| 120 } | 110 } |
| 121 | 111 |
| 122 /** @return Whether any autocomplete information is specified on the current text. */ | 112 /** @return Whether any autocomplete information is specified on the current text. */ |
| 123 @VisibleForTesting | 113 @VisibleForTesting |
| 124 public boolean hasAutocomplete() { | 114 public boolean hasAutocomplete() { |
| 125 return getText().getSpanStart(mAutocompleteSpan) >= 0 | 115 return getText().getSpanStart(mAutocompleteSpan) >= 0 |
| 126 || mAutocompleteSpan.mAutocompleteText != null | 116 || mAutocompleteSpan.mAutocompleteText != null |
| 127 || mAutocompleteSpan.mUserText != null; | 117 || mAutocompleteSpan.mUserText != null; |
| 128 } | 118 } |
| 129 | 119 |
| 130 /** | 120 /** |
| 131 * Whether we want to be showing inline autocomplete results. We don't want to show them as the | 121 * Whether we want to be showing inline autocomplete results. We don't want to show them as the |
| 132 * user deletes input. Also if there is a composition (e.g. while using the Japanese IME), | 122 * user deletes input. Also if there is a composition (e.g. while using the Japanese IME), |
| 133 * we must not autocomplete or we'll destroy the composition. | 123 * we must not autocomplete or we'll destroy the composition. |
| 134 * @return Whether we want to be showing inline autocomplete results. | 124 * @return Whether we want to be showing inline autocomplete results. |
| 135 */ | 125 */ |
| 136 public boolean shouldAutocomplete() { | 126 public boolean shouldAutocomplete() { |
| 137 if (mLastEditWasDelete) return false; | 127 if (mLastEditWasDelete) return false; |
| 138 Editable text = getText(); | 128 Editable text = getText(); |
| 139 | 129 |
| 140 return isCursorAtEndOfTypedText() && !isHandlingBatchInput() | 130 return isCursorAtEndOfTypedText() && mBatchEditNestCount == 0 |
| 141 && BaseInputConnection.getComposingSpanEnd(text) | 131 && BaseInputConnection.getComposingSpanEnd(text) |
| 142 == BaseInputConnection.getComposingSpanStart(text); | 132 == BaseInputConnection.getComposingSpanStart(text); |
| 143 } | 133 } |
| 144 | 134 |
| 145 @Override | 135 private void onPostEndBatchEdit() { |
| 146 public void onBeginBatchEdit() { | |
| 147 if (DEBUG) Log.i(TAG, "onBeginBatchEdit"); | |
| 148 mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAutocomplete Span); | |
| 149 mBeforeBatchEditFullText = getText().toString(); | |
| 150 | |
| 151 super.onBeginBatchEdit(); | |
| 152 mInBatchEditMode = true; | |
| 153 mTextDeletedInBatchMode = false; | |
| 154 } | |
| 155 | |
| 156 @Override | |
| 157 public void onEndBatchEdit() { | |
| 158 if (DEBUG) Log.i(TAG, "onEndBatchEdit"); | |
| 159 super.onEndBatchEdit(); | |
| 160 mInBatchEditMode = false; | |
| 161 if (mSelectionChangedInBatchMode) { | 136 if (mSelectionChangedInBatchMode) { |
| 162 validateSelection(getSelectionStart(), getSelectionEnd()); | 137 validateSelection(getSelectionStart(), getSelectionEnd()); |
| 163 mSelectionChangedInBatchMode = false; | 138 mSelectionChangedInBatchMode = false; |
| 164 } | 139 } |
| 165 | 140 |
| 166 String newText = getText().toString(); | 141 String newText = getText().toString(); |
| 167 if (!TextUtils.equals(mBeforeBatchEditFullText, newText) | 142 if (!TextUtils.equals(mBeforeBatchEditFullText, newText) |
| 168 || getText().getSpanStart(mAutocompleteSpan) != mBeforeBatchEdit AutocompleteIndex) { | 143 || getText().getSpanStart(mAutocompleteSpan) != mBeforeBatchEdit AutocompleteIndex) { |
| 169 // If the text being typed is a single character that matches the ne xt character in the | 144 // If the text being typed is a single character that matches the ne xt character in the |
| 170 // previously visible autocomplete text, we reapply the autocomplete text to prevent | 145 // previously visible autocomplete text, we reapply the autocomplete text to prevent |
| 171 // a visual flickering when the autocomplete text is cleared and the n quickly reapplied | 146 // a visual flickering when the autocomplete text is cleared and the n quickly reapplied |
| 172 // when the next round of suggestions is received. | 147 // when the next round of suggestions is received. |
| 173 if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1 | 148 if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1 |
| 174 && mBeforeBatchEditFullText != null | 149 && mBeforeBatchEditFullText != null |
| 175 && mBeforeBatchEditFullText.startsWith(newText) && !mTextDel etedInBatchMode | 150 && mBeforeBatchEditFullText.startsWith(newText) && !mTextDel etedInBatchMode |
| 176 && newText.length() - mBeforeBatchEditAutocompleteIndex == 1 ) { | 151 && newText.length() - mBeforeBatchEditAutocompleteIndex == 1 ) { |
| 177 setAutocompleteText(newText, mBeforeBatchEditFullText.substring( newText.length())); | 152 setAutocompleteText(newText, mBeforeBatchEditFullText.substring( newText.length())); |
| 178 } | 153 } |
| 179 notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode, true); | 154 notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode, true); |
| 180 } | 155 } |
| 181 | 156 |
| 182 mTextDeletedInBatchMode = false; | 157 mTextDeletedInBatchMode = false; |
| 183 mBeforeBatchEditAutocompleteIndex = -1; | 158 mBeforeBatchEditAutocompleteIndex = -1; |
| 184 mBeforeBatchEditFullText = null; | 159 mBeforeBatchEditFullText = null; |
| 185 } | 160 } |
| 186 | 161 |
| 187 @Override | 162 @Override |
| 188 protected void onSelectionChanged(int selStart, int selEnd) { | 163 protected void onSelectionChanged(int selStart, int selEnd) { |
| 189 if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd); | 164 if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd); |
| 190 if (!mInBatchEditMode) { | 165 if (mBatchEditNestCount == 0) { |
| 191 int beforeTextLength = getText().length(); | 166 int beforeTextLength = getText().length(); |
| 192 if (validateSelection(selStart, selEnd)) { | 167 if (validateSelection(selStart, selEnd)) { |
| 193 boolean textDeleted = getText().length() < beforeTextLength; | 168 boolean textDeleted = getText().length() < beforeTextLength; |
| 194 notifyAutocompleteTextStateChanged(textDeleted, false); | 169 notifyAutocompleteTextStateChanged(textDeleted, false); |
| 195 } | 170 } |
| 196 } else { | 171 } else { |
| 197 mSelectionChangedInBatchMode = true; | 172 mSelectionChangedInBatchMode = true; |
| 198 } | 173 } |
| 199 super.onSelectionChanged(selStart, selEnd); | 174 super.onSelectionChanged(selStart, selEnd); |
| 200 } | 175 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 260 // in the case of autocomplete we want the last typed character to b e shown, which | 235 // in the case of autocomplete we want the last typed character to b e shown, which |
| 261 // is the start of selection. | 236 // is the start of selection. |
| 262 mDisableTextScrollingFromAutocomplete = false; | 237 mDisableTextScrollingFromAutocomplete = false; |
| 263 bringPointIntoView(getSelectionStart()); | 238 bringPointIntoView(getSelectionStart()); |
| 264 retVal = true; | 239 retVal = true; |
| 265 } | 240 } |
| 266 return retVal; | 241 return retVal; |
| 267 } | 242 } |
| 268 | 243 |
| 269 /** | 244 /** |
| 270 * Autocompletes the text on the url bar and selects the text that was not e ntered by the | 245 * Autocompletes the text and selects the text that was not entered by the u ser. Using append() |
| 271 * user. Using append() instead of setText() to preserve the soft-keyboard l ayout. | 246 * instead of setText() to preserve the soft-keyboard layout. |
| 272 * @param userText user The text entered by the user. | 247 * @param userText user The text entered by the user. |
| 273 * @param inlineAutocompleteText The suggested autocompletion for the user's text. | 248 * @param inlineAutocompleteText The suggested autocompletion for the user's text. |
| 274 */ | 249 */ |
| 275 public void setAutocompleteText(CharSequence userText, CharSequence inlineAu tocompleteText) { | 250 public void setAutocompleteText(CharSequence userText, CharSequence inlineAu tocompleteText) { |
| 276 if (DEBUG) { | 251 if (DEBUG) { |
| 277 Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteT ext: %s", userText, | 252 Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteT ext: %s", userText, |
| 278 inlineAutocompleteText); | 253 inlineAutocompleteText); |
| 279 } | 254 } |
| 280 boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText); | 255 boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText); |
| 281 | 256 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 331 | 306 |
| 332 @Override | 307 @Override |
| 333 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { | 308 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { |
| 334 if (DEBUG) { | 309 if (DEBUG) { |
| 335 Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d, lengthAfter: %d", | 310 Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d, lengthAfter: %d", |
| 336 text, start, lengthBefore, lengthAfter); | 311 text, start, lengthBefore, lengthAfter); |
| 337 } | 312 } |
| 338 | 313 |
| 339 super.onTextChanged(text, start, lengthBefore, lengthAfter); | 314 super.onTextChanged(text, start, lengthBefore, lengthAfter); |
| 340 boolean textDeleted = lengthAfter == 0; | 315 boolean textDeleted = lengthAfter == 0; |
| 341 if (!mInBatchEditMode) { | 316 if (mBatchEditNestCount == 0) { |
| 342 notifyAutocompleteTextStateChanged(textDeleted, true); | 317 notifyAutocompleteTextStateChanged(textDeleted, true); |
| 343 } else { | 318 } else { |
| 344 mTextDeletedInBatchMode = textDeleted; | 319 mTextDeletedInBatchMode = textDeleted; |
| 345 } | 320 } |
| 346 } | 321 } |
| 347 | 322 |
| 348 @Override | 323 @Override |
| 349 public void setText(CharSequence text, BufferType type) { | 324 public void setText(CharSequence text, BufferType type) { |
| 350 if (DEBUG) Log.i(TAG, "setText -- text: %s", text); | 325 if (DEBUG) Log.i(TAG, "setText -- text: %s", text); |
| 351 | 326 |
| 352 mDisableTextScrollingFromAutocomplete = false; | 327 mDisableTextScrollingFromAutocomplete = false; |
| 353 | 328 |
| 354 // Avoid setting the same text to the URL bar as it will mess up the scr oll/cursor | 329 // Avoid setting the same text as it will mess up the scroll/cursor posi tion. |
| 355 // position. | |
| 356 // Setting the text is also quite expensive, so only do it when the text has changed | 330 // Setting the text is also quite expensive, so only do it when the text has changed |
| 357 // (since we apply spans when the URL is not focused, we only optimize t his when the | 331 // (since we apply spans when the view is not focused, we only optimize this when the |
| 358 // URL is being edited). | 332 // text is being edited). |
| 359 if (!TextUtils.equals(getEditableText(), text)) { | 333 if (!TextUtils.equals(getEditableText(), text)) { |
| 360 // Certain OEM implementations of setText trigger disk reads. crbug. com/633298 | 334 // Certain OEM implementations of setText trigger disk reads. crbug. com/633298 |
| 361 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads( ); | 335 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads( ); |
| 362 try { | 336 try { |
| 363 super.setText(text, type); | 337 super.setText(text, type); |
| 364 } finally { | 338 } finally { |
| 365 StrictMode.setThreadPolicy(oldPolicy); | 339 StrictMode.setThreadPolicy(oldPolicy); |
| 366 } | 340 } |
| 367 } | 341 } |
| 368 | 342 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 423 | 397 |
| 424 @VisibleForTesting | 398 @VisibleForTesting |
| 425 public void setIgnoreImeForTest(boolean ignore) { | 399 public void setIgnoreImeForTest(boolean ignore) { |
| 426 mIgnoreImeForTest = ignore; | 400 mIgnoreImeForTest = ignore; |
| 427 } | 401 } |
| 428 | 402 |
| 429 private InputConnectionWrapper mInputConnection = new InputConnectionWrapper (null, true) { | 403 private InputConnectionWrapper mInputConnection = new InputConnectionWrapper (null, true) { |
| 430 private final char[] mTempSelectionChar = new char[1]; | 404 private final char[] mTempSelectionChar = new char[1]; |
| 431 | 405 |
| 432 @Override | 406 @Override |
| 407 public boolean beginBatchEdit() { | |
| 408 ++mBatchEditNestCount; | |
| 409 if (mBatchEditNestCount == 1) { | |
| 410 if (DEBUG) Log.i(TAG, "beginBatchEdit"); | |
| 411 mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAuto completeSpan); | |
| 412 mBeforeBatchEditFullText = getText().toString(); | |
| 413 | |
| 414 boolean retVal = super.beginBatchEdit(); | |
| 415 mTextDeletedInBatchMode = false; | |
| 416 return retVal; | |
| 417 } | |
| 418 return super.beginBatchEdit(); | |
| 419 } | |
| 420 | |
| 421 @Override | |
| 422 public boolean endBatchEdit() { | |
| 423 mBatchEditNestCount = Math.max(mBatchEditNestCount - 1, 0); | |
| 424 if (mBatchEditNestCount == 0) { | |
| 425 if (DEBUG) Log.i(TAG, "endBatchEdit"); | |
| 426 boolean retVal = super.endBatchEdit(); | |
| 427 onPostEndBatchEdit(); | |
| 428 return retVal; | |
| 429 } | |
| 430 return super.endBatchEdit(); | |
| 431 } | |
| 432 | |
| 433 @Override | |
| 433 public boolean commitText(CharSequence text, int newCursorPosition) { | 434 public boolean commitText(CharSequence text, int newCursorPosition) { |
| 434 if (DEBUG) Log.i(TAG, "commitText: [%s]", text); | 435 if (DEBUG) Log.i(TAG, "commitText: [%s]", text); |
| 435 Editable currentText = getText(); | 436 Editable currentText = getText(); |
| 436 if (currentText == null) return super.commitText(text, newCursorPosi tion); | 437 if (currentText == null) return super.commitText(text, newCursorPosi tion); |
| 437 | 438 |
| 438 int selectionStart = Selection.getSelectionStart(currentText); | 439 int selectionStart = Selection.getSelectionStart(currentText); |
| 439 int selectionEnd = Selection.getSelectionEnd(currentText); | 440 int selectionEnd = Selection.getSelectionEnd(currentText); |
| 440 int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan); | 441 int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan); |
| 441 // If the text being committed is a single character that matches th e next character | 442 // 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 // in the selection (assumed to be the autocomplete text), we only m ove the text |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 458 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); | 459 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); |
| 459 event.setFromIndex(selectionStart); | 460 event.setFromIndex(selectionStart); |
| 460 event.setRemovedCount(0); | 461 event.setRemovedCount(0); |
| 461 event.setAddedCount(1); | 462 event.setAddedCount(1); |
| 462 event.setBeforeText(currentText.toString().substring(0, selectionStart)); | 463 event.setBeforeText(currentText.toString().substring(0, selectionStart)); |
| 463 sendAccessibilityEventUnchecked(event); | 464 sendAccessibilityEventUnchecked(event); |
| 464 } | 465 } |
| 465 | 466 |
| 466 setAutocompleteText(currentText.subSequence(0, selectionStar t + 1), | 467 setAutocompleteText(currentText.subSequence(0, selectionStar t + 1), |
| 467 currentText.subSequence(selectionStart + 1, selectio nEnd)); | 468 currentText.subSequence(selectionStart + 1, selectio nEnd)); |
| 468 if (!mInBatchEditMode) { | 469 if (mBatchEditNestCount == 0) { |
| 469 notifyAutocompleteTextStateChanged(false, false); | 470 notifyAutocompleteTextStateChanged(false, false); |
| 470 } | 471 } |
| 471 return true; | 472 return true; |
| 472 } | 473 } |
| 473 } | 474 } |
| 474 | 475 |
| 475 boolean retVal = super.commitText(text, newCursorPosition); | 476 boolean retVal = super.commitText(text, newCursorPosition); |
| 476 | 477 |
| 477 // Ensure the autocomplete span is removed if it is no longer valid after committing the | 478 // Ensure the autocomplete span is removed if it is no longer valid after committing the |
| 478 // text. | 479 // text. |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 594 } | 595 } |
| 595 | 596 |
| 596 /** Removes this span from the current text and clears the internal stat e. */ | 597 /** Removes this span from the current text and clears the internal stat e. */ |
| 597 public void clearSpan() { | 598 public void clearSpan() { |
| 598 getText().removeSpan(this); | 599 getText().removeSpan(this); |
| 599 mAutocompleteText = null; | 600 mAutocompleteText = null; |
| 600 mUserText = null; | 601 mUserText = null; |
| 601 } | 602 } |
| 602 } | 603 } |
| 603 } | 604 } |
| OLD | NEW |