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

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

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.omnibox;
6
7 import android.content.ClipData;
8 import android.content.ClipboardManager;
9 import android.content.Context;
10 import android.content.res.ColorStateList;
11 import android.content.res.Resources;
12 import android.graphics.Canvas;
13 import android.graphics.Rect;
14 import android.os.SystemClock;
15 import android.text.Editable;
16 import android.text.Layout;
17 import android.text.Selection;
18 import android.text.Spanned;
19 import android.text.TextUtils;
20 import android.text.TextWatcher;
21 import android.util.AttributeSet;
22 import android.view.GestureDetector;
23 import android.view.KeyEvent;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.accessibility.AccessibilityEvent;
27 import android.view.accessibility.AccessibilityManager;
28 import android.view.accessibility.AccessibilityNodeInfo;
29 import android.view.inputmethod.BaseInputConnection;
30 import android.view.inputmethod.EditorInfo;
31 import android.view.inputmethod.InputConnection;
32 import android.view.inputmethod.InputConnectionWrapper;
33
34 import com.google.android.apps.chrome.R;
35
36 import org.chromium.base.VisibleForTesting;
37 import org.chromium.chrome.browser.UrlUtilities;
38 import org.chromium.chrome.browser.omnibox.LocationBarLayout.OmniboxLivenessList ener;
39 import org.chromium.chrome.browser.tab.ChromeTab;
40 import org.chromium.chrome.browser.widget.VerticallyFixedEditText;
41 import org.chromium.content.browser.ContentViewCore;
42 import org.chromium.ui.UiUtils;
43
44 import java.net.MalformedURLException;
45 import java.net.URI;
46 import java.net.URISyntaxException;
47 import java.net.URL;
48
49 /**
50 * The URL text entry view for the Omnibox.
51 */
52 public class UrlBar extends VerticallyFixedEditText {
53 private static final String TAG = "UrlBar";
54
55 /** The contents of the URL that precede the path/query after being formatte d. */
56 private String mFormattedUrlLocation;
57
58 /** The contents of the URL that precede the path/query before formatting. * /
59 private String mOriginalUrlLocation;
60
61 /** Overrides the text announced during accessibility events. */
62 private String mAccessibilityTextOverride;
63
64 private TextWatcher mLocationBarTextWatcher;
65
66 private boolean mShowKeyboardOnWindowFocus;
67
68 private boolean mFirstDrawComplete;
69
70 /**
71 * The text direction of the URL or query: LAYOUT_DIRECTION_LOCALE, LAYOUT_D IRECTION_LTR, or
72 * LAYOUT_DIRECTION_RTL.
73 * */
74 private int mUrlDirection;
75
76 private UrlBarDelegate mUrlBarDelegate;
77
78 private UrlDirectionListener mUrlDirectionListener;
79
80 private final AutocompleteSpan mAutocompleteSpan;
81
82 /**
83 * The gesture detector is used to detect long presses. Long presses require special treatment
84 * because the URL bar has custom touch event handling. See: {@link #onTouch Event}.
85 */
86 private final GestureDetector mGestureDetector;
87 private boolean mFocused;
88
89 private final ColorStateList mDarkHintColor;
90 private final int mDarkDefaultTextColor;
91 private final int mDarkHighlightColor;
92
93 private final int mLightHintColor;
94 private final int mLightDefaultTextColor;
95 private final int mLightHighlightColor;
96
97 private Boolean mUseDarkColors;
98
99 private AccessibilityManager mAccessibilityManager;
100 private boolean mDisableTextAccessibilityEvents;
101
102 /**
103 * Whether default TextView scrolling should be disabled because autocomplet e has been added.
104 * This allows the user entered text to be shown instead of the end of the a utocomplete.
105 */
106 private boolean mDisableTextScrollingFromAutocomplete;
107
108 private OmniboxLivenessListener mOmniboxLivenessListener;
109
110 private long mFirstFocusTimeMs;
111
112 /**
113 * Implement this to get updates when the direction of the text in the URL b ar changes.
114 * E.g. If the user is typing a URL, then erases it and starts typing a quer y in Arabic,
115 * the direction will change from left-to-right to right-to-left.
116 */
117 interface UrlDirectionListener {
118 /**
119 * Called whenever the layout direction of the UrlBar changes.
120 * @param layoutDirection the new direction: android.view.View.LAYOUT_DI RECTION_LTR or
121 * android.view.View.LAYOUT_DIRECTION_RTL
122 */
123 public void onUrlDirectionChanged(int layoutDirection);
124 }
125
126 /**
127 * Delegate used to communicate with the content side and the parent layout.
128 */
129 public interface UrlBarDelegate {
130 /**
131 * @return The current active {@link ChromeTab}.
132 */
133 ChromeTab getCurrentTab();
134
135 /**
136 * Notify the linked {@link TextWatcher} to ignore any changes made in t he UrlBar text.
137 * @param ignore Whether the changes should be ignored.
138 */
139 void setIgnoreURLBarModification(boolean ignore);
140
141 /**
142 * Called at the beginning of the focus change event before the underlyi ng TextView
143 * behavior is triggered.
144 * @param gainFocus Whether the URL is gaining focus or not.
145 */
146 void onUrlPreFocusChanged(boolean gainFocus);
147
148 /**
149 * Called to notify that back key has been pressed while the focus in on the url bar.
150 */
151 void backKeyPressed();
152
153 /**
154 * @return Whether original url is shown for preview page.
155 */
156 boolean showingOriginalUrlForPreview();
157
158 /**
159 * @return Whether the light security theme should be used.
160 */
161 boolean shouldEmphasizeHttpsScheme();
162 }
163
164 public UrlBar(Context context, AttributeSet attrs) {
165 super(context, attrs);
166
167 Resources resources = getResources();
168
169 mDarkDefaultTextColor = resources.getColor(R.color.url_emphasis_default_ text);
170 mDarkHintColor = getHintTextColors();
171 mDarkHighlightColor = getHighlightColor();
172
173 mLightDefaultTextColor = resources.getColor(R.color.url_emphasis_light_d efault_text);
174 mLightHintColor = resources.getColor(R.color.locationbar_light_hint_text );
175 mLightHighlightColor = resources.getColor(R.color.locationbar_light_sele ction_color);
176
177 setUseDarkTextColors(true);
178
179 mUrlDirection = LAYOUT_DIRECTION_LOCALE;
180 mAutocompleteSpan = new AutocompleteSpan();
181
182 // The URL Bar is derived from an text edit class, and as such is focusa ble by
183 // default. This means that if it is created before the first draw of th e UI it
184 // will (as the only focusable element of the UI) get focus on the first draw.
185 // We react to this by greying out the tab area and bringing up the keyb oard,
186 // which we don't want to do at startup. Prevent this by disabling focus until
187 // the first draw.
188 setFocusable(false);
189 setFocusableInTouchMode(false);
190
191 mGestureDetector = new GestureDetector(
192 getContext(), new GestureDetector.SimpleOnGestureListener() {
193 @Override
194 public void onLongPress(MotionEvent e) {
195 performLongClick();
196 }
197
198 @Override
199 public boolean onSingleTapUp(MotionEvent e) {
200 requestFocus();
201 return true;
202 }
203 });
204 mGestureDetector.setOnDoubleTapListener(null);
205
206 mAccessibilityManager =
207 (AccessibilityManager) context.getSystemService(Context.ACCESSIB ILITY_SERVICE);
208 }
209
210 /**
211 * Specifies whether the URL bar should use dark text colors or light colors .
212 * @param useDarkColors Whether the text colors should be dark (i.e. appropr iate for use
213 * on a light background).
214 */
215 public void setUseDarkTextColors(boolean useDarkColors) {
216 if (mUseDarkColors != null && mUseDarkColors.booleanValue() == useDarkCo lors) return;
217
218 mUseDarkColors = useDarkColors;
219 if (mUseDarkColors) {
220 setTextColor(mDarkDefaultTextColor);
221 setHighlightColor(mDarkHighlightColor);
222 } else {
223 setTextColor(mLightDefaultTextColor);
224 setHighlightColor(mLightHighlightColor);
225 }
226
227 // Note: Setting the hint text color only takes effect if there is not t ext in the URL bar.
228 // To get around this, set the URL to empty before setting the hin t color and revert
229 // back to the previous text after.
230 boolean hasNonEmptyText = false;
231 Editable text = getText();
232 if (!TextUtils.isEmpty(text)) {
233 setText("");
234 hasNonEmptyText = true;
235 }
236 if (useDarkColors) {
237 setHintTextColor(mDarkHintColor);
238 } else {
239 setHintTextColor(mLightHintColor);
240 }
241 if (hasNonEmptyText) setText(text);
242
243 if (!hasFocus()) {
244 deEmphasizeUrl();
245 emphasizeUrl();
246 }
247 }
248
249 /**
250 * @return The search query text (non-null).
251 */
252 public String getQueryText() {
253 return getEditableText() != null ? getEditableText().toString() : "";
254 }
255
256 /**
257 * @return Whether the current cursor position is at the end of the user typ ed text (i.e.
258 * at the beginning of the inline autocomplete text if present other wise the very
259 * end of the current text).
260 */
261 public boolean isCursorAtEndOfTypedText() {
262 final int selectionStart = getSelectionStart();
263 final int selectionEnd = getSelectionEnd();
264
265 int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan);
266 int expectedSelectionEnd = getText().length();
267 if (expectedSelectionStart < 0) {
268 expectedSelectionStart = expectedSelectionEnd;
269 }
270
271 return selectionStart == expectedSelectionStart && selectionEnd == expec tedSelectionEnd;
272 }
273
274 /**
275 * @return The user text without the autocomplete text.
276 */
277 public String getTextWithoutAutocomplete() {
278 int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan);
279 if (autoCompleteIndex < 0) {
280 return getQueryText();
281 } else {
282 return getQueryText().substring(0, autoCompleteIndex);
283 }
284 }
285
286 /** @return Whether any autocomplete information is specified on the current text. */
287 @VisibleForTesting
288 protected boolean hasAutocomplete() {
289 return getText().getSpanStart(mAutocompleteSpan) >= 0
290 || mAutocompleteSpan.mAutocompleteText != null
291 || mAutocompleteSpan.mUserText != null;
292 }
293
294 @Override
295 protected void onSelectionChanged(int selStart, int selEnd) {
296 int spanStart = getText().getSpanStart(mAutocompleteSpan);
297 int spanEnd = getText().getSpanEnd(mAutocompleteSpan);
298 if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) {
299 // On selection changes, the autocomplete text has been accepted by the user or needs
300 // to be deleted below.
301 mAutocompleteSpan.clearSpan();
302
303 // The autocomplete text will be deleted any time the selection occu rs entirely before
304 // the start of the autocomplete text. This is required because cer tain keyboards will
305 // insert characters temporarily when starting a key entry gesture ( whether it be
306 // swyping a word or long pressing to get a special character). Whe n this temporary
307 // character appears, Chrome may decide to append some autocomplete, but the keyboard
308 // will then remove this temporary character only while leaving the autocomplete text
309 // alone. See crbug/273763 for more details.
310 if (selEnd <= spanStart) getText().delete(spanStart, getText().lengt h());
311 }
312 super.onSelectionChanged(selStart, selEnd);
313 }
314
315 @Override
316 protected void onFocusChanged(boolean focused, int direction, Rect previousl yFocusedRect) {
317 mFocused = focused;
318 mUrlBarDelegate.onUrlPreFocusChanged(focused);
319 if (!focused) mAutocompleteSpan.clearSpan();
320 super.onFocusChanged(focused, direction, previouslyFocusedRect);
321
322 if (focused && mFirstFocusTimeMs == 0) {
323 mFirstFocusTimeMs = SystemClock.elapsedRealtime();
324 if (mOmniboxLivenessListener != null) mOmniboxLivenessListener.onOmn iboxFocused();
325 }
326 }
327
328 /**
329 * @return The elapsed realtime timestamp in ms of the first time the url ba r was focused,
330 * 0 if never.
331 */
332 public long getFirstFocusTime() {
333 return mFirstFocusTimeMs;
334 }
335
336 @Override
337 protected void onWindowVisibilityChanged(int visibility) {
338 super.onWindowVisibilityChanged(visibility);
339 if (visibility == View.GONE && isFocused()) mShowKeyboardOnWindowFocus = true;
340 }
341
342 @Override
343 public void onWindowFocusChanged(boolean hasWindowFocus) {
344 super.onWindowFocusChanged(hasWindowFocus);
345 if (hasWindowFocus) {
346 if (mShowKeyboardOnWindowFocus && isFocused()) {
347 // Without the call to post(..), the keyboard was not getting sh own when the
348 // window regained focus despite this being the final call in th e view system
349 // flow.
350 post(new Runnable() {
351 @Override
352 public void run() {
353 UiUtils.showKeyboard(UrlBar.this);
354 }
355 });
356 }
357 mShowKeyboardOnWindowFocus = false;
358 }
359 }
360
361 @Override
362 public View focusSearch(int direction) {
363 if (direction == View.FOCUS_BACKWARD
364 && mUrlBarDelegate.getCurrentTab().getView() != null) {
365 return mUrlBarDelegate.getCurrentTab().getView();
366 } else {
367 return super.focusSearch(direction);
368 }
369 }
370
371 @Override
372 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
373 if (keyCode == KeyEvent.KEYCODE_BACK) {
374 if (event.getAction() == KeyEvent.ACTION_DOWN
375 && event.getRepeatCount() == 0) {
376 // Tell the framework to start tracking this event.
377 getKeyDispatcherState().startTracking(event, this);
378 return true;
379 } else if (event.getAction() == KeyEvent.ACTION_UP) {
380 getKeyDispatcherState().handleUpEvent(event);
381 if (event.isTracking() && !event.isCanceled()) {
382 mUrlBarDelegate.backKeyPressed();
383 return true;
384 }
385 }
386 }
387 return super.onKeyPreIme(keyCode, event);
388 }
389
390 @Override
391 public boolean onTouchEvent(MotionEvent event) {
392 ChromeTab currentTab = mUrlBarDelegate.getCurrentTab();
393 if (currentTab != null
394 && currentTab.getBackgroundContentViewHelper() != null
395 && mUrlBarDelegate.showingOriginalUrlForPreview()) {
396 // When we are showing preview, we treat click on UrlBar as an event to force swapping
397 // of content views.
398 currentTab.getBackgroundContentViewHelper().forceSwappingContentView s();
399 }
400
401 if (!mFocused) {
402 mGestureDetector.onTouchEvent(event);
403 return true;
404 }
405
406 if (event.getAction() == MotionEvent.ACTION_DOWN && currentTab != null) {
407 // Make sure to hide the current ContentView ActionBar.
408 ContentViewCore viewCore = currentTab.getContentViewCore();
409 if (viewCore != null) viewCore.hideSelectActionMode();
410 }
411
412 return super.onTouchEvent(event);
413 }
414
415 @Override
416 public boolean bringPointIntoView(int offset) {
417 if (mDisableTextScrollingFromAutocomplete) return false;
418 return super.bringPointIntoView(offset);
419 }
420
421 @Override
422 public boolean onPreDraw() {
423 boolean retVal = super.onPreDraw();
424 if (mDisableTextScrollingFromAutocomplete) {
425 // super.onPreDraw will put the selection at the end of the text sel ection, but
426 // in the case of autocomplete we want the last typed character to b e shown, which
427 // is the start of selection.
428 mDisableTextScrollingFromAutocomplete = false;
429 bringPointIntoView(getSelectionStart());
430 retVal = true;
431 }
432 return retVal;
433 }
434
435 @Override
436 public void onDraw(Canvas canvas) {
437 super.onDraw(canvas);
438
439 if (!mFirstDrawComplete) {
440 mFirstDrawComplete = true;
441
442 // We have now avoided the first draw problem (see the comment in
443 // the constructor) so we want to make the URL bar focusable so that
444 // touches etc. activate it.
445 setFocusable(true);
446 setFocusableInTouchMode(true);
447
448 // The URL bar will now react correctly to a focus change event
449 if (mOmniboxLivenessListener != null) mOmniboxLivenessListener.onOmn iboxInteractive();
450 }
451
452 // Notify listeners if the URL's direction has changed.
453 updateUrlDirection();
454 }
455
456 /**
457 * If the direction of the URL has changed, update mUrlDirection and notify the
458 * UrlDirectionListeners.
459 */
460 private void updateUrlDirection() {
461 Layout layout = getLayout();
462 if (layout == null) return;
463
464 int urlDirection;
465 if (length() == 0) {
466 urlDirection = LAYOUT_DIRECTION_LOCALE;
467 } else if (layout.getParagraphDirection(0) == Layout.DIR_LEFT_TO_RIGHT) {
468 urlDirection = LAYOUT_DIRECTION_LTR;
469 } else {
470 urlDirection = LAYOUT_DIRECTION_RTL;
471 }
472
473 if (urlDirection != mUrlDirection) {
474 mUrlDirection = urlDirection;
475 if (mUrlDirectionListener != null) {
476 mUrlDirectionListener.onUrlDirectionChanged(urlDirection);
477 }
478 }
479 }
480
481 /**
482 * @return The text direction of the URL, e.g. LAYOUT_DIRECTION_LTR.
483 */
484 public int getUrlDirection() {
485 return mUrlDirection;
486 }
487
488 /**
489 * Sets the listener for changes in the url bar's layout direction. Also cal ls
490 * onUrlDirectionChanged() immediately on the listener.
491 *
492 * @param listener The UrlDirectionListener to receive callbacks when the ur l direction changes,
493 * or null to unregister any previously registered listener.
494 */
495 public void setUrlDirectionListener(UrlDirectionListener listener) {
496 mUrlDirectionListener = listener;
497 if (mUrlDirectionListener != null) {
498 mUrlDirectionListener.onUrlDirectionChanged(mUrlDirection);
499 }
500 }
501
502 void setLocationBarTextWatcher(TextWatcher locationBarTextWatcher) {
503 mLocationBarTextWatcher = locationBarTextWatcher;
504 }
505
506 /**
507 * Set the url delegate to handle communication from the {@link UrlBar} to t he rest of the UI.
508 * @param delegate The {@link UrlBarDelegate} to be used.
509 */
510 public void setDelegate(UrlBarDelegate delegate) {
511 mUrlBarDelegate = delegate;
512 }
513
514 /**
515 * Set {@link OmniboxLivenessListener} to be used for receiving interaction related messages
516 * during startup.
517 * @param listener The listener to use for sending the messages.
518 */
519 @VisibleForTesting
520 public void setOmniboxLivenessListener(OmniboxLivenessListener listener) {
521 mOmniboxLivenessListener = listener;
522 }
523
524 /**
525 * Signal {@link OmniboxLivenessListener} that the omnibox is completely ope rational now.
526 */
527 @VisibleForTesting
528 public void onOmniboxFullyFunctional() {
529 if (mOmniboxLivenessListener != null) mOmniboxLivenessListener.onOmnibox FullyFunctional();
530 }
531
532 @Override
533 public boolean onTextContextMenuItem(int id) {
534 if (id == android.R.id.paste) {
535 ClipboardManager clipboard = (ClipboardManager) getContext()
536 .getSystemService(Context.CLIPBOARD_SERVICE);
537 ClipData clipData = clipboard.getPrimaryClip();
538 if (clipData != null) {
539 StringBuilder builder = new StringBuilder();
540 for (int i = 0; i < clipData.getItemCount(); i++) {
541 builder.append(clipData.getItemAt(i).coerceToText(getContext ()));
542 }
543 String pasteString = OmniboxViewUtil.sanitizeTextForPaste(builde r.toString());
544
545 int min = 0;
546 int max = getText().length();
547
548 if (isFocused()) {
549 final int selStart = getSelectionStart();
550 final int selEnd = getSelectionEnd();
551
552 min = Math.max(0, Math.min(selStart, selEnd));
553 max = Math.max(0, Math.max(selStart, selEnd));
554 }
555
556 Selection.setSelection(getText(), max);
557 getText().replace(min, max, pasteString);
558 return true;
559 }
560 }
561
562 if (mOriginalUrlLocation == null || mFormattedUrlLocation == null) {
563 return super.onTextContextMenuItem(id);
564 }
565
566 int selectedStartIndex = getSelectionStart();
567 int selectedEndIndex = getSelectionEnd();
568
569 // If we are copying/cutting the full previously formatted URL, reset th e URL
570 // text before initiating the TextViews handling of the context menu.
571 String currentText = getText().toString();
572 if (selectedStartIndex == 0
573 && (id == android.R.id.cut || id == android.R.id.copy)
574 && currentText.startsWith(mFormattedUrlLocation)
575 && selectedEndIndex >= mFormattedUrlLocation.length()) {
576 String newText = mOriginalUrlLocation
577 + currentText.substring(mFormattedUrlLocation.length());
578 selectedEndIndex = selectedEndIndex - mFormattedUrlLocation.length()
579 + mOriginalUrlLocation.length();
580 mUrlBarDelegate.setIgnoreURLBarModification(true);
581 setText(newText);
582 setSelection(0, selectedEndIndex);
583 boolean retVal = super.onTextContextMenuItem(id);
584 if (getText().toString().equals(newText)) {
585 setText(currentText);
586 setSelection(getText().length());
587 }
588 mUrlBarDelegate.setIgnoreURLBarModification(false);
589 return retVal;
590 }
591 return super.onTextContextMenuItem(id);
592 }
593
594 /**
595 * Sets the text content of the URL bar.
596 *
597 * @param url The original URL (or generic text) that can be used for copy/c ut/paste.
598 * @param formattedUrl Formatted URL for user display. Null if there isn't o ne.
599 * @return Whether the visible text has changed.
600 */
601 public boolean setUrl(String url, String formattedUrl) {
602 if (!TextUtils.isEmpty(formattedUrl)) {
603 try {
604 URL javaUrl = new URL(url);
605 mFormattedUrlLocation =
606 getUrlContentsPrePath(formattedUrl, javaUrl.getHost());
607 mOriginalUrlLocation =
608 getUrlContentsPrePath(url, javaUrl.getHost());
609 } catch (MalformedURLException mue) {
610 mOriginalUrlLocation = null;
611 mFormattedUrlLocation = null;
612 }
613 } else {
614 mOriginalUrlLocation = null;
615 mFormattedUrlLocation = null;
616 formattedUrl = url;
617 }
618
619 Editable previousText = getEditableText();
620 setText(formattedUrl);
621
622 if (!isFocused()) scrollToTLD();
623
624 return !TextUtils.equals(previousText, getEditableText());
625 }
626
627 /**
628 * Autocompletes the text on the url bar and selects the text that was not e ntered by the
629 * user. Using append() instead of setText() to preserve the soft-keyboard l ayout.
630 * @param userText user The text entered by the user.
631 * @param inlineAutocompleteText The suggested autocompletion for the user's text.
632 */
633 public void setAutocompleteText(CharSequence userText, CharSequence inlineAu tocompleteText) {
634 boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText);
635
636 if (!emptyAutocomplete) mDisableTextScrollingFromAutocomplete = true;
637
638 int autocompleteIndex = userText.length();
639
640 String previousText = getQueryText();
641 CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText );
642
643 mUrlBarDelegate.setIgnoreURLBarModification(true);
644 mDisableTextAccessibilityEvents = true;
645
646 if (!TextUtils.equals(previousText, newText)) {
647 // The previous text may also have included autocomplete text, so we only
648 // append the new autocomplete text that has changed.
649 if (TextUtils.indexOf(newText, previousText) == 0) {
650 append(newText.subSequence(previousText.length(), newText.length ()));
651 } else {
652 setUrl(newText.toString(), null);
653 }
654 }
655
656 if (getSelectionStart() != autocompleteIndex
657 || getSelectionEnd() != getText().length()) {
658 setSelection(autocompleteIndex, getText().length());
659
660 if (inlineAutocompleteText.length() != 0) {
661 // Sending a TYPE_VIEW_TEXT_SELECTION_CHANGED accessibility even t causes the
662 // previous TYPE_VIEW_TEXT_CHANGED event to be swallowed. As a r esult the user
663 // hears the autocomplete text but *not* the text they typed. In stead we send a
664 // TYPE_ANNOUNCEMENT event, which doesn't swallow the text-chang ed event.
665 announceForAccessibility(inlineAutocompleteText);
666 }
667 }
668
669 if (emptyAutocomplete) {
670 mAutocompleteSpan.clearSpan();
671 } else {
672 mAutocompleteSpan.setSpan(userText, inlineAutocompleteText);
673 }
674
675 mUrlBarDelegate.setIgnoreURLBarModification(false);
676 mDisableTextAccessibilityEvents = false;
677 }
678
679 /**
680 * Overrides the text announced when focusing on the field for accessibility . This value will
681 * be cleared automatically when the text content changes for this view.
682 * @param accessibilityOverride The text to be announced instead of the curr ent text value
683 * (or null if the text content should be read) .
684 */
685 public void setAccessibilityTextOverride(String accessibilityOverride) {
686 mAccessibilityTextOverride = accessibilityOverride;
687 }
688
689 private void scrollToTLD() {
690 Editable url = getText();
691 if (url == null || url.length() < 1) return;
692 String urlString = url.toString();
693 URL javaUrl;
694 try {
695 javaUrl = new URL(urlString);
696 } catch (MalformedURLException mue) {
697 return;
698 }
699 String host = javaUrl.getHost();
700 if (host == null || host.isEmpty()) return;
701 int hostStart = urlString.indexOf(host);
702 int hostEnd = hostStart + host.length();
703 setSelection(hostEnd);
704 }
705
706 @Override
707 public void setText(CharSequence text, BufferType type) {
708 mDisableTextScrollingFromAutocomplete = false;
709
710 // Avoid setting the same text to the URL bar as it will mess up the scr oll/cursor
711 // position.
712 // Setting the text is also quite expensive, so only do it when the text has changed
713 // (since we apply spans when the URL is not focused, we only optimize t his when the
714 // URL is being edited).
715 if (!TextUtils.equals(getEditableText(), text)) {
716 super.setText(text, type);
717 mAccessibilityTextOverride = null;
718 }
719
720 // Verify the autocomplete is still valid after the text change.
721 if (mAutocompleteSpan != null
722 && mAutocompleteSpan.mUserText != null
723 && mAutocompleteSpan.mAutocompleteText != null) {
724 if (getText().getSpanStart(mAutocompleteSpan) < 0) {
725 mAutocompleteSpan.clearSpan();
726 } else {
727 Editable editableText = getEditableText();
728 CharSequence previousUserText = mAutocompleteSpan.mUserText;
729 CharSequence previousAutocompleteText = mAutocompleteSpan.mAutoc ompleteText;
730 if (editableText.length()
731 < (previousUserText.length() + previousAutocompleteText. length())) {
732 mAutocompleteSpan.clearSpan();
733 } else if (TextUtils.indexOf(getText(), previousUserText) != 0
734 || TextUtils.indexOf(getText(), previousAutocompleteText )
735 != previousUserText.length()) {
736 mAutocompleteSpan.clearSpan();
737 }
738 }
739 }
740 }
741
742 /**
743 * Returns the portion of the URL that precedes the path/query section of th e URL.
744 *
745 * @param url The url to be used to find the preceding portion.
746 * @param host The host to be located in the URL to determine the location o f the path.
747 * @return The URL contents that precede the path (or the passed in URL if t he host is
748 * not found).
749 */
750 private static String getUrlContentsPrePath(String url, String host) {
751 String urlPrePath = url;
752 int hostIndex = url.indexOf(host);
753 if (hostIndex >= 0) {
754 int pathIndex = url.indexOf('/', hostIndex);
755 if (pathIndex > 0) {
756 urlPrePath = url.substring(0, pathIndex);
757 } else {
758 urlPrePath = url;
759 }
760 }
761 return urlPrePath;
762 }
763
764 @Override
765 public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
766 if (mDisableTextAccessibilityEvents) {
767 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECT ION_CHANGED
768 || event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT _CHANGED) {
769 return;
770 }
771 }
772 super.sendAccessibilityEventUnchecked(event);
773 }
774
775 @Override
776 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
777 super.onInitializeAccessibilityNodeInfo(info);
778
779 if (mAccessibilityTextOverride != null) {
780 info.setText(mAccessibilityTextOverride);
781 }
782 }
783
784 InputConnectionWrapper mInputConnection = new InputConnectionWrapper(null, t rue) {
785 private final char[] mTempSelectionChar = new char[1];
786
787 @Override
788 public boolean commitText(CharSequence text, int newCursorPosition) {
789 Editable currentText = getText();
790 if (currentText == null) return super.commitText(text, newCursorPosi tion);
791
792 int selectionStart = Selection.getSelectionStart(currentText);
793 int selectionEnd = Selection.getSelectionEnd(currentText);
794 int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan);
795 // If the text being committed is a single character that matches th e next character
796 // in the selection (assumed to be the autocomplete text), we only m ove the text
797 // selection instead clearing the autocomplete text causing flickeri ng as the
798 // autocomplete text will appear once the next suggestions are recei ved.
799 //
800 // To be confident that the selection is an autocomplete, we ensure the selection
801 // is at least one character and the end of the selection is the end of the
802 // currently entered text.
803 if (newCursorPosition == 1 && selectionStart > 0 && selectionStart ! = selectionEnd
804 && selectionEnd >= currentText.length()
805 && autocompleteIndex == selectionStart
806 && text.length() == 1) {
807 currentText.getChars(selectionStart, selectionStart + 1, mTempSe lectionChar, 0);
808 if (mTempSelectionChar[0] == text.charAt(0)) {
809
810 // Since the text isn't changing, TalkBack won't read out th e typed characters.
811 // To work around this, explicitly send an accessibility eve nt. crbug.com/416595
812 if (mAccessibilityManager != null && mAccessibilityManager.i sEnabled()) {
813 AccessibilityEvent event = AccessibilityEvent.obtain(
814 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
815 event.setFromIndex(selectionStart);
816 event.setRemovedCount(0);
817 event.setAddedCount(1);
818 event.setBeforeText(currentText.toString().substring(0, selectionStart));
819 sendAccessibilityEventUnchecked(event);
820 }
821
822 if (mLocationBarTextWatcher != null) {
823 mLocationBarTextWatcher.beforeTextChanged(currentText, 0 , 0, 0);
824 }
825 setAutocompleteText(
826 currentText.subSequence(0, selectionStart + 1),
827 currentText.subSequence(selectionStart + 1, selectio nEnd));
828 if (mLocationBarTextWatcher != null) {
829 mLocationBarTextWatcher.afterTextChanged(currentText);
830 }
831 return true;
832 }
833 }
834 return super.commitText(text, newCursorPosition);
835 }
836
837 @Override
838 public boolean setComposingText(CharSequence text, int newCursorPosition ) {
839 Editable currentText = getText();
840 int autoCompleteSpanStart = currentText.getSpanStart(mAutocompleteSp an);
841 if (autoCompleteSpanStart >= 0) {
842 int composingEnd = BaseInputConnection.getComposingSpanEnd(curre ntText);
843
844 // On certain device/keyboard combinations, the composing region s are specified
845 // with a noticeable delay after the initial character is typed, and in certain
846 // circumstances it does not check that the current state of the text matches the
847 // expectations of it's composing region.
848 // For example, you can be typing:
849 // chrome://f
850 // Chrome will autocomplete to:
851 // chrome://f[lags]
852 // And after the autocomplete has been set, the keyboard will se t the composing
853 // region to the last character and it assumes it is 'f' as it w as the last
854 // character the keyboard sent. If we commit this composition, the text will
855 // look like:
856 // chrome://flag[f]
857 // And if we use the autocomplete clearing logic below, it will look like:
858 // chrome://f[f]
859 // To work around this, we see if the composition matches all th e characters prior
860 // to the autocomplete and just readjust the composing region to be that subset.
861 //
862 // See crbug.com/366732
863 if (composingEnd == currentText.length()
864 && autoCompleteSpanStart >= text.length()
865 && TextUtils.equals(
866 currentText.subSequence(
867 autoCompleteSpanStart - text.length(),
868 autoCompleteSpanStart),
869 text)) {
870 setComposingRegion(
871 autoCompleteSpanStart - text.length(), autoCompleteS panStart);
872 }
873
874 // Once composing text is being modified, the autocomplete text has been accepted
875 // or has to be deleted.
876 mAutocompleteSpan.clearSpan();
877 Selection.setSelection(currentText, autoCompleteSpanStart);
878 currentText.delete(autoCompleteSpanStart, currentText.length());
879 }
880 return super.setComposingText(text, newCursorPosition);
881 }
882 };
883
884 @Override
885 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
886 mInputConnection.setTarget(super.onCreateInputConnection(outAttrs));
887 return mInputConnection;
888 }
889
890 /**
891 * Emphasize the TLD and second domain of the URL.
892 */
893 public void emphasizeUrl() {
894 Editable url = getText();
895 if (OmniboxUrlEmphasizer.hasEmphasisSpans(url) || hasFocus()) {
896 return;
897 }
898
899 if (url.length() < 1) {
900 return;
901 }
902
903 if (mUrlBarDelegate.showingOriginalUrlForPreview()) {
904 // We will make the whole url as greyed out(Tailing url color). This is the UI
905 // treatment we show to indicate that we are showing original url fo r preview page.
906 OmniboxUrlEmphasizer.greyOutUrl(url, getResources(), mUseDarkColors) ;
907 return;
908 }
909
910 // We retrieve the domain and registry from the full URL (the url bar sh ows a simplified
911 // version of the URL).
912 ChromeTab currentTab = mUrlBarDelegate.getCurrentTab();
913 if (currentTab == null || currentTab.getProfile() == null) return;
914
915 boolean isInternalPage = false;
916 try {
917 String tabUrl = currentTab.getUrl();
918 isInternalPage = UrlUtilities.isInternalScheme(new URI(tabUrl));
919 } catch (URISyntaxException e) {
920 // Ignore as this only is for applying color
921 }
922
923 OmniboxUrlEmphasizer.emphasizeUrl(url, getResources(), currentTab.getPro file(),
924 currentTab.getSecurityLevel(), isInternalPage,
925 mUseDarkColors, mUrlBarDelegate.shouldEmphasizeHttpsScheme());
926 }
927
928 /**
929 * Reset the modifications done to emphasize the TLD and second domain of th e URL.
930 */
931 public void deEmphasizeUrl() {
932 OmniboxUrlEmphasizer.deEmphasizeUrl(getText());
933 }
934
935 /**
936 * Simple span used for tracking the current autocomplete state.
937 */
938 private class AutocompleteSpan {
939 private CharSequence mUserText;
940 private CharSequence mAutocompleteText;
941
942 /**
943 * Adds the span to the current text.
944 * @param userText The user entered text.
945 * @param autocompleteText The autocomplete text being appended.
946 */
947 public void setSpan(CharSequence userText, CharSequence autocompleteText ) {
948 Editable text = getText();
949 text.removeSpan(this);
950 mAutocompleteText = autocompleteText;
951 mUserText = userText;
952 text.setSpan(
953 this,
954 userText.length(),
955 text.length(),
956 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
957 }
958
959 /** Removes this span from the current text and clears the internal stat e. */
960 public void clearSpan() {
961 getText().removeSpan(this);
962 mAutocompleteText = null;
963 mUserText = null;
964 }
965 }
966 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698