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

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

Issue 2885973002: Refactor autocomplete-specific logic into a separate class (Closed)
Patch Set: address tedchoc's comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java ('k') | chrome/android/java_sources.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698