Index: chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f8e14a2abf01741d1a54be948b7717e8584d1b42 |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java |
@@ -0,0 +1,358 @@ |
+// 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 android.animation.ObjectAnimator; |
+import android.annotation.SuppressLint; |
+import android.content.Context; |
+import android.content.res.ColorStateList; |
+import android.graphics.Canvas; |
+import android.graphics.Rect; |
+import android.text.Selection; |
+import android.text.TextUtils; |
+import android.util.AttributeSet; |
+import android.view.KeyEvent; |
+import android.view.MotionEvent; |
+import android.view.TouchDelegate; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.view.WindowManager; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.chrome.browser.Tab; |
+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.ntp.NewTabPage; |
+import org.chromium.chrome.browser.util.FeatureUtilities; |
+import org.chromium.chrome.browser.widget.TintedImageButton; |
+import org.chromium.ui.UiUtils; |
+ |
+/** |
+ * A location bar implementation specific for smaller/phone screens. |
+ */ |
+public class LocationBarPhone extends LocationBarLayout { |
+ private static final int KEYBOARD_MODE_CHANGE_DELAY_MS = 300; |
+ private static final int KEYBOARD_HIDE_DELAY_MS = 150; |
+ |
+ private static final int ACTION_BUTTON_TOUCH_OVERFLOW_LEFT = 15; |
+ |
+ private View mFirstVisibleFocusedView; |
+ private View mIncognitoBadge; |
+ private View mUrlActionsContainer; |
+ private TintedImageButton mMenuButton; |
+ private int mIncognitoBadgePadding; |
+ private boolean mVoiceSearchEnabled; |
+ private boolean mUrlFocusChangeInProgress; |
+ private float mUrlFocusChangePercent; |
+ private Runnable mKeyboardResizeModeTask; |
+ private ObjectAnimator mOmniboxBackgroundAnimator; |
+ |
+ /** |
+ * Constructor used to inflate from XML. |
+ */ |
+ public LocationBarPhone(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ } |
+ |
+ @Override |
+ protected void onFinishInflate() { |
+ super.onFinishInflate(); |
+ |
+ mFirstVisibleFocusedView = findViewById(R.id.url_container); |
+ mIncognitoBadge = findViewById(R.id.incognito_badge); |
+ mIncognitoBadgePadding = |
+ getResources().getDimensionPixelSize(R.dimen.location_bar_incognito_badge_padding); |
+ |
+ mUrlActionsContainer = findViewById(R.id.url_action_container); |
+ Rect delegateArea = new Rect(); |
+ mUrlActionsContainer.getHitRect(delegateArea); |
+ delegateArea.left -= ACTION_BUTTON_TOUCH_OVERFLOW_LEFT; |
+ TouchDelegate touchDelegate = new TouchDelegate(delegateArea, mUrlActionsContainer); |
+ assert mUrlActionsContainer.getParent() == this; |
+ setTouchDelegate(touchDelegate); |
+ |
+ mMenuButton = (TintedImageButton) findViewById(R.id.document_menu_button); |
+ |
+ if (hasVisibleViewsAfterUrlBarWhenUnfocused()) mUrlActionsContainer.setVisibility(VISIBLE); |
+ if (!showMenuButtonInOmnibox()) { |
+ ((ViewGroup) mMenuButton.getParent()).removeView(mMenuButton); |
+ } |
+ } |
+ |
+ @Override |
+ public void setMenuButtonHelper(final AppMenuButtonHelper helper) { |
+ super.setMenuButtonHelper(helper); |
+ mMenuButton.setOnTouchListener(new OnTouchListener() { |
+ @SuppressLint("ClickableViewAccessibility") |
+ @Override |
+ public boolean onTouch(View v, MotionEvent event) { |
+ return helper.onTouch(v, event); |
+ } |
+ }); |
+ mMenuButton.setOnKeyListener(new OnKeyListener() { |
+ @Override |
+ public boolean onKey(View view, int keyCode, KeyEvent event) { |
+ if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { |
+ return helper.onEnterKeyPress(view); |
+ } |
+ return false; |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ public View getMenuAnchor() { |
+ return mMenuButton; |
+ } |
+ |
+ @Override |
+ public void getContentRect(Rect outRect) { |
+ super.getContentRect(outRect); |
+ if (mIncognitoBadge.getVisibility() == View.GONE) return; |
+ |
+ if (!ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ outRect.left += mIncognitoBadge.getWidth(); |
+ } else { |
+ outRect.right -= mIncognitoBadge.getWidth(); |
+ } |
+ } |
+ |
+ /** |
+ * @return The first view visible when the location bar is focused. |
+ */ |
+ public View getFirstViewVisibleWhenFocused() { |
+ return mFirstVisibleFocusedView; |
+ } |
+ |
+ /** |
+ * @return Whether there are visible views that are aligned following the Url Bar when it |
+ * does not have foucs. |
+ */ |
+ public boolean hasVisibleViewsAfterUrlBarWhenUnfocused() { |
+ return showMenuButtonInOmnibox(); |
+ } |
+ |
+ /** |
+ * @return Whether the menu should be shown in the omnibox instead of outside of it. |
+ */ |
+ public boolean showMenuButtonInOmnibox() { |
+ return FeatureUtilities.isDocumentMode(getContext()); |
+ } |
+ |
+ /** |
+ * Updates percentage of current the URL focus change animation. |
+ * @param percent 1.0 is 100% focused, 0 is completely unfocused. |
+ */ |
+ public void setUrlFocusChangePercent(float percent) { |
+ mUrlFocusChangePercent = percent; |
+ |
+ if (percent > 0f && !hasVisibleViewsAfterUrlBarWhenUnfocused()) { |
+ mUrlActionsContainer.setVisibility(VISIBLE); |
+ } |
+ |
+ mDeleteButton.setAlpha(percent); |
+ mMicButton.setAlpha(percent); |
+ if (showMenuButtonInOmnibox()) mMenuButton.setAlpha(1f - percent); |
+ |
+ updateDeleteButtonVisibility(); |
+ } |
+ |
+ @Override |
+ public void onUrlFocusChange(boolean hasFocus) { |
+ if (mOmniboxBackgroundAnimator != null && mOmniboxBackgroundAnimator.isRunning()) { |
+ mOmniboxBackgroundAnimator.cancel(); |
+ mOmniboxBackgroundAnimator = null; |
+ } |
+ if (hasFocus) { |
+ // Remove the focus of this view once the URL field has taken focus as this view no |
+ // longer needs it. |
+ setFocusable(false); |
+ setFocusableInTouchMode(false); |
+ } |
+ mUrlFocusChangeInProgress = true; |
+ super.onUrlFocusChange(hasFocus); |
+ } |
+ |
+ @Override |
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { |
+ boolean needsCanvasRestore = false; |
+ if (child == mUrlContainer && mUrlActionsContainer.getVisibility() == VISIBLE) { |
+ canvas.save(); |
+ |
+ // Clip the URL bar contents to ensure they do not draw under the URL actions during |
+ // focus animations. Based on the RTL state of the location bar, the url actions |
+ // container can be on the left or right side, so clip accordingly. |
+ if (mUrlContainer.getLeft() < mUrlActionsContainer.getLeft()) { |
+ canvas.clipRect(0, 0, (int) mUrlActionsContainer.getX(), getBottom()); |
+ } else { |
+ canvas.clipRect(mUrlActionsContainer.getX() + mUrlActionsContainer.getWidth(), |
+ 0, getWidth(), getBottom()); |
+ } |
+ needsCanvasRestore = true; |
+ } |
+ boolean retVal = super.drawChild(canvas, child, drawingTime); |
+ if (needsCanvasRestore) { |
+ canvas.restore(); |
+ } |
+ return retVal; |
+ } |
+ |
+ /** |
+ * Handles any actions to be performed after all other actions triggered by the URL focus |
+ * change. This will be called after any animations are performed to transition from one |
+ * focus state to the other. |
+ * @param hasFocus Whether the URL field has gained focus. |
+ */ |
+ public void finishUrlFocusChange(boolean hasFocus) { |
+ final WindowDelegate windowDelegate = getWindowDelegate(); |
+ if (!hasFocus) { |
+ // Remove the selection from the url text. The ending selection position |
+ // will determine the scroll position when the url field is restored. If |
+ // we do not clear this, it will scroll to the end of the text when you |
+ // enter/exit the tab stack. |
+ // We set the selection to 0 instead of removing the selection to avoid a crash that |
+ // happens if you clear the selection instead. |
+ // |
+ // Triggering the bug happens by: |
+ // 1.) Selecting some portion of the URL (where the two selection handles |
+ // appear) |
+ // 2.) Trigger a text change in the URL bar (i.e. by triggering a new URL load |
+ // by a command line intent) |
+ // 3.) Simultaneously moving one of the selection handles left and right. This will |
+ // occasionally throw an AssertionError on the bounds of the selection. |
+ Selection.setSelection(mUrlBar.getText(), 0); |
+ |
+ // The animation rendering may not yet be 100% complete and hiding the keyboard makes |
+ // the animation quite choppy. |
+ postDelayed(new Runnable() { |
+ @Override |
+ public void run() { |
+ UiUtils.hideKeyboard(mUrlBar); |
+ } |
+ }, KEYBOARD_HIDE_DELAY_MS); |
+ // Convert the keyboard back to resize mode (delay the change for an arbitrary amount |
+ // of time in hopes the keyboard will be completely hidden before making this change). |
+ if (mKeyboardResizeModeTask == null |
+ && windowDelegate.getWindowSoftInputMode() |
+ != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { |
+ mKeyboardResizeModeTask = new Runnable() { |
+ @Override |
+ public void run() { |
+ windowDelegate.setWindowSoftInputMode( |
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); |
+ mKeyboardResizeModeTask = null; |
+ } |
+ }; |
+ postDelayed(mKeyboardResizeModeTask, KEYBOARD_MODE_CHANGE_DELAY_MS); |
+ } |
+ if (!hasVisibleViewsAfterUrlBarWhenUnfocused()) { |
+ mUrlActionsContainer.setVisibility(GONE); |
+ } |
+ } else { |
+ if (mKeyboardResizeModeTask != null) { |
+ removeCallbacks(mKeyboardResizeModeTask); |
+ mKeyboardResizeModeTask = null; |
+ } |
+ if (windowDelegate.getWindowSoftInputMode() |
+ != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) { |
+ windowDelegate.setWindowSoftInputMode( |
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); |
+ } |
+ UiUtils.showKeyboard(mUrlBar); |
+ // As the position of the navigation icon has changed, ensure the suggestions are |
+ // updated to reflect the new position. |
+ if (getSuggestionList() != null && getSuggestionList().isShown()) { |
+ getSuggestionList().invalidateSuggestionViews(); |
+ } |
+ } |
+ mUrlFocusChangeInProgress = false; |
+ updateDeleteButtonVisibility(); |
+ |
+ NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); |
+ if (hasFocus && ntp != null && ntp.isLocationBarShownInNTP()) { |
+ fadeInOmniboxResultsContainerBackground(); |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean shouldShowDeleteButton() { |
+ boolean hasText = !TextUtils.isEmpty(mUrlBar.getText()); |
+ return hasText && (mUrlBar.hasFocus() || mUrlFocusChangeInProgress); |
+ } |
+ |
+ @Override |
+ protected void updateDeleteButtonVisibility() { |
+ boolean enabled = shouldShowDeleteButton(); |
+ mDeleteButton.setEnabled(enabled); |
+ mDeleteButton.setVisibility(enabled ? VISIBLE : INVISIBLE); |
+ |
+ boolean showMicButton = mVoiceSearchEnabled && !enabled |
+ && (mUrlBar.hasFocus() || mUrlFocusChangeInProgress |
+ || mUrlFocusChangePercent > 0f); |
+ mMicButton.setVisibility(showMicButton ? VISIBLE : INVISIBLE); |
+ } |
+ |
+ @Override |
+ public void updateMicButtonState() { |
+ mVoiceSearchEnabled = isVoiceSearchEnabled(); |
+ mMicButton.setVisibility(mVoiceSearchEnabled && mUrlBar.hasFocus() |
+ && mDeleteButton.getVisibility() != VISIBLE ? VISIBLE : INVISIBLE); |
+ } |
+ |
+ @Override |
+ protected void updateLocationBarIconContainerVisibility() { |
+ super.updateLocationBarIconContainerVisibility(); |
+ updateIncognitoBadgePadding(); |
+ } |
+ |
+ private void updateIncognitoBadgePadding() { |
+ // This can be triggered in the super.onFinishInflate, so we need to null check in this |
+ // place only. |
+ if (mIncognitoBadge == null) return; |
+ |
+ if (findViewById(R.id.location_bar_icon).getVisibility() == GONE) { |
+ ApiCompatibilityUtils.setPaddingRelative( |
+ mIncognitoBadge, 0, 0, mIncognitoBadgePadding, 0); |
+ } else { |
+ ApiCompatibilityUtils.setPaddingRelative(mIncognitoBadge, 0, 0, 0, 0); |
+ } |
+ } |
+ |
+ @Override |
+ public void updateVisualsForState() { |
+ super.updateVisualsForState(); |
+ |
+ Tab tab = getCurrentTab(); |
+ boolean isIncognito = tab != null && tab.isIncognito(); |
+ mIncognitoBadge.setVisibility(isIncognito ? VISIBLE : GONE); |
+ updateIncognitoBadgePadding(); |
+ |
+ if (showMenuButtonInOmnibox()) { |
+ boolean useLightDrawables = isIncognito; |
+ if (getToolbarDataProvider().isUsingBrandColor()) { |
+ int currentPrimaryColor = getToolbarDataProvider().getPrimaryColor(); |
+ useLightDrawables |= |
+ BrandColorUtils.shouldUseLightDrawablesForToolbar(currentPrimaryColor); |
+ } |
+ ColorStateList dark = getResources().getColorStateList(R.color.dark_mode_tint); |
+ ColorStateList white = getResources().getColorStateList(R.color.light_mode_tint); |
+ mMenuButton.setTint(useLightDrawables ? white : dark); |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean shouldAnimateIconChanges() { |
+ return super.shouldAnimateIconChanges() || mUrlFocusChangeInProgress; |
+ } |
+ |
+ @Override |
+ public void setLayoutDirection(int layoutDirection) { |
+ super.setLayoutDirection(layoutDirection); |
+ updateIncognitoBadgePadding(); |
+ } |
+} |