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 |