| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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.ClipData; | 7 import android.content.ClipData; |
| 8 import android.content.ClipboardManager; | 8 import android.content.ClipboardManager; |
| 9 import android.content.Context; | 9 import android.content.Context; |
| 10 import android.content.res.Resources; | 10 import android.content.res.Resources; |
| 11 import android.graphics.Canvas; | 11 import android.graphics.Canvas; |
| 12 import android.graphics.Paint; | 12 import android.graphics.Paint; |
| 13 import android.graphics.Rect; | 13 import android.graphics.Rect; |
| 14 import android.net.Uri; | 14 import android.net.Uri; |
| 15 import android.os.Build; | 15 import android.os.Build; |
| 16 import android.os.StrictMode; | 16 import android.os.StrictMode; |
| 17 import android.os.SystemClock; | 17 import android.os.SystemClock; |
| 18 import android.text.Editable; | 18 import android.text.Editable; |
| 19 import android.text.Layout; | 19 import android.text.Layout; |
| 20 import android.text.Selection; | 20 import android.text.Selection; |
| 21 import android.text.Spanned; | |
| 22 import android.text.TextUtils; | 21 import android.text.TextUtils; |
| 23 import android.text.style.ReplacementSpan; | 22 import android.text.style.ReplacementSpan; |
| 24 import android.util.AttributeSet; | 23 import android.util.AttributeSet; |
| 25 import android.util.Pair; | 24 import android.util.Pair; |
| 26 import android.view.GestureDetector; | 25 import android.view.GestureDetector; |
| 27 import android.view.KeyEvent; | 26 import android.view.KeyEvent; |
| 28 import android.view.MotionEvent; | 27 import android.view.MotionEvent; |
| 29 import android.view.View; | 28 import android.view.View; |
| 30 import android.view.accessibility.AccessibilityEvent; | |
| 31 import android.view.accessibility.AccessibilityManager; | |
| 32 import android.view.accessibility.AccessibilityNodeInfo; | 29 import android.view.accessibility.AccessibilityNodeInfo; |
| 33 import android.view.inputmethod.BaseInputConnection; | |
| 34 import android.view.inputmethod.EditorInfo; | |
| 35 import android.view.inputmethod.InputConnection; | |
| 36 import android.view.inputmethod.InputConnectionWrapper; | |
| 37 import android.widget.TextView; | 30 import android.widget.TextView; |
| 38 | 31 |
| 39 import org.chromium.base.ApiCompatibilityUtils; | 32 import org.chromium.base.ApiCompatibilityUtils; |
| 40 import org.chromium.base.Log; | 33 import org.chromium.base.Log; |
| 41 import org.chromium.base.SysUtils; | 34 import org.chromium.base.SysUtils; |
| 42 import org.chromium.base.VisibleForTesting; | 35 import org.chromium.base.VisibleForTesting; |
| 43 import org.chromium.chrome.R; | 36 import org.chromium.chrome.R; |
| 44 import org.chromium.chrome.browser.WindowDelegate; | 37 import org.chromium.chrome.browser.WindowDelegate; |
| 45 import org.chromium.chrome.browser.metrics.StartupMetrics; | 38 import org.chromium.chrome.browser.metrics.StartupMetrics; |
| 46 import org.chromium.chrome.browser.omnibox.LocationBarLayout.OmniboxLivenessList
ener; | 39 import org.chromium.chrome.browser.omnibox.LocationBarLayout.OmniboxLivenessList
ener; |
| 47 import org.chromium.chrome.browser.tab.Tab; | 40 import org.chromium.chrome.browser.tab.Tab; |
| 48 import org.chromium.chrome.browser.util.UrlUtilities; | 41 import org.chromium.chrome.browser.util.UrlUtilities; |
| 49 import org.chromium.chrome.browser.widget.VerticallyFixedEditText; | |
| 50 import org.chromium.content.browser.ContentViewCore; | 42 import org.chromium.content.browser.ContentViewCore; |
| 51 import org.chromium.ui.UiUtils; | 43 import org.chromium.ui.UiUtils; |
| 52 | 44 |
| 53 import java.net.MalformedURLException; | 45 import java.net.MalformedURLException; |
| 54 import java.net.URI; | 46 import java.net.URI; |
| 55 import java.net.URISyntaxException; | 47 import java.net.URISyntaxException; |
| 56 import java.net.URL; | 48 import java.net.URL; |
| 57 | 49 |
| 58 /** | 50 /** |
| 59 * The URL text entry view for the Omnibox. | 51 * The URL text entry view for the Omnibox. |
| 60 */ | 52 */ |
| 61 public class UrlBar extends VerticallyFixedEditText { | 53 public class UrlBar extends AutocompleteEditText { |
| 62 private static final String TAG = "cr_UrlBar"; | 54 private static final String TAG = "cr_UrlBar"; |
| 63 | 55 |
| 64 private static final boolean DEBUG = false; | 56 private static final boolean DEBUG = false; |
| 65 | 57 |
| 66 // TextView becomes very slow on long strings, so we limit maximum length | 58 // TextView becomes very slow on long strings, so we limit maximum length |
| 67 // of what is displayed to the user, see limitDisplayableLength(). | 59 // of what is displayed to the user, see limitDisplayableLength(). |
| 68 private static final int MAX_DISPLAYABLE_LENGTH = 4000; | 60 private static final int MAX_DISPLAYABLE_LENGTH = 4000; |
| 69 private static final int MAX_DISPLAYABLE_LENGTH_LOW_END = 1000; | 61 private static final int MAX_DISPLAYABLE_LENGTH_LOW_END = 1000; |
| 70 | 62 |
| 71 // Unicode "Left-To-Right Mark" (LRM) character. | 63 // Unicode "Left-To-Right Mark" (LRM) character. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 82 /** | 74 /** |
| 83 * The text direction of the URL or query: LAYOUT_DIRECTION_LOCALE, LAYOUT_D
IRECTION_LTR, or | 75 * The text direction of the URL or query: LAYOUT_DIRECTION_LOCALE, LAYOUT_D
IRECTION_LTR, or |
| 84 * LAYOUT_DIRECTION_RTL. | 76 * LAYOUT_DIRECTION_RTL. |
| 85 * */ | 77 * */ |
| 86 private int mUrlDirection; | 78 private int mUrlDirection; |
| 87 | 79 |
| 88 private UrlBarDelegate mUrlBarDelegate; | 80 private UrlBarDelegate mUrlBarDelegate; |
| 89 | 81 |
| 90 private UrlDirectionListener mUrlDirectionListener; | 82 private UrlDirectionListener mUrlDirectionListener; |
| 91 | 83 |
| 92 private final AutocompleteSpan mAutocompleteSpan; | |
| 93 | |
| 94 /** | 84 /** |
| 95 * The gesture detector is used to detect long presses. Long presses require
special treatment | 85 * The gesture detector is used to detect long presses. Long presses require
special treatment |
| 96 * because the URL bar has custom touch event handling. See: {@link #onTouch
Event}. | 86 * because the URL bar has custom touch event handling. See: {@link #onTouch
Event}. |
| 97 */ | 87 */ |
| 98 private final GestureDetector mGestureDetector; | 88 private final GestureDetector mGestureDetector; |
| 99 | 89 |
| 100 private final KeyboardHideHelper mKeyboardHideHelper; | 90 private final KeyboardHideHelper mKeyboardHideHelper; |
| 101 | 91 |
| 102 private boolean mFocused; | 92 private boolean mFocused; |
| 103 private boolean mAllowFocus = true; | 93 private boolean mAllowFocus = true; |
| 104 | 94 |
| 105 private final int mDarkHintColor; | 95 private final int mDarkHintColor; |
| 106 private final int mDarkDefaultTextColor; | 96 private final int mDarkDefaultTextColor; |
| 107 private final int mDarkHighlightColor; | 97 private final int mDarkHighlightColor; |
| 108 | 98 |
| 109 private final int mLightHintColor; | 99 private final int mLightHintColor; |
| 110 private final int mLightDefaultTextColor; | 100 private final int mLightDefaultTextColor; |
| 111 private final int mLightHighlightColor; | 101 private final int mLightHighlightColor; |
| 112 | 102 |
| 113 private Boolean mUseDarkColors; | 103 private Boolean mUseDarkColors; |
| 114 | 104 |
| 115 private AccessibilityManager mAccessibilityManager; | |
| 116 | |
| 117 /** | |
| 118 * Whether default TextView scrolling should be disabled because autocomplet
e has been added. | |
| 119 * This allows the user entered text to be shown instead of the end of the a
utocomplete. | |
| 120 */ | |
| 121 private boolean mDisableTextScrollingFromAutocomplete; | |
| 122 | |
| 123 private OmniboxLivenessListener mOmniboxLivenessListener; | 105 private OmniboxLivenessListener mOmniboxLivenessListener; |
| 124 | 106 |
| 125 private long mFirstFocusTimeMs; | 107 private long mFirstFocusTimeMs; |
| 126 | 108 |
| 127 private boolean mInBatchEditMode; | 109 private boolean mIsPastedText; |
| 128 private int mBeforeBatchEditAutocompleteIndex = -1; | |
| 129 private String mBeforeBatchEditFullText; | |
| 130 private boolean mSelectionChangedInBatchMode; | |
| 131 private boolean mTextDeletedInBatchMode; | |
| 132 | 110 |
| 133 private boolean mIsPastedText; | |
| 134 // Used as a hint to indicate the text may contain an ellipsize span. This
will be true if an | 111 // Used as a hint to indicate the text may contain an ellipsize span. This
will be true if an |
| 135 // ellispize span was applied the last time the text changed. A true value
here does not | 112 // ellispize span was applied the last time the text changed. A true value
here does not |
| 136 // guarantee that the text does contain the span currently as newly set text
may have cleared | 113 // guarantee that the text does contain the span currently as newly set text
may have cleared |
| 137 // this (and it the value will only be recalculated after the text has been
changed). | 114 // this (and it the value will only be recalculated after the text has been
changed). |
| 138 private boolean mDidEllipsizeTextHint; | 115 private boolean mDidEllipsizeTextHint; |
| 139 | 116 |
| 140 // Set to true when the URL bar text is modified programmatically. Initially
set | |
| 141 // to true until the old state has been loaded. | |
| 142 private boolean mIgnoreTextChangeFromAutocomplete = true; | |
| 143 private boolean mLastUrlEditWasDelete; | |
| 144 | |
| 145 /** This tracks whether or not the last ACTION_DOWN event was when the url b
ar had focus. */ | 117 /** This tracks whether or not the last ACTION_DOWN event was when the url b
ar had focus. */ |
| 146 boolean mDownEventHadFocus; | 118 boolean mDownEventHadFocus; |
| 147 | 119 |
| 148 /** | 120 /** |
| 149 * Implement this to get updates when the direction of the text in the URL b
ar changes. | 121 * Implement this to get updates when the direction of the text in the URL b
ar changes. |
| 150 * E.g. If the user is typing a URL, then erases it and starts typing a quer
y in Arabic, | 122 * E.g. If the user is typing a URL, then erases it and starts typing a quer
y in Arabic, |
| 151 * the direction will change from left-to-right to right-to-left. | 123 * the direction will change from left-to-right to right-to-left. |
| 152 */ | 124 */ |
| 153 interface UrlDirectionListener { | 125 interface UrlDirectionListener { |
| 154 /** | 126 /** |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 mLightDefaultTextColor = | 173 mLightDefaultTextColor = |
| 202 ApiCompatibilityUtils.getColor(resources, R.color.url_emphasis_l
ight_default_text); | 174 ApiCompatibilityUtils.getColor(resources, R.color.url_emphasis_l
ight_default_text); |
| 203 mLightHintColor = | 175 mLightHintColor = |
| 204 ApiCompatibilityUtils.getColor(resources, R.color.locationbar_li
ght_hint_text); | 176 ApiCompatibilityUtils.getColor(resources, R.color.locationbar_li
ght_hint_text); |
| 205 mLightHighlightColor = ApiCompatibilityUtils.getColor(resources, | 177 mLightHighlightColor = ApiCompatibilityUtils.getColor(resources, |
| 206 R.color.locationbar_light_selection_color); | 178 R.color.locationbar_light_selection_color); |
| 207 | 179 |
| 208 setUseDarkTextColors(true); | 180 setUseDarkTextColors(true); |
| 209 | 181 |
| 210 mUrlDirection = LAYOUT_DIRECTION_LOCALE; | 182 mUrlDirection = LAYOUT_DIRECTION_LOCALE; |
| 211 mAutocompleteSpan = new AutocompleteSpan(); | |
| 212 | 183 |
| 213 // The URL Bar is derived from an text edit class, and as such is focusa
ble by | 184 // The URL Bar is derived from an text edit class, and as such is focusa
ble by |
| 214 // default. This means that if it is created before the first draw of th
e UI it | 185 // default. This means that if it is created before the first draw of th
e UI it |
| 215 // will (as the only focusable element of the UI) get focus on the first
draw. | 186 // will (as the only focusable element of the UI) get focus on the first
draw. |
| 216 // We react to this by greying out the tab area and bringing up the keyb
oard, | 187 // We react to this by greying out the tab area and bringing up the keyb
oard, |
| 217 // which we don't want to do at startup. Prevent this by disabling focus
until | 188 // which we don't want to do at startup. Prevent this by disabling focus
until |
| 218 // the first draw. | 189 // the first draw. |
| 219 setFocusable(false); | 190 setFocusable(false); |
| 220 setFocusableInTouchMode(false); | 191 setFocusableInTouchMode(false); |
| 221 | 192 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 232 return true; | 203 return true; |
| 233 } | 204 } |
| 234 }); | 205 }); |
| 235 mGestureDetector.setOnDoubleTapListener(null); | 206 mGestureDetector.setOnDoubleTapListener(null); |
| 236 mKeyboardHideHelper = new KeyboardHideHelper(this, new Runnable() { | 207 mKeyboardHideHelper = new KeyboardHideHelper(this, new Runnable() { |
| 237 @Override | 208 @Override |
| 238 public void run() { | 209 public void run() { |
| 239 if (mUrlBarDelegate != null) mUrlBarDelegate.backKeyPressed(); | 210 if (mUrlBarDelegate != null) mUrlBarDelegate.backKeyPressed(); |
| 240 } | 211 } |
| 241 }); | 212 }); |
| 242 | |
| 243 mAccessibilityManager = | |
| 244 (AccessibilityManager) context.getSystemService(Context.ACCESSIB
ILITY_SERVICE); | |
| 245 } | 213 } |
| 246 | 214 |
| 247 /** | 215 /** |
| 248 * Initialize the delegate that allows interaction with the Window. | 216 * Initialize the delegate that allows interaction with the Window. |
| 249 */ | 217 */ |
| 250 public void setWindowDelegate(WindowDelegate windowDelegate) { | 218 public void setWindowDelegate(WindowDelegate windowDelegate) { |
| 251 mKeyboardHideHelper.setWindowDelegate(windowDelegate); | 219 mKeyboardHideHelper.setWindowDelegate(windowDelegate); |
| 252 } | 220 } |
| 253 | 221 |
| 254 /** | 222 /** |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 } | 264 } |
| 297 | 265 |
| 298 @Override | 266 @Override |
| 299 public boolean onKeyPreIme(int keyCode, KeyEvent event) { | 267 public boolean onKeyPreIme(int keyCode, KeyEvent event) { |
| 300 if (KeyEvent.KEYCODE_BACK == keyCode && event.getAction() == KeyEvent.AC
TION_UP) { | 268 if (KeyEvent.KEYCODE_BACK == keyCode && event.getAction() == KeyEvent.AC
TION_UP) { |
| 301 mKeyboardHideHelper.monitorForKeyboardHidden(); | 269 mKeyboardHideHelper.monitorForKeyboardHidden(); |
| 302 } | 270 } |
| 303 return super.onKeyPreIme(keyCode, event); | 271 return super.onKeyPreIme(keyCode, event); |
| 304 } | 272 } |
| 305 | 273 |
| 274 @Override |
| 275 public boolean shouldAutocomplete() { |
| 276 if (isPastedText()) return false; |
| 277 return super.shouldAutocomplete(); |
| 278 } |
| 279 |
| 306 /** | 280 /** |
| 307 * Sets whether text changes should trigger autocomplete. | 281 * See {@link AutocompleteEditText#setIgnoreTextChangesForAutocomplete(boole
an)}. |
| 308 * <p> | 282 * <p> |
| 309 * {@link #setDelegate(UrlBarDelegate)} must be called with a non-null insta
nce prior to | 283 * {@link #setDelegate(UrlBarDelegate)} must be called with a non-null insta
nce prior to |
| 310 * enabling autocomplete. | 284 * enabling autocomplete. |
| 311 * | |
| 312 * @param ignoreAutocomplete Whether text changes should be ignored and no a
uto complete | |
| 313 * triggered. | |
| 314 */ | 285 */ |
| 286 @Override |
| 315 public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete)
{ | 287 public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete)
{ |
| 316 assert mUrlBarDelegate != null; | 288 assert mUrlBarDelegate != null; |
| 317 | 289 super.setIgnoreTextChangesForAutocomplete(ignoreAutocomplete); |
| 318 mIgnoreTextChangeFromAutocomplete = ignoreAutocomplete; | |
| 319 } | |
| 320 | |
| 321 /** | |
| 322 * @return The search query text (non-null). | |
| 323 */ | |
| 324 public String getQueryText() { | |
| 325 return getEditableText() != null ? getEditableText().toString() : ""; | |
| 326 } | |
| 327 | |
| 328 /** | |
| 329 * @return Whether the current cursor position is at the end of the user typ
ed text (i.e. | |
| 330 * at the beginning of the inline autocomplete text if present other
wise the very | |
| 331 * end of the current text). | |
| 332 */ | |
| 333 private boolean isCursorAtEndOfTypedText() { | |
| 334 final int selectionStart = getSelectionStart(); | |
| 335 final int selectionEnd = getSelectionEnd(); | |
| 336 | |
| 337 int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan); | |
| 338 int expectedSelectionEnd = getText().length(); | |
| 339 if (expectedSelectionStart < 0) { | |
| 340 expectedSelectionStart = expectedSelectionEnd; | |
| 341 } | |
| 342 | |
| 343 return selectionStart == expectedSelectionStart && selectionEnd == expec
tedSelectionEnd; | |
| 344 } | |
| 345 | |
| 346 /** | |
| 347 * @return Whether the URL is currently in batch edit mode triggered by an I
ME. No external | |
| 348 * text changes should be triggered while this is true. | |
| 349 */ | |
| 350 // isInBatchEditMode is a package protected method on TextView, so we intent
ionally chose | |
| 351 // a different name. | |
| 352 private boolean isHandlingBatchInput() { | |
| 353 return mInBatchEditMode; | |
| 354 } | |
| 355 | |
| 356 /** | |
| 357 * @return The user text without the autocomplete text. | |
| 358 */ | |
| 359 public String getTextWithoutAutocomplete() { | |
| 360 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan); | |
| 361 if (autoCompleteIndex < 0) { | |
| 362 return getQueryText(); | |
| 363 } else { | |
| 364 return getQueryText().substring(0, autoCompleteIndex); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 /** @return Whether any autocomplete information is specified on the current
text. */ | |
| 369 @VisibleForTesting | |
| 370 protected boolean hasAutocomplete() { | |
| 371 return getText().getSpanStart(mAutocompleteSpan) >= 0 | |
| 372 || mAutocompleteSpan.mAutocompleteText != null | |
| 373 || mAutocompleteSpan.mUserText != null; | |
| 374 } | |
| 375 | |
| 376 /** | |
| 377 * Whether we want to be showing inline autocomplete results. We don't want
to show them as the | |
| 378 * user deletes input. Also if there is a composition (e.g. while using the
Japanese IME), | |
| 379 * we must not autocomplete or we'll destroy the composition. | |
| 380 * @return Whether we want to be showing inline autocomplete results. | |
| 381 */ | |
| 382 public boolean shouldAutocomplete() { | |
| 383 if (mLastUrlEditWasDelete) return false; | |
| 384 Editable text = getText(); | |
| 385 | |
| 386 return isCursorAtEndOfTypedText() | |
| 387 && !isPastedText() | |
| 388 && !isHandlingBatchInput() | |
| 389 && BaseInputConnection.getComposingSpanEnd(text) | |
| 390 == BaseInputConnection.getComposingSpanStart(text); | |
| 391 } | |
| 392 | |
| 393 @Override | |
| 394 public void onBeginBatchEdit() { | |
| 395 if (DEBUG) Log.i(TAG, "onBeginBatchEdit"); | |
| 396 mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAutocomplete
Span); | |
| 397 mBeforeBatchEditFullText = getText().toString(); | |
| 398 | |
| 399 super.onBeginBatchEdit(); | |
| 400 mInBatchEditMode = true; | |
| 401 mTextDeletedInBatchMode = false; | |
| 402 } | |
| 403 | |
| 404 @Override | |
| 405 public void onEndBatchEdit() { | |
| 406 if (DEBUG) Log.i(TAG, "onEndBatchEdit"); | |
| 407 super.onEndBatchEdit(); | |
| 408 mInBatchEditMode = false; | |
| 409 limitDisplayableLength(); | |
| 410 if (mSelectionChangedInBatchMode) { | |
| 411 validateSelection(getSelectionStart(), getSelectionEnd()); | |
| 412 mSelectionChangedInBatchMode = false; | |
| 413 } | |
| 414 | |
| 415 String newText = getText().toString(); | |
| 416 if (!TextUtils.equals(mBeforeBatchEditFullText, newText) | |
| 417 || getText().getSpanStart(mAutocompleteSpan) != mBeforeBatchEdit
AutocompleteIndex) { | |
| 418 // If the text being typed is a single character that matches the ne
xt character in the | |
| 419 // previously visible autocomplete text, we reapply the autocomplete
text to prevent | |
| 420 // a visual flickering when the autocomplete text is cleared and the
n quickly reapplied | |
| 421 // when the next round of suggestions is received. | |
| 422 if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1 | |
| 423 && mBeforeBatchEditFullText != null | |
| 424 && mBeforeBatchEditFullText.startsWith(newText) | |
| 425 && !mTextDeletedInBatchMode | |
| 426 && newText.length() - mBeforeBatchEditAutocompleteIndex == 1
) { | |
| 427 setAutocompleteText(newText, mBeforeBatchEditFullText.substring(
newText.length())); | |
| 428 } | |
| 429 notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode); | |
| 430 } | |
| 431 | |
| 432 mTextDeletedInBatchMode = false; | |
| 433 mBeforeBatchEditAutocompleteIndex = -1; | |
| 434 mBeforeBatchEditFullText = null; | |
| 435 } | |
| 436 | |
| 437 @Override | |
| 438 protected void onSelectionChanged(int selStart, int selEnd) { | |
| 439 if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d",
selStart, selEnd); | |
| 440 if (!mInBatchEditMode) { | |
| 441 int beforeTextLength = getText().length(); | |
| 442 if (validateSelection(selStart, selEnd)) { | |
| 443 boolean textDeleted = getText().length() < beforeTextLength; | |
| 444 notifyAutocompleteTextStateChanged(textDeleted); | |
| 445 } | |
| 446 } else { | |
| 447 mSelectionChangedInBatchMode = true; | |
| 448 } | |
| 449 super.onSelectionChanged(selStart, selEnd); | |
| 450 } | |
| 451 | |
| 452 /** | |
| 453 * Validates the selection and clears the autocomplete span if needed. The
autocomplete text | |
| 454 * will be deleted if the selection occurs entirely before the autocomplete
region. | |
| 455 * | |
| 456 * @param selStart The start of the selection. | |
| 457 * @param selEnd The end of the selection. | |
| 458 * @return Whether the autocomplete span was removed as a result of this val
idation. | |
| 459 */ | |
| 460 private boolean validateSelection(int selStart, int selEnd) { | |
| 461 int spanStart = getText().getSpanStart(mAutocompleteSpan); | |
| 462 int spanEnd = getText().getSpanEnd(mAutocompleteSpan); | |
| 463 | |
| 464 if (DEBUG) { | |
| 465 Log.i(TAG, "validateSelection -- selStart: %d, selEnd: %d, spanStart
: %d, spanEnd: %d", | |
| 466 selStart, selEnd, spanStart, spanEnd); | |
| 467 } | |
| 468 | |
| 469 if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) { | |
| 470 CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompl
eteText; | |
| 471 | |
| 472 // On selection changes, the autocomplete text has been accepted by
the user or needs | |
| 473 // to be deleted below. | |
| 474 mAutocompleteSpan.clearSpan(); | |
| 475 | |
| 476 // The autocomplete text will be deleted any time the selection occu
rs entirely before | |
| 477 // the start of the autocomplete text. This is required because cer
tain keyboards will | |
| 478 // insert characters temporarily when starting a key entry gesture (
whether it be | |
| 479 // swyping a word or long pressing to get a special character). Whe
n this temporary | |
| 480 // character appears, Chrome may decide to append some autocomplete,
but the keyboard | |
| 481 // will then remove this temporary character only while leaving the
autocomplete text | |
| 482 // alone. See crbug/273763 for more details. | |
| 483 if (selEnd <= spanStart && TextUtils.equals(previousAutocompleteText
, | |
| 484 getText().subSequence(spanStart, getText().length()))) { | |
| 485 getText().delete(spanStart, getText().length()); | |
| 486 } | |
| 487 return true; | |
| 488 } | |
| 489 return false; | |
| 490 } | 290 } |
| 491 | 291 |
| 492 @Override | 292 @Override |
| 493 protected void onFocusChanged(boolean focused, int direction, Rect previousl
yFocusedRect) { | 293 protected void onFocusChanged(boolean focused, int direction, Rect previousl
yFocusedRect) { |
| 494 mFocused = focused; | 294 mFocused = focused; |
| 495 if (!focused) mAutocompleteSpan.clearSpan(); | |
| 496 super.onFocusChanged(focused, direction, previouslyFocusedRect); | 295 super.onFocusChanged(focused, direction, previouslyFocusedRect); |
| 497 | 296 |
| 498 if (focused && mFirstFocusTimeMs == 0) { | 297 if (focused && mFirstFocusTimeMs == 0) { |
| 499 mFirstFocusTimeMs = SystemClock.elapsedRealtime(); | 298 mFirstFocusTimeMs = SystemClock.elapsedRealtime(); |
| 500 if (mOmniboxLivenessListener != null) mOmniboxLivenessListener.onOmn
iboxFocused(); | 299 if (mOmniboxLivenessListener != null) mOmniboxLivenessListener.onOmn
iboxFocused(); |
| 501 } | 300 } |
| 502 | 301 |
| 503 if (focused) StartupMetrics.getInstance().recordFocusedOmnibox(); | 302 if (focused) StartupMetrics.getInstance().recordFocusedOmnibox(); |
| 504 | 303 |
| 505 fixupTextDirection(); | 304 fixupTextDirection(); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 @Override | 395 @Override |
| 597 public boolean performLongClick(float x, float y) { | 396 public boolean performLongClick(float x, float y) { |
| 598 // If the touch event that triggered this was when the url bar was in a
different focus | 397 // If the touch event that triggered this was when the url bar was in a
different focus |
| 599 // state, ignore the event. | 398 // state, ignore the event. |
| 600 if (mDownEventHadFocus != mFocused) return true; | 399 if (mDownEventHadFocus != mFocused) return true; |
| 601 | 400 |
| 602 return super.performLongClick(x, y); | 401 return super.performLongClick(x, y); |
| 603 } | 402 } |
| 604 | 403 |
| 605 @Override | 404 @Override |
| 606 public boolean bringPointIntoView(int offset) { | |
| 607 if (mDisableTextScrollingFromAutocomplete) return false; | |
| 608 return super.bringPointIntoView(offset); | |
| 609 } | |
| 610 | |
| 611 @Override | |
| 612 public boolean onPreDraw() { | |
| 613 boolean retVal = super.onPreDraw(); | |
| 614 if (mDisableTextScrollingFromAutocomplete) { | |
| 615 // super.onPreDraw will put the selection at the end of the text sel
ection, but | |
| 616 // in the case of autocomplete we want the last typed character to b
e shown, which | |
| 617 // is the start of selection. | |
| 618 mDisableTextScrollingFromAutocomplete = false; | |
| 619 bringPointIntoView(getSelectionStart()); | |
| 620 retVal = true; | |
| 621 } | |
| 622 return retVal; | |
| 623 } | |
| 624 | |
| 625 @Override | |
| 626 public void onDraw(Canvas canvas) { | 405 public void onDraw(Canvas canvas) { |
| 627 super.onDraw(canvas); | 406 super.onDraw(canvas); |
| 628 | 407 |
| 629 if (!mFirstDrawComplete) { | 408 if (!mFirstDrawComplete) { |
| 630 mFirstDrawComplete = true; | 409 mFirstDrawComplete = true; |
| 631 | 410 |
| 632 // We have now avoided the first draw problem (see the comment in | 411 // We have now avoided the first draw problem (see the comment in |
| 633 // the constructor) so we want to make the URL bar focusable so that | 412 // the constructor) so we want to make the URL bar focusable so that |
| 634 // touches etc. activate it. | 413 // touches etc. activate it. |
| 635 setFocusable(mAllowFocus); | 414 setFocusable(mAllowFocus); |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 818 | 597 |
| 819 Editable previousText = getEditableText(); | 598 Editable previousText = getEditableText(); |
| 820 setText(formattedUrl); | 599 setText(formattedUrl); |
| 821 | 600 |
| 822 if (!isFocused()) scrollToTLD(); | 601 if (!isFocused()) scrollToTLD(); |
| 823 | 602 |
| 824 return !TextUtils.equals(previousText, getEditableText()); | 603 return !TextUtils.equals(previousText, getEditableText()); |
| 825 } | 604 } |
| 826 | 605 |
| 827 /** | 606 /** |
| 828 * Autocompletes the text on the url bar and selects the text that was not e
ntered by the | |
| 829 * user. Using append() instead of setText() to preserve the soft-keyboard l
ayout. | |
| 830 * @param userText user The text entered by the user. | |
| 831 * @param inlineAutocompleteText The suggested autocompletion for the user's
text. | |
| 832 */ | |
| 833 public void setAutocompleteText(CharSequence userText, CharSequence inlineAu
tocompleteText) { | |
| 834 if (DEBUG) { | |
| 835 Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteT
ext: %s", | |
| 836 userText, inlineAutocompleteText); | |
| 837 } | |
| 838 boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText); | |
| 839 | |
| 840 if (!emptyAutocomplete) mDisableTextScrollingFromAutocomplete = true; | |
| 841 | |
| 842 int autocompleteIndex = userText.length(); | |
| 843 | |
| 844 String previousText = getQueryText(); | |
| 845 CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText
); | |
| 846 | |
| 847 setIgnoreTextChangesForAutocomplete(true); | |
| 848 | |
| 849 if (!TextUtils.equals(previousText, newText)) { | |
| 850 // The previous text may also have included autocomplete text, so we
only | |
| 851 // append the new autocomplete text that has changed. | |
| 852 if (TextUtils.indexOf(newText, previousText) == 0) { | |
| 853 append(newText.subSequence(previousText.length(), newText.length
())); | |
| 854 } else { | |
| 855 setUrl(newText.toString(), null); | |
| 856 } | |
| 857 } | |
| 858 | |
| 859 if (getSelectionStart() != autocompleteIndex | |
| 860 || getSelectionEnd() != getText().length()) { | |
| 861 setSelection(autocompleteIndex, getText().length()); | |
| 862 | |
| 863 if (inlineAutocompleteText.length() != 0) { | |
| 864 // Sending a TYPE_VIEW_TEXT_SELECTION_CHANGED accessibility even
t causes the | |
| 865 // previous TYPE_VIEW_TEXT_CHANGED event to be swallowed. As a r
esult the user | |
| 866 // hears the autocomplete text but *not* the text they typed. In
stead we send a | |
| 867 // TYPE_ANNOUNCEMENT event, which doesn't swallow the text-chang
ed event. | |
| 868 announceForAccessibility(inlineAutocompleteText); | |
| 869 } | |
| 870 } | |
| 871 | |
| 872 if (emptyAutocomplete) { | |
| 873 mAutocompleteSpan.clearSpan(); | |
| 874 } else { | |
| 875 mAutocompleteSpan.setSpan(userText, inlineAutocompleteText); | |
| 876 } | |
| 877 | |
| 878 setIgnoreTextChangesForAutocomplete(false); | |
| 879 } | |
| 880 | |
| 881 /** | |
| 882 * Returns the length of the autocomplete text currently displayed, zero if
none is | |
| 883 * currently displayed. | |
| 884 */ | |
| 885 public int getAutocompleteLength() { | |
| 886 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan); | |
| 887 if (autoCompleteIndex < 0) return 0; | |
| 888 return getText().length() - autoCompleteIndex; | |
| 889 } | |
| 890 | |
| 891 /** | |
| 892 * Scroll to ensure the TLD is visible. | 607 * Scroll to ensure the TLD is visible. |
| 893 * @return Whether the TLD was discovered and successfully scrolled to. | 608 * @return Whether the TLD was discovered and successfully scrolled to. |
| 894 */ | 609 */ |
| 895 public boolean scrollToTLD() { | 610 public boolean scrollToTLD() { |
| 896 Editable url = getText(); | 611 Editable url = getText(); |
| 897 if (url == null || url.length() < 1) return false; | 612 if (url == null || url.length() < 1) return false; |
| 898 String urlString = url.toString(); | 613 String urlString = url.toString(); |
| 899 Pair<String, String> urlComponents = | 614 Pair<String, String> urlComponents = |
| 900 LocationBarLayout.splitPathFromUrlDisplayText(urlString); | 615 LocationBarLayout.splitPathFromUrlDisplayText(urlString); |
| 901 | 616 |
| 902 if (TextUtils.isEmpty(urlComponents.first)) return false; | 617 if (TextUtils.isEmpty(urlComponents.first)) return false; |
| 903 | 618 |
| 904 // Do not scroll to the end of the host for URLs such as data:, javascri
pt:, etc... | 619 // Do not scroll to the end of the host for URLs such as data:, javascri
pt:, etc... |
| 905 if (urlComponents.second == null) { | 620 if (urlComponents.second == null) { |
| 906 Uri uri = Uri.parse(urlString); | 621 Uri uri = Uri.parse(urlString); |
| 907 String scheme = uri.getScheme(); | 622 String scheme = uri.getScheme(); |
| 908 if (!TextUtils.isEmpty(scheme) | 623 if (!TextUtils.isEmpty(scheme) |
| 909 && LocationBarLayout.UNSUPPORTED_SCHEMES_TO_SPLIT.contains(s
cheme)) { | 624 && LocationBarLayout.UNSUPPORTED_SCHEMES_TO_SPLIT.contains(s
cheme)) { |
| 910 return false; | 625 return false; |
| 911 } | 626 } |
| 912 } | 627 } |
| 913 | 628 |
| 914 setSelection(urlComponents.first.length()); | 629 setSelection(urlComponents.first.length()); |
| 915 return true; | 630 return true; |
| 916 } | 631 } |
| 917 | 632 |
| 918 @Override | 633 @Override |
| 919 protected void onTextChanged(CharSequence text, int start, int lengthBefore,
int lengthAfter) { | 634 protected void onTextChanged(CharSequence text, int start, int lengthBefore,
int lengthAfter) { |
| 920 if (DEBUG) { | |
| 921 Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d,
lengthAfter: %d", | |
| 922 text, start, lengthBefore, lengthAfter); | |
| 923 } | |
| 924 | |
| 925 super.onTextChanged(text, start, lengthBefore, lengthAfter); | 635 super.onTextChanged(text, start, lengthBefore, lengthAfter); |
| 926 if (!mInBatchEditMode) { | |
| 927 limitDisplayableLength(); | |
| 928 notifyAutocompleteTextStateChanged(lengthAfter == 0); | |
| 929 } else { | |
| 930 mTextDeletedInBatchMode = lengthAfter == 0; | |
| 931 } | |
| 932 mIsPastedText = false; | 636 mIsPastedText = false; |
| 933 } | 637 } |
| 934 | 638 |
| 935 @Override | 639 @Override |
| 936 public void setText(CharSequence text, BufferType type) { | 640 public void setText(CharSequence text, BufferType type) { |
| 937 if (DEBUG) Log.i(TAG, "setText -- text: %s", text); | 641 if (DEBUG) Log.i(TAG, "setText -- text: %s", text); |
| 938 | 642 super.setText(text, type); |
| 939 mDisableTextScrollingFromAutocomplete = false; | |
| 940 | |
| 941 // Avoid setting the same text to the URL bar as it will mess up the scr
oll/cursor | |
| 942 // position. | |
| 943 // Setting the text is also quite expensive, so only do it when the text
has changed | |
| 944 // (since we apply spans when the URL is not focused, we only optimize t
his when the | |
| 945 // URL is being edited). | |
| 946 if (!TextUtils.equals(getEditableText(), text)) { | |
| 947 // Certain OEM implementations of setText trigger disk reads. crbug.
com/633298 | |
| 948 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(
); | |
| 949 try { | |
| 950 super.setText(text, type); | |
| 951 } finally { | |
| 952 StrictMode.setThreadPolicy(oldPolicy); | |
| 953 } | |
| 954 } | |
| 955 | |
| 956 // Verify the autocomplete is still valid after the text change. | |
| 957 // Note: mAutocompleteSpan may be still null here if setText() is called
in View | |
| 958 // constructor. | |
| 959 if (mAutocompleteSpan != null | |
| 960 && mAutocompleteSpan.mUserText != null | |
| 961 && mAutocompleteSpan.mAutocompleteText != null) { | |
| 962 if (getText().getSpanStart(mAutocompleteSpan) < 0) { | |
| 963 mAutocompleteSpan.clearSpan(); | |
| 964 } else { | |
| 965 clearAutocompleteSpanIfInvalid(); | |
| 966 } | |
| 967 } | |
| 968 | |
| 969 fixupTextDirection(); | 643 fixupTextDirection(); |
| 970 } | 644 } |
| 971 | 645 |
| 972 private void clearAutocompleteSpanIfInvalid() { | |
| 973 Editable editableText = getEditableText(); | |
| 974 CharSequence previousUserText = mAutocompleteSpan.mUserText; | |
| 975 CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteT
ext; | |
| 976 if (editableText.length() | |
| 977 != (previousUserText.length() + previousAutocompleteText.length(
))) { | |
| 978 mAutocompleteSpan.clearSpan(); | |
| 979 } else if (TextUtils.indexOf(getText(), previousUserText) != 0 | |
| 980 || TextUtils.indexOf(getText(), | |
| 981 previousAutocompleteText, previousUserText.length()) !=
0) { | |
| 982 mAutocompleteSpan.clearSpan(); | |
| 983 } | |
| 984 } | |
| 985 | |
| 986 private void limitDisplayableLength() { | 646 private void limitDisplayableLength() { |
| 987 // To limit displayable length we replace middle portion of the string w
ith ellipsis. | 647 // To limit displayable length we replace middle portion of the string w
ith ellipsis. |
| 988 // That affects only presentation of the text, and doesn't affect other
aspects like | 648 // That affects only presentation of the text, and doesn't affect other
aspects like |
| 989 // copying to the clipboard, getting text with getText(), etc. | 649 // copying to the clipboard, getting text with getText(), etc. |
| 990 final int maxLength = SysUtils.isLowEndDevice() | 650 final int maxLength = SysUtils.isLowEndDevice() |
| 991 ? MAX_DISPLAYABLE_LENGTH_LOW_END : MAX_DISPLAYABLE_LENGTH; | 651 ? MAX_DISPLAYABLE_LENGTH_LOW_END : MAX_DISPLAYABLE_LENGTH; |
| 992 | 652 |
| 993 Editable text = getText(); | 653 Editable text = getText(); |
| 994 int textLength = text.length(); | 654 int textLength = text.length(); |
| 995 if (textLength <= maxLength) { | 655 if (textLength <= maxLength) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1032 if (pathIndex > 0) { | 692 if (pathIndex > 0) { |
| 1033 urlPrePath = url.substring(0, pathIndex); | 693 urlPrePath = url.substring(0, pathIndex); |
| 1034 } else { | 694 } else { |
| 1035 urlPrePath = url; | 695 urlPrePath = url; |
| 1036 } | 696 } |
| 1037 } | 697 } |
| 1038 return urlPrePath; | 698 return urlPrePath; |
| 1039 } | 699 } |
| 1040 | 700 |
| 1041 @Override | 701 @Override |
| 1042 public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { | |
| 1043 if (mIgnoreTextChangeFromAutocomplete) { | |
| 1044 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECT
ION_CHANGED | |
| 1045 || event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT
_CHANGED) { | |
| 1046 return; | |
| 1047 } | |
| 1048 } | |
| 1049 super.sendAccessibilityEventUnchecked(event); | |
| 1050 } | |
| 1051 | |
| 1052 @Override | |
| 1053 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { | 702 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { |
| 1054 // Certain OEM implementations of onInitializeAccessibilityNodeInfo trig
ger disk reads | 703 // Certain OEM implementations of onInitializeAccessibilityNodeInfo trig
ger disk reads |
| 1055 // to access the clipboard. crbug.com/640993 | 704 // to access the clipboard. crbug.com/640993 |
| 1056 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | 705 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
| 1057 try { | 706 try { |
| 1058 super.onInitializeAccessibilityNodeInfo(info); | 707 super.onInitializeAccessibilityNodeInfo(info); |
| 1059 } finally { | 708 } finally { |
| 1060 StrictMode.setThreadPolicy(oldPolicy); | 709 StrictMode.setThreadPolicy(oldPolicy); |
| 1061 } | 710 } |
| 1062 } | 711 } |
| 1063 | 712 |
| 1064 @VisibleForTesting | |
| 1065 InputConnectionWrapper mInputConnection = new InputConnectionWrapper(null, t
rue) { | |
| 1066 private final char[] mTempSelectionChar = new char[1]; | |
| 1067 | |
| 1068 @Override | |
| 1069 public boolean commitText(CharSequence text, int newCursorPosition) { | |
| 1070 Editable currentText = getText(); | |
| 1071 if (currentText == null) return super.commitText(text, newCursorPosi
tion); | |
| 1072 | |
| 1073 int selectionStart = Selection.getSelectionStart(currentText); | |
| 1074 int selectionEnd = Selection.getSelectionEnd(currentText); | |
| 1075 int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan); | |
| 1076 // If the text being committed is a single character that matches th
e next character | |
| 1077 // in the selection (assumed to be the autocomplete text), we only m
ove the text | |
| 1078 // selection instead clearing the autocomplete text causing flickeri
ng as the | |
| 1079 // autocomplete text will appear once the next suggestions are recei
ved. | |
| 1080 // | |
| 1081 // To be confident that the selection is an autocomplete, we ensure
the selection | |
| 1082 // is at least one character and the end of the selection is the end
of the | |
| 1083 // currently entered text. | |
| 1084 if (newCursorPosition == 1 && selectionStart > 0 && selectionStart !
= selectionEnd | |
| 1085 && selectionEnd >= currentText.length() | |
| 1086 && autocompleteIndex == selectionStart | |
| 1087 && text.length() == 1) { | |
| 1088 currentText.getChars(selectionStart, selectionStart + 1, mTempSe
lectionChar, 0); | |
| 1089 if (mTempSelectionChar[0] == text.charAt(0)) { | |
| 1090 | |
| 1091 // Since the text isn't changing, TalkBack won't read out th
e typed characters. | |
| 1092 // To work around this, explicitly send an accessibility eve
nt. crbug.com/416595 | |
| 1093 if (mAccessibilityManager != null && mAccessibilityManager.i
sEnabled()) { | |
| 1094 AccessibilityEvent event = AccessibilityEvent.obtain( | |
| 1095 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); | |
| 1096 event.setFromIndex(selectionStart); | |
| 1097 event.setRemovedCount(0); | |
| 1098 event.setAddedCount(1); | |
| 1099 event.setBeforeText(currentText.toString().substring(0,
selectionStart)); | |
| 1100 sendAccessibilityEventUnchecked(event); | |
| 1101 } | |
| 1102 | |
| 1103 setAutocompleteText( | |
| 1104 currentText.subSequence(0, selectionStart + 1), | |
| 1105 currentText.subSequence(selectionStart + 1, selectio
nEnd)); | |
| 1106 if (!mInBatchEditMode) { | |
| 1107 notifyAutocompleteTextStateChanged(false); | |
| 1108 } | |
| 1109 return true; | |
| 1110 } | |
| 1111 } | |
| 1112 | |
| 1113 boolean retVal = super.commitText(text, newCursorPosition); | |
| 1114 | |
| 1115 // Ensure the autocomplete span is removed if it is no longer valid
after committing the | |
| 1116 // text. | |
| 1117 if (getText().getSpanStart(mAutocompleteSpan) >= 0) clearAutocomplet
eSpanIfInvalid(); | |
| 1118 | |
| 1119 return retVal; | |
| 1120 } | |
| 1121 | |
| 1122 @Override | |
| 1123 public boolean setComposingText(CharSequence text, int newCursorPosition
) { | |
| 1124 Editable currentText = getText(); | |
| 1125 int autoCompleteSpanStart = currentText.getSpanStart(mAutocompleteSp
an); | |
| 1126 if (autoCompleteSpanStart >= 0) { | |
| 1127 int composingEnd = BaseInputConnection.getComposingSpanEnd(curre
ntText); | |
| 1128 | |
| 1129 // On certain device/keyboard combinations, the composing region
s are specified | |
| 1130 // with a noticeable delay after the initial character is typed,
and in certain | |
| 1131 // circumstances it does not check that the current state of the
text matches the | |
| 1132 // expectations of it's composing region. | |
| 1133 // For example, you can be typing: | |
| 1134 // chrome://f | |
| 1135 // Chrome will autocomplete to: | |
| 1136 // chrome://f[lags] | |
| 1137 // And after the autocomplete has been set, the keyboard will se
t the composing | |
| 1138 // region to the last character and it assumes it is 'f' as it w
as the last | |
| 1139 // character the keyboard sent. If we commit this composition,
the text will | |
| 1140 // look like: | |
| 1141 // chrome://flag[f] | |
| 1142 // And if we use the autocomplete clearing logic below, it will
look like: | |
| 1143 // chrome://f[f] | |
| 1144 // To work around this, we see if the composition matches all th
e characters prior | |
| 1145 // to the autocomplete and just readjust the composing region to
be that subset. | |
| 1146 // | |
| 1147 // See crbug.com/366732 | |
| 1148 if (composingEnd == currentText.length() | |
| 1149 && autoCompleteSpanStart >= text.length() | |
| 1150 && TextUtils.equals( | |
| 1151 currentText.subSequence( | |
| 1152 autoCompleteSpanStart - text.length(), | |
| 1153 autoCompleteSpanStart), | |
| 1154 text)) { | |
| 1155 setComposingRegion( | |
| 1156 autoCompleteSpanStart - text.length(), autoCompleteS
panStart); | |
| 1157 } | |
| 1158 | |
| 1159 // Once composing text is being modified, the autocomplete text
has been accepted | |
| 1160 // or has to be deleted. | |
| 1161 mAutocompleteSpan.clearSpan(); | |
| 1162 Selection.setSelection(currentText, autoCompleteSpanStart); | |
| 1163 currentText.delete(autoCompleteSpanStart, currentText.length()); | |
| 1164 } | |
| 1165 return super.setComposingText(text, newCursorPosition); | |
| 1166 } | |
| 1167 }; | |
| 1168 | |
| 1169 @Override | |
| 1170 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { | |
| 1171 mInputConnection.setTarget(super.onCreateInputConnection(outAttrs)); | |
| 1172 return mInputConnection; | |
| 1173 } | |
| 1174 | |
| 1175 /** | 713 /** |
| 1176 * Emphasize components of the URL for readability. | 714 * Emphasize components of the URL for readability. |
| 1177 */ | 715 */ |
| 1178 public void emphasizeUrl() { | 716 public void emphasizeUrl() { |
| 1179 Editable url = getText(); | 717 Editable url = getText(); |
| 1180 if (OmniboxUrlEmphasizer.hasEmphasisSpans(url) || hasFocus()) { | 718 if (OmniboxUrlEmphasizer.hasEmphasisSpans(url) || hasFocus()) { |
| 1181 return; | 719 return; |
| 1182 } | 720 } |
| 1183 | 721 |
| 1184 if (url.length() < 1) { | 722 if (url.length() < 1) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1219 public CharSequence getAccessibilityClassName() { | 757 public CharSequence getAccessibilityClassName() { |
| 1220 // When UrlBar is used as a read-only TextView, force Talkback to pronou
nce it like | 758 // When UrlBar is used as a read-only TextView, force Talkback to pronou
nce it like |
| 1221 // TextView. Otherwise Talkback will say "Edit box, http://...". crbug.c
om/636988 | 759 // TextView. Otherwise Talkback will say "Edit box, http://...". crbug.c
om/636988 |
| 1222 if (isEnabled()) { | 760 if (isEnabled()) { |
| 1223 return super.getAccessibilityClassName(); | 761 return super.getAccessibilityClassName(); |
| 1224 } else { | 762 } else { |
| 1225 return TextView.class.getName(); | 763 return TextView.class.getName(); |
| 1226 } | 764 } |
| 1227 } | 765 } |
| 1228 | 766 |
| 1229 private void notifyAutocompleteTextStateChanged(boolean textDeleted) { | 767 @Override |
| 768 protected void replaceAllTextFromAutocomplete(String text) { |
| 769 setUrl(text, null); |
| 770 } |
| 771 |
| 772 @Override |
| 773 public void onAutocompleteTextStateChanged(boolean textDeleted, boolean upda
teDisplay) { |
| 1230 if (mUrlBarDelegate == null) return; | 774 if (mUrlBarDelegate == null) return; |
| 1231 if (!hasFocus()) return; | 775 if (updateDisplay) limitDisplayableLength(); |
| 1232 if (mIgnoreTextChangeFromAutocomplete) return; | |
| 1233 | 776 |
| 1234 mLastUrlEditWasDelete = textDeleted; | |
| 1235 mUrlBarDelegate.onTextChangedForAutocomplete(textDeleted); | 777 mUrlBarDelegate.onTextChangedForAutocomplete(textDeleted); |
| 1236 } | 778 } |
| 1237 | 779 |
| 1238 /** | 780 /** |
| 1239 * Simple span used for tracking the current autocomplete state. | |
| 1240 */ | |
| 1241 private class AutocompleteSpan { | |
| 1242 private CharSequence mUserText; | |
| 1243 private CharSequence mAutocompleteText; | |
| 1244 | |
| 1245 /** | |
| 1246 * Adds the span to the current text. | |
| 1247 * @param userText The user entered text. | |
| 1248 * @param autocompleteText The autocomplete text being appended. | |
| 1249 */ | |
| 1250 public void setSpan(CharSequence userText, CharSequence autocompleteText
) { | |
| 1251 Editable text = getText(); | |
| 1252 text.removeSpan(this); | |
| 1253 mAutocompleteText = autocompleteText; | |
| 1254 mUserText = userText; | |
| 1255 text.setSpan( | |
| 1256 this, | |
| 1257 userText.length(), | |
| 1258 text.length(), | |
| 1259 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); | |
| 1260 } | |
| 1261 | |
| 1262 /** Removes this span from the current text and clears the internal stat
e. */ | |
| 1263 public void clearSpan() { | |
| 1264 getText().removeSpan(this); | |
| 1265 mAutocompleteText = null; | |
| 1266 mUserText = null; | |
| 1267 } | |
| 1268 } | |
| 1269 | |
| 1270 /** | |
| 1271 * Span that displays ellipsis instead of the text. Used to hide portion of | 781 * Span that displays ellipsis instead of the text. Used to hide portion of |
| 1272 * very large string to get decent performance from TextView. | 782 * very large string to get decent performance from TextView. |
| 1273 */ | 783 */ |
| 1274 private static class EllipsisSpan extends ReplacementSpan { | 784 private static class EllipsisSpan extends ReplacementSpan { |
| 1275 private static final String ELLIPSIS = "..."; | 785 private static final String ELLIPSIS = "..."; |
| 1276 | 786 |
| 1277 public static final EllipsisSpan INSTANCE = new EllipsisSpan(); | 787 public static final EllipsisSpan INSTANCE = new EllipsisSpan(); |
| 1278 | 788 |
| 1279 @Override | 789 @Override |
| 1280 public int getSize(Paint paint, CharSequence text, | 790 public int getSize(Paint paint, CharSequence text, |
| 1281 int start, int end, Paint.FontMetricsInt fm) { | 791 int start, int end, Paint.FontMetricsInt fm) { |
| 1282 return (int) paint.measureText(ELLIPSIS); | 792 return (int) paint.measureText(ELLIPSIS); |
| 1283 } | 793 } |
| 1284 | 794 |
| 1285 @Override | 795 @Override |
| 1286 public void draw(Canvas canvas, CharSequence text, int start, int end, | 796 public void draw(Canvas canvas, CharSequence text, int start, int end, |
| 1287 float x, int top, int y, int bottom, Paint paint) { | 797 float x, int top, int y, int bottom, Paint paint) { |
| 1288 canvas.drawText(ELLIPSIS, x, y, paint); | 798 canvas.drawText(ELLIPSIS, x, y, paint); |
| 1289 } | 799 } |
| 1290 } | 800 } |
| 1291 } | 801 } |
| OLD | NEW |