Index: chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0ba64c3f7589aa254eb9920bec7ff26d1a2dfd1b |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java |
@@ -0,0 +1,2394 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.omnibox; |
+ |
+import static org.chromium.chrome.browser.toolbar.ToolbarPhone.URL_FOCUS_CHANGE_ANIMATION_DURATION_MS; |
+ |
+import android.animation.Animator; |
+import android.animation.AnimatorListenerAdapter; |
+import android.animation.AnimatorSet; |
+import android.animation.ObjectAnimator; |
+import android.annotation.SuppressLint; |
+import android.app.Activity; |
+import android.content.ClipData; |
+import android.content.ClipboardManager; |
+import android.content.ContentResolver; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.content.res.ColorStateList; |
+import android.graphics.Color; |
+import android.graphics.PorterDuff; |
+import android.graphics.Rect; |
+import android.graphics.drawable.ColorDrawable; |
+import android.graphics.drawable.Drawable; |
+import android.net.Uri; |
+import android.os.Parcelable; |
+import android.os.SystemClock; |
+import android.provider.Settings; |
+import android.speech.RecognizerIntent; |
+import android.text.Editable; |
+import android.text.InputType; |
+import android.text.Selection; |
+import android.text.TextUtils; |
+import android.text.TextWatcher; |
+import android.util.AttributeSet; |
+import android.util.Log; |
+import android.util.Pair; |
+import android.util.SparseArray; |
+import android.view.ActionMode; |
+import android.view.KeyEvent; |
+import android.view.LayoutInflater; |
+import android.view.Menu; |
+import android.view.MenuItem; |
+import android.view.MotionEvent; |
+import android.view.View; |
+import android.view.View.OnClickListener; |
+import android.view.ViewGroup; |
+import android.view.ViewStub; |
+import android.view.inputmethod.BaseInputConnection; |
+import android.widget.FrameLayout; |
+import android.widget.ImageButton; |
+import android.widget.ImageView; |
+import android.widget.ListView; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.CollectionUtil; |
+import org.chromium.base.CommandLine; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.metrics.RecordHistogram; |
+import org.chromium.base.metrics.RecordUserAction; |
+import org.chromium.chrome.browser.ChromeSwitches; |
+import org.chromium.chrome.browser.ContextualMenuBar; |
+import org.chromium.chrome.browser.ContextualMenuBar.ActionBarDelegate; |
+import org.chromium.chrome.browser.CustomSelectionActionModeCallback; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.WebsiteSettingsPopup; |
+import org.chromium.chrome.browser.WindowDelegate; |
+import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper; |
+import org.chromium.chrome.browser.document.BrandColorUtils; |
+import org.chromium.chrome.browser.dom_distiller.DomDistillerServiceFactory; |
+import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils; |
+import org.chromium.chrome.browser.ntp.NativePageFactory; |
+import org.chromium.chrome.browser.ntp.NewTabPage; |
+import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate; |
+import org.chromium.chrome.browser.ntp.NewTabPageUma; |
+import org.chromium.chrome.browser.omnibox.AutocompleteController.OnSuggestionsReceivedListener; |
+import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxResultItem; |
+import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxSuggestionDelegate; |
+import org.chromium.chrome.browser.omnibox.OmniboxSuggestion.Type; |
+import org.chromium.chrome.browser.omnibox.VoiceSuggestionProvider.VoiceResult; |
+import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; |
+import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager; |
+import org.chromium.chrome.browser.profiles.Profile; |
+import org.chromium.chrome.browser.search_engines.TemplateUrlService; |
+import org.chromium.chrome.browser.ssl.ConnectionSecurityHelperSecurityLevel; |
+import org.chromium.chrome.browser.tab.BackgroundContentViewHelper; |
+import org.chromium.chrome.browser.tab.ChromeTab; |
+import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; |
+import org.chromium.chrome.browser.util.FeatureUtilities; |
+import org.chromium.chrome.browser.util.KeyNavigationUtil; |
+import org.chromium.chrome.browser.util.ViewUtils; |
+import org.chromium.chrome.browser.widget.TintedImageButton; |
+import org.chromium.components.dom_distiller.core.DomDistillerService; |
+import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content.browser.accessibility.BrowserAccessibilityManager; |
+import org.chromium.content_public.browser.LoadUrlParams; |
+import org.chromium.content_public.browser.WebContents; |
+import org.chromium.ui.UiUtils; |
+import org.chromium.ui.base.DeviceFormFactor; |
+import org.chromium.ui.base.PageTransition; |
+import org.chromium.ui.base.WindowAndroid; |
+import org.chromium.ui.interpolators.BakedBezierInterpolator; |
+ |
+import java.util.ArrayList; |
+import java.util.HashSet; |
+import java.util.List; |
+ |
+/** |
+ * This class represents the location bar where the user types in URLs and |
+ * search terms. |
+ */ |
+public class LocationBarLayout extends FrameLayout implements OnClickListener, |
+ OnSuggestionsReceivedListener, LocationBar, FakeboxDelegate, |
+ WindowAndroid.IntentCallback { |
+ |
+ // Delay triggering the omnibox results upon key press to allow the location bar to repaint |
+ // with the new characters. |
+ private static final long OMNIBOX_SUGGESTION_START_DELAY_MS = 30; |
+ |
+ private static final int OMNIBOX_CONTAINER_BACKGROUND_FADE_MS = 250; |
+ |
+ // The minimum confidence threshold that will result in navigating directly to a voice search |
+ // response (as opposed to treating it like a typed string in the Omnibox). |
+ private static final float VOICE_SEARCH_CONFIDENCE_NAVIGATE_THRESHOLD = 0.9f; |
+ |
+ private static final int CONTENT_OVERLAY_COLOR = Color.argb(166, 0, 0, 0); |
+ private static final int OMNIBOX_RESULTS_BG_COLOR = Color.rgb(245, 245, 246); |
+ private static final int OMNIBOX_INCOGNITO_RESULTS_BG_COLOR = Color.rgb(50, 50, 50); |
+ |
+ /** |
+ * URI schemes that ContentView can handle. |
+ * |
+ * Copied from UrlUtilities.java. UrlUtilities uses a URI to check for schemes, which |
+ * is more strict than Uri and causes the path stripping to fail. |
+ * |
+ * The following additions have been made: "chrome", "ftp". |
+ */ |
+ private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHashSet( |
+ "about", "data", "file", "ftp", "http", "https", "inline", "javascript", "chrome"); |
+ private static final HashSet<String> UNSUPPORTED_SCHEMES_TO_SPLIT = |
+ CollectionUtil.newHashSet("file", "javascript", "data"); |
+ |
+ protected ImageView mNavigationButton; |
+ protected ImageButton mSecurityButton; |
+ protected TintedImageButton mDeleteButton; |
+ protected TintedImageButton mMicButton; |
+ protected UrlBar mUrlBar; |
+ protected UrlContainer mUrlContainer; |
+ private ContextualMenuBar mContextualMenuBar = null; |
+ |
+ private AutocompleteController mAutocomplete; |
+ |
+ private ToolbarDataProvider mToolbarDataProvider; |
+ private UrlFocusChangeListener mUrlFocusChangeListener; |
+ |
+ private boolean mNativeInitialized; |
+ |
+ private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>(); |
+ |
+ // The type of the navigation button currently showing. |
+ private NavigationButtonType mNavigationButtonType; |
+ |
+ // The type of the security icon currently active. |
+ private int mSecurityIconType; |
+ |
+ private final OmniboxResultsAdapter mSuggestionListAdapter; |
+ private OmniboxSuggestionsList mSuggestionList; |
+ |
+ private final List<OmniboxResultItem> mSuggestionItems; |
+ |
+ /** |
+ * The text shown in the URL bar (user text + inline autocomplete) after the most recent set of |
+ * omnibox suggestions was received. When the user presses enter in the omnibox, this value is |
+ * compared to the URL bar text to determine whether the first suggestion is still valid. |
+ */ |
+ private String mUrlTextAfterSuggestionsReceived; |
+ |
+ // Set to true when the URL bar text is modified programmatically. Initially set |
+ // to true until the old state has been loaded. |
+ private boolean mIgnoreURLBarModification = true; |
+ private boolean mIgnoreOmniboxItemSelection = true; |
+ |
+ private boolean mLastUrlEditWasDelete = false; |
+ |
+ // True if we are showing the search query instead of the url. |
+ private boolean mQueryInTheOmnibox = false; |
+ |
+ private String mOriginalUrl = ""; |
+ |
+ private WindowAndroid mWindowAndroid; |
+ private WindowDelegate mWindowDelegate; |
+ |
+ private Runnable mRequestSuggestions; |
+ |
+ private ViewGroup mOmniboxResultsContainer; |
+ private ObjectAnimator mFadeInOmniboxBackgroundAnimator; |
+ private ObjectAnimator mFadeOutOmniboxBackgroundAnimator; |
+ private Animator mOmniboxBackgroundAnimator; |
+ |
+ private boolean mSuggestionsShown; |
+ private boolean mUrlHasFocus; |
+ private boolean mUrlFocusedFromFakebox; |
+ private boolean mHasRecordedUrlFocusSource; |
+ |
+ // Set to true when the user has started typing new input in the omnibox, set to false |
+ // when the omnibox loses focus or becomes empty. |
+ private boolean mHasStartedNewOmniboxEditSession; |
+ // The timestamp (using SystemClock.elapsedRealtime()) at the point when the user started |
+ // modifying the omnibox with new input. |
+ private long mNewOmniboxEditSessionTimestamp = -1; |
+ |
+ private boolean mSecurityButtonShown; |
+ |
+ private AnimatorSet mLocationBarIconActiveAnimator; |
+ private AnimatorSet mSecurityButtonShowAnimator; |
+ private AnimatorSet mNavigationIconShowAnimator; |
+ |
+ private TextWatcher mTextWatcher; |
+ |
+ private OmniboxPrerender mOmniboxPrerender; |
+ |
+ private View mFocusedTabView; |
+ private int mFocusedTabImportantForAccessibilityState; |
+ private BrowserAccessibilityManager mFocusedTabAccessibilityManager; |
+ |
+ // True if we are showing original url for preview page. This is will be true when there is a |
+ // background page loaded in background content view. |
+ private boolean mShowingOriginalUrlForPreview; |
+ |
+ private boolean mSuggestionModalShown; |
+ private boolean mUseDarkColors; |
+ |
+ // True if the user has just selected a suggestion from the suggestion list. This suppresses |
+ // the recording of the dismissal of the suggestion list. (The list is only considered to have |
+ // been dismissed if the user didn't choose one of the suggestions shown.) This signal is used |
+ // instead of a parameter to hideSuggestions because that method is often called from multiple |
+ // code paths in a not necessarily obvious or even deterministic order. |
+ private boolean mSuggestionSelectionInProgress; |
+ |
+ private CustomSelectionActionModeCallback mDefaultActionModeCallbackForTextEdit; |
+ |
+ /** |
+ * Listener for receiving the messages related with interacting with the omnibox during startup. |
+ */ |
+ public interface OmniboxLivenessListener { |
+ /** |
+ * Called after the first draw when the omnibox can receive touch events. |
+ */ |
+ void onOmniboxInteractive(); |
+ |
+ /** |
+ * Called when the native libraries are loaded and listeners with native components |
+ * have been initialized. |
+ */ |
+ void onOmniboxFullyFunctional(); |
+ |
+ /** |
+ * Called when the omnibox is focused. |
+ */ |
+ void onOmniboxFocused(); |
+ } |
+ |
+ /** |
+ * Class to handle text changes in the URL bar, ensuring that the appropriate |
+ * buttons are displayed as the text changes, and requesting suggestions. |
+ */ |
+ private final class UrlBarTextWatcher implements TextWatcher { |
+ @Override |
+ public void afterTextChanged(final Editable editableText) { |
+ updateDeleteButtonVisibility(); |
+ updateNavigationButton(); |
+ |
+ if (mIgnoreURLBarModification) return; |
+ |
+ if (!mHasStartedNewOmniboxEditSession && mNativeInitialized) { |
+ RecordUserAction.record("MobileFirstEditInOmnibox"); |
+ mAutocomplete.resetSession(); |
+ mHasStartedNewOmniboxEditSession = true; |
+ mNewOmniboxEditSessionTimestamp = SystemClock.elapsedRealtime(); |
+ } |
+ |
+ if (!isInTouchMode() && mSuggestionList != null) { |
+ mSuggestionList.setSelection(0); |
+ } |
+ |
+ final String textWithoutAutocomplete = mUrlBar.getTextWithoutAutocomplete(); |
+ |
+ stopAutocomplete(false); |
+ if (TextUtils.isEmpty(textWithoutAutocomplete)) { |
+ hideSuggestions(); |
+ startZeroSuggest(); |
+ } else { |
+ assert mRequestSuggestions == null : "Multiple omnibox requests in flight."; |
+ mRequestSuggestions = new Runnable() { |
+ @Override |
+ public void run() { |
+ boolean preventAutocomplete = !shouldAutocomplete() |
+ || (editableText != null && Selection.getSelectionEnd(editableText) |
+ != editableText.length()); |
+ mRequestSuggestions = null; |
+ if (getCurrentTab() == null) return; |
+ mAutocomplete.start( |
+ getCurrentTab().getProfile(), |
+ getCurrentTab().getUrl(), |
+ textWithoutAutocomplete, preventAutocomplete); |
+ } |
+ }; |
+ if (mNativeInitialized) { |
+ postDelayed(mRequestSuggestions, OMNIBOX_SUGGESTION_START_DELAY_MS); |
+ } else { |
+ mDeferredNativeRunnables.add(mRequestSuggestions); |
+ } |
+ } |
+ |
+ // Occasionally, was seeing the selection in the URL not being cleared during |
+ // very rapid editing. This is here to hopefully force a selection reset during |
+ // deletes. |
+ if (mLastUrlEditWasDelete) mUrlBar.setSelection(mUrlBar.getSelectionStart()); |
+ } |
+ |
+ @Override |
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
+ cancelPendingAutocompleteStart(); |
+ } |
+ |
+ @Override |
+ public void onTextChanged(CharSequence s, int start, int before, int count) { |
+ // We need to determine whether the text change was triggered by a delete (so we |
+ // don't autocomplete of the entered text in that case). With soft-keyboard, there |
+ // is no way to know that the delete button was pressed, so we track text removal |
+ // changes. |
+ mLastUrlEditWasDelete = (count == 0); |
+ } |
+ } |
+ |
+ /** |
+ * Class to handle input from a hardware keyboard when the focus is on the URL bar. In |
+ * particular, handle navigating the suggestions list from the keyboard. |
+ */ |
+ private final class UrlBarKeyListener implements OnKeyListener { |
+ @Override |
+ public boolean onKey(View v, int keyCode, KeyEvent event) { |
+ if (KeyNavigationUtil.isGoDown(event) |
+ && mSuggestionList != null |
+ && mSuggestionList.isShown()) { |
+ int suggestionCount = mSuggestionListAdapter.getCount(); |
+ if (mSuggestionList.getSelectedItemPosition() < suggestionCount - 1) { |
+ if (suggestionCount > 0) mIgnoreOmniboxItemSelection = false; |
+ } else { |
+ // Do not pass down events when the last item is already selected as it will |
+ // dismiss the suggestion list. |
+ return true; |
+ } |
+ |
+ if (mSuggestionList.getSelectedItemPosition() |
+ == ListView.INVALID_POSITION) { |
+ // When clearing the selection after a text change, state is not reset |
+ // correctly so hitting down again will cause it to start from the previous |
+ // selection point. We still have to send the key down event to let the list |
+ // view items take focus, but then we select the first item explicitly. |
+ boolean result = mSuggestionList.onKeyDown(keyCode, event); |
+ mSuggestionList.setSelection(0); |
+ return result; |
+ } else { |
+ return mSuggestionList.onKeyDown(keyCode, event); |
+ } |
+ } else if (KeyNavigationUtil.isGoUp(event) |
+ && mSuggestionList != null |
+ && mSuggestionList.isShown()) { |
+ if (mSuggestionList.getSelectedItemPosition() != 0 |
+ && mSuggestionListAdapter.getCount() > 0) { |
+ mIgnoreOmniboxItemSelection = false; |
+ } |
+ return mSuggestionList.onKeyDown(keyCode, event); |
+ } else if (KeyNavigationUtil.isGoRight(event) |
+ && mSuggestionList != null |
+ && mSuggestionList.isShown() |
+ && mSuggestionList.getSelectedItemPosition() |
+ != ListView.INVALID_POSITION) { |
+ OmniboxResultItem selectedItem = |
+ (OmniboxResultItem) mSuggestionListAdapter.getItem( |
+ mSuggestionList.getSelectedItemPosition()); |
+ // Set the UrlBar text to empty, so that it will trigger a text change when we |
+ // set the text to the suggestion again. |
+ setUrlBarText(null, null, ""); |
+ mUrlBar.setText(selectedItem.getSuggestion().getFillIntoEdit()); |
+ mSuggestionList.setSelection(0); |
+ mUrlBar.setSelection(mUrlBar.getText().length()); |
+ return true; |
+ } else if (KeyNavigationUtil.isEnter(event) |
+ && LocationBarLayout.this.getVisibility() == VISIBLE) { |
+ UiUtils.hideKeyboard(mUrlBar); |
+ mSuggestionSelectionInProgress = true; |
+ final String urlText = mUrlBar.getQueryText(); |
+ if (mNativeInitialized) { |
+ findMatchAndLoadUrl(urlText); |
+ } else { |
+ mDeferredNativeRunnables.add(new Runnable() { |
+ @Override |
+ public void run() { |
+ findMatchAndLoadUrl(urlText); |
+ } |
+ }); |
+ } |
+ return true; |
+ } |
+ |
+ return false; |
+ } |
+ |
+ private void findMatchAndLoadUrl(String urlText) { |
+ int suggestionMatchPosition; |
+ OmniboxSuggestion suggestionMatch; |
+ |
+ if (mSuggestionList != null |
+ && mSuggestionList.isShown() |
+ && mSuggestionList.getSelectedItemPosition() |
+ != ListView.INVALID_POSITION) { |
+ // Bluetooth keyboard case: the user highlighted a suggestion with the arrow |
+ // keys, then pressed enter. |
+ suggestionMatchPosition = mSuggestionList.getSelectedItemPosition(); |
+ OmniboxResultItem selectedItem = |
+ (OmniboxResultItem) mSuggestionListAdapter.getItem(suggestionMatchPosition); |
+ suggestionMatch = selectedItem.getSuggestion(); |
+ } else if (!mSuggestionItems.isEmpty() |
+ && urlText.equals(mUrlTextAfterSuggestionsReceived)) { |
+ // Common case: the user typed something, received suggestions, then pressed enter. |
+ suggestionMatch = mSuggestionItems.get(0).getSuggestion(); |
+ suggestionMatchPosition = 0; |
+ } else { |
+ // Less common case: there are no valid omnibox suggestions. This can happen if the |
+ // user tapped the URL bar to dismiss the suggestions, then pressed enter. This can |
+ // also happen if the user presses enter before any suggestions have been received |
+ // from the autocomplete controller. |
+ suggestionMatch = mAutocomplete.classify(urlText); |
+ suggestionMatchPosition = 0; |
+ |
+ // If urlText couldn't be classified, bail. |
+ if (suggestionMatch == null) return; |
+ } |
+ |
+ String suggestionMatchUrl = updateSuggestionUrlIfNeeded(suggestionMatch, |
+ suggestionMatchPosition); |
+ |
+ // It's important to use the page transition from the suggestion or we might end |
+ // up saving generated URLs as typed URLs, which would then pollute the subsequent |
+ // omnibox results. |
+ loadUrlFromOmniboxMatch(suggestionMatchUrl, suggestionMatch.getTransition(), |
+ suggestionMatchPosition, suggestionMatch.getType()); |
+ } |
+ } |
+ |
+ /** |
+ * Specifies the types of buttons shown to signify different types of navigation elements. |
+ */ |
+ protected enum NavigationButtonType { |
+ PAGE, |
+ MAGNIFIER, |
+ EMPTY, |
+ } |
+ |
+ /** |
+ * @param outRect Populated with a {@link Rect} that represents the {@link Tab} specific content |
+ * of this {@link LocationBar}. |
+ */ |
+ public void getContentRect(Rect outRect) { |
+ outRect.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), |
+ getHeight() - getPaddingBottom()); |
+ } |
+ |
+ /** |
+ * A widget for showing a list of omnibox suggestions. |
+ */ |
+ @VisibleForTesting |
+ public class OmniboxSuggestionsList extends ListView { |
+ private final int mSuggestionHeight; |
+ private final int mSuggestionAnswerHeight; |
+ private final View mAnchorView; |
+ |
+ private final int[] mTempPosition = new int[2]; |
+ private final Rect mTempRect = new Rect(); |
+ |
+ private final int mBackgroundVerticalPadding; |
+ |
+ private float mMaxRequiredWidth; |
+ private float mMaxMatchContentsWidth; |
+ |
+ /** |
+ * Constructs a new list designed for containing omnibox suggestions. |
+ * @param context Context used for contained views. |
+ */ |
+ public OmniboxSuggestionsList(Context context) { |
+ super(context, null, android.R.attr.dropDownListViewStyle); |
+ setDivider(null); |
+ setFocusable(true); |
+ setFocusableInTouchMode(true); |
+ |
+ mSuggestionHeight = context.getResources().getDimensionPixelOffset( |
+ R.dimen.omnibox_suggestion_height); |
+ mSuggestionAnswerHeight = context.getResources().getDimensionPixelOffset( |
+ R.dimen.omnibox_suggestion_answer_height); |
+ |
+ int paddingTop = context.getResources().getDimensionPixelOffset( |
+ R.dimen.omnibox_suggestion_list_padding_top); |
+ int paddingBottom = context.getResources().getDimensionPixelOffset( |
+ R.dimen.omnibox_suggestion_list_padding_bottom); |
+ ApiCompatibilityUtils.setPaddingRelative(this, 0, paddingTop, 0, paddingBottom); |
+ |
+ Drawable background = getSuggestionPopupBackground(); |
+ setBackground(background); |
+ background.getPadding(mTempRect); |
+ |
+ mBackgroundVerticalPadding = |
+ mTempRect.top + mTempRect.bottom + getPaddingTop() + getPaddingBottom(); |
+ |
+ mAnchorView = LocationBarLayout.this.getRootView().findViewById(R.id.toolbar); |
+ } |
+ |
+ private void show() { |
+ updateLayoutParams(); |
+ if (getVisibility() != VISIBLE) { |
+ mIgnoreOmniboxItemSelection = true; // Reset to default value. |
+ setVisibility(VISIBLE); |
+ if (getSelectedItemPosition() != 0) setSelection(0); |
+ } |
+ updateSuggestionsLayoutDirection(mUrlBar.getUrlDirection()); |
+ } |
+ |
+ /** |
+ * Invalidates all of the suggestion views in the list. Only applicable when this |
+ * is visible. |
+ */ |
+ public void invalidateSuggestionViews() { |
+ if (!isShown()) return; |
+ ListView suggestionsList = mSuggestionList; |
+ for (int i = 0; i < suggestionsList.getChildCount(); i++) { |
+ if (suggestionsList.getChildAt(i) instanceof SuggestionView) { |
+ suggestionsList.getChildAt(i).postInvalidateOnAnimation(); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Updates the maximum widths required to render the suggestions. |
+ * This is needed for infinite suggestions where we try to vertically align the leading |
+ * ellipsis. |
+ */ |
+ public void resetMaxTextWidths() { |
+ mMaxRequiredWidth = 0; |
+ mMaxMatchContentsWidth = 0; |
+ } |
+ |
+ /** |
+ * Updates the max text width values for the suggestions. |
+ * @param requiredWidth a new required width. |
+ * @param matchContentsWidth a new match contents width. |
+ */ |
+ public void updateMaxTextWidths(float requiredWidth, float matchContentsWidth) { |
+ mMaxRequiredWidth = Math.max(mMaxRequiredWidth, requiredWidth); |
+ mMaxMatchContentsWidth = Math.max(mMaxMatchContentsWidth, matchContentsWidth); |
+ } |
+ |
+ /** |
+ * @return max required width for the suggestions. |
+ */ |
+ public float getMaxRequiredWidth() { |
+ return mMaxRequiredWidth; |
+ } |
+ |
+ /** |
+ * @return max match contents width for the suggestions. |
+ */ |
+ public float getMaxMatchContentsWidth() { |
+ return mMaxMatchContentsWidth; |
+ } |
+ |
+ private void updateLayoutParams() { |
+ boolean updateLayout = false; |
+ FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams(); |
+ if (layoutParams == null) { |
+ layoutParams = new FrameLayout.LayoutParams(0, 0); |
+ setLayoutParams(layoutParams); |
+ } |
+ |
+ // Compare the relative positions of the anchor view to the list parent view to |
+ // determine the offset to apply to the suggestions list. By using layout positioning, |
+ // this avoids issues where getLocationInWindow can be inaccurate on certain devices. |
+ View contentView = |
+ LocationBarLayout.this.getRootView().findViewById(android.R.id.content); |
+ ViewUtils.getRelativeLayoutPosition(contentView, mAnchorView, mTempPosition); |
+ int anchorX = mTempPosition[0]; |
+ int anchorY = mTempPosition[1]; |
+ |
+ ViewUtils.getRelativeLayoutPosition(contentView, (View) getParent(), mTempPosition); |
+ int parentY = mTempPosition[1]; |
+ |
+ int anchorBottomRelativeToContent = anchorY + mAnchorView.getMeasuredHeight(); |
+ int desiredTopMargin = anchorBottomRelativeToContent - parentY; |
+ if (layoutParams.topMargin != desiredTopMargin) { |
+ layoutParams.topMargin = desiredTopMargin; |
+ updateLayout = true; |
+ } |
+ |
+ int contentLeft = contentView.getLeft(); |
+ int anchorLeftRelativeToContent = anchorX - contentLeft; |
+ if (layoutParams.leftMargin != anchorLeftRelativeToContent) { |
+ layoutParams.leftMargin = anchorLeftRelativeToContent; |
+ updateLayout = true; |
+ } |
+ |
+ getWindowDelegate().getWindowVisibleDisplayFrame(mTempRect); |
+ int decorHeight = getWindowDelegate().getDecorViewHeight(); |
+ int availableViewportHeight = Math.min(mTempRect.height(), decorHeight); |
+ int availableListHeight = availableViewportHeight - anchorBottomRelativeToContent; |
+ int desiredHeight = Math.min(availableListHeight, getIdealHeight()); |
+ if (layoutParams.height != desiredHeight) { |
+ layoutParams.height = desiredHeight; |
+ updateLayout = true; |
+ } |
+ |
+ int desiredWidth = getDesiredWidth(); |
+ if (layoutParams.width != desiredWidth) { |
+ layoutParams.width = desiredWidth; |
+ updateLayout = true; |
+ } |
+ |
+ if (updateLayout) requestLayout(); |
+ } |
+ |
+ private int getIdealHeight() { |
+ int idealListSize = mBackgroundVerticalPadding; |
+ for (int i = 0; i < mSuggestionItems.size(); i++) { |
+ OmniboxResultItem item = mSuggestionItems.get(i); |
+ if (!TextUtils.isEmpty(item.getSuggestion().getAnswerContents())) { |
+ idealListSize += mSuggestionAnswerHeight; |
+ } else { |
+ idealListSize += mSuggestionHeight; |
+ } |
+ } |
+ return idealListSize; |
+ } |
+ |
+ private int getDesiredWidth() { |
+ return mAnchorView.getWidth(); |
+ } |
+ |
+ @Override |
+ public void onWindowFocusChanged(boolean hasWindowFocus) { |
+ super.onWindowFocusChanged(hasWindowFocus); |
+ if (!hasWindowFocus && !mSuggestionModalShown) hideSuggestions(); |
+ } |
+ |
+ @Override |
+ protected void layoutChildren() { |
+ super.layoutChildren(); |
+ // In ICS, the selected view is not marked as selected despite calling setSelection(0), |
+ // so we bootstrap this after the children have been laid out. |
+ if (!isInTouchMode() && getSelectedView() != null) { |
+ getSelectedView().setSelected(true); |
+ } |
+ } |
+ } |
+ |
+ public LocationBarLayout(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ |
+ LayoutInflater.from(context).inflate(R.layout.location_bar, this, true); |
+ mNavigationButton = (ImageView) findViewById(R.id.navigation_button); |
+ assert mNavigationButton != null : "Missing navigation type view."; |
+ mNavigationButtonType = DeviceFormFactor.isTablet(context) |
+ ? NavigationButtonType.PAGE : NavigationButtonType.EMPTY; |
+ |
+ mSecurityButton = (ImageButton) findViewById(R.id.security_button); |
+ mSecurityIconType = ConnectionSecurityHelperSecurityLevel.NONE; |
+ |
+ mDeleteButton = (TintedImageButton) findViewById(R.id.delete_button); |
+ |
+ mUrlBar = (UrlBar) findViewById(R.id.url_bar); |
+ // The HTC Sense IME will attempt to autocomplete words in the Omnibox when Prediction is |
+ // enabled. We want to disable this feature and rely on the Omnibox's implementation. |
+ // Their IME does not respect ~TYPE_TEXT_FLAG_AUTO_COMPLETE nor any of the other InputType |
+ // options I tried, but setting the filter variation prevents it. Sadly, it also removes |
+ // the .com button, but the prediction was buggy as it would autocomplete words even when |
+ // typing at the beginning of the omnibox text when other content was present (messing up |
+ // what was previously there). See bug: http://b/issue?id=6200071 |
+ String defaultIme = Settings.Secure.getString(getContext().getContentResolver(), |
+ Settings.Secure.DEFAULT_INPUT_METHOD); |
+ if (defaultIme != null && defaultIme.contains("com.htc.android.htcime")) { |
+ mUrlBar.setInputType(mUrlBar.getInputType() | InputType.TYPE_TEXT_VARIATION_FILTER); |
+ } |
+ mUrlBar.setDelegate(this); |
+ |
+ mUrlContainer = (UrlContainer) findViewById(R.id.url_container); |
+ |
+ mSuggestionItems = new ArrayList<OmniboxResultItem>(); |
+ mSuggestionListAdapter = new OmniboxResultsAdapter(getContext(), this, mSuggestionItems); |
+ |
+ mMicButton = (TintedImageButton) findViewById(R.id.mic_button); |
+ } |
+ |
+ @Override |
+ protected void onFinishInflate() { |
+ super.onFinishInflate(); |
+ |
+ mUrlBar.setCursorVisible(false); |
+ mNavigationButton.setVisibility(VISIBLE); |
+ mSecurityButton.setVisibility(INVISIBLE); |
+ |
+ setLayoutTransition(null); |
+ |
+ AnimatorListenerAdapter iconChangeAnimatorListener = new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ if (animation == mSecurityButtonShowAnimator) { |
+ mNavigationButton.setVisibility(INVISIBLE); |
+ } else if (animation == mNavigationIconShowAnimator) { |
+ mSecurityButton.setVisibility(INVISIBLE); |
+ } |
+ } |
+ |
+ @Override |
+ public void onAnimationStart(Animator animation) { |
+ if (animation == mSecurityButtonShowAnimator) { |
+ mSecurityButton.setVisibility(VISIBLE); |
+ } else if (animation == mNavigationIconShowAnimator) { |
+ mNavigationButton.setVisibility(VISIBLE); |
+ } |
+ } |
+ }; |
+ |
+ mSecurityButtonShowAnimator = new AnimatorSet(); |
+ mSecurityButtonShowAnimator.playTogether( |
+ ObjectAnimator.ofFloat(mNavigationButton, ALPHA, 0), |
+ ObjectAnimator.ofFloat(mSecurityButton, ALPHA, 1)); |
+ mSecurityButtonShowAnimator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ mSecurityButtonShowAnimator.addListener(iconChangeAnimatorListener); |
+ |
+ mNavigationIconShowAnimator = new AnimatorSet(); |
+ mNavigationIconShowAnimator.playTogether( |
+ ObjectAnimator.ofFloat(mNavigationButton, ALPHA, 1), |
+ ObjectAnimator.ofFloat(mSecurityButton, ALPHA, 0)); |
+ mNavigationIconShowAnimator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ mNavigationIconShowAnimator.addListener(iconChangeAnimatorListener); |
+ |
+ mUrlBar.setOnKeyListener(new UrlBarKeyListener()); |
+ |
+ mTextWatcher = new UrlBarTextWatcher(); |
+ mUrlBar.setLocationBarTextWatcher(mTextWatcher); |
+ |
+ // mLocationBar's direction is tied to this UrlBar's text direction. Icons inside the |
+ // location bar, e.g. lock, refresh, X, should be reversed if UrlBar's text is RTL. |
+ mUrlBar.setUrlDirectionListener(new UrlBar.UrlDirectionListener() { |
+ @Override |
+ public void onUrlDirectionChanged(int layoutDirection) { |
+ ApiCompatibilityUtils.setLayoutDirection(LocationBarLayout.this, layoutDirection); |
+ updateSuggestionsLayoutDirection(layoutDirection); |
+ } |
+ }); |
+ |
+ mUrlBar.setSelectAllOnFocus(true); |
+ } |
+ |
+ @Override |
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
+ updateLayoutParams(); |
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
+ } |
+ |
+ private void updateSuggestionsLayoutDirection(int layoutDirection) { |
+ if (mSuggestionList != null && mSuggestionList.isShown()) { |
+ ListView listView = mSuggestionList; |
+ ApiCompatibilityUtils.setLayoutDirection(listView, layoutDirection); |
+ for (int i = 0; i < listView.getChildCount(); i++) { |
+ ApiCompatibilityUtils.setLayoutDirection(listView.getChildAt(i), |
+ layoutDirection); |
+ } |
+ } |
+ } |
+ |
+ @Override |
+ public void initializeControls(WindowDelegate windowDelegate, |
+ ActionBarDelegate actionBarDelegate, WindowAndroid windowAndroid) { |
+ mWindowDelegate = windowDelegate; |
+ |
+ mContextualMenuBar = new ContextualMenuBar(getContext(), actionBarDelegate); |
+ mContextualMenuBar.setCustomSelectionActionModeCallback( |
+ new CustomSelectionActionModeCallback() { |
+ @Override |
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
+ super.onPrepareActionMode(mode, menu); |
+ mode.getMenuInflater().inflate(R.menu.textselectionmenu, menu); |
+ return true; |
+ } |
+ |
+ @Override |
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
+ if (item.getItemId() == R.id.copy_url) { |
+ ClipboardManager clipboard = |
+ (ClipboardManager) getContext().getSystemService( |
+ Context.CLIPBOARD_SERVICE); |
+ ClipData clip = ClipData.newPlainText("url", mOriginalUrl); |
+ clipboard.setPrimaryClip(clip); |
+ mode.finish(); |
+ return true; |
+ } else { |
+ return super.onActionItemClicked(mode, item); |
+ } |
+ } |
+ }); |
+ |
+ mWindowAndroid = windowAndroid; |
+ mMicButton.setOnClickListener(this); |
+ } |
+ |
+ /** |
+ * @return The WindowDelegate for the LocationBar. This should be used for all Window related |
+ * state queries. |
+ */ |
+ protected WindowDelegate getWindowDelegate() { |
+ return mWindowDelegate; |
+ } |
+ |
+ /** |
+ * Handles native dependent initialization for this class. |
+ */ |
+ @Override |
+ public void onNativeLibraryReady() { |
+ mNativeInitialized = true; |
+ |
+ mSecurityButton.setOnClickListener(this); |
+ mNavigationButton.setOnClickListener(this); |
+ updateMicButtonState(); |
+ mDeleteButton.setOnClickListener(this); |
+ |
+ mAutocomplete = new AutocompleteController(this); |
+ |
+ mOmniboxPrerender = new OmniboxPrerender(); |
+ |
+ for (Runnable deferredRunnable : mDeferredNativeRunnables) { |
+ post(deferredRunnable); |
+ } |
+ mDeferredNativeRunnables.clear(); |
+ |
+ mUrlBar.onOmniboxFullyFunctional(); |
+ |
+ updateCustomSelectionActionModeCallback(); |
+ updateVisualsForState(); |
+ } |
+ |
+ /** |
+ * @return Whether or not to animate icon changes. |
+ */ |
+ protected boolean shouldAnimateIconChanges() { |
+ return mUrlHasFocus; |
+ } |
+ |
+ /** |
+ * Sets the autocomplete controller for the location bar. |
+ * |
+ * @param controller The controller that will handle autocomplete/omnibox suggestions. |
+ * @note Only used for testing. |
+ */ |
+ @VisibleForTesting |
+ public void setAutocompleteController(AutocompleteController controller) { |
+ mAutocomplete = controller; |
+ } |
+ |
+ /** |
+ * Updates the profile used for generating autocomplete suggestions. |
+ * @param profile The profile to be used. |
+ */ |
+ @Override |
+ public void setAutocompleteProfile(Profile profile) { |
+ // This will only be called once at least one tab exists, and the tab model is told to |
+ // update its state. During Chrome initialization the tab model update happens after the |
+ // call to onNativeLibraryReady, so this assert will not fire. |
+ assert mNativeInitialized : |
+ "Setting Autocomplete Profile before native side initialized"; |
+ mAutocomplete.setProfile(profile); |
+ mOmniboxPrerender.initializeForProfile(profile); |
+ } |
+ |
+ private void changeLocationBarIcon(boolean showSecurityButton) { |
+ if (mLocationBarIconActiveAnimator != null && mLocationBarIconActiveAnimator.isRunning()) { |
+ mLocationBarIconActiveAnimator.cancel(); |
+ } |
+ View viewToBeShown = showSecurityButton ? mSecurityButton : mNavigationButton; |
+ if (viewToBeShown.getVisibility() == VISIBLE && viewToBeShown.getAlpha() == 1) { |
+ return; |
+ } |
+ if (showSecurityButton) { |
+ mLocationBarIconActiveAnimator = mSecurityButtonShowAnimator; |
+ } else { |
+ mLocationBarIconActiveAnimator = mNavigationIconShowAnimator; |
+ } |
+ if (shouldAnimateIconChanges()) { |
+ mLocationBarIconActiveAnimator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ } else { |
+ mLocationBarIconActiveAnimator.setDuration(0); |
+ } |
+ mLocationBarIconActiveAnimator.start(); |
+ } |
+ |
+ @Override |
+ public void onUrlPreFocusChanged(boolean gainFocus) { |
+ if (mToolbarDataProvider == null || mToolbarDataProvider.getTab() == null) return; |
+ |
+ if (!mQueryInTheOmnibox |
+ && FeatureUtilities.isDocumentMode(getContext()) |
+ && !TextUtils.isEmpty(mUrlBar.getText())) { |
+ mUrlBar.setUrl(mToolbarDataProvider.getTab().getUrl(), null); |
+ } |
+ } |
+ |
+ @Override |
+ public void setUrlBarFocus(boolean shouldBeFocused) { |
+ if (shouldBeFocused) { |
+ mUrlBar.requestFocus(); |
+ } else { |
+ mUrlBar.clearFocus(); |
+ } |
+ } |
+ |
+ @Override |
+ public long getFirstUrlBarFocusTime() { |
+ return mUrlBar.getFirstFocusTime(); |
+ } |
+ |
+ /** |
+ * Triggered when the URL input field has gained or lost focus. |
+ * @param hasFocus Whether the URL field has gained focus. |
+ */ |
+ public void onUrlFocusChange(boolean hasFocus) { |
+ mUrlHasFocus = hasFocus; |
+ updateFocusSource(hasFocus); |
+ updateDeleteButtonVisibility(); |
+ Tab currentTab = getCurrentTab(); |
+ if (hasFocus) { |
+ mUrlBar.deEmphasizeUrl(); |
+ } else { |
+ hideSuggestions(); |
+ |
+ // Focus change caused by a close-tab may result in an invalid current tab. |
+ if (currentTab != null) { |
+ setUrlToPageUrl(); |
+ emphasizeUrl(); |
+ } |
+ } |
+ |
+ if (getToolbarDataProvider().isUsingBrandColor()) { |
+ updateVisualsForState(); |
+ if (mUrlHasFocus) mUrlBar.selectAll(); |
+ } |
+ |
+ if (mUrlFocusChangeListener != null) mUrlFocusChangeListener.onUrlFocusChange(hasFocus); |
+ changeLocationBarIcon( |
+ (!DeviceFormFactor.isTablet(getContext()) || !hasFocus) && isSecurityButtonShown()); |
+ mUrlBar.setCursorVisible(hasFocus); |
+ if (mQueryInTheOmnibox) mUrlBar.setSelection(mUrlBar.getSelectionEnd()); |
+ |
+ updateOmniboxResultsContainer(); |
+ if (hasFocus) updateOmniboxResultsContainerBackground(true); |
+ |
+ if (hasFocus && currentTab != null && !currentTab.isIncognito()) { |
+ if (mNativeInitialized |
+ && TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()) { |
+ GeolocationHeader.primeLocationForGeoHeader(getContext()); |
+ } else { |
+ mDeferredNativeRunnables.add(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()) { |
+ GeolocationHeader.primeLocationForGeoHeader(getContext()); |
+ } |
+ } |
+ }); |
+ } |
+ } |
+ |
+ if (mNativeInitialized) { |
+ startZeroSuggest(); |
+ } else { |
+ mDeferredNativeRunnables.add(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (TextUtils.isEmpty(mUrlBar.getQueryText())) { |
+ startZeroSuggest(); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ // Add and remove text watcher from the URL bar with focus, so that it's |
+ // not called when we modify the displayed information on focus. |
+ if (hasFocus) { |
+ mUrlBar.addTextChangedListener(mTextWatcher); |
+ } else { |
+ mUrlBar.removeTextChangedListener(mTextWatcher); |
+ } |
+ |
+ if (!hasFocus) { |
+ mHasStartedNewOmniboxEditSession = false; |
+ mNewOmniboxEditSessionTimestamp = -1; |
+ } |
+ } |
+ |
+ /** |
+ * Make a zero suggest request if native is loaded, the URL bar has focus, and the |
+ * current tab is not incognito. |
+ */ |
+ private void startZeroSuggest() { |
+ // Reset "edited" state in the omnibox if zero suggest is triggered -- new edits |
+ // now count as a new session. |
+ mHasStartedNewOmniboxEditSession = false; |
+ mNewOmniboxEditSessionTimestamp = -1; |
+ Tab currentTab = getCurrentTab(); |
+ if (mNativeInitialized |
+ && mUrlHasFocus |
+ && currentTab != null |
+ && !currentTab.isIncognito()) { |
+ mAutocomplete.startZeroSuggest(currentTab.getProfile(), mUrlBar.getQueryText(), |
+ currentTab.getUrl(), mQueryInTheOmnibox, mUrlFocusedFromFakebox); |
+ } |
+ } |
+ |
+ @Override |
+ public void setDefaultTextEditActionModeCallback(CustomSelectionActionModeCallback callback) { |
+ mDefaultActionModeCallbackForTextEdit = callback; |
+ } |
+ |
+ /** |
+ * If query in the omnibox, sets UrlBar's ActionModeCallback to show copy url button. Else, |
+ * it is set to the default one. |
+ */ |
+ private void updateCustomSelectionActionModeCallback() { |
+ if (mQueryInTheOmnibox) { |
+ mUrlBar.setCustomSelectionActionModeCallback( |
+ mContextualMenuBar.getCustomSelectionActionModeCallback()); |
+ } else { |
+ mUrlBar.setCustomSelectionActionModeCallback(mDefaultActionModeCallbackForTextEdit); |
+ } |
+ } |
+ |
+ @Override |
+ public void requestUrlFocusFromFakebox(String pastedText) { |
+ mUrlFocusedFromFakebox = true; |
+ mUrlBar.requestFocus(); |
+ |
+ if (pastedText != null) { |
+ // This must be happen after requestUrlFocus(), which changes the selection. |
+ mUrlBar.setUrl(pastedText, null); |
+ mUrlBar.setSelection(mUrlBar.getText().length()); |
+ } |
+ } |
+ |
+ /** |
+ * Sets the toolbar that owns this LocationBar. |
+ */ |
+ @Override |
+ public void setToolbarDataProvider(ToolbarDataProvider toolbarDataProvider) { |
+ mToolbarDataProvider = toolbarDataProvider; |
+ |
+ mUrlBar.setOnFocusChangeListener(new View.OnFocusChangeListener() { |
+ @Override |
+ public void onFocusChange(View v, final boolean hasFocus) { |
+ onUrlFocusChange(hasFocus); |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ public void setMenuButtonHelper(AppMenuButtonHelper helper) { } |
+ |
+ @Override |
+ public View getMenuAnchor() { |
+ return null; |
+ } |
+ |
+ /** |
+ * Sets the URL focus change listner that will be notified when the URL gains or loses focus. |
+ * @param listener The listener to be registered. |
+ */ |
+ @Override |
+ public void setUrlFocusChangeListener(UrlFocusChangeListener listener) { |
+ mUrlFocusChangeListener = listener; |
+ } |
+ |
+ /** |
+ * @return The toolbar data provider. |
+ */ |
+ @VisibleForTesting |
+ protected ToolbarDataProvider getToolbarDataProvider() { |
+ return mToolbarDataProvider; |
+ } |
+ |
+ private static NavigationButtonType suggestionTypeToNavigationButtonType( |
+ OmniboxSuggestion.Type suggestionType) { |
+ switch (suggestionType) { |
+ case NAVSUGGEST: |
+ case HISTORY_URL: |
+ case URL_WHAT_YOU_TYPED: |
+ case HISTORY_TITLE: |
+ case HISTORY_BODY: |
+ case HISTORY_KEYWORD: |
+ return NavigationButtonType.PAGE; |
+ case SEARCH_WHAT_YOU_TYPED: |
+ case SEARCH_HISTORY: |
+ case SEARCH_SUGGEST: |
+ case SEARCH_SUGGEST_ENTITY: |
+ case SEARCH_SUGGEST_TAIL: |
+ case SEARCH_SUGGEST_PERSONALIZED: |
+ case SEARCH_SUGGEST_PROFILE: |
+ case VOICE_SUGGEST: |
+ case SEARCH_OTHER_ENGINE: |
+ case OPEN_HISTORY_PAGE: |
+ return NavigationButtonType.MAGNIFIER; |
+ default: |
+ assert false; |
+ return NavigationButtonType.MAGNIFIER; |
+ } |
+ } |
+ |
+ // Updates the navigation button based on the URL string |
+ private void updateNavigationButton() { |
+ boolean isTablet = DeviceFormFactor.isTablet(getContext()); |
+ NavigationButtonType type = NavigationButtonType.EMPTY; |
+ if (isTablet && !mSuggestionItems.isEmpty()) { |
+ // If there are suggestions showing, show the icon for the default suggestion. |
+ type = suggestionTypeToNavigationButtonType( |
+ mSuggestionItems.get(0).getSuggestion().getType()); |
+ } else if (mQueryInTheOmnibox) { |
+ type = NavigationButtonType.MAGNIFIER; |
+ } else if (isTablet) { |
+ type = NavigationButtonType.PAGE; |
+ } |
+ |
+ if (type != mNavigationButtonType) setNavigationButtonType(type); |
+ } |
+ |
+ /** |
+ * @return Whether the query is shown in the omnibox instead of the url. |
+ */ |
+ public boolean showingQueryInTheOmnibox() { |
+ return mQueryInTheOmnibox; |
+ } |
+ |
+ /** |
+ * @return Whether original url is shown for preview page. |
+ */ |
+ @Override |
+ public boolean showingOriginalUrlForPreview() { |
+ return mShowingOriginalUrlForPreview; |
+ } |
+ |
+ private int getSecurityLevel() { |
+ if (getCurrentTab() == null) return ConnectionSecurityHelperSecurityLevel.NONE; |
+ return getCurrentTab().getSecurityLevel(); |
+ } |
+ |
+ /** |
+ * Determines the icon that should be displayed for the current security level. |
+ * @param securityLevel The security level for which the resource will be returned. |
+ * @param usingLightTheme Whether light themed security assets should be used. |
+ * @return The resource ID of the icon that should be displayed, 0 if no icon should show. |
+ */ |
+ public static int getSecurityIconResource(int securityLevel, boolean usingLightTheme) { |
+ switch (securityLevel) { |
+ case ConnectionSecurityHelperSecurityLevel.NONE: |
+ return 0; |
+ case ConnectionSecurityHelperSecurityLevel.SECURITY_WARNING: |
+ return R.drawable.omnibox_https_warning; |
+ case ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR: |
+ return R.drawable.omnibox_https_invalid; |
+ case ConnectionSecurityHelperSecurityLevel.SECURE: |
+ case ConnectionSecurityHelperSecurityLevel.EV_SECURE: |
+ return usingLightTheme |
+ ? R.drawable.omnibox_https_valid_light : R.drawable.omnibox_https_valid; |
+ default: |
+ assert false; |
+ } |
+ return 0; |
+ } |
+ |
+ /** |
+ * Updates the security icon displayed in the LocationBar. |
+ */ |
+ @Override |
+ public void updateSecurityIcon(int securityLevel) { |
+ if (showingOriginalUrlForPreview()) { |
+ securityLevel = ConnectionSecurityHelperSecurityLevel.NONE; |
+ } |
+ if (mQueryInTheOmnibox) { |
+ if (securityLevel == ConnectionSecurityHelperSecurityLevel.SECURE |
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.EV_SECURE) { |
+ securityLevel = ConnectionSecurityHelperSecurityLevel.NONE; |
+ } else if (securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_WARNING |
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR) { |
+ setUrlToPageUrl(); |
+ } |
+ } |
+ |
+ // ImageView#setImageResource is no-op if given resource is the current one. |
+ mSecurityButton.setImageResource( |
+ getSecurityIconResource(securityLevel, !shouldEmphasizeHttpsScheme())); |
+ |
+ if (mSecurityIconType == securityLevel) return; |
+ mSecurityIconType = securityLevel; |
+ |
+ if (securityLevel == ConnectionSecurityHelperSecurityLevel.NONE) { |
+ updateSecurityButton(false); |
+ } else { |
+ updateSecurityButton(true); |
+ // Since we emphasize the schema of the URL based on the security type, we need to |
+ // refresh the emphasis. |
+ mUrlBar.deEmphasizeUrl(); |
+ } |
+ emphasizeUrl(); |
+ } |
+ |
+ private void emphasizeUrl() { |
+ if (!mQueryInTheOmnibox) mUrlBar.emphasizeUrl(); |
+ } |
+ |
+ @Override |
+ public boolean shouldEmphasizeHttpsScheme() { |
+ int securityLevel = getSecurityLevel(); |
+ if (securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR |
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_WARNING |
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_POLICY_WARNING) { |
+ return true; |
+ } |
+ if (getToolbarDataProvider().isUsingBrandColor()) return false; |
+ if (getToolbarDataProvider().isIncognito()) return false; |
+ return true; |
+ } |
+ |
+ /** |
+ * Updates the display of the security button. |
+ * @param enabled Whether the security button should be displayed. |
+ */ |
+ private void updateSecurityButton(boolean enabled) { |
+ changeLocationBarIcon(enabled |
+ && (!DeviceFormFactor.isTablet(getContext()) || !mUrlHasFocus)); |
+ mSecurityButtonShown = enabled; |
+ updateLocationBarIconContainerVisibility(); |
+ } |
+ |
+ /** |
+ * @return Whether the security button is currently being displayed. |
+ */ |
+ @VisibleForTesting |
+ public boolean isSecurityButtonShown() { |
+ return mSecurityButtonShown; |
+ } |
+ |
+ /** |
+ * Sets the type of the current navigation type and updates the UI to match it. |
+ * @param buttonType The type of navigation button to be shown. |
+ */ |
+ private void setNavigationButtonType(NavigationButtonType buttonType) { |
+ switch (buttonType) { |
+ case PAGE: |
+ Drawable page = ApiCompatibilityUtils.getDrawable( |
+ getResources(), R.drawable.ic_omnibox_page); |
+ page.setColorFilter(mUseDarkColors |
+ ? getResources().getColor(R.color.light_normal_color) |
+ : Color.WHITE, PorterDuff.Mode.SRC_IN); |
+ mNavigationButton.setImageDrawable(page); |
+ break; |
+ case MAGNIFIER: |
+ mNavigationButton.setImageResource(R.drawable.ic_omnibox_magnifier); |
+ break; |
+ case EMPTY: |
+ mNavigationButton.setImageResource(0); |
+ break; |
+ default: |
+ assert false; |
+ } |
+ |
+ if (mNavigationButton.getVisibility() != VISIBLE) { |
+ mNavigationButton.setVisibility(VISIBLE); |
+ } |
+ mNavigationButtonType = buttonType; |
+ updateLocationBarIconContainerVisibility(); |
+ } |
+ |
+ /** |
+ * Update the visibility of the location bar icon container based on the state of the |
+ * security and navigation icons. |
+ */ |
+ protected void updateLocationBarIconContainerVisibility() { |
+ boolean showContainer = |
+ mSecurityButtonShown || mNavigationButtonType != NavigationButtonType.EMPTY; |
+ findViewById(R.id.location_bar_icon).setVisibility(showContainer ? VISIBLE : GONE); |
+ } |
+ |
+ private boolean isStoredArticle(String url) { |
+ DomDistillerService domDistillerService = |
+ DomDistillerServiceFactory.getForProfile(Profile.getLastUsedProfile()); |
+ String entryIdFromUrl = DomDistillerUrlUtils.getValueForKeyInUrl(url, "entry_id"); |
+ if (TextUtils.isEmpty(entryIdFromUrl)) return false; |
+ return domDistillerService.hasEntry(entryIdFromUrl); |
+ } |
+ |
+ /** |
+ * Updates the layout params for the location bar start aligned views. |
+ */ |
+ protected void updateLayoutParams() { |
+ int startMargin = 0; |
+ int urlContainerChildIndex = -1; |
+ for (int i = 0; i < getChildCount(); i++) { |
+ View childView = getChildAt(i); |
+ if (childView.getVisibility() != GONE) { |
+ LayoutParams childLayoutParams = (LayoutParams) childView.getLayoutParams(); |
+ if (ApiCompatibilityUtils.getMarginStart(childLayoutParams) != startMargin) { |
+ ApiCompatibilityUtils.setMarginStart(childLayoutParams, startMargin); |
+ childView.setLayoutParams(childLayoutParams); |
+ } |
+ if (childView == mUrlContainer) { |
+ urlContainerChildIndex = i; |
+ break; |
+ } |
+ int widthMeasureSpec; |
+ int heightMeasureSpec; |
+ if (childLayoutParams.width == LayoutParams.WRAP_CONTENT) { |
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ getMeasuredWidth(), MeasureSpec.AT_MOST); |
+ } else if (childLayoutParams.width == LayoutParams.MATCH_PARENT) { |
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ getMeasuredWidth(), MeasureSpec.EXACTLY); |
+ } else { |
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ childLayoutParams.width, MeasureSpec.EXACTLY); |
+ } |
+ if (childLayoutParams.height == LayoutParams.WRAP_CONTENT) { |
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ getMeasuredHeight(), MeasureSpec.AT_MOST); |
+ } else if (childLayoutParams.height == LayoutParams.MATCH_PARENT) { |
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ getMeasuredHeight(), MeasureSpec.EXACTLY); |
+ } else { |
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec( |
+ childLayoutParams.height, MeasureSpec.EXACTLY); |
+ } |
+ childView.measure(widthMeasureSpec, heightMeasureSpec); |
+ startMargin += childView.getMeasuredWidth(); |
+ } |
+ } |
+ |
+ assert urlContainerChildIndex != -1; |
+ int urlContainerMarginEnd = 0; |
+ for (int i = urlContainerChildIndex + 1; i < getChildCount(); i++) { |
+ View childView = getChildAt(i); |
+ if (childView.getVisibility() != GONE) { |
+ LayoutParams childLayoutParams = (LayoutParams) childView.getLayoutParams(); |
+ urlContainerMarginEnd = Math.max(urlContainerMarginEnd, |
+ childLayoutParams.width |
+ + ApiCompatibilityUtils.getMarginStart(childLayoutParams) |
+ + ApiCompatibilityUtils.getMarginEnd(childLayoutParams)); |
+ } |
+ } |
+ LayoutParams urlLayoutParams = (LayoutParams) mUrlContainer.getLayoutParams(); |
+ if (ApiCompatibilityUtils.getMarginEnd(urlLayoutParams) != urlContainerMarginEnd) { |
+ ApiCompatibilityUtils.setMarginEnd(urlLayoutParams, urlContainerMarginEnd); |
+ mUrlContainer.setLayoutParams(urlLayoutParams); |
+ } |
+ } |
+ |
+ /** |
+ * @return Whether the delete button should be shown. |
+ */ |
+ protected boolean shouldShowDeleteButton() { |
+ // Show the delete button at the endon the right when the bar has focus and has some text. |
+ return mUrlBar.hasFocus() && !TextUtils.isEmpty(mUrlBar.getText()); |
+ } |
+ |
+ /** |
+ * Updates the display of the delete URL content button. |
+ */ |
+ protected void updateDeleteButtonVisibility() { |
+ } |
+ |
+ /** |
+ * @return The suggestion list popup containing the omnibox results (or |
+ * null if it has not yet been created). |
+ */ |
+ @VisibleForTesting |
+ public OmniboxSuggestionsList getSuggestionList() { |
+ return mSuggestionList; |
+ } |
+ |
+ /** |
+ * Initiates the mSuggestionListPopup. Done on demand to not slow down |
+ * the initial inflation of the location bar. |
+ */ |
+ private void initSuggestionList() { |
+ // Only called from onSuggestionsReceived(), which is a callback from a listener set up by |
+ // onNativeLibraryReady(), so this assert is safe. |
+ assert mNativeInitialized : "Trying to initialize suggestions list before native init"; |
+ if (mSuggestionList != null) return; |
+ |
+ OnLayoutChangeListener suggestionListResizer = new OnLayoutChangeListener() { |
+ @Override |
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, |
+ int oldLeft, int oldTop, int oldRight, int oldBottom) { |
+ // On ICS, this update does not take affect unless it is posted to the end of the |
+ // current message queue. |
+ post(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mSuggestionList.isShown()) mSuggestionList.updateLayoutParams(); |
+ } |
+ }); |
+ } |
+ }; |
+ getRootView().findViewById(R.id.control_container) |
+ .addOnLayoutChangeListener(suggestionListResizer); |
+ |
+ mSuggestionList = new OmniboxSuggestionsList(getContext()); |
+ mOmniboxResultsContainer.addView(mSuggestionList); |
+ mSuggestionList.setAdapter(mSuggestionListAdapter); |
+ mSuggestionList.setClipToPadding(false); |
+ mSuggestionListAdapter.setSuggestionDelegate(new OmniboxSuggestionDelegate() { |
+ @Override |
+ public void onSelection(OmniboxSuggestion suggestion, int position) { |
+ mSuggestionSelectionInProgress = true; |
+ String suggestionMatchUrl = updateSuggestionUrlIfNeeded(suggestion, position); |
+ loadUrlFromOmniboxMatch(suggestionMatchUrl, suggestion.getTransition(), position, |
+ suggestion.getType()); |
+ hideSuggestions(); |
+ UiUtils.hideKeyboard(mUrlBar); |
+ } |
+ |
+ @Override |
+ public void onRefineSuggestion(OmniboxSuggestion suggestion) { |
+ stopAutocomplete(false); |
+ mUrlBar.setUrl(suggestion.getFillIntoEdit(), null); |
+ mUrlBar.setSelection(mUrlBar.getText().length()); |
+ RecordUserAction.record("MobileOmniboxRefineSuggestion"); |
+ } |
+ |
+ @Override |
+ public void onSetUrlToSuggestion(OmniboxSuggestion suggestion) { |
+ if (mIgnoreOmniboxItemSelection) return; |
+ setUrlBarText(null, null, suggestion.getFillIntoEdit()); |
+ mUrlBar.setSelection(mUrlBar.getText().length()); |
+ mIgnoreOmniboxItemSelection = true; |
+ } |
+ |
+ @Override |
+ public void onDeleteSuggestion(int position) { |
+ if (mAutocomplete != null) mAutocomplete.deleteSuggestion(position); |
+ } |
+ |
+ @Override |
+ public void onGestureDown() { |
+ stopAutocomplete(false); |
+ } |
+ |
+ @Override |
+ public void onShowModal() { |
+ mSuggestionModalShown = true; |
+ } |
+ |
+ @Override |
+ public void onHideModal() { |
+ mSuggestionModalShown = false; |
+ } |
+ |
+ @Override |
+ public void onTextWidthsUpdated(float requiredWidth, float matchContentsWidth) { |
+ mSuggestionList.updateMaxTextWidths(requiredWidth, matchContentsWidth); |
+ } |
+ |
+ @Override |
+ public float getMaxRequiredWidth() { |
+ return mSuggestionList.getMaxRequiredWidth(); |
+ } |
+ |
+ @Override |
+ public float getMaxMatchContentsWidth() { |
+ return mSuggestionList.getMaxMatchContentsWidth(); |
+ } |
+ }); |
+ } |
+ |
+ /** |
+ * @return The view that the suggestion popup should be anchored below. |
+ */ |
+ protected View getSuggestionPopupAnchorView() { |
+ return this; |
+ } |
+ |
+ /** |
+ * @return The background for the omnibox suggestions popup. |
+ */ |
+ protected Drawable getSuggestionPopupBackground() { |
+ if (mToolbarDataProvider.isIncognito()) { |
+ return new ColorDrawable(OMNIBOX_INCOGNITO_RESULTS_BG_COLOR); |
+ } else { |
+ return new ColorDrawable(OMNIBOX_RESULTS_BG_COLOR); |
+ } |
+ } |
+ |
+ /** |
+ * Handles showing/hiding the suggestions list. |
+ * @param visible Whether the suggestions list should be visible. |
+ */ |
+ protected void setSuggestionsListVisibility(final boolean visible) { |
+ mSuggestionsShown = visible; |
+ if (mSuggestionList != null) { |
+ final boolean isShowing = mSuggestionList.isShown(); |
+ if (visible && !isShowing) { |
+ mSuggestionList.show(); |
+ } else if (!visible && isShowing) { |
+ mSuggestionList.setVisibility(GONE); |
+ } |
+ } |
+ updateOmniboxResultsContainer(); |
+ } |
+ |
+ /** |
+ * Updates the URL we will navigate to from suggestion, if needed. This will update the search |
+ * URL to be of the corpus type if query in the omnibox is displayed and update aqs= parameter |
+ * on regular web search URLs. |
+ * |
+ * @param suggestion The chosen omnibox suggestion. |
+ * @param selectedIndex The index of the chosen omnibox suggestion. |
+ * @return The url to navigate to. |
+ */ |
+ private String updateSuggestionUrlIfNeeded(OmniboxSuggestion suggestion, int selectedIndex) { |
+ // Only called once we have suggestions, and don't have a listener though which we can |
+ // receive suggestions until the native side is ready, so this is safe |
+ assert mNativeInitialized |
+ : "updateSuggestionUrlIfNeeded called before native initialization"; |
+ |
+ String updatedUrl = null; |
+ // Only replace URL queries for corpus search refinements, this does not work well |
+ // for regular web searches. |
+ // TODO(mariakhomenko): improve efficiency by just checking whether corpus exists. |
+ if (mQueryInTheOmnibox && !suggestion.isUrlSuggestion() |
+ && !TextUtils.isEmpty(mToolbarDataProvider.getCorpusChipText())) { |
+ String query = suggestion.getFillIntoEdit(); |
+ Tab currentTab = getCurrentTab(); |
+ if (currentTab != null && !TextUtils.isEmpty(currentTab.getUrl()) |
+ && !TextUtils.isEmpty(query)) { |
+ updatedUrl = TemplateUrlService.getInstance().replaceSearchTermsInUrl( |
+ query, currentTab.getUrl()); |
+ } |
+ } else if (suggestion.getType() != Type.VOICE_SUGGEST) { |
+ // TODO(mariakhomenko): Ideally we want to update match destination URL with new aqs |
+ // for query in the omnibox and voice suggestions, but it's currently difficult to do. |
+ long elapsedTimeSinceInputChange = mNewOmniboxEditSessionTimestamp > 0 |
+ ? (SystemClock.elapsedRealtime() - mNewOmniboxEditSessionTimestamp) : -1; |
+ updatedUrl = mAutocomplete.updateMatchDestinationUrlWithQueryFormulationTime( |
+ selectedIndex, elapsedTimeSinceInputChange); |
+ } |
+ |
+ return updatedUrl == null ? suggestion.getUrl() : updatedUrl; |
+ } |
+ |
+ private void clearSuggestions(boolean notifyChange) { |
+ mSuggestionItems.clear(); |
+ // Make sure to notify the adapter. If the ListView becomes out of sync |
+ // with its adapter and it has not been notified, it will throw an |
+ // exception when some UI events are propagated. |
+ if (notifyChange) mSuggestionListAdapter.notifyDataSetChanged(); |
+ } |
+ |
+ /** |
+ * Hides the omnibox suggestion popup. |
+ * |
+ * <p> |
+ * Signals the autocomplete controller to stop generating omnibox suggestions. |
+ * |
+ * @see AutocompleteController#stop(boolean) |
+ */ |
+ @Override |
+ public void hideSuggestions() { |
+ if (mAutocomplete == null) return; |
+ |
+ recordSuggestionsDismissed(); |
+ |
+ stopAutocomplete(true); |
+ |
+ setSuggestionsListVisibility(false); |
+ clearSuggestions(true); |
+ updateNavigationButton(); |
+ |
+ mSuggestionSelectionInProgress = false; |
+ } |
+ |
+ /** |
+ * Records a UMA action indicating that the user dismissed the suggestion list (e.g. pressed |
+ * the back button or the 'x' button in the Omnibox). If there was an answer shown its type |
+ * is recorded. |
+ * |
+ * The action is not recorded if mSelectionInProgress is true. This allows us to avoid |
+ * recording the action in the case where the user is selecting a suggestion which is not |
+ * considered a dismissal. |
+ */ |
+ private void recordSuggestionsDismissed() { |
+ if (mSuggestionSelectionInProgress || mSuggestionItems.size() == 0) return; |
+ |
+ int answerTypeShown = 0; |
+ for (int i = 0; i < mSuggestionItems.size(); i++) { |
+ OmniboxSuggestion suggestion = mSuggestionItems.get(i).getSuggestion(); |
+ if (suggestion.hasAnswer()) { |
+ try { |
+ answerTypeShown = Integer.parseInt(suggestion.getAnswerType()); |
+ } catch (NumberFormatException e) { |
+ Log.e(getClass().getSimpleName(), |
+ "Answer type in dismissed suggestions is not an int: " |
+ + suggestion.getAnswerType()); |
+ } |
+ break; |
+ } |
+ } |
+ RecordHistogram.recordSparseSlowlyHistogram( |
+ "Omnibox.SuggestionsDismissed.AnswerType", answerTypeShown); |
+ } |
+ |
+ /** |
+ * Signals the autocomplete controller to stop generating omnibox suggestions and cancels the |
+ * queued task to start the autocomplete controller, if any. |
+ * |
+ * @param clear Whether to clear the most recent autocomplete results. |
+ */ |
+ private void stopAutocomplete(boolean clear) { |
+ if (mAutocomplete != null) mAutocomplete.stop(clear); |
+ cancelPendingAutocompleteStart(); |
+ } |
+ |
+ /** |
+ * Cancels the queued task to start the autocomplete controller, if any. |
+ */ |
+ private void cancelPendingAutocompleteStart() { |
+ if (mRequestSuggestions != null) { |
+ // There is a request for suggestions either waiting for the native side |
+ // to start, or on the message queue. Remove it from wherever it is. |
+ if (!mDeferredNativeRunnables.remove(mRequestSuggestions)) { |
+ removeCallbacks(mRequestSuggestions); |
+ } |
+ mRequestSuggestions = null; |
+ } |
+ } |
+ |
+ @Override |
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { |
+ // Don't restore the state of the location bar, it can lead to all kind of bad states with |
+ // the popup. |
+ // When we restore tabs, we focus the selected tab so the URL of the page shows. |
+ } |
+ |
+ /** |
+ * Performs a search query on the current {@link ChromeTab}. This calls |
+ * {@link TemplateUrlService#getUrlForSearchQuery(String)} to get a url based on {@code query} |
+ * and loads that url in the current {@link ChromeTab}. |
+ * @param query The {@link String} that represents the text query that should be searched for. |
+ */ |
+ @VisibleForTesting |
+ public void performSearchQueryForTest(String query) { |
+ if (TextUtils.isEmpty(query)) return; |
+ |
+ String queryUrl = TemplateUrlService.getInstance().getUrlForSearchQuery(query); |
+ |
+ if (!TextUtils.isEmpty(queryUrl)) { |
+ loadUrl(queryUrl, PageTransition.GENERATED); |
+ } else { |
+ setSearchQuery(query); |
+ } |
+ } |
+ |
+ /** |
+ * Sets the query string in the omnibox (ensuring the URL bar has focus and triggering |
+ * autocomplete for the specified query) as if the user typed it. |
+ * |
+ * @param query The query to be set in the omnibox. |
+ */ |
+ public void setSearchQuery(final String query) { |
+ if (TextUtils.isEmpty(query)) return; |
+ |
+ if (!mNativeInitialized) { |
+ mDeferredNativeRunnables.add(new Runnable() { |
+ @Override |
+ public void run() { |
+ setSearchQuery(query); |
+ } |
+ }); |
+ return; |
+ } |
+ |
+ setUrlBarText(null, null, query); |
+ mUrlBar.setSelection(0, mUrlBar.getText().length()); |
+ mUrlBar.requestFocus(); |
+ stopAutocomplete(false); |
+ if (getCurrentTab() != null) { |
+ mAutocomplete.start( |
+ getCurrentTab().getProfile(), getCurrentTab().getUrl(), query, false); |
+ } |
+ post(new Runnable() { |
+ @Override |
+ public void run() { |
+ UiUtils.showKeyboard(mUrlBar); |
+ } |
+ }); |
+ } |
+ |
+ /** |
+ * Whether {@code v} is a location icon which can be clicked to show the |
+ * origin info dialog. |
+ */ |
+ private boolean isLocationIcon(View v) { |
+ return v == mSecurityButton || v == mNavigationButton; |
+ } |
+ |
+ @Override |
+ public void onClick(View v) { |
+ if (v == mDeleteButton) { |
+ if (!TextUtils.isEmpty(mUrlBar.getQueryText())) { |
+ setUrlBarText(null, null, ""); |
+ hideSuggestions(); |
+ } |
+ |
+ startZeroSuggest(); |
+ return; |
+ } else if (!mUrlHasFocus && isLocationIcon(v)) { |
+ Tab currentTab = getCurrentTab(); |
+ if (currentTab != null && currentTab.getWebContents() != null) { |
+ WebsiteSettingsPopup.show(getContext(), currentTab.getProfile(), |
+ currentTab.getWebContents()); |
+ } |
+ } else if (v == mMicButton) { |
+ RecordUserAction.record("MobileOmniboxVoiceSearch"); |
+ startVoiceRecognition(); |
+ } |
+ } |
+ |
+ /** |
+ * Whether we want to be showing inline autocomplete results. We don't want to show them as the |
+ * user deletes input. Also if there is a composition (e.g. while using the Japanese IME), |
+ * we must not autocomplete or we'll destroy the composition. |
+ * @return Whether we want to be showing inline autocomplete results. |
+ */ |
+ private boolean shouldAutocomplete() { |
+ if (mLastUrlEditWasDelete) return false; |
+ Editable text = mUrlBar.getText(); |
+ |
+ return mUrlBar.isCursorAtEndOfTypedText() |
+ && BaseInputConnection.getComposingSpanEnd(text) |
+ == BaseInputConnection.getComposingSpanStart(text); |
+ } |
+ |
+ @Override |
+ public void onSuggestionsReceived(List<OmniboxSuggestion> newSuggestions, |
+ String inlineAutocompleteText) { |
+ // This is a callback from a listener that is set up by onNativeLibraryReady, |
+ // so can only be called once the native side is set up. |
+ assert mNativeInitialized : "Suggestions received before native side intialialized"; |
+ |
+ if (getCurrentTab() == null) { |
+ // If the current tab is not available, drop the suggestions and hide the autocomplete. |
+ hideSuggestions(); |
+ return; |
+ } |
+ |
+ String userText = mUrlBar.getTextWithoutAutocomplete(); |
+ mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText; |
+ |
+ boolean itemsChanged = false; |
+ boolean itemCountChanged = false; |
+ // If the length of the incoming suggestions matches that of those currently being shown, |
+ // replace them inline to allow transient entries to retain their proper highlighting. |
+ if (mSuggestionItems.size() == newSuggestions.size()) { |
+ for (int index = 0; index < newSuggestions.size(); index++) { |
+ OmniboxResultItem suggestionItem = mSuggestionItems.get(index); |
+ OmniboxSuggestion suggestion = suggestionItem.getSuggestion(); |
+ OmniboxSuggestion newSuggestion = newSuggestions.get(index); |
+ // Determine whether the suggestions have changed. If not, save some time by not |
+ // redrawing the suggestions UI. |
+ if (suggestion.equals(newSuggestion) |
+ && suggestion.getType() != OmniboxSuggestion.Type.SEARCH_SUGGEST_TAIL) { |
+ if (suggestionItem.getMatchedQuery().equals(userText)) { |
+ continue; |
+ } else if (!suggestion.getDisplayText().startsWith(userText) |
+ && !suggestion.getUrl().contains(userText)) { |
+ continue; |
+ } |
+ } |
+ mSuggestionItems.set(index, new OmniboxResultItem(newSuggestion, userText)); |
+ itemsChanged = true; |
+ } |
+ } else { |
+ itemsChanged = true; |
+ itemCountChanged = true; |
+ clearSuggestions(false); |
+ for (int i = 0; i < newSuggestions.size(); i++) { |
+ mSuggestionItems.add(new OmniboxResultItem(newSuggestions.get(i), userText)); |
+ } |
+ } |
+ |
+ if (mSuggestionItems.isEmpty()) { |
+ if (mSuggestionsShown) hideSuggestions(); |
+ return; |
+ } |
+ |
+ if (shouldAutocomplete()) { |
+ mUrlBar.setAutocompleteText(userText, inlineAutocompleteText); |
+ } |
+ |
+ // Show the suggestion list. |
+ initSuggestionList(); // It may not have been initialized yet. |
+ mSuggestionList.resetMaxTextWidths(); |
+ |
+ if (itemsChanged) mSuggestionListAdapter.notifySuggestionsChanged(); |
+ if (mUrlBar.hasFocus()) { |
+ setSuggestionsListVisibility(true); |
+ if (itemCountChanged) { |
+ mSuggestionList.updateLayoutParams(); |
+ } |
+ } |
+ |
+ // Update the navigation button to show the default suggestion's icon. |
+ updateNavigationButton(); |
+ |
+ if (!CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_INSTANT) |
+ && PrivacyPreferencesManager.getInstance(getContext()).shouldPrerender()) { |
+ mOmniboxPrerender.prerenderMaybe( |
+ userText, |
+ getOriginalUrl(), |
+ mAutocomplete.getCurrentNativeAutocompleteResult(), |
+ getCurrentTab().getProfile(), |
+ getCurrentTab()); |
+ } |
+ } |
+ |
+ @Override |
+ public void backKeyPressed() { |
+ hideSuggestions(); |
+ UiUtils.hideKeyboard(mUrlBar); |
+ // Revert the URL to match the current page. |
+ setUrlToPageUrl(); |
+ // Focus the page. |
+ Tab currentTab = getCurrentTab(); |
+ if (currentTab != null) currentTab.requestFocus(); |
+ } |
+ |
+ /** |
+ * @return Returns the original url of the page. |
+ */ |
+ public String getOriginalUrl() { |
+ return mOriginalUrl; |
+ } |
+ |
+ /** |
+ * Given the URL display text, this will remove any path portion contained within. |
+ * @param displayText The text to strip the path from. |
+ * @return A pair where the first item is the text without any path content (if the path was |
+ * successfully found), and the second item is the path content (or null if no path |
+ * was found or parsing the path failed). |
+ * @see ToolbarDataProvider#getText() |
+ */ |
+ // TODO(tedchoc): Move this logic into the original display text calculation. |
+ @VisibleForTesting |
+ public static Pair<String, String> splitPathFromUrlDisplayText(String displayText) { |
+ int pathSearchOffset = 0; |
+ Uri uri = Uri.parse(displayText); |
+ String scheme = uri.getScheme(); |
+ if (!TextUtils.isEmpty(scheme)) { |
+ if (UNSUPPORTED_SCHEMES_TO_SPLIT.contains(scheme)) { |
+ return Pair.create(displayText, null); |
+ } else if (ACCEPTED_SCHEMES.contains(scheme)) { |
+ for (pathSearchOffset = scheme.length(); |
+ pathSearchOffset < displayText.length(); |
+ pathSearchOffset++) { |
+ char c = displayText.charAt(pathSearchOffset); |
+ if (c != ':' && c != '/') break; |
+ } |
+ } |
+ } |
+ int pathOffset = -1; |
+ if (pathSearchOffset < displayText.length()) { |
+ pathOffset = displayText.indexOf('/', pathSearchOffset); |
+ } |
+ if (pathOffset != -1) { |
+ String prePathText = displayText.substring(0, pathOffset); |
+ // If the '/' is the last character and the beginning of the path, then just drop |
+ // the path entirely. |
+ String pathText = pathOffset == displayText.length() - 1 |
+ ? null : displayText.substring(pathOffset); |
+ return Pair.create(prePathText, pathText); |
+ } |
+ return Pair.create(displayText, null); |
+ } |
+ |
+ /** |
+ * Sets the displayed URL to be the URL of the page currently showing. |
+ * |
+ * <p>The URL is converted to the most user friendly format (removing HTTP:// for example). |
+ * |
+ * <p>If the current tab is null, the URL text will be cleared. |
+ */ |
+ @Override |
+ public void setUrlToPageUrl() { |
+ // If the URL is currently focused, do not replace the text they have entered with the URL. |
+ // Once they stop editing the URL, the current tab's URL will automatically be filled in. |
+ if (mUrlBar.hasFocus()) return; |
+ |
+ mQueryInTheOmnibox = false; |
+ |
+ if (getCurrentTab() == null) { |
+ setUrlBarText(null, null, ""); |
+ return; |
+ } |
+ |
+ // Profile may be null if switching to a tab that has not yet been initialized. |
+ Profile profile = getCurrentTab().getProfile(); |
+ if (profile != null) mOmniboxPrerender.clear(profile); |
+ |
+ String url = getCurrentTab().getUrl().trim(); |
+ mOriginalUrl = url; |
+ |
+ if (NativePageFactory.isNativePageUrl(url, getCurrentTab().isIncognito())) { |
+ // Don't show anything for Chrome URLs. |
+ setUrlBarText("", null, null); |
+ return; |
+ } |
+ |
+ // Background view has similar case as snapshot. |
+ BackgroundContentViewHelper backgroundViewHelper = |
+ getCurrentTab().getBackgroundContentViewHelper(); |
+ boolean hasPendingBackgroundPage = |
+ backgroundViewHelper != null && backgroundViewHelper.hasPendingBackgroundPage(); |
+ boolean isTransitioningFromPreviewPageToOriginal = |
+ showingOriginalUrlForPreview() && !hasPendingBackgroundPage; |
+ mShowingOriginalUrlForPreview = hasPendingBackgroundPage; |
+ |
+ boolean showingQuery = false; |
+ String displayText = mToolbarDataProvider.getText(); |
+ int securityLevel = getSecurityLevel(); |
+ if (securityLevel != ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR |
+ && !TextUtils.isEmpty(displayText) |
+ && mToolbarDataProvider.wouldReplaceURL()) { |
+ url = displayText.trim(); |
+ showingQuery = true; |
+ mQueryInTheOmnibox = true; |
+ } |
+ String path = null; |
+ if (!showingQuery && FeatureUtilities.isDocumentMode(getContext())) { |
+ Pair<String, String> urlText = splitPathFromUrlDisplayText(displayText); |
+ displayText = urlText.first; |
+ path = urlText.second; |
+ } |
+ |
+ if (DomDistillerUrlUtils.isDistilledPage(url)) { |
+ if (isStoredArticle(url)) { |
+ DomDistillerService domDistillerService = |
+ DomDistillerServiceFactory.getForProfile(profile); |
+ String originalUrl = domDistillerService.getUrlForEntry( |
+ DomDistillerUrlUtils.getValueForKeyInUrl(url, "entry_id")); |
+ displayText = |
+ DomDistillerTabUtils.getFormattedUrlFromOriginalDistillerUrl(originalUrl); |
+ } else if (DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url) != null) { |
+ String originalUrl = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); |
+ displayText = |
+ DomDistillerTabUtils.getFormattedUrlFromOriginalDistillerUrl(originalUrl); |
+ } |
+ } |
+ |
+ if (setUrlBarText(displayText, path, url) || isTransitioningFromPreviewPageToOriginal) { |
+ mUrlBar.deEmphasizeUrl(); |
+ emphasizeUrl(); |
+ } |
+ if (showingQuery) { |
+ updateNavigationButton(); |
+ } |
+ updateCustomSelectionActionModeCallback(); |
+ } |
+ |
+ /** |
+ * Changes the text on the url bar |
+ * @param displayText The text (URL or search terms) for user display. |
+ * @param trailingText The trailing text (path portion of the URL) to be displayed separately. |
+ * @param text The original text (URL or search terms) for copy/cut. |
+ * @return Whether the URL was changed as a result of this call. |
+ */ |
+ private boolean setUrlBarText(String displayText, String trailingText, String text) { |
+ mIgnoreURLBarModification = true; |
+ boolean urlChanged = mUrlContainer.setUrlText(displayText, trailingText, text); |
+ mIgnoreURLBarModification = false; |
+ return urlChanged; |
+ } |
+ |
+ /** |
+ * Sets whether modifications to the URL bar should be ignored. |
+ */ |
+ @Override |
+ public void setIgnoreURLBarModification(boolean value) { |
+ mIgnoreURLBarModification = value; |
+ } |
+ |
+ private void loadUrlFromOmniboxMatch(String url, int transition, int matchPosition, |
+ OmniboxSuggestion.Type type) { |
+ // loadUrl modifies AutocompleteController's state clearing the native |
+ // AutocompleteResults needed by onSuggestionsSelected. Therefore, |
+ // loadUrl should should be invoked last. |
+ Tab currentTab = getCurrentTab(); |
+ String currentPageUrl = currentTab != null ? currentTab.getUrl() : ""; |
+ WebContents webContents = currentTab != null ? currentTab.getWebContents() : null; |
+ long elapsedTimeSinceModified = mNewOmniboxEditSessionTimestamp > 0 |
+ ? (SystemClock.elapsedRealtime() - mNewOmniboxEditSessionTimestamp) : -1; |
+ mAutocomplete.onSuggestionSelected(matchPosition, type, currentPageUrl, |
+ mQueryInTheOmnibox, mUrlFocusedFromFakebox, elapsedTimeSinceModified, |
+ webContents); |
+ loadUrl(url, transition); |
+ } |
+ |
+ private void loadUrl(String url, int transition) { |
+ Tab currentTab = getCurrentTab(); |
+ |
+ // The code of the rest of this class ensures that this can't be called until the native |
+ // side is initialized |
+ assert mNativeInitialized : "Loading URL before native side initialized"; |
+ |
+ if (currentTab != null |
+ && (currentTab.isNativePage() || NewTabPage.isNTPUrl(currentTab.getUrl()))) { |
+ NewTabPageUma.recordOmniboxNavigation(url, transition); |
+ // Passing in an empty string should not do anything unless the user is at the NTP. |
+ // Since the NTP has no url, pressing enter while clicking on the URL bar should refresh |
+ // the page as it does when you click and press enter on any other site. |
+ if (url.isEmpty()) url = currentTab.getUrl(); |
+ } |
+ |
+ // Loads the |url| in the current ContentView and gives focus to the ContentView. |
+ if (currentTab != null && !url.isEmpty()) { |
+ LoadUrlParams loadUrlParams = new LoadUrlParams(url); |
+ loadUrlParams.setVerbatimHeaders( |
+ GeolocationHeader.getGeoHeader(getContext(), url, currentTab.isIncognito())); |
+ loadUrlParams.setTransitionType(transition | PageTransition.FROM_ADDRESS_BAR); |
+ currentTab.loadUrl(loadUrlParams); |
+ |
+ setUrlToPageUrl(); |
+ RecordUserAction.record("MobileOmniboxSearch"); |
+ RecordUserAction.record("MobileTabClobbered"); |
+ } else { |
+ setUrlToPageUrl(); |
+ } |
+ |
+ if (currentTab != null) currentTab.requestFocus(); |
+ |
+ // Prevent any upcoming omnibox suggestions from showing. We have to do this after we load |
+ // the URL as this will hide the suggestions and trigger a cancel of the prerendered page. |
+ stopAutocomplete(true); |
+ } |
+ |
+ /** |
+ * Update the location bar visuals based on a loading state change. |
+ * @param updateUrl Whether to update the URL as a result of the this call. |
+ */ |
+ @Override |
+ public void updateLoadingState(boolean updateUrl) { |
+ if (updateUrl) setUrlToPageUrl(); |
+ updateNavigationButton(); |
+ updateSecurityIcon(getSecurityLevel()); |
+ } |
+ |
+ /** |
+ * @return The ChromeTab currently showing. |
+ */ |
+ @Override |
+ public ChromeTab getCurrentTab() { |
+ if (mToolbarDataProvider == null) return null; |
+ return ChromeTab.fromTab(mToolbarDataProvider.getTab()); |
+ } |
+ |
+ private ContentViewCore getContentViewCore() { |
+ Tab currentTab = getCurrentTab(); |
+ return currentTab != null ? currentTab.getContentViewCore() : null; |
+ } |
+ |
+ private void updateOmniboxResultsContainer() { |
+ if (mSuggestionsShown || mUrlHasFocus) { |
+ if (mOmniboxResultsContainer == null) { |
+ ViewStub overlayStub = |
+ (ViewStub) getRootView().findViewById(R.id.omnibox_results_container_stub); |
+ mOmniboxResultsContainer = (ViewGroup) overlayStub.inflate(); |
+ mOmniboxResultsContainer.setBackgroundColor(CONTENT_OVERLAY_COLOR); |
+ // Prevent touch events from propagating down to the chrome view. |
+ mOmniboxResultsContainer.setOnTouchListener(new OnTouchListener() { |
+ @Override |
+ @SuppressLint("ClickableViewAccessibility") |
+ public boolean onTouch(View v, MotionEvent event) { |
+ int action = event.getActionMasked(); |
+ if (action == MotionEvent.ACTION_CANCEL |
+ || action == MotionEvent.ACTION_UP) { |
+ mUrlBar.clearFocus(); |
+ updateOmniboxResultsContainerBackground(false); |
+ } |
+ return true; |
+ } |
+ }); |
+ } |
+ updateOmniboxResultsContainerVisibility(true); |
+ } else if (mOmniboxResultsContainer != null) { |
+ updateOmniboxResultsContainerBackground(false); |
+ } |
+ } |
+ |
+ private void updateOmniboxResultsContainerVisibility(boolean visible) { |
+ boolean currentlyVisible = mOmniboxResultsContainer.getVisibility() == VISIBLE; |
+ if (currentlyVisible == visible) { |
+ // This early return is necessary. Otherwise, calling |
+ // updateOmniboxResultsContainerVisibility(true) twice in a row will update |
+ // mFocusedTabImportantForAccessibilityState incorrectly and cause |
+ // mFocusedTabView to be stuck in IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS mode. |
+ // http://crbug.com/445560 |
+ return; |
+ } |
+ |
+ if (visible) { |
+ mOmniboxResultsContainer.setVisibility(VISIBLE); |
+ |
+ if (getContentViewCore() != null) { |
+ mFocusedTabAccessibilityManager = |
+ getContentViewCore().getBrowserAccessibilityManager(); |
+ if (mFocusedTabAccessibilityManager != null) { |
+ mFocusedTabAccessibilityManager.setVisible(false); |
+ } |
+ } |
+ |
+ if (getCurrentTab() != null && getCurrentTab().getView() != null) { |
+ mFocusedTabView = getCurrentTab().getView(); |
+ mFocusedTabImportantForAccessibilityState = |
+ mFocusedTabView.getImportantForAccessibility(); |
+ mFocusedTabView.setImportantForAccessibility( |
+ IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); |
+ } |
+ } else { |
+ mOmniboxResultsContainer.setVisibility(INVISIBLE); |
+ |
+ if (mFocusedTabAccessibilityManager != null) { |
+ mFocusedTabAccessibilityManager.setVisible(true); |
+ mFocusedTabAccessibilityManager = null; |
+ } |
+ |
+ if (mFocusedTabView != null) { |
+ mFocusedTabView.setImportantForAccessibility( |
+ mFocusedTabImportantForAccessibilityState); |
+ mFocusedTabView = null; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Set the background of the omnibox results container. |
+ * @param visible Whether the background should be made visible. |
+ */ |
+ private void updateOmniboxResultsContainerBackground(boolean visible) { |
+ if (getToolbarDataProvider() == null) return; |
+ |
+ NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); |
+ boolean locationBarShownInNTP = ntp != null && ntp.isLocationBarShownInNTP(); |
+ if (visible) { |
+ if (locationBarShownInNTP) { |
+ mOmniboxResultsContainer.getBackground().setAlpha(0); |
+ } else { |
+ fadeInOmniboxResultsContainerBackground(); |
+ } |
+ } else { |
+ if (locationBarShownInNTP) { |
+ updateOmniboxResultsContainerVisibility(false); |
+ } else { |
+ fadeOutOmniboxResultsContainerBackground(); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Trigger a fade in of the omnibox results background. |
+ */ |
+ protected void fadeInOmniboxResultsContainerBackground() { |
+ if (mFadeInOmniboxBackgroundAnimator == null) { |
+ mFadeInOmniboxBackgroundAnimator = ObjectAnimator.ofInt( |
+ getRootView().findViewById(R.id.omnibox_results_container).getBackground(), |
+ "alpha", 0, 255); |
+ mFadeInOmniboxBackgroundAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); |
+ mFadeInOmniboxBackgroundAnimator.setInterpolator( |
+ BakedBezierInterpolator.FADE_IN_CURVE); |
+ } |
+ runOmniboxResultsFadeAnimation(mFadeInOmniboxBackgroundAnimator); |
+ } |
+ |
+ private void fadeOutOmniboxResultsContainerBackground() { |
+ if (mFadeOutOmniboxBackgroundAnimator == null) { |
+ mFadeOutOmniboxBackgroundAnimator = ObjectAnimator.ofInt( |
+ getRootView().findViewById(R.id.omnibox_results_container).getBackground(), |
+ "alpha", 255, 0); |
+ mFadeOutOmniboxBackgroundAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); |
+ mFadeOutOmniboxBackgroundAnimator.setInterpolator( |
+ BakedBezierInterpolator.FADE_OUT_CURVE); |
+ mFadeOutOmniboxBackgroundAnimator.addListener(new AnimatorListenerAdapter() { |
+ private boolean mIsCancelled; |
+ |
+ @Override |
+ public void onAnimationStart(Animator animation) { |
+ mIsCancelled = false; |
+ } |
+ |
+ @Override |
+ public void onAnimationCancel(Animator animation) { |
+ mIsCancelled = true; |
+ } |
+ |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ if (mIsCancelled) return; |
+ updateOmniboxResultsContainerVisibility(false); |
+ } |
+ }); |
+ } |
+ runOmniboxResultsFadeAnimation(mFadeOutOmniboxBackgroundAnimator); |
+ } |
+ |
+ private void runOmniboxResultsFadeAnimation(Animator fadeAnimation) { |
+ if (mOmniboxBackgroundAnimator == fadeAnimation |
+ && mOmniboxBackgroundAnimator.isRunning()) { |
+ return; |
+ } else if (mOmniboxBackgroundAnimator != null) { |
+ mOmniboxBackgroundAnimator.cancel(); |
+ } |
+ mOmniboxBackgroundAnimator = fadeAnimation; |
+ mOmniboxBackgroundAnimator.start(); |
+ } |
+ |
+ /** |
+ * @return Whether voice search is supported in the current browser configuration. |
+ */ |
+ protected boolean isVoiceSearchEnabled() { |
+ return mToolbarDataProvider != null && !mToolbarDataProvider.isIncognito() |
+ && FeatureUtilities.isRecognitionIntentPresent(getContext(), true); |
+ } |
+ |
+ @Override |
+ protected void onWindowVisibilityChanged(int visibility) { |
+ super.onWindowVisibilityChanged(visibility); |
+ if (visibility == View.VISIBLE) updateMicButtonState(); |
+ } |
+ |
+ /** |
+ * Call to notify the location bar that the state of the voice search microphone button may |
+ * need to be updated. |
+ */ |
+ @Override |
+ public void updateMicButtonState() { |
+ mMicButton.setVisibility(isVoiceSearchEnabled() ? View.VISIBLE : View.GONE); |
+ } |
+ |
+ /** |
+ * Call to force the UI to update the state of various buttons based on whether or not the |
+ * current tab is incognito. |
+ */ |
+ @Override |
+ public void updateVisualsForState() { |
+ if (updateUseDarkColors() || getToolbarDataProvider().isUsingBrandColor()) { |
+ updateSecurityIcon(getSecurityLevel()); |
+ } |
+ ColorStateList colorStateList = getResources().getColorStateList(mUseDarkColors |
+ ? R.color.dark_mode_tint : R.color.light_mode_tint); |
+ mMicButton.setTint(colorStateList); |
+ mDeleteButton.setTint(colorStateList); |
+ |
+ setNavigationButtonType(mNavigationButtonType); |
+ mUrlContainer.setUseDarkTextColors(mUseDarkColors); |
+ |
+ if (mSuggestionList != null) { |
+ mSuggestionList.setBackground(getSuggestionPopupBackground()); |
+ } |
+ mSuggestionListAdapter.setUseDarkColors(mUseDarkColors); |
+ } |
+ |
+ /** |
+ * Checks the current specs and updates {@link LocationBar#mUseDarkColors} if necessary. |
+ * @return Whether {@link LocationBar#mUseDarkColors} has been updated. |
+ */ |
+ private boolean updateUseDarkColors() { |
+ Tab tab = getCurrentTab(); |
+ boolean brandColorNeedsLightText = false; |
+ if (getToolbarDataProvider().isUsingBrandColor() && !mUrlHasFocus) { |
+ int currentPrimaryColor = getToolbarDataProvider().getPrimaryColor(); |
+ brandColorNeedsLightText = |
+ BrandColorUtils.shouldUseLightDrawablesForToolbar(currentPrimaryColor); |
+ } |
+ |
+ boolean useDarkColors = tab == null || !(tab.isIncognito() || brandColorNeedsLightText); |
+ boolean hasChanged = useDarkColors != mUseDarkColors; |
+ mUseDarkColors = useDarkColors; |
+ |
+ return hasChanged; |
+ } |
+ |
+ /** |
+ * Triggers a voice recognition intent to allow the user to specify a search query. |
+ */ |
+ @Override |
+ public void startVoiceRecognition() { |
+ Activity activity = mWindowAndroid.getActivity().get(); |
+ if (activity == null) return; |
+ |
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); |
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, |
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); |
+ intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, |
+ activity.getComponentName().flattenToString()); |
+ intent.putExtra(RecognizerIntent.EXTRA_WEB_SEARCH_ONLY, true); |
+ |
+ if (mWindowAndroid.showCancelableIntent(intent, this, R.string.voice_search_error) < 0) { |
+ // Requery whether or not the recognition intent can be handled. |
+ FeatureUtilities.isRecognitionIntentPresent(activity, false); |
+ updateMicButtonState(); |
+ } |
+ } |
+ |
+ // WindowAndroid.IntentCallback implementation: |
+ @Override |
+ public void onIntentCompleted(WindowAndroid window, int resultCode, |
+ ContentResolver contentResolver, Intent data) { |
+ if (resultCode != Activity.RESULT_OK) return; |
+ if (data.getExtras() == null) return; |
+ |
+ VoiceResult topResult = mAutocomplete.onVoiceResults(data.getExtras()); |
+ if (topResult == null) return; |
+ |
+ String topResultQuery = topResult.getMatch(); |
+ if (TextUtils.isEmpty(topResultQuery)) return; |
+ |
+ if (topResult.getConfidence() < VOICE_SEARCH_CONFIDENCE_NAVIGATE_THRESHOLD) { |
+ setSearchQuery(topResultQuery); |
+ return; |
+ } |
+ |
+ String url = AutocompleteController.nativeQualifyPartialURLQuery(topResultQuery); |
+ if (url == null) { |
+ url = TemplateUrlService.getInstance().getUrlForVoiceSearchQuery( |
+ topResultQuery); |
+ } |
+ loadUrl(url, PageTransition.TYPED); |
+ } |
+ |
+ /** |
+ * Tracks how the URL bar was focused (i.e. from the omnibox or the fakebox) and records a UMA |
+ * stat for this. Should be called whenever the URL bar gains or loses focus. |
+ * @param hasFocus Whether the URL bar now has focus. |
+ */ |
+ private void updateFocusSource(boolean hasFocus) { |
+ if (!hasFocus) { |
+ mUrlFocusedFromFakebox = false; |
+ mHasRecordedUrlFocusSource = false; |
+ return; |
+ } |
+ |
+ // Record UMA event for how the URL bar was focused. |
+ assert !mHasRecordedUrlFocusSource; |
+ if (mHasRecordedUrlFocusSource) return; |
+ |
+ Tab currentTab = getCurrentTab(); |
+ if (currentTab == null) return; |
+ |
+ String url = currentTab.getUrl(); |
+ if (mUrlFocusedFromFakebox) { |
+ RecordUserAction.record("MobileFocusedFakeboxOnNtp"); |
+ } else { |
+ if (currentTab.isNativePage() && NewTabPage.isNTPUrl(url)) { |
+ RecordUserAction.record("MobileFocusedOmniboxOnNtp"); |
+ } else { |
+ RecordUserAction.record("MobileFocusedOmniboxNotOnNtp"); |
+ } |
+ } |
+ mHasRecordedUrlFocusSource = true; |
+ } |
+ |
+ @Override |
+ public void onTabLoadingNTP(NewTabPage ntp) { |
+ ntp.setFakeboxDelegate(this); |
+ } |
+ |
+ @Override |
+ public View getContainerView() { |
+ return this; |
+ } |
+} |